aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs
diff options
context:
space:
mode:
authorthegeorg <thegeorg@yandex-team.com>2023-10-03 11:19:48 +0300
committerthegeorg <thegeorg@yandex-team.com>2023-10-03 11:43:28 +0300
commitcda0c13f23f6b169fb0a49dc504b40a0aaecea09 (patch)
tree26476e92e5af2c856e017afb1df8f8dff42495bf /contrib/libs
parent4854116da9c5e3c95bb8440f2ea997c54b6e1a61 (diff)
downloadydb-cda0c13f23f6b169fb0a49dc504b40a0aaecea09.tar.gz
Move contrib/tools/jdk to build/platform/java/jdk/testing
Diffstat (limited to 'contrib/libs')
-rw-r--r--contrib/libs/blake2/COPYING117
-rw-r--r--contrib/libs/blake2/README.md14
-rw-r--r--contrib/libs/blake2/include/blake2.h1
-rw-r--r--contrib/libs/blake2/src/blake2-config.h71
-rw-r--r--contrib/libs/blake2/src/blake2-dispatch.c577
-rw-r--r--contrib/libs/blake2/src/blake2-impl.h160
-rw-r--r--contrib/libs/blake2/src/blake2.h182
-rw-r--r--contrib/libs/blake2/src/blake2b-load-sse2.h68
-rw-r--r--contrib/libs/blake2/src/blake2b-load-sse41.h402
-rw-r--r--contrib/libs/blake2/src/blake2b-ref.c386
-rw-r--r--contrib/libs/blake2/src/blake2b-round.h160
-rw-r--r--contrib/libs/blake2/src/blake2b.c443
-rw-r--r--contrib/libs/blake2/src/blake2bp.c274
-rw-r--r--contrib/libs/blake2/src/blake2s-load-sse2.h59
-rw-r--r--contrib/libs/blake2/src/blake2s-load-sse41.h229
-rw-r--r--contrib/libs/blake2/src/blake2s-load-xop.h189
-rw-r--r--contrib/libs/blake2/src/blake2s-ref.c375
-rw-r--r--contrib/libs/blake2/src/blake2s-round.h91
-rw-r--r--contrib/libs/blake2/src/blake2s.c422
-rw-r--r--contrib/libs/blake2/src/blake2sp.c274
-rw-r--r--contrib/libs/blake2/src/config.h157
-rw-r--r--contrib/libs/blake2/ya.make49
-rw-r--r--contrib/libs/libarchive/CONTRIBUTING.md98
-rw-r--r--contrib/libs/libarchive/COPYING65
-rw-r--r--contrib/libs/libarchive/INSTALL35
-rw-r--r--contrib/libs/libarchive/NEWS755
-rw-r--r--contrib/libs/libarchive/README.md244
-rw-r--r--contrib/libs/libarchive/SECURITY.md19
-rw-r--r--contrib/libs/libarchive/config-linux.h1396
-rw-r--r--contrib/libs/libarchive/config-osx.h42
-rw-r--r--contrib/libs/libarchive/config-win.h121
-rw-r--r--contrib/libs/libarchive/config.h9
-rw-r--r--contrib/libs/libarchive/libarchive/archive.h1212
-rw-r--r--contrib/libs/libarchive/libarchive/archive_acl.c2097
-rw-r--r--contrib/libs/libarchive/libarchive/archive_acl_private.h83
-rw-r--r--contrib/libs/libarchive/libarchive/archive_check_magic.c175
-rw-r--r--contrib/libs/libarchive/libarchive/archive_cmdline.c227
-rw-r--r--contrib/libs/libarchive/libarchive/archive_cmdline_private.h47
-rw-r--r--contrib/libs/libarchive/libarchive/archive_cryptor.c534
-rw-r--r--contrib/libs/libarchive/libarchive/archive_cryptor_private.h188
-rw-r--r--contrib/libs/libarchive/libarchive/archive_digest.c1565
-rw-r--r--contrib/libs/libarchive/libarchive/archive_digest_private.h426
-rw-r--r--contrib/libs/libarchive/libarchive/archive_endian.h195
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry.c2149
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry.h723
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_copy_bhfi.c75
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_copy_stat.c83
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_link_resolver.c447
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_locale.h92
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_private.h200
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_sparse.c156
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_stat.c118
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_strmode.c87
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_xattr.c156
-rw-r--r--contrib/libs/libarchive/libarchive/archive_getdate.c1104
-rw-r--r--contrib/libs/libarchive/libarchive/archive_getdate.h39
-rw-r--r--contrib/libs/libarchive/libarchive/archive_hmac.c339
-rw-r--r--contrib/libs/libarchive/libarchive/archive_hmac_private.h119
-rw-r--r--contrib/libs/libarchive/libarchive/archive_match.c1875
-rw-r--r--contrib/libs/libarchive/libarchive/archive_openssl_evp_private.h54
-rw-r--r--contrib/libs/libarchive/libarchive/archive_openssl_hmac_private.h54
-rw-r--r--contrib/libs/libarchive/libarchive/archive_options.c218
-rw-r--r--contrib/libs/libarchive/libarchive/archive_options_private.h51
-rw-r--r--contrib/libs/libarchive/libarchive/archive_pack_dev.c337
-rw-r--r--contrib/libs/libarchive/libarchive/archive_pack_dev.h49
-rw-r--r--contrib/libs/libarchive/libarchive/archive_pathmatch.c463
-rw-r--r--contrib/libs/libarchive/libarchive/archive_pathmatch.h52
-rw-r--r--contrib/libs/libarchive/libarchive/archive_platform.h233
-rw-r--r--contrib/libs/libarchive/libarchive/archive_platform_acl.h55
-rw-r--r--contrib/libs/libarchive/libarchive/archive_ppmd7.c1168
-rw-r--r--contrib/libs/libarchive/libarchive/archive_ppmd7_private.h119
-rw-r--r--contrib/libs/libarchive/libarchive/archive_ppmd8.c1287
-rw-r--r--contrib/libs/libarchive/libarchive/archive_ppmd8_private.h148
-rw-r--r--contrib/libs/libarchive/libarchive/archive_ppmd_private.h151
-rw-r--r--contrib/libs/libarchive/libarchive/archive_private.h180
-rw-r--r--contrib/libs/libarchive/libarchive/archive_random.c301
-rw-r--r--contrib/libs/libarchive/libarchive/archive_random_private.h36
-rw-r--r--contrib/libs/libarchive/libarchive/archive_rb.c709
-rw-r--r--contrib/libs/libarchive/libarchive/archive_rb.h113
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read.c1756
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_add_passphrase.c190
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_append_filter.c204
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_data_into_fd.c144
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_disk_entry_from_file.c1086
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_disk_posix.c2760
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_disk_private.h98
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_disk_set_standard_lookup.c313
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_disk_windows.c2547
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_extract.c60
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_extract2.c155
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_open_fd.c211
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_open_file.c180
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_open_filename.c586
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_open_memory.c186
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_private.h267
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_set_format.c117
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_set_options.c133
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_all.c85
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_by_code.c83
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_bzip2.c365
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_compress.c452
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_grzip.c112
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_gzip.c536
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_lrzip.c122
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_lz4.c742
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_lzop.c499
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_none.c52
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_program.c496
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_rpm.c287
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_uu.c683
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_xz.c793
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_zstd.c297
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_7zip.c4074
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_all.c89
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_ar.c638
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_by_code.c92
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_cab.c3228
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_cpio.c1104
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_empty.c96
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_iso9660.c3279
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_lha.c2919
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_mtree.c2156
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_rar.c3788
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_rar5.c4251
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_raw.c192
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_tar.c2946
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_warc.c848
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_xar.c3332
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_zip.c4270
-rw-r--r--contrib/libs/libarchive/libarchive/archive_string.c4244
-rw-r--r--contrib/libs/libarchive/libarchive/archive_string.h243
-rw-r--r--contrib/libs/libarchive/libarchive/archive_string_composition.h2292
-rw-r--r--contrib/libs/libarchive/libarchive/archive_string_sprintf.c192
-rw-r--r--contrib/libs/libarchive/libarchive/archive_util.c704
-rw-r--r--contrib/libs/libarchive/libarchive/archive_version_details.c151
-rw-r--r--contrib/libs/libarchive/libarchive/archive_virtual.c163
-rw-r--r--contrib/libs/libarchive/libarchive/archive_windows.c938
-rw-r--r--contrib/libs/libarchive/libarchive/archive_windows.h315
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write.c859
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter.c72
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_b64encode.c304
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_by_name.c77
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_bzip2.c401
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_compress.c447
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_grzip.c135
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_gzip.c442
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_lrzip.c197
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_lz4.c700
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_lzop.c478
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_none.c43
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_program.c391
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_uuencode.c295
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_xz.c545
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_zstd.c467
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_disk_posix.c4759
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_disk_private.h45
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_disk_set_standard_lookup.c263
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_disk_windows.c2911
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_open_fd.c146
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_open_file.c111
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_open_filename.c270
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_open_memory.c115
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_private.h166
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format.c125
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_7zip.c2322
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_ar.c570
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_by_name.c94
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_cpio.c11
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_binary.c610
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_newc.c457
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_odc.c500
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_filter_by_ext.c142
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_gnutar.c755
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_iso9660.c8161
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_mtree.c2217
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_pax.c2063
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_private.h42
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_raw.c125
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_shar.c641
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_ustar.c758
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_v7tar.c638
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_warc.c444
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_xar.c3255
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_zip.c1693
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_options.c130
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_passphrase.c95
-rw-r--r--contrib/libs/libarchive/libarchive/archive_xxhash.h48
-rw-r--r--contrib/libs/libarchive/libarchive/filter_fork.h46
-rw-r--r--contrib/libs/libarchive/libarchive/filter_fork_posix.c240
-rw-r--r--contrib/libs/libarchive/libarchive/filter_fork_windows.c245
-rw-r--r--contrib/libs/libarchive/libarchive/xxhash.c529
-rw-r--r--contrib/libs/libarchive/ya.make183
-rw-r--r--contrib/libs/linux-headers/linux/fiemap.h70
-rw-r--r--contrib/libs/pcre2/AUTHORS36
-rw-r--r--contrib/libs/pcre2/COPYING5
-rw-r--r--contrib/libs/pcre2/ChangeLog2827
-rw-r--r--contrib/libs/pcre2/INSTALL368
-rw-r--r--contrib/libs/pcre2/NEWS436
-rw-r--r--contrib/libs/pcre2/README924
-rw-r--r--contrib/libs/pcre2/README.md56
-rw-r--r--contrib/libs/pcre2/pcre2.h993
-rw-r--r--contrib/libs/pcre2/src/pcre2_auto_possess.c1365
-rw-r--r--contrib/libs/pcre2/src/pcre2_chartables.c202
-rw-r--r--contrib/libs/pcre2/src/pcre2_compile.c10631
-rw-r--r--contrib/libs/pcre2/src/pcre2_config.c252
-rw-r--r--contrib/libs/pcre2/src/pcre2_config.h446
-rw-r--r--contrib/libs/pcre2/src/pcre2_context.c494
-rw-r--r--contrib/libs/pcre2/src/pcre2_convert.c1181
-rw-r--r--contrib/libs/pcre2/src/pcre2_dfa_match.c4066
-rw-r--r--contrib/libs/pcre2/src/pcre2_error.c341
-rw-r--r--contrib/libs/pcre2/src/pcre2_extuni.c148
-rw-r--r--contrib/libs/pcre2/src/pcre2_find_bracket.c219
-rw-r--r--contrib/libs/pcre2/src/pcre2_internal.h2047
-rw-r--r--contrib/libs/pcre2/src/pcre2_intmodedep.h934
-rw-r--r--contrib/libs/pcre2/src/pcre2_jit_compile.c14507
-rw-r--r--contrib/libs/pcre2/src/pcre2_jit_match.c186
-rw-r--r--contrib/libs/pcre2/src/pcre2_jit_misc.c234
-rw-r--r--contrib/libs/pcre2/src/pcre2_jit_simd_inc.h1858
-rw-r--r--contrib/libs/pcre2/src/pcre2_maketables.c163
-rw-r--r--contrib/libs/pcre2/src/pcre2_match.c7544
-rw-r--r--contrib/libs/pcre2/src/pcre2_match_data.c173
-rw-r--r--contrib/libs/pcre2/src/pcre2_newline.c243
-rw-r--r--contrib/libs/pcre2/src/pcre2_ord2utf.c120
-rw-r--r--contrib/libs/pcre2/src/pcre2_pattern_info.c432
-rw-r--r--contrib/libs/pcre2/src/pcre2_script_run.c344
-rw-r--r--contrib/libs/pcre2/src/pcre2_serialize.c286
-rw-r--r--contrib/libs/pcre2/src/pcre2_string_utils.c237
-rw-r--r--contrib/libs/pcre2/src/pcre2_study.c1825
-rw-r--r--contrib/libs/pcre2/src/pcre2_substitute.c1009
-rw-r--r--contrib/libs/pcre2/src/pcre2_substring.c547
-rw-r--r--contrib/libs/pcre2/src/pcre2_tables.c234
-rw-r--r--contrib/libs/pcre2/src/pcre2_ucd.c5396
-rw-r--r--contrib/libs/pcre2/src/pcre2_ucp.h394
-rw-r--r--contrib/libs/pcre2/src/pcre2_ucptables.c1524
-rw-r--r--contrib/libs/pcre2/src/pcre2_valid_utf.c398
-rw-r--r--contrib/libs/pcre2/src/pcre2_xclass.c289
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitConfig.h162
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitConfigInternal.h851
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitExecAllocator.c411
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitLir.c3136
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitLir.h1823
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeARM_32.c3700
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeARM_64.c2417
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeARM_T2_32.c3150
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeMIPS_32.c314
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeMIPS_64.c319
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeMIPS_common.c3720
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativePPC_32.c340
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativePPC_64.c579
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativePPC_common.c2851
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeRISCV_32.c73
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeRISCV_64.c183
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeRISCV_common.c2762
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeS390X.c3747
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeX86_32.c1298
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeX86_64.c1092
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeX86_common.c3422
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitProtExecAllocator.c474
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitUtils.c344
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitWXExecAllocator.c204
-rw-r--r--contrib/libs/pcre2/ya.make70
261 files changed, 236118 insertions, 0 deletions
diff --git a/contrib/libs/blake2/COPYING b/contrib/libs/blake2/COPYING
new file mode 100644
index 00000000000..2c4afabdb63
--- /dev/null
+++ b/contrib/libs/blake2/COPYING
@@ -0,0 +1,117 @@
+CC0 1.0 Universal
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator and
+subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for the
+purpose of contributing to a commons of creative, cultural and scientific
+works ("Commons") that the public can reliably and without fear of later
+claims of infringement build upon, modify, incorporate in other works, reuse
+and redistribute as freely as possible in any form whatsoever and for any
+purposes, including without limitation commercial purposes. These owners may
+contribute to the Commons to promote the ideal of a free culture and the
+further production of creative, cultural and scientific works, or to gain
+reputation or greater distribution for their Work in part through the use and
+efforts of others.
+
+For these and/or other purposes and motivations, and without any expectation
+of additional consideration or compensation, the person associating CC0 with a
+Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
+and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
+and publicly distribute the Work under its terms, with knowledge of his or her
+Copyright and Related Rights in the Work and the meaning and intended legal
+effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not limited
+to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display, communicate,
+ and translate a Work;
+
+ ii. moral rights retained by the original author(s) and/or performer(s);
+
+ iii. publicity and privacy rights pertaining to a person's image or likeness
+ depicted in a Work;
+
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+
+ v. rights protecting the extraction, dissemination, use and reuse of data in
+ a Work;
+
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation thereof,
+ including any amended or successor version of such directive); and
+
+ vii. other similar, equivalent or corresponding rights throughout the world
+ based on applicable law or treaty, and any national implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention of,
+applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
+unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
+and Related Rights and associated claims and causes of action, whether now
+known or unknown (including existing as well as future claims and causes of
+action), in the Work (i) in all territories worldwide, (ii) for the maximum
+duration provided by applicable law or treaty (including future time
+extensions), (iii) in any current or future medium and for any number of
+copies, and (iv) for any purpose whatsoever, including without limitation
+commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
+the Waiver for the benefit of each member of the public at large and to the
+detriment of Affirmer's heirs and successors, fully intending that such Waiver
+shall not be subject to revocation, rescission, cancellation, termination, or
+any other legal or equitable action to disrupt the quiet enjoyment of the Work
+by the public as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason be
+judged legally invalid or ineffective under applicable law, then the Waiver
+shall be preserved to the maximum extent permitted taking into account
+Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
+is so judged Affirmer hereby grants to each affected person a royalty-free,
+non transferable, non sublicensable, non exclusive, irrevocable and
+unconditional license to exercise Affirmer's Copyright and Related Rights in
+the Work (i) in all territories worldwide, (ii) for the maximum duration
+provided by applicable law or treaty (including future time extensions), (iii)
+in any current or future medium and for any number of copies, and (iv) for any
+purpose whatsoever, including without limitation commercial, advertising or
+promotional purposes (the "License"). The License shall be deemed effective as
+of the date CC0 was applied by Affirmer to the Work. Should any part of the
+License for any reason be judged legally invalid or ineffective under
+applicable law, such partial invalidity or ineffectiveness shall not
+invalidate the remainder of the License, and in such case Affirmer hereby
+affirms that he or she will not (i) exercise any of his or her remaining
+Copyright and Related Rights in the Work or (ii) assert any associated claims
+and causes of action with respect to the Work, in either case contrary to
+Affirmer's express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+
+ b. Affirmer offers the Work as-is and makes no representations or warranties
+ of any kind concerning the Work, express, implied, statutory or otherwise,
+ including without limitation warranties of title, merchantability, fitness
+ for a particular purpose, non infringement, or the absence of latent or
+ other defects, accuracy, or the present or absence of errors, whether or not
+ discoverable, all to the greatest extent permissible under applicable law.
+
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without limitation
+ any person's Copyright and Related Rights in the Work. Further, Affirmer
+ disclaims responsibility for obtaining any necessary consents, permissions
+ or other rights required for any use of the Work.
+
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to this
+ CC0 or use of the Work.
+
+For more information, please see
+<http://creativecommons.org/publicdomain/zero/1.0/>
+
diff --git a/contrib/libs/blake2/README.md b/contrib/libs/blake2/README.md
new file mode 100644
index 00000000000..17faa8f4d5c
--- /dev/null
+++ b/contrib/libs/blake2/README.md
@@ -0,0 +1,14 @@
+# libb2
+
+C library providing BLAKE2b, BLAKE2s, BLAKE2bp, BLAKE2sp
+
+Installation:
+
+```
+$ ./autogen.sh
+$ ./configure
+$ make
+$ sudo make install
+```
+
+Contact: contact@blake2.net
diff --git a/contrib/libs/blake2/include/blake2.h b/contrib/libs/blake2/include/blake2.h
new file mode 100644
index 00000000000..731ebdc993a
--- /dev/null
+++ b/contrib/libs/blake2/include/blake2.h
@@ -0,0 +1 @@
+#include "../src/blake2.h" /* inclink generated by yamaker */
diff --git a/contrib/libs/blake2/src/blake2-config.h b/contrib/libs/blake2/src/blake2-config.h
new file mode 100644
index 00000000000..c09cb4bcf06
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2-config.h
@@ -0,0 +1,71 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2_CONFIG_H__
+#define __BLAKE2_CONFIG_H__
+
+#if defined(__SSE2__)
+#define HAVE_SSE2
+#endif
+
+#if defined(__SSSE3__)
+#define HAVE_SSSE3
+#endif
+
+#if defined(__SSE4_1__)
+#define HAVE_SSE4_1
+#endif
+
+#if defined(__AVX__)
+#define HAVE_AVX
+#endif
+
+#if defined(__XOP__)
+#define HAVE_XOP
+#endif
+
+
+#ifdef HAVE_AVX2
+#ifndef HAVE_AVX
+#define HAVE_AVX
+#endif
+#endif
+
+#ifdef HAVE_XOP
+#ifndef HAVE_AVX
+#define HAVE_AVX
+#endif
+#endif
+
+#ifdef HAVE_AVX
+#ifndef HAVE_SSE4_1
+#define HAVE_SSE4_1
+#endif
+#endif
+
+#ifdef HAVE_SSE4_1
+#ifndef HAVE_SSSE3
+#define HAVE_SSSE3
+#endif
+#endif
+
+#ifdef HAVE_SSSE3
+#define HAVE_SSE2
+#endif
+
+#if !defined(HAVE_SSE2)
+#error "This code requires at least SSE2."
+#endif
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2-dispatch.c b/contrib/libs/blake2/src/blake2-dispatch.c
new file mode 100644
index 00000000000..96bb3408bb1
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2-dispatch.c
@@ -0,0 +1,577 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#include <stdio.h>
+#if defined(WIN32)
+#include <windows.h>
+#endif
+#include "blake2.h"
+
+#if defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64)
+#define HAVE_X86
+#endif
+
+typedef enum
+{
+ NONE = 0,
+#if defined(HAVE_X86)
+ SSE2 = 1,
+ SSSE3 = 2,
+ SSE41 = 3,
+ AVX = 4,
+ XOP = 5,
+ /* AVX2 = 6, */
+#endif
+} cpu_feature_t;
+
+static const char feature_names[][8] =
+{
+ "none",
+#if defined(HAVE_X86)
+ "sse2",
+ "ssse3",
+ "sse41",
+ "avx",
+ "xop",
+ /* "avx2" */
+#endif
+};
+
+#if defined(HAVE_X86)
+
+#if defined(__GNUC__)
+static inline void cpuid( uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx )
+{
+ __asm__ __volatile__(
+#if defined(__i386__) /* This is needed for -fPIC to work on i386 */
+ "movl %%ebx, %%esi\n\t"
+#endif
+ "cpuid\n\t"
+#if defined(__i386__)
+ "xchgl %%ebx, %%esi\n\t"
+ : "=a"( *eax ), "=S"( *ebx ), "=c"( *ecx ), "=d"( *edx ) : "a"( *eax ) );
+#else
+ : "=a"( *eax ), "=b"( *ebx ), "=c"( *ecx ), "=d"( *edx ) : "a"( *eax ) );
+#endif
+}
+
+static inline uint64_t xgetbv(uint32_t xcr)
+{
+ uint32_t a, d;
+ __asm__ __volatile__(
+ "xgetbv"
+ : "=a"(a),"=d"(d)
+ : "c"(xcr)
+ );
+ return ((uint64_t)d << 32) | a;
+}
+
+#elif defined(_MSC_VER)
+#include <intrin.h>
+static inline void cpuid( uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx )
+{
+ int regs[4];
+ __cpuid( regs, *eax );
+ *eax = regs[0];
+ *ebx = regs[1];
+ *ecx = regs[2];
+ *edx = regs[3];
+}
+#else
+#error "Don't know how to call cpuid on this compiler!"
+#endif
+
+#endif /* HAVE_X86 */
+
+static inline cpu_feature_t get_cpu_features( void )
+{
+#if defined(HAVE_X86)
+ static volatile int initialized = 0;
+ static cpu_feature_t feature = NONE; // Safe default
+ uint32_t eax, ecx, edx, ebx;
+
+ if( initialized )
+ return feature;
+
+ eax = 1;
+ cpuid( &eax, &ebx, &ecx, &edx );
+
+ if( 1 & ( edx >> 26 ) )
+ feature = SSE2;
+
+ if( 1 & ( ecx >> 9 ) )
+ feature = SSSE3;
+
+ if( 1 & ( ecx >> 19 ) )
+ feature = SSE41;
+
+#if defined(WIN32) /* Work around the fact that Windows <7 does NOT support AVX... */
+ if( IsProcessorFeaturePresent(17) ) /* Some environments don't know about PF_XSAVE_ENABLED */
+#endif
+ {
+ /* check for AVX and OSXSAVE bits */
+ if( 1 & ( ecx >> 28 ) & (ecx >> 27) ) {
+#if !defined(WIN32) /* Already checked for this in WIN32 */
+ if( (xgetbv(0) & 6) == 6 ) /* XCR0 */
+#endif
+ feature = AVX;
+ }
+
+
+ eax = 0x80000001;
+ cpuid( &eax, &ebx, &ecx, &edx );
+
+ if( 1 & ( ecx >> 11 ) )
+ feature = XOP;
+ }
+
+ /* For future architectures */
+ /*
+ eax = 7; ecx = 0;
+ cpuid(&eax, &ebx, &ecx, &edx);
+
+ if(1&(ebx >> 5))
+ feature = AVX2;
+ */
+ /* fprintf( stderr, "Using %s engine\n", feature_names[feature] ); */
+ initialized = 1;
+ return feature;
+#else
+ return NONE;
+#endif
+}
+
+
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+ int blake2b_init_ref( blake2b_state *S, size_t outlen );
+ int blake2b_init_key_ref( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_init_param_ref( blake2b_state *S, const blake2b_param *P );
+ int blake2b_update_ref( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final_ref( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b_ref( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+#if defined(HAVE_X86)
+
+ int blake2b_init_sse2( blake2b_state *S, size_t outlen );
+ int blake2b_init_key_sse2( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_init_param_sse2( blake2b_state *S, const blake2b_param *P );
+ int blake2b_update_sse2( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final_sse2( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b_sse2( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2b_init_ssse3( blake2b_state *S, size_t outlen );
+ int blake2b_init_key_ssse3( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_init_param_ssse3( blake2b_state *S, const blake2b_param *P );
+ int blake2b_update_ssse3( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final_ssse3( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b_ssse3( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2b_init_sse41( blake2b_state *S, size_t outlen );
+ int blake2b_init_key_sse41( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_init_param_sse41( blake2b_state *S, const blake2b_param *P );
+ int blake2b_update_sse41( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final_sse41( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b_sse41( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2b_init_avx( blake2b_state *S, size_t outlen );
+ int blake2b_init_key_avx( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_init_param_avx( blake2b_state *S, const blake2b_param *P );
+ int blake2b_update_avx( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final_avx( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b_avx( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2b_init_xop( blake2b_state *S, size_t outlen );
+ int blake2b_init_key_xop( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_init_param_xop( blake2b_state *S, const blake2b_param *P );
+ int blake2b_update_xop( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final_xop( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b_xop( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+#endif /* HAVE_X86 */
+
+ int blake2s_init_ref( blake2s_state *S, size_t outlen );
+ int blake2s_init_key_ref( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_init_param_ref( blake2s_state *S, const blake2s_param *P );
+ int blake2s_update_ref( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final_ref( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s_ref( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+#if defined(HAVE_X86)
+
+ int blake2s_init_sse2( blake2s_state *S, size_t outlen );
+ int blake2s_init_key_sse2( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_init_param_sse2( blake2s_state *S, const blake2s_param *P );
+ int blake2s_update_sse2( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final_sse2( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s_sse2( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2s_init_ssse3( blake2s_state *S, size_t outlen );
+ int blake2s_init_key_ssse3( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_init_param_ssse3( blake2s_state *S, const blake2s_param *P );
+ int blake2s_update_ssse3( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final_ssse3( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s_ssse3( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2s_init_sse41( blake2s_state *S, size_t outlen );
+ int blake2s_init_key_sse41( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_init_param_sse41( blake2s_state *S, const blake2s_param *P );
+ int blake2s_update_sse41( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final_sse41( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s_sse41( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2s_init_avx( blake2s_state *S, size_t outlen );
+ int blake2s_init_key_avx( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_init_param_avx( blake2s_state *S, const blake2s_param *P );
+ int blake2s_update_avx( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final_avx( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s_avx( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2s_init_xop( blake2s_state *S, size_t outlen );
+ int blake2s_init_key_xop( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_init_param_xop( blake2s_state *S, const blake2s_param *P );
+ int blake2s_update_xop( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final_xop( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s_xop( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+#endif /* HAVE_X86 */
+
+#if defined(__cplusplus)
+}
+#endif
+
+typedef int ( *blake2b_init_fn )( blake2b_state *, size_t );
+typedef int ( *blake2b_init_key_fn )( blake2b_state *, size_t, const void *, size_t );
+typedef int ( *blake2b_init_param_fn )( blake2b_state *, const blake2b_param * );
+typedef int ( *blake2b_update_fn )( blake2b_state *, const uint8_t *, size_t );
+typedef int ( *blake2b_final_fn )( blake2b_state *, uint8_t *, size_t );
+typedef int ( *blake2b_fn )( uint8_t *, const void *, const void *, size_t, size_t, size_t );
+
+typedef int ( *blake2s_init_fn )( blake2s_state *, size_t );
+typedef int ( *blake2s_init_key_fn )( blake2s_state *, size_t, const void *, size_t );
+typedef int ( *blake2s_init_param_fn )( blake2s_state *, const blake2s_param * );
+typedef int ( *blake2s_update_fn )( blake2s_state *, const uint8_t *, size_t );
+typedef int ( *blake2s_final_fn )( blake2s_state *, uint8_t *, size_t );
+typedef int ( *blake2s_fn )( uint8_t *, const void *, const void *, size_t, size_t, size_t );
+
+static const blake2b_init_fn blake2b_init_table[] =
+{
+ blake2b_init_ref,
+#if defined(HAVE_X86)
+ blake2b_init_sse2,
+ blake2b_init_ssse3,
+ blake2b_init_sse41,
+ blake2b_init_avx,
+ blake2b_init_xop
+#endif
+};
+
+static const blake2b_init_key_fn blake2b_init_key_table[] =
+{
+ blake2b_init_key_ref,
+#if defined(HAVE_X86)
+ blake2b_init_key_sse2,
+ blake2b_init_key_ssse3,
+ blake2b_init_key_sse41,
+ blake2b_init_key_avx,
+ blake2b_init_key_xop
+#endif
+};
+
+static const blake2b_init_param_fn blake2b_init_param_table[] =
+{
+ blake2b_init_param_ref,
+#if defined(HAVE_X86)
+ blake2b_init_param_sse2,
+ blake2b_init_param_ssse3,
+ blake2b_init_param_sse41,
+ blake2b_init_param_avx,
+ blake2b_init_param_xop
+#endif
+};
+
+static const blake2b_update_fn blake2b_update_table[] =
+{
+ blake2b_update_ref,
+#if defined(HAVE_X86)
+ blake2b_update_sse2,
+ blake2b_update_ssse3,
+ blake2b_update_sse41,
+ blake2b_update_avx,
+ blake2b_update_xop
+#endif
+};
+
+static const blake2b_final_fn blake2b_final_table[] =
+{
+ blake2b_final_ref,
+#if defined(HAVE_X86)
+ blake2b_final_sse2,
+ blake2b_final_ssse3,
+ blake2b_final_sse41,
+ blake2b_final_avx,
+ blake2b_final_xop
+#endif
+};
+
+static const blake2b_fn blake2b_table[] =
+{
+ blake2b_ref,
+#if defined(HAVE_X86)
+ blake2b_sse2,
+ blake2b_ssse3,
+ blake2b_sse41,
+ blake2b_avx,
+ blake2b_xop
+#endif
+};
+
+static const blake2s_init_fn blake2s_init_table[] =
+{
+ blake2s_init_ref,
+#if defined(HAVE_X86)
+ blake2s_init_sse2,
+ blake2s_init_ssse3,
+ blake2s_init_sse41,
+ blake2s_init_avx,
+ blake2s_init_xop
+#endif
+};
+
+static const blake2s_init_key_fn blake2s_init_key_table[] =
+{
+ blake2s_init_key_ref,
+#if defined(HAVE_X86)
+ blake2s_init_key_sse2,
+ blake2s_init_key_ssse3,
+ blake2s_init_key_sse41,
+ blake2s_init_key_avx,
+ blake2s_init_key_xop
+#endif
+};
+
+static const blake2s_init_param_fn blake2s_init_param_table[] =
+{
+ blake2s_init_param_ref,
+#if defined(HAVE_X86)
+ blake2s_init_param_sse2,
+ blake2s_init_param_ssse3,
+ blake2s_init_param_sse41,
+ blake2s_init_param_avx,
+ blake2s_init_param_xop
+#endif
+};
+
+static const blake2s_update_fn blake2s_update_table[] =
+{
+ blake2s_update_ref,
+#if defined(HAVE_X86)
+ blake2s_update_sse2,
+ blake2s_update_ssse3,
+ blake2s_update_sse41,
+ blake2s_update_avx,
+ blake2s_update_xop
+#endif
+};
+
+static const blake2s_final_fn blake2s_final_table[] =
+{
+ blake2s_final_ref,
+#if defined(HAVE_X86)
+ blake2s_final_sse2,
+ blake2s_final_ssse3,
+ blake2s_final_sse41,
+ blake2s_final_avx,
+ blake2s_final_xop
+#endif
+};
+
+static const blake2s_fn blake2s_table[] =
+{
+ blake2s_ref,
+#if defined(HAVE_X86)
+ blake2s_sse2,
+ blake2s_ssse3,
+ blake2s_sse41,
+ blake2s_avx,
+ blake2s_xop
+#endif
+};
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+ int blake2b_init_dispatch( blake2b_state *S, size_t outlen );
+ int blake2b_init_key_dispatch( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_init_param_dispatch( blake2b_state *S, const blake2b_param *P );
+ int blake2b_update_dispatch( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final_dispatch( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b_dispatch( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2s_init_dispatch( blake2s_state *S, size_t outlen );
+ int blake2s_init_key_dispatch( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_init_param_dispatch( blake2s_state *S, const blake2s_param *P );
+ int blake2s_update_dispatch( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final_dispatch( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s_dispatch( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+#if defined(__cplusplus)
+}
+#endif
+
+static blake2b_init_fn blake2b_init_ptr = blake2b_init_dispatch;
+static blake2b_init_key_fn blake2b_init_key_ptr = blake2b_init_key_dispatch;
+static blake2b_init_param_fn blake2b_init_param_ptr = blake2b_init_param_dispatch;
+static blake2b_update_fn blake2b_update_ptr = blake2b_update_dispatch;
+static blake2b_final_fn blake2b_final_ptr = blake2b_final_dispatch;
+static blake2b_fn blake2b_ptr = blake2b_dispatch;
+
+static blake2s_init_fn blake2s_init_ptr = blake2s_init_dispatch;
+static blake2s_init_key_fn blake2s_init_key_ptr = blake2s_init_key_dispatch;
+static blake2s_init_param_fn blake2s_init_param_ptr = blake2s_init_param_dispatch;
+static blake2s_update_fn blake2s_update_ptr = blake2s_update_dispatch;
+static blake2s_final_fn blake2s_final_ptr = blake2s_final_dispatch;
+static blake2s_fn blake2s_ptr = blake2s_dispatch;
+
+int blake2b_init_dispatch( blake2b_state *S, size_t outlen )
+{
+ blake2b_init_ptr = blake2b_init_table[get_cpu_features()];
+ return blake2b_init_ptr( S, outlen );
+}
+
+int blake2b_init_key_dispatch( blake2b_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ blake2b_init_key_ptr = blake2b_init_key_table[get_cpu_features()];
+ return blake2b_init_key_ptr( S, outlen, key, keylen );
+}
+
+int blake2b_init_param_dispatch( blake2b_state *S, const blake2b_param *P )
+{
+ blake2b_init_param_ptr = blake2b_init_param_table[get_cpu_features()];
+ return blake2b_init_param_ptr( S, P );
+}
+
+int blake2b_update_dispatch( blake2b_state *S, const uint8_t *in, size_t inlen )
+{
+ blake2b_update_ptr = blake2b_update_table[get_cpu_features()];
+ return blake2b_update_ptr( S, in, inlen );
+}
+
+int blake2b_final_dispatch( blake2b_state *S, uint8_t *out, size_t outlen )
+{
+ blake2b_final_ptr = blake2b_final_table[get_cpu_features()];
+ return blake2b_final_ptr( S, out, outlen );
+}
+
+int blake2b_dispatch( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ blake2b_ptr = blake2b_table[get_cpu_features()];
+ return blake2b_ptr( out, in, key, outlen, inlen, keylen );
+}
+
+BLAKE2_API int blake2b_init( blake2b_state *S, size_t outlen )
+{
+ return blake2b_init_ptr( S, outlen );
+}
+
+BLAKE2_API int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ return blake2b_init_key_ptr( S, outlen, key, keylen );
+}
+
+BLAKE2_API int blake2b_init_param( blake2b_state *S, const blake2b_param *P )
+{
+ return blake2b_init_param_ptr( S, P );
+}
+
+BLAKE2_API int blake2b_update( blake2b_state *S, const uint8_t *in, size_t inlen )
+{
+ return blake2b_update_ptr( S, in, inlen );
+}
+
+BLAKE2_API int blake2b_final( blake2b_state *S, uint8_t *out, size_t outlen )
+{
+ return blake2b_final_ptr( S, out, outlen );
+}
+
+BLAKE2_API int blake2b( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ return blake2b_ptr( out, in, key, outlen, inlen, keylen );
+}
+
+int blake2s_init_dispatch( blake2s_state *S, size_t outlen )
+{
+ blake2s_init_ptr = blake2s_init_table[get_cpu_features()];
+ return blake2s_init_ptr( S, outlen );
+}
+
+int blake2s_init_key_dispatch( blake2s_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ blake2s_init_key_ptr = blake2s_init_key_table[get_cpu_features()];
+ return blake2s_init_key_ptr( S, outlen, key, keylen );
+}
+
+int blake2s_init_param_dispatch( blake2s_state *S, const blake2s_param *P )
+{
+ blake2s_init_param_ptr = blake2s_init_param_table[get_cpu_features()];
+ return blake2s_init_param_ptr( S, P );
+}
+
+int blake2s_update_dispatch( blake2s_state *S, const uint8_t *in, size_t inlen )
+{
+ blake2s_update_ptr = blake2s_update_table[get_cpu_features()];
+ return blake2s_update_ptr( S, in, inlen );
+}
+
+int blake2s_final_dispatch( blake2s_state *S, uint8_t *out, size_t outlen )
+{
+ blake2s_final_ptr = blake2s_final_table[get_cpu_features()];
+ return blake2s_final_ptr( S, out, outlen );
+}
+
+int blake2s_dispatch( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ blake2s_ptr = blake2s_table[get_cpu_features()];
+ return blake2s_ptr( out, in, key, outlen, inlen, keylen );
+}
+
+BLAKE2_API int blake2s_init( blake2s_state *S, size_t outlen )
+{
+ return blake2s_init_ptr( S, outlen );
+}
+
+BLAKE2_API int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ return blake2s_init_key_ptr( S, outlen, key, keylen );
+}
+
+BLAKE2_API int blake2s_init_param( blake2s_state *S, const blake2s_param *P )
+{
+ return blake2s_init_param_ptr( S, P );
+}
+
+BLAKE2_API int blake2s_update( blake2s_state *S, const uint8_t *in, size_t inlen )
+{
+ return blake2s_update_ptr( S, in, inlen );
+}
+
+BLAKE2_API int blake2s_final( blake2s_state *S, uint8_t *out, size_t outlen )
+{
+ return blake2s_final_ptr( S, out, outlen );
+}
+
+BLAKE2_API int blake2s( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ return blake2s_ptr( out, in, key, outlen, inlen, keylen );
+}
+
diff --git a/contrib/libs/blake2/src/blake2-impl.h b/contrib/libs/blake2/src/blake2-impl.h
new file mode 100644
index 00000000000..801f1ae7b7f
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2-impl.h
@@ -0,0 +1,160 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2_IMPL_H__
+#define __BLAKE2_IMPL_H__
+
+#if defined(_WIN32) || defined(WIN32)
+#include <windows.h>
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include "config.h"
+
+#define BLAKE2_IMPL_CAT(x,y) x ## y
+#define BLAKE2_IMPL_EVAL(x,y) BLAKE2_IMPL_CAT(x,y)
+#define BLAKE2_IMPL_NAME(fun) BLAKE2_IMPL_EVAL(fun, SUFFIX)
+
+static inline uint32_t load32( const void *src )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+ uint32_t w;
+ memcpy( &w, src, sizeof( w ) );
+ return w;
+#else
+ const uint8_t *p = ( uint8_t * )src;
+ uint32_t w = *p++;
+ w |= ( uint32_t )( *p++ ) << 8;
+ w |= ( uint32_t )( *p++ ) << 16;
+ w |= ( uint32_t )( *p++ ) << 24;
+ return w;
+#endif
+}
+
+static inline uint64_t load64( const void *src )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+ uint64_t w;
+ memcpy( &w, src, sizeof( w ) );
+ return w;
+#else
+ const uint8_t *p = ( uint8_t * )src;
+ uint64_t w = *p++;
+ w |= ( uint64_t )( *p++ ) << 8;
+ w |= ( uint64_t )( *p++ ) << 16;
+ w |= ( uint64_t )( *p++ ) << 24;
+ w |= ( uint64_t )( *p++ ) << 32;
+ w |= ( uint64_t )( *p++ ) << 40;
+ w |= ( uint64_t )( *p++ ) << 48;
+ w |= ( uint64_t )( *p++ ) << 56;
+ return w;
+#endif
+}
+
+static inline void store32( void *dst, uint32_t w )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+ memcpy( dst, &w, sizeof( w ) );
+#else
+ uint8_t *p = ( uint8_t * )dst;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w;
+#endif
+}
+
+static inline void store64( void *dst, uint64_t w )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+ memcpy( dst, &w, sizeof( w ) );
+#else
+ uint8_t *p = ( uint8_t * )dst;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w;
+#endif
+}
+
+static inline uint64_t load48( const void *src )
+{
+ const uint8_t *p = ( const uint8_t * )src;
+ uint64_t w = *p++;
+ w |= ( uint64_t )( *p++ ) << 8;
+ w |= ( uint64_t )( *p++ ) << 16;
+ w |= ( uint64_t )( *p++ ) << 24;
+ w |= ( uint64_t )( *p++ ) << 32;
+ w |= ( uint64_t )( *p++ ) << 40;
+ return w;
+}
+
+static inline void store48( void *dst, uint64_t w )
+{
+ uint8_t *p = ( uint8_t * )dst;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w;
+}
+
+static inline uint32_t rotl32( const uint32_t w, const unsigned c )
+{
+ return ( w << c ) | ( w >> ( 32 - c ) );
+}
+
+static inline uint64_t rotl64( const uint64_t w, const unsigned c )
+{
+ return ( w << c ) | ( w >> ( 64 - c ) );
+}
+
+static inline uint32_t rotr32( const uint32_t w, const unsigned c )
+{
+ return ( w >> c ) | ( w << ( 32 - c ) );
+}
+
+static inline uint64_t rotr64( const uint64_t w, const unsigned c )
+{
+ return ( w >> c ) | ( w << ( 64 - c ) );
+}
+
+/* prevents compiler optimizing out memset() */
+static inline void secure_zero_memory(void *v, size_t n)
+{
+#if defined(_WIN32) || defined(WIN32)
+ SecureZeroMemory(v, n);
+#else
+// prioritize first the general C11 call
+#if defined(HAVE_MEMSET_S)
+ memset_s(v, n, 0, n);
+#elif defined(HAVE_EXPLICIT_BZERO)
+ explicit_bzero(v, n);
+#elif defined(HAVE_EXPLICIT_MEMSET)
+ explicit_memset(v, 0, n);
+#else
+ memset(v, 0, n);
+ __asm__ __volatile__("" :: "r"(v) : "memory");
+#endif
+#endif
+}
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2.h b/contrib/libs/blake2/src/blake2.h
new file mode 100644
index 00000000000..5ca17f61074
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2.h
@@ -0,0 +1,182 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2_H__
+#define __BLAKE2_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+ #define BLAKE2_DLL_IMPORT __declspec(dllimport)
+ #define BLAKE2_DLL_EXPORT __declspec(dllexport)
+ #define BLAKE2_DLL_PRIVATE
+#elif __GNUC__ >= 4
+ #define BLAKE2_DLL_IMPORT __attribute__ ((visibility ("default")))
+ #define BLAKE2_DLL_EXPORT __attribute__ ((visibility ("default")))
+ #define BLAKE2_DLL_PRIVATE __attribute__ ((visibility ("hidden")))
+#else
+ #define BLAKE2_DLL_IMPORT
+ #define BLAKE2_DLL_EXPORT
+ #define BLAKE2_DLL_PRIVATE
+#endif
+
+#if defined(BLAKE2_DLL)
+ #if defined(BLAKE2_DLL_EXPORTS) // defined if we are building the DLL
+ #define BLAKE2_API BLAKE2_DLL_EXPORT
+ #else
+ #define BLAKE2_API BLAKE2_DLL_IMPORT
+ #endif
+ #define BLAKE2_PRIVATE BLAKE2_DLL_PRIVATE // must only be used by hidden logic
+#else
+ #define BLAKE2_API
+ #define BLAKE2_PRIVATE
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#elif defined(_MSC_VER) && !defined(inline)
+#define inline __inline
+#endif
+
+ enum blake2s_constant
+ {
+ BLAKE2S_BLOCKBYTES = 64,
+ BLAKE2S_OUTBYTES = 32,
+ BLAKE2S_KEYBYTES = 32,
+ BLAKE2S_SALTBYTES = 8,
+ BLAKE2S_PERSONALBYTES = 8
+ };
+
+ enum blake2b_constant
+ {
+ BLAKE2B_BLOCKBYTES = 128,
+ BLAKE2B_OUTBYTES = 64,
+ BLAKE2B_KEYBYTES = 64,
+ BLAKE2B_SALTBYTES = 16,
+ BLAKE2B_PERSONALBYTES = 16
+ };
+
+#pragma pack(push, 1)
+ typedef struct __blake2s_param
+ {
+ uint8_t digest_length; // 1
+ uint8_t key_length; // 2
+ uint8_t fanout; // 3
+ uint8_t depth; // 4
+ uint32_t leaf_length; // 8
+ uint8_t node_offset[6];// 14
+ uint8_t node_depth; // 15
+ uint8_t inner_length; // 16
+ // uint8_t reserved[0];
+ uint8_t salt[BLAKE2S_SALTBYTES]; // 24
+ uint8_t personal[BLAKE2S_PERSONALBYTES]; // 32
+ } blake2s_param;
+
+ typedef struct __blake2s_state
+ {
+ uint32_t h[8];
+ uint32_t t[2];
+ uint32_t f[2];
+ uint8_t buf[2 * BLAKE2S_BLOCKBYTES];
+ uint32_t buflen;
+ uint8_t outlen;
+ uint8_t last_node;
+ } blake2s_state;
+
+ typedef struct __blake2b_param
+ {
+ uint8_t digest_length; // 1
+ uint8_t key_length; // 2
+ uint8_t fanout; // 3
+ uint8_t depth; // 4
+ uint32_t leaf_length; // 8
+ uint64_t node_offset; // 16
+ uint8_t node_depth; // 17
+ uint8_t inner_length; // 18
+ uint8_t reserved[14]; // 32
+ uint8_t salt[BLAKE2B_SALTBYTES]; // 48
+ uint8_t personal[BLAKE2B_PERSONALBYTES]; // 64
+ } blake2b_param;
+
+ typedef struct __blake2b_state
+ {
+ uint64_t h[8];
+ uint64_t t[2];
+ uint64_t f[2];
+ uint8_t buf[2 * BLAKE2B_BLOCKBYTES];
+ uint32_t buflen;
+ uint8_t outlen;
+ uint8_t last_node;
+ } blake2b_state;
+
+ typedef struct __blake2sp_state
+ {
+ blake2s_state S[8][1];
+ blake2s_state R[1];
+ uint8_t buf[8 * BLAKE2S_BLOCKBYTES];
+ uint32_t buflen;
+ uint8_t outlen;
+ } blake2sp_state;
+
+ typedef struct __blake2bp_state
+ {
+ blake2b_state S[4][1];
+ blake2b_state R[1];
+ uint8_t buf[4 * BLAKE2B_BLOCKBYTES];
+ uint32_t buflen;
+ uint8_t outlen;
+ } blake2bp_state;
+#pragma pack(pop)
+
+ // Streaming API
+ BLAKE2_API int blake2s_init( blake2s_state *S, size_t outlen );
+ BLAKE2_API int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ BLAKE2_API int blake2s_init_param( blake2s_state *S, const blake2s_param *P );
+ BLAKE2_API int blake2s_update( blake2s_state *S, const uint8_t *in, size_t inlen );
+ BLAKE2_API int blake2s_final( blake2s_state *S, uint8_t *out, size_t outlen );
+
+ BLAKE2_API int blake2b_init( blake2b_state *S, size_t outlen );
+ BLAKE2_API int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ BLAKE2_API int blake2b_init_param( blake2b_state *S, const blake2b_param *P );
+ BLAKE2_API int blake2b_update( blake2b_state *S, const uint8_t *in, size_t inlen );
+ BLAKE2_API int blake2b_final( blake2b_state *S, uint8_t *out, size_t outlen );
+
+ BLAKE2_API int blake2sp_init( blake2sp_state *S, size_t outlen );
+ BLAKE2_API int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen );
+ BLAKE2_API int blake2sp_update( blake2sp_state *S, const uint8_t *in, size_t inlen );
+ BLAKE2_API int blake2sp_final( blake2sp_state *S, uint8_t *out, size_t outlen );
+
+ BLAKE2_API int blake2bp_init( blake2bp_state *S, size_t outlen );
+ BLAKE2_API int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen );
+ BLAKE2_API int blake2bp_update( blake2bp_state *S, const uint8_t *in, size_t inlen );
+ BLAKE2_API int blake2bp_final( blake2bp_state *S, uint8_t *out, size_t outlen );
+
+ // Simple API
+ BLAKE2_API int blake2s( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+ BLAKE2_API int blake2b( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ BLAKE2_API int blake2sp( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+ BLAKE2_API int blake2bp( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ static inline int blake2( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+ {
+ return blake2b( out, in, key, outlen, inlen, keylen );
+ }
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2b-load-sse2.h b/contrib/libs/blake2/src/blake2b-load-sse2.h
new file mode 100644
index 00000000000..1ba153c87d7
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2b-load-sse2.h
@@ -0,0 +1,68 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2B_LOAD_SSE2_H__
+#define __BLAKE2B_LOAD_SSE2_H__
+
+#define LOAD_MSG_0_1(b0, b1) b0 = _mm_set_epi64x(m2, m0); b1 = _mm_set_epi64x(m6, m4)
+#define LOAD_MSG_0_2(b0, b1) b0 = _mm_set_epi64x(m3, m1); b1 = _mm_set_epi64x(m7, m5)
+#define LOAD_MSG_0_3(b0, b1) b0 = _mm_set_epi64x(m10, m8); b1 = _mm_set_epi64x(m14, m12)
+#define LOAD_MSG_0_4(b0, b1) b0 = _mm_set_epi64x(m11, m9); b1 = _mm_set_epi64x(m15, m13)
+#define LOAD_MSG_1_1(b0, b1) b0 = _mm_set_epi64x(m4, m14); b1 = _mm_set_epi64x(m13, m9)
+#define LOAD_MSG_1_2(b0, b1) b0 = _mm_set_epi64x(m8, m10); b1 = _mm_set_epi64x(m6, m15)
+#define LOAD_MSG_1_3(b0, b1) b0 = _mm_set_epi64x(m0, m1); b1 = _mm_set_epi64x(m5, m11)
+#define LOAD_MSG_1_4(b0, b1) b0 = _mm_set_epi64x(m2, m12); b1 = _mm_set_epi64x(m3, m7)
+#define LOAD_MSG_2_1(b0, b1) b0 = _mm_set_epi64x(m12, m11); b1 = _mm_set_epi64x(m15, m5)
+#define LOAD_MSG_2_2(b0, b1) b0 = _mm_set_epi64x(m0, m8); b1 = _mm_set_epi64x(m13, m2)
+#define LOAD_MSG_2_3(b0, b1) b0 = _mm_set_epi64x(m3, m10); b1 = _mm_set_epi64x(m9, m7)
+#define LOAD_MSG_2_4(b0, b1) b0 = _mm_set_epi64x(m6, m14); b1 = _mm_set_epi64x(m4, m1)
+#define LOAD_MSG_3_1(b0, b1) b0 = _mm_set_epi64x(m3, m7); b1 = _mm_set_epi64x(m11, m13)
+#define LOAD_MSG_3_2(b0, b1) b0 = _mm_set_epi64x(m1, m9); b1 = _mm_set_epi64x(m14, m12)
+#define LOAD_MSG_3_3(b0, b1) b0 = _mm_set_epi64x(m5, m2); b1 = _mm_set_epi64x(m15, m4)
+#define LOAD_MSG_3_4(b0, b1) b0 = _mm_set_epi64x(m10, m6); b1 = _mm_set_epi64x(m8, m0)
+#define LOAD_MSG_4_1(b0, b1) b0 = _mm_set_epi64x(m5, m9); b1 = _mm_set_epi64x(m10, m2)
+#define LOAD_MSG_4_2(b0, b1) b0 = _mm_set_epi64x(m7, m0); b1 = _mm_set_epi64x(m15, m4)
+#define LOAD_MSG_4_3(b0, b1) b0 = _mm_set_epi64x(m11, m14); b1 = _mm_set_epi64x(m3, m6)
+#define LOAD_MSG_4_4(b0, b1) b0 = _mm_set_epi64x(m12, m1); b1 = _mm_set_epi64x(m13, m8)
+#define LOAD_MSG_5_1(b0, b1) b0 = _mm_set_epi64x(m6, m2); b1 = _mm_set_epi64x(m8, m0)
+#define LOAD_MSG_5_2(b0, b1) b0 = _mm_set_epi64x(m10, m12); b1 = _mm_set_epi64x(m3, m11)
+#define LOAD_MSG_5_3(b0, b1) b0 = _mm_set_epi64x(m7, m4); b1 = _mm_set_epi64x(m1, m15)
+#define LOAD_MSG_5_4(b0, b1) b0 = _mm_set_epi64x(m5, m13); b1 = _mm_set_epi64x(m9, m14)
+#define LOAD_MSG_6_1(b0, b1) b0 = _mm_set_epi64x(m1, m12); b1 = _mm_set_epi64x(m4, m14)
+#define LOAD_MSG_6_2(b0, b1) b0 = _mm_set_epi64x(m15, m5); b1 = _mm_set_epi64x(m10, m13)
+#define LOAD_MSG_6_3(b0, b1) b0 = _mm_set_epi64x(m6, m0); b1 = _mm_set_epi64x(m8, m9)
+#define LOAD_MSG_6_4(b0, b1) b0 = _mm_set_epi64x(m3, m7); b1 = _mm_set_epi64x(m11, m2)
+#define LOAD_MSG_7_1(b0, b1) b0 = _mm_set_epi64x(m7, m13); b1 = _mm_set_epi64x(m3, m12)
+#define LOAD_MSG_7_2(b0, b1) b0 = _mm_set_epi64x(m14, m11); b1 = _mm_set_epi64x(m9, m1)
+#define LOAD_MSG_7_3(b0, b1) b0 = _mm_set_epi64x(m15, m5); b1 = _mm_set_epi64x(m2, m8)
+#define LOAD_MSG_7_4(b0, b1) b0 = _mm_set_epi64x(m4, m0); b1 = _mm_set_epi64x(m10, m6)
+#define LOAD_MSG_8_1(b0, b1) b0 = _mm_set_epi64x(m14, m6); b1 = _mm_set_epi64x(m0, m11)
+#define LOAD_MSG_8_2(b0, b1) b0 = _mm_set_epi64x(m9, m15); b1 = _mm_set_epi64x(m8, m3)
+#define LOAD_MSG_8_3(b0, b1) b0 = _mm_set_epi64x(m13, m12); b1 = _mm_set_epi64x(m10, m1)
+#define LOAD_MSG_8_4(b0, b1) b0 = _mm_set_epi64x(m7, m2); b1 = _mm_set_epi64x(m5, m4)
+#define LOAD_MSG_9_1(b0, b1) b0 = _mm_set_epi64x(m8, m10); b1 = _mm_set_epi64x(m1, m7)
+#define LOAD_MSG_9_2(b0, b1) b0 = _mm_set_epi64x(m4, m2); b1 = _mm_set_epi64x(m5, m6)
+#define LOAD_MSG_9_3(b0, b1) b0 = _mm_set_epi64x(m9, m15); b1 = _mm_set_epi64x(m13, m3)
+#define LOAD_MSG_9_4(b0, b1) b0 = _mm_set_epi64x(m14, m11); b1 = _mm_set_epi64x(m0, m12)
+#define LOAD_MSG_10_1(b0, b1) b0 = _mm_set_epi64x(m2, m0); b1 = _mm_set_epi64x(m6, m4)
+#define LOAD_MSG_10_2(b0, b1) b0 = _mm_set_epi64x(m3, m1); b1 = _mm_set_epi64x(m7, m5)
+#define LOAD_MSG_10_3(b0, b1) b0 = _mm_set_epi64x(m10, m8); b1 = _mm_set_epi64x(m14, m12)
+#define LOAD_MSG_10_4(b0, b1) b0 = _mm_set_epi64x(m11, m9); b1 = _mm_set_epi64x(m15, m13)
+#define LOAD_MSG_11_1(b0, b1) b0 = _mm_set_epi64x(m4, m14); b1 = _mm_set_epi64x(m13, m9)
+#define LOAD_MSG_11_2(b0, b1) b0 = _mm_set_epi64x(m8, m10); b1 = _mm_set_epi64x(m6, m15)
+#define LOAD_MSG_11_3(b0, b1) b0 = _mm_set_epi64x(m0, m1); b1 = _mm_set_epi64x(m5, m11)
+#define LOAD_MSG_11_4(b0, b1) b0 = _mm_set_epi64x(m2, m12); b1 = _mm_set_epi64x(m3, m7)
+
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2b-load-sse41.h b/contrib/libs/blake2/src/blake2b-load-sse41.h
new file mode 100644
index 00000000000..f6c1bc8393f
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2b-load-sse41.h
@@ -0,0 +1,402 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2B_LOAD_SSE41_H__
+#define __BLAKE2B_LOAD_SSE41_H__
+
+#define LOAD_MSG_0_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m0, m1); \
+b1 = _mm_unpacklo_epi64(m2, m3); \
+} while(0)
+
+
+#define LOAD_MSG_0_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m0, m1); \
+b1 = _mm_unpackhi_epi64(m2, m3); \
+} while(0)
+
+
+#define LOAD_MSG_0_3(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m4, m5); \
+b1 = _mm_unpacklo_epi64(m6, m7); \
+} while(0)
+
+
+#define LOAD_MSG_0_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m4, m5); \
+b1 = _mm_unpackhi_epi64(m6, m7); \
+} while(0)
+
+
+#define LOAD_MSG_1_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m7, m2); \
+b1 = _mm_unpackhi_epi64(m4, m6); \
+} while(0)
+
+
+#define LOAD_MSG_1_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m5, m4); \
+b1 = _mm_alignr_epi8(m3, m7, 8); \
+} while(0)
+
+
+#define LOAD_MSG_1_3(b0, b1) \
+do \
+{ \
+b0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1,0,3,2)); \
+b1 = _mm_unpackhi_epi64(m5, m2); \
+} while(0)
+
+
+#define LOAD_MSG_1_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m6, m1); \
+b1 = _mm_unpackhi_epi64(m3, m1); \
+} while(0)
+
+
+#define LOAD_MSG_2_1(b0, b1) \
+do \
+{ \
+b0 = _mm_alignr_epi8(m6, m5, 8); \
+b1 = _mm_unpackhi_epi64(m2, m7); \
+} while(0)
+
+
+#define LOAD_MSG_2_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m4, m0); \
+b1 = _mm_blend_epi16(m1, m6, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_2_3(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m5, m1, 0xF0); \
+b1 = _mm_unpackhi_epi64(m3, m4); \
+} while(0)
+
+
+#define LOAD_MSG_2_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m7, m3); \
+b1 = _mm_alignr_epi8(m2, m0, 8); \
+} while(0)
+
+
+#define LOAD_MSG_3_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m3, m1); \
+b1 = _mm_unpackhi_epi64(m6, m5); \
+} while(0)
+
+
+#define LOAD_MSG_3_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m4, m0); \
+b1 = _mm_unpacklo_epi64(m6, m7); \
+} while(0)
+
+
+#define LOAD_MSG_3_3(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m1, m2, 0xF0); \
+b1 = _mm_blend_epi16(m2, m7, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_3_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m3, m5); \
+b1 = _mm_unpacklo_epi64(m0, m4); \
+} while(0)
+
+
+#define LOAD_MSG_4_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m4, m2); \
+b1 = _mm_unpacklo_epi64(m1, m5); \
+} while(0)
+
+
+#define LOAD_MSG_4_2(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m0, m3, 0xF0); \
+b1 = _mm_blend_epi16(m2, m7, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_4_3(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m7, m5, 0xF0); \
+b1 = _mm_blend_epi16(m3, m1, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_4_4(b0, b1) \
+do \
+{ \
+b0 = _mm_alignr_epi8(m6, m0, 8); \
+b1 = _mm_blend_epi16(m4, m6, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_5_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m1, m3); \
+b1 = _mm_unpacklo_epi64(m0, m4); \
+} while(0)
+
+
+#define LOAD_MSG_5_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m6, m5); \
+b1 = _mm_unpackhi_epi64(m5, m1); \
+} while(0)
+
+
+#define LOAD_MSG_5_3(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m2, m3, 0xF0); \
+b1 = _mm_unpackhi_epi64(m7, m0); \
+} while(0)
+
+
+#define LOAD_MSG_5_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m6, m2); \
+b1 = _mm_blend_epi16(m7, m4, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_6_1(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m6, m0, 0xF0); \
+b1 = _mm_unpacklo_epi64(m7, m2); \
+} while(0)
+
+
+#define LOAD_MSG_6_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m2, m7); \
+b1 = _mm_alignr_epi8(m5, m6, 8); \
+} while(0)
+
+
+#define LOAD_MSG_6_3(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m0, m3); \
+b1 = _mm_shuffle_epi32(m4, _MM_SHUFFLE(1,0,3,2)); \
+} while(0)
+
+
+#define LOAD_MSG_6_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m3, m1); \
+b1 = _mm_blend_epi16(m1, m5, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_7_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m6, m3); \
+b1 = _mm_blend_epi16(m6, m1, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_7_2(b0, b1) \
+do \
+{ \
+b0 = _mm_alignr_epi8(m7, m5, 8); \
+b1 = _mm_unpackhi_epi64(m0, m4); \
+} while(0)
+
+
+#define LOAD_MSG_7_3(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m2, m7); \
+b1 = _mm_unpacklo_epi64(m4, m1); \
+} while(0)
+
+
+#define LOAD_MSG_7_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m0, m2); \
+b1 = _mm_unpacklo_epi64(m3, m5); \
+} while(0)
+
+
+#define LOAD_MSG_8_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m3, m7); \
+b1 = _mm_alignr_epi8(m0, m5, 8); \
+} while(0)
+
+
+#define LOAD_MSG_8_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m7, m4); \
+b1 = _mm_alignr_epi8(m4, m1, 8); \
+} while(0)
+
+
+#define LOAD_MSG_8_3(b0, b1) \
+do \
+{ \
+b0 = m6; \
+b1 = _mm_alignr_epi8(m5, m0, 8); \
+} while(0)
+
+
+#define LOAD_MSG_8_4(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m1, m3, 0xF0); \
+b1 = m2; \
+} while(0)
+
+
+#define LOAD_MSG_9_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m5, m4); \
+b1 = _mm_unpackhi_epi64(m3, m0); \
+} while(0)
+
+
+#define LOAD_MSG_9_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m1, m2); \
+b1 = _mm_blend_epi16(m3, m2, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_9_3(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m7, m4); \
+b1 = _mm_unpackhi_epi64(m1, m6); \
+} while(0)
+
+
+#define LOAD_MSG_9_4(b0, b1) \
+do \
+{ \
+b0 = _mm_alignr_epi8(m7, m5, 8); \
+b1 = _mm_unpacklo_epi64(m6, m0); \
+} while(0)
+
+
+#define LOAD_MSG_10_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m0, m1); \
+b1 = _mm_unpacklo_epi64(m2, m3); \
+} while(0)
+
+
+#define LOAD_MSG_10_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m0, m1); \
+b1 = _mm_unpackhi_epi64(m2, m3); \
+} while(0)
+
+
+#define LOAD_MSG_10_3(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m4, m5); \
+b1 = _mm_unpacklo_epi64(m6, m7); \
+} while(0)
+
+
+#define LOAD_MSG_10_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m4, m5); \
+b1 = _mm_unpackhi_epi64(m6, m7); \
+} while(0)
+
+
+#define LOAD_MSG_11_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m7, m2); \
+b1 = _mm_unpackhi_epi64(m4, m6); \
+} while(0)
+
+
+#define LOAD_MSG_11_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m5, m4); \
+b1 = _mm_alignr_epi8(m3, m7, 8); \
+} while(0)
+
+
+#define LOAD_MSG_11_3(b0, b1) \
+do \
+{ \
+b0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1,0,3,2)); \
+b1 = _mm_unpackhi_epi64(m5, m2); \
+} while(0)
+
+
+#define LOAD_MSG_11_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m6, m1); \
+b1 = _mm_unpackhi_epi64(m3, m1); \
+} while(0)
+
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2b-ref.c b/contrib/libs/blake2/src/blake2b-ref.c
new file mode 100644
index 00000000000..4d40764d0e8
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2b-ref.c
@@ -0,0 +1,386 @@
+/*
+ BLAKE2 reference source code package - reference C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+static const uint64_t blake2b_IV[8] =
+{
+ 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL,
+ 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL,
+ 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
+ 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
+};
+
+static const uint8_t blake2b_sigma[12][16] =
+{
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
+ { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
+ { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
+ { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
+ { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
+ { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
+ { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
+ { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
+ { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
+};
+
+
+static inline int blake2b_set_lastnode( blake2b_state *S )
+{
+ S->f[1] = ~0ULL;
+ return 0;
+}
+
+static inline int blake2b_clear_lastnode( blake2b_state *S )
+{
+ S->f[1] = 0ULL;
+ return 0;
+}
+
+/* Some helper functions, not necessarily useful */
+static inline int blake2b_set_lastblock( blake2b_state *S )
+{
+ if( S->last_node ) blake2b_set_lastnode( S );
+
+ S->f[0] = ~0ULL;
+ return 0;
+}
+
+static inline int blake2b_clear_lastblock( blake2b_state *S )
+{
+ if( S->last_node ) blake2b_clear_lastnode( S );
+
+ S->f[0] = 0ULL;
+ return 0;
+}
+
+static inline int blake2b_increment_counter( blake2b_state *S, const uint64_t inc )
+{
+ S->t[0] += inc;
+ S->t[1] += ( S->t[0] < inc );
+ return 0;
+}
+
+
+
+// Parameter-related functions
+static inline int blake2b_param_set_digest_length( blake2b_param *P, const uint8_t digest_length )
+{
+ P->digest_length = digest_length;
+ return 0;
+}
+
+static inline int blake2b_param_set_fanout( blake2b_param *P, const uint8_t fanout )
+{
+ P->fanout = fanout;
+ return 0;
+}
+
+static inline int blake2b_param_set_max_depth( blake2b_param *P, const uint8_t depth )
+{
+ P->depth = depth;
+ return 0;
+}
+
+static inline int blake2b_param_set_leaf_length( blake2b_param *P, const uint32_t leaf_length )
+{
+ store32( &P->leaf_length, leaf_length );
+ return 0;
+}
+
+static inline int blake2b_param_set_node_offset( blake2b_param *P, const uint64_t node_offset )
+{
+ store64( &P->node_offset, node_offset );
+ return 0;
+}
+
+static inline int blake2b_param_set_node_depth( blake2b_param *P, const uint8_t node_depth )
+{
+ P->node_depth = node_depth;
+ return 0;
+}
+
+static inline int blake2b_param_set_inner_length( blake2b_param *P, const uint8_t inner_length )
+{
+ P->inner_length = inner_length;
+ return 0;
+}
+
+static inline int blake2b_param_set_salt( blake2b_param *P, const uint8_t salt[BLAKE2B_SALTBYTES] )
+{
+ memcpy( P->salt, salt, BLAKE2B_SALTBYTES );
+ return 0;
+}
+
+static inline int blake2b_param_set_personal( blake2b_param *P, const uint8_t personal[BLAKE2B_PERSONALBYTES] )
+{
+ memcpy( P->personal, personal, BLAKE2B_PERSONALBYTES );
+ return 0;
+}
+
+static inline int blake2b_init0( blake2b_state *S )
+{
+ memset( S, 0, sizeof( blake2b_state ) );
+
+ for( int i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i];
+
+ return 0;
+}
+
+#define blake2b_init BLAKE2_IMPL_NAME(blake2b_init)
+#define blake2b_init_param BLAKE2_IMPL_NAME(blake2b_init_param)
+#define blake2b_init_key BLAKE2_IMPL_NAME(blake2b_init_key)
+#define blake2b_update BLAKE2_IMPL_NAME(blake2b_update)
+#define blake2b_final BLAKE2_IMPL_NAME(blake2b_final)
+#define blake2b BLAKE2_IMPL_NAME(blake2b)
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+ int blake2b_init( blake2b_state *S, size_t outlen );
+ int blake2b_init_param( blake2b_state *S, const blake2b_param *P );
+ int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_update( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+#if defined(__cplusplus)
+}
+#endif
+
+/* init xors IV with input parameter block */
+int blake2b_init_param( blake2b_state *S, const blake2b_param *P )
+{
+ blake2b_init0( S );
+ uint8_t *p = ( uint8_t * )( P );
+
+ /* IV XOR ParamBlock */
+ for( size_t i = 0; i < 8; ++i )
+ S->h[i] ^= load64( p + sizeof( S->h[i] ) * i );
+
+ S->outlen = P->digest_length;
+ return 0;
+}
+
+
+
+int blake2b_init( blake2b_state *S, size_t outlen )
+{
+ blake2b_param P[1];
+
+ if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1;
+
+ P->digest_length = ( uint8_t ) outlen;
+ P->key_length = 0;
+ P->fanout = 1;
+ P->depth = 1;
+ store32( &P->leaf_length, 0 );
+ store64( &P->node_offset, 0 );
+ P->node_depth = 0;
+ P->inner_length = 0;
+ memset( P->reserved, 0, sizeof( P->reserved ) );
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+ return blake2b_init_param( S, P );
+}
+
+
+int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ blake2b_param P[1];
+
+ if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1;
+
+ if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1;
+
+ P->digest_length = ( uint8_t ) outlen;
+ P->key_length = ( uint8_t ) keylen;
+ P->fanout = 1;
+ P->depth = 1;
+ store32( &P->leaf_length, 0 );
+ store64( &P->node_offset, 0 );
+ P->node_depth = 0;
+ P->inner_length = 0;
+ memset( P->reserved, 0, sizeof( P->reserved ) );
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+
+ if( blake2b_init_param( S, P ) < 0 ) return -1;
+
+ {
+ uint8_t block[BLAKE2B_BLOCKBYTES];
+ memset( block, 0, BLAKE2B_BLOCKBYTES );
+ memcpy( block, key, keylen );
+ blake2b_update( S, block, BLAKE2B_BLOCKBYTES );
+ secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
+ }
+ return 0;
+}
+
+static int blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] )
+{
+ uint64_t m[16];
+ uint64_t v[16];
+ size_t i;
+
+ for( i = 0; i < 16; ++i )
+ m[i] = load64( block + i * sizeof( m[i] ) );
+
+ for( i = 0; i < 8; ++i )
+ v[i] = S->h[i];
+
+ v[ 8] = blake2b_IV[0];
+ v[ 9] = blake2b_IV[1];
+ v[10] = blake2b_IV[2];
+ v[11] = blake2b_IV[3];
+ v[12] = S->t[0] ^ blake2b_IV[4];
+ v[13] = S->t[1] ^ blake2b_IV[5];
+ v[14] = S->f[0] ^ blake2b_IV[6];
+ v[15] = S->f[1] ^ blake2b_IV[7];
+#define G(r,i,a,b,c,d) \
+ do { \
+ a = a + b + m[blake2b_sigma[r][2*i+0]]; \
+ d = rotr64(d ^ a, 32); \
+ c = c + d; \
+ b = rotr64(b ^ c, 24); \
+ a = a + b + m[blake2b_sigma[r][2*i+1]]; \
+ d = rotr64(d ^ a, 16); \
+ c = c + d; \
+ b = rotr64(b ^ c, 63); \
+ } while(0)
+#define ROUND(r) \
+ do { \
+ G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
+ G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
+ G(r,2,v[ 2],v[ 6],v[10],v[14]); \
+ G(r,3,v[ 3],v[ 7],v[11],v[15]); \
+ G(r,4,v[ 0],v[ 5],v[10],v[15]); \
+ G(r,5,v[ 1],v[ 6],v[11],v[12]); \
+ G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
+ G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
+ } while(0)
+ ROUND( 0 );
+ ROUND( 1 );
+ ROUND( 2 );
+ ROUND( 3 );
+ ROUND( 4 );
+ ROUND( 5 );
+ ROUND( 6 );
+ ROUND( 7 );
+ ROUND( 8 );
+ ROUND( 9 );
+ ROUND( 10 );
+ ROUND( 11 );
+
+ for( i = 0; i < 8; ++i )
+ S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
+
+#undef G
+#undef ROUND
+ return 0;
+}
+
+
+int blake2b_update( blake2b_state *S, const uint8_t *in, size_t inlen )
+{
+ while( inlen > 0 )
+ {
+ uint32_t left = S->buflen;
+ uint32_t fill = 2 * BLAKE2B_BLOCKBYTES - left;
+
+ if( inlen > fill )
+ {
+ memcpy( S->buf + left, in, fill ); // Fill buffer
+ S->buflen += fill;
+ blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
+ blake2b_compress( S, S->buf ); // Compress
+ memcpy( S->buf, S->buf + BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES ); // Shift buffer left
+ S->buflen -= BLAKE2B_BLOCKBYTES;
+ in += fill;
+ inlen -= fill;
+ }
+ else // inlen <= fill
+ {
+ memcpy( S->buf + left, in, inlen );
+ S->buflen += ( uint32_t ) inlen; // Be lazy, do not compress
+ in += inlen;
+ inlen -= inlen;
+ }
+ }
+
+ return 0;
+}
+
+int blake2b_final( blake2b_state *S, uint8_t *out, size_t outlen )
+{
+ uint8_t buffer[BLAKE2B_OUTBYTES];
+ size_t i;
+
+ if(S->outlen != outlen) return -1;
+
+ if( S->buflen > BLAKE2B_BLOCKBYTES )
+ {
+ blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
+ blake2b_compress( S, S->buf );
+ S->buflen -= BLAKE2B_BLOCKBYTES;
+ memmove( S->buf, S->buf + BLAKE2B_BLOCKBYTES, S->buflen );
+ }
+
+ blake2b_increment_counter( S, S->buflen );
+ blake2b_set_lastblock( S );
+ memset( S->buf + S->buflen, 0, 2 * BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */
+ blake2b_compress( S, S->buf );
+
+ for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */
+ store64( buffer + sizeof( S->h[i] ) * i, S->h[i] );
+
+ memcpy( out, buffer, outlen );
+ return 0;
+}
+
+int blake2b( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ blake2b_state S[1];
+
+ /* Verify parameters */
+ if ( NULL == in && inlen > 0 ) return -1;
+
+ if ( NULL == out ) return -1;
+
+ if( NULL == key && keylen > 0 ) return -1;
+
+ if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
+
+ if( keylen > BLAKE2B_KEYBYTES ) return -1;
+
+ if( keylen > 0 )
+ {
+ if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1;
+ }
+ else
+ {
+ if( blake2b_init( S, outlen ) < 0 ) return -1;
+ }
+
+ if( blake2b_update( S, ( uint8_t * )in, inlen ) < 0 ) return -1;
+ return blake2b_final( S, out, outlen );
+}
+
+
diff --git a/contrib/libs/blake2/src/blake2b-round.h b/contrib/libs/blake2/src/blake2b-round.h
new file mode 100644
index 00000000000..cebc22550da
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2b-round.h
@@ -0,0 +1,160 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2B_ROUND_H__
+#define __BLAKE2B_ROUND_H__
+
+#define LOAD(p) _mm_load_si128( (__m128i *)(p) )
+#define STORE(p,r) _mm_store_si128((__m128i *)(p), r)
+
+#define LOADU(p) _mm_loadu_si128( (__m128i *)(p) )
+#define STOREU(p,r) _mm_storeu_si128((__m128i *)(p), r)
+
+#define TOF(reg) _mm_castsi128_ps((reg))
+#define TOI(reg) _mm_castps_si128((reg))
+
+#define LIKELY(x) __builtin_expect((x),1)
+
+
+/* Microarchitecture-specific macros */
+#ifndef HAVE_XOP
+#ifdef HAVE_SSSE3
+#define _mm_roti_epi64(x, c) \
+ (-(c) == 32) ? _mm_shuffle_epi32((x), _MM_SHUFFLE(2,3,0,1)) \
+ : (-(c) == 24) ? _mm_shuffle_epi8((x), r24) \
+ : (-(c) == 16) ? _mm_shuffle_epi8((x), r16) \
+ : (-(c) == 63) ? _mm_xor_si128(_mm_srli_epi64((x), -(c)), _mm_add_epi64((x), (x))) \
+ : _mm_xor_si128(_mm_srli_epi64((x), -(c)), _mm_slli_epi64((x), 64-(-(c))))
+#else
+#define _mm_roti_epi64(r, c) _mm_xor_si128(_mm_srli_epi64( (r), -(c) ),_mm_slli_epi64( (r), 64-(-(c)) ))
+#endif
+#else
+/* ... */
+#endif
+
+
+
+#define G1(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1) \
+ row1l = _mm_add_epi64(_mm_add_epi64(row1l, b0), row2l); \
+ row1h = _mm_add_epi64(_mm_add_epi64(row1h, b1), row2h); \
+ \
+ row4l = _mm_xor_si128(row4l, row1l); \
+ row4h = _mm_xor_si128(row4h, row1h); \
+ \
+ row4l = _mm_roti_epi64(row4l, -32); \
+ row4h = _mm_roti_epi64(row4h, -32); \
+ \
+ row3l = _mm_add_epi64(row3l, row4l); \
+ row3h = _mm_add_epi64(row3h, row4h); \
+ \
+ row2l = _mm_xor_si128(row2l, row3l); \
+ row2h = _mm_xor_si128(row2h, row3h); \
+ \
+ row2l = _mm_roti_epi64(row2l, -24); \
+ row2h = _mm_roti_epi64(row2h, -24); \
+
+#define G2(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1) \
+ row1l = _mm_add_epi64(_mm_add_epi64(row1l, b0), row2l); \
+ row1h = _mm_add_epi64(_mm_add_epi64(row1h, b1), row2h); \
+ \
+ row4l = _mm_xor_si128(row4l, row1l); \
+ row4h = _mm_xor_si128(row4h, row1h); \
+ \
+ row4l = _mm_roti_epi64(row4l, -16); \
+ row4h = _mm_roti_epi64(row4h, -16); \
+ \
+ row3l = _mm_add_epi64(row3l, row4l); \
+ row3h = _mm_add_epi64(row3h, row4h); \
+ \
+ row2l = _mm_xor_si128(row2l, row3l); \
+ row2h = _mm_xor_si128(row2h, row3h); \
+ \
+ row2l = _mm_roti_epi64(row2l, -63); \
+ row2h = _mm_roti_epi64(row2h, -63); \
+
+#if defined(HAVE_SSSE3)
+#define DIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \
+ t0 = _mm_alignr_epi8(row2h, row2l, 8); \
+ t1 = _mm_alignr_epi8(row2l, row2h, 8); \
+ row2l = t0; \
+ row2h = t1; \
+ \
+ t0 = row3l; \
+ row3l = row3h; \
+ row3h = t0; \
+ \
+ t0 = _mm_alignr_epi8(row4h, row4l, 8); \
+ t1 = _mm_alignr_epi8(row4l, row4h, 8); \
+ row4l = t1; \
+ row4h = t0;
+
+#define UNDIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \
+ t0 = _mm_alignr_epi8(row2l, row2h, 8); \
+ t1 = _mm_alignr_epi8(row2h, row2l, 8); \
+ row2l = t0; \
+ row2h = t1; \
+ \
+ t0 = row3l; \
+ row3l = row3h; \
+ row3h = t0; \
+ \
+ t0 = _mm_alignr_epi8(row4l, row4h, 8); \
+ t1 = _mm_alignr_epi8(row4h, row4l, 8); \
+ row4l = t1; \
+ row4h = t0;
+#else
+
+#define DIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \
+ t0 = row4l;\
+ t1 = row2l;\
+ row4l = row3l;\
+ row3l = row3h;\
+ row3h = row4l;\
+ row4l = _mm_unpackhi_epi64(row4h, _mm_unpacklo_epi64(t0, t0)); \
+ row4h = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(row4h, row4h)); \
+ row2l = _mm_unpackhi_epi64(row2l, _mm_unpacklo_epi64(row2h, row2h)); \
+ row2h = _mm_unpackhi_epi64(row2h, _mm_unpacklo_epi64(t1, t1))
+
+#define UNDIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \
+ t0 = row3l;\
+ row3l = row3h;\
+ row3h = t0;\
+ t0 = row2l;\
+ t1 = row4l;\
+ row2l = _mm_unpackhi_epi64(row2h, _mm_unpacklo_epi64(row2l, row2l)); \
+ row2h = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(row2h, row2h)); \
+ row4l = _mm_unpackhi_epi64(row4l, _mm_unpacklo_epi64(row4h, row4h)); \
+ row4h = _mm_unpackhi_epi64(row4h, _mm_unpacklo_epi64(t1, t1))
+
+#endif
+
+#if defined(HAVE_SSE4_1)
+#include "blake2b-load-sse41.h"
+#else
+#include "blake2b-load-sse2.h"
+#endif
+
+#define ROUND(r) \
+ LOAD_MSG_ ##r ##_1(b0, b1); \
+ G1(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \
+ LOAD_MSG_ ##r ##_2(b0, b1); \
+ G2(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \
+ DIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h); \
+ LOAD_MSG_ ##r ##_3(b0, b1); \
+ G1(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \
+ LOAD_MSG_ ##r ##_4(b0, b1); \
+ G2(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \
+ UNDIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h);
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2b.c b/contrib/libs/blake2/src/blake2b.c
new file mode 100644
index 00000000000..6ac57f24c01
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2b.c
@@ -0,0 +1,443 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+#include "blake2-config.h"
+
+#if defined(_MSC_VER)
+#include <intrin.h>
+#endif
+
+#if defined(HAVE_SSE2)
+#include <emmintrin.h>
+// MSVC only defines _mm_set_epi64x for x86_64...
+#if defined(_MSC_VER) && !defined(_M_X64) && !defined(__clang__)
+static inline __m128i _mm_set_epi64x( const uint64_t u1, const uint64_t u0 )
+{
+ return _mm_set_epi32( u1 >> 32, u1, u0 >> 32, u0 );
+}
+#endif
+#endif
+
+#if defined(HAVE_SSSE3)
+#include <tmmintrin.h>
+#endif
+#if defined(HAVE_SSE4_1)
+#include <smmintrin.h>
+#endif
+#if defined(HAVE_AVX)
+#include <immintrin.h>
+#endif
+#if defined(HAVE_XOP) && !defined(_MSC_VER)
+#include <x86intrin.h>
+#endif
+
+
+
+#include "blake2b-round.h"
+
+static const uint64_t blake2b_IV[8] =
+{
+ 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL,
+ 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL,
+ 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
+ 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
+};
+
+static const uint8_t blake2b_sigma[12][16] =
+{
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
+ { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
+ { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
+ { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
+ { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
+ { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
+ { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
+ { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
+ { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
+};
+
+
+/* Some helper functions, not necessarily useful */
+static inline int blake2b_set_lastnode( blake2b_state *S )
+{
+ S->f[1] = ~0ULL;
+ return 0;
+}
+
+static inline int blake2b_clear_lastnode( blake2b_state *S )
+{
+ S->f[1] = 0ULL;
+ return 0;
+}
+
+static inline int blake2b_set_lastblock( blake2b_state *S )
+{
+ if( S->last_node ) blake2b_set_lastnode( S );
+
+ S->f[0] = ~0ULL;
+ return 0;
+}
+
+static inline int blake2b_clear_lastblock( blake2b_state *S )
+{
+ if( S->last_node ) blake2b_clear_lastnode( S );
+
+ S->f[0] = 0ULL;
+ return 0;
+}
+
+
+static inline int blake2b_increment_counter( blake2b_state *S, const uint64_t inc )
+{
+#if defined(__x86_64__) && (defined(__GNUC__) || defined(__clang__))
+ // ADD/ADC chain
+ __uint128_t t = ( ( __uint128_t )S->t[1] << 64 ) | S->t[0];
+ t += inc;
+ S->t[0] = ( uint64_t )( t >> 0 );
+ S->t[1] = ( uint64_t )( t >> 64 );
+#else
+ S->t[0] += inc;
+ S->t[1] += ( S->t[0] < inc );
+#endif
+ return 0;
+}
+
+
+// Parameter-related functions
+static inline int blake2b_param_set_digest_length( blake2b_param *P, const uint8_t digest_length )
+{
+ P->digest_length = digest_length;
+ return 0;
+}
+
+static inline int blake2b_param_set_fanout( blake2b_param *P, const uint8_t fanout )
+{
+ P->fanout = fanout;
+ return 0;
+}
+
+static inline int blake2b_param_set_max_depth( blake2b_param *P, const uint8_t depth )
+{
+ P->depth = depth;
+ return 0;
+}
+
+static inline int blake2b_param_set_leaf_length( blake2b_param *P, const uint32_t leaf_length )
+{
+ P->leaf_length = leaf_length;
+ return 0;
+}
+
+static inline int blake2b_param_set_node_offset( blake2b_param *P, const uint64_t node_offset )
+{
+ P->node_offset = node_offset;
+ return 0;
+}
+
+static inline int blake2b_param_set_node_depth( blake2b_param *P, const uint8_t node_depth )
+{
+ P->node_depth = node_depth;
+ return 0;
+}
+
+static inline int blake2b_param_set_inner_length( blake2b_param *P, const uint8_t inner_length )
+{
+ P->inner_length = inner_length;
+ return 0;
+}
+
+static inline int blake2b_param_set_salt( blake2b_param *P, const uint8_t salt[BLAKE2B_SALTBYTES] )
+{
+ memcpy( P->salt, salt, BLAKE2B_SALTBYTES );
+ return 0;
+}
+
+static inline int blake2b_param_set_personal( blake2b_param *P, const uint8_t personal[BLAKE2B_PERSONALBYTES] )
+{
+ memcpy( P->personal, personal, BLAKE2B_PERSONALBYTES );
+ return 0;
+}
+
+static inline int blake2b_init0( blake2b_state *S )
+{
+ memset( S, 0, sizeof( blake2b_state ) );
+
+ for( int i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i];
+
+ return 0;
+}
+
+
+
+#define blake2b_init BLAKE2_IMPL_NAME(blake2b_init)
+#define blake2b_init_param BLAKE2_IMPL_NAME(blake2b_init_param)
+#define blake2b_init_key BLAKE2_IMPL_NAME(blake2b_init_key)
+#define blake2b_update BLAKE2_IMPL_NAME(blake2b_update)
+#define blake2b_final BLAKE2_IMPL_NAME(blake2b_final)
+#define blake2b BLAKE2_IMPL_NAME(blake2b)
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+ int blake2b_init( blake2b_state *S, size_t outlen );
+ int blake2b_init_param( blake2b_state *S, const blake2b_param *P );
+ int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_update( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+#if defined(__cplusplus)
+}
+#endif
+
+/* init xors IV with input parameter block */
+int blake2b_init_param( blake2b_state *S, const blake2b_param *P )
+{
+ uint8_t *p, *h, *v;
+ //blake2b_init0( S );
+ v = ( uint8_t * )( blake2b_IV );
+ h = ( uint8_t * )( S->h );
+ p = ( uint8_t * )( P );
+ /* IV XOR ParamBlock */
+ memset( S, 0, sizeof( blake2b_state ) );
+
+ for( int i = 0; i < BLAKE2B_OUTBYTES; ++i ) h[i] = v[i] ^ p[i];
+
+ S->outlen = P->digest_length;
+ return 0;
+}
+
+
+/* Some sort of default parameter block initialization, for sequential blake2b */
+
+int blake2b_init( blake2b_state *S, size_t outlen )
+{
+ if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1;
+
+ const blake2b_param P =
+ {
+ ( uint8_t ) outlen,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ {0},
+ {0},
+ {0}
+ };
+ return blake2b_init_param( S, &P );
+}
+
+int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1;
+
+ if ( ( !keylen ) || keylen > BLAKE2B_KEYBYTES ) return -1;
+
+ const blake2b_param P =
+ {
+ ( uint8_t ) outlen,
+ ( uint8_t ) keylen,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ {0},
+ {0},
+ {0}
+ };
+
+ if( blake2b_init_param( S, &P ) < 0 )
+ return 0;
+
+ {
+ uint8_t block[BLAKE2B_BLOCKBYTES];
+ memset( block, 0, BLAKE2B_BLOCKBYTES );
+ memcpy( block, key, keylen );
+ blake2b_update( S, block, BLAKE2B_BLOCKBYTES );
+ secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
+ }
+ return 0;
+}
+
+static inline int blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] )
+{
+ __m128i row1l, row1h;
+ __m128i row2l, row2h;
+ __m128i row3l, row3h;
+ __m128i row4l, row4h;
+ __m128i b0, b1;
+ __m128i t0, t1;
+#if defined(HAVE_SSSE3) && !defined(HAVE_XOP)
+ const __m128i r16 = _mm_setr_epi8( 2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9 );
+ const __m128i r24 = _mm_setr_epi8( 3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10 );
+#endif
+#if defined(HAVE_SSE4_1)
+ const __m128i m0 = LOADU( block + 00 );
+ const __m128i m1 = LOADU( block + 16 );
+ const __m128i m2 = LOADU( block + 32 );
+ const __m128i m3 = LOADU( block + 48 );
+ const __m128i m4 = LOADU( block + 64 );
+ const __m128i m5 = LOADU( block + 80 );
+ const __m128i m6 = LOADU( block + 96 );
+ const __m128i m7 = LOADU( block + 112 );
+#else
+ const uint64_t m0 = ( ( uint64_t * )block )[ 0];
+ const uint64_t m1 = ( ( uint64_t * )block )[ 1];
+ const uint64_t m2 = ( ( uint64_t * )block )[ 2];
+ const uint64_t m3 = ( ( uint64_t * )block )[ 3];
+ const uint64_t m4 = ( ( uint64_t * )block )[ 4];
+ const uint64_t m5 = ( ( uint64_t * )block )[ 5];
+ const uint64_t m6 = ( ( uint64_t * )block )[ 6];
+ const uint64_t m7 = ( ( uint64_t * )block )[ 7];
+ const uint64_t m8 = ( ( uint64_t * )block )[ 8];
+ const uint64_t m9 = ( ( uint64_t * )block )[ 9];
+ const uint64_t m10 = ( ( uint64_t * )block )[10];
+ const uint64_t m11 = ( ( uint64_t * )block )[11];
+ const uint64_t m12 = ( ( uint64_t * )block )[12];
+ const uint64_t m13 = ( ( uint64_t * )block )[13];
+ const uint64_t m14 = ( ( uint64_t * )block )[14];
+ const uint64_t m15 = ( ( uint64_t * )block )[15];
+#endif
+ row1l = LOADU( &S->h[0] );
+ row1h = LOADU( &S->h[2] );
+ row2l = LOADU( &S->h[4] );
+ row2h = LOADU( &S->h[6] );
+ row3l = LOADU( &blake2b_IV[0] );
+ row3h = LOADU( &blake2b_IV[2] );
+ row4l = _mm_xor_si128( LOADU( &blake2b_IV[4] ), LOADU( &S->t[0] ) );
+ row4h = _mm_xor_si128( LOADU( &blake2b_IV[6] ), LOADU( &S->f[0] ) );
+ ROUND( 0 );
+ ROUND( 1 );
+ ROUND( 2 );
+ ROUND( 3 );
+ ROUND( 4 );
+ ROUND( 5 );
+ ROUND( 6 );
+ ROUND( 7 );
+ ROUND( 8 );
+ ROUND( 9 );
+ ROUND( 10 );
+ ROUND( 11 );
+ row1l = _mm_xor_si128( row3l, row1l );
+ row1h = _mm_xor_si128( row3h, row1h );
+ STOREU( &S->h[0], _mm_xor_si128( LOADU( &S->h[0] ), row1l ) );
+ STOREU( &S->h[2], _mm_xor_si128( LOADU( &S->h[2] ), row1h ) );
+ row2l = _mm_xor_si128( row4l, row2l );
+ row2h = _mm_xor_si128( row4h, row2h );
+ STOREU( &S->h[4], _mm_xor_si128( LOADU( &S->h[4] ), row2l ) );
+ STOREU( &S->h[6], _mm_xor_si128( LOADU( &S->h[6] ), row2h ) );
+ return 0;
+}
+
+
+int blake2b_update( blake2b_state *S, const uint8_t *in, size_t inlen )
+{
+ while( inlen > 0 )
+ {
+ uint32_t left = S->buflen;
+ uint32_t fill = 2 * BLAKE2B_BLOCKBYTES - left;
+
+ if( inlen > fill )
+ {
+ memcpy( S->buf + left, in, fill ); // Fill buffer
+ S->buflen += fill;
+ blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
+ blake2b_compress( S, S->buf ); // Compress
+ memcpy( S->buf, S->buf + BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES ); // Shift buffer left
+ S->buflen -= BLAKE2B_BLOCKBYTES;
+ in += fill;
+ inlen -= fill;
+ }
+ else // inlen <= fill
+ {
+ memcpy( S->buf + left, in, inlen );
+ S->buflen += ( uint32_t ) inlen; // Be lazy, do not compress
+ in += inlen;
+ inlen -= inlen;
+ }
+ }
+
+ return 0;
+}
+
+
+int blake2b_final( blake2b_state *S, uint8_t *out, size_t outlen )
+{
+ if(S->outlen != outlen) return -1;
+
+ if( S->buflen > BLAKE2B_BLOCKBYTES )
+ {
+ blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
+ blake2b_compress( S, S->buf );
+ S->buflen -= BLAKE2B_BLOCKBYTES;
+ memmove( S->buf, S->buf + BLAKE2B_BLOCKBYTES, S->buflen );
+ }
+
+ blake2b_increment_counter( S, S->buflen );
+ blake2b_set_lastblock( S );
+ memset( S->buf + S->buflen, 0, 2 * BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */
+ blake2b_compress( S, S->buf );
+ memcpy( out, &S->h[0], outlen );
+ return 0;
+}
+
+
+int blake2b( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ blake2b_state S[1];
+
+ /* Verify parameters */
+ if ( NULL == in && inlen > 0 ) return -1;
+
+ if ( NULL == out ) return -1;
+
+ if( NULL == key && keylen > 0 ) return -1;
+
+ if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
+
+ if( keylen > BLAKE2B_KEYBYTES ) return -1;
+
+ if( keylen )
+ {
+ if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1;
+ }
+ else
+ {
+ if( blake2b_init( S, outlen ) < 0 ) return -1;
+ }
+
+ if( blake2b_update( S, ( uint8_t * )in, inlen ) < 0) return -1;
+ return blake2b_final( S, out, outlen );
+}
+
+#if defined(SUPERCOP)
+int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen )
+{
+ return blake2b( out, in, NULL, BLAKE2B_OUTBYTES, inlen, 0 );
+}
+#endif
diff --git a/contrib/libs/blake2/src/blake2bp.c b/contrib/libs/blake2/src/blake2bp.c
new file mode 100644
index 00000000000..45221611710
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2bp.c
@@ -0,0 +1,274 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#if defined(_OPENMP)
+#include <omp.h>
+#endif
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+#define PARALLELISM_DEGREE 4
+
+static int blake2bp_init_leaf( blake2b_state *S, uint8_t outlen, uint8_t keylen, uint64_t offset )
+{
+ blake2b_param P[1];
+ P->digest_length = outlen;
+ P->key_length = keylen;
+ P->fanout = PARALLELISM_DEGREE;
+ P->depth = 2;
+ store32(&P->leaf_length, 0);
+ store64(&P->node_offset, offset);
+ P->node_depth = 0;
+ P->inner_length = BLAKE2B_OUTBYTES;
+ memset( P->reserved, 0, sizeof( P->reserved ) );
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+ blake2b_init_param( S, P );
+ S->outlen = P->inner_length;
+ return 0;
+}
+
+static int blake2bp_init_root( blake2b_state *S, uint8_t outlen, uint8_t keylen )
+{
+ blake2b_param P[1];
+ P->digest_length = outlen;
+ P->key_length = keylen;
+ P->fanout = PARALLELISM_DEGREE;
+ P->depth = 2;
+ store32(&P->leaf_length, 0);
+ store64(&P->node_offset, 0);
+ P->node_depth = 1;
+ P->inner_length = BLAKE2B_OUTBYTES;
+ memset( P->reserved, 0, sizeof( P->reserved ) );
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+ blake2b_init_param( S, P );
+ S->outlen = P->digest_length;
+ return 0;
+}
+
+
+int blake2bp_init( blake2bp_state *S, size_t outlen )
+{
+ if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
+
+ memset( S->buf, 0, sizeof( S->buf ) );
+ S->buflen = 0;
+
+ if( blake2bp_init_root( S->R, ( uint8_t ) outlen, 0 ) < 0 )
+ return -1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ if( blake2bp_init_leaf( S->S[i], ( uint8_t ) outlen, 0, i ) < 0 ) return -1;
+
+ S->R->last_node = 1;
+ S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
+ S->outlen = ( uint8_t ) outlen;
+ return 0;
+}
+
+int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
+
+ if( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1;
+
+ memset( S->buf, 0, sizeof( S->buf ) );
+ S->buflen = 0;
+
+ if( blake2bp_init_root( S->R, ( uint8_t ) outlen, ( uint8_t ) keylen ) < 0 )
+ return -1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ if( blake2bp_init_leaf( S->S[i], ( uint8_t ) outlen, ( uint8_t ) keylen, i ) < 0 )
+ return -1;
+
+ S->R->last_node = 1;
+ S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
+ S->outlen = ( uint8_t ) outlen;
+ {
+ uint8_t block[BLAKE2B_BLOCKBYTES];
+ memset( block, 0, BLAKE2B_BLOCKBYTES );
+ memcpy( block, key, keylen );
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2b_update( S->S[i], block, BLAKE2B_BLOCKBYTES );
+
+ secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
+ }
+ return 0;
+}
+
+
+int blake2bp_update( blake2bp_state *S, const uint8_t *in, size_t inlen )
+{
+ size_t left = S->buflen;
+ size_t fill = sizeof( S->buf ) - left;
+
+ if( left && inlen >= fill )
+ {
+ memcpy( S->buf + left, in, fill );
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES );
+
+ in += fill;
+ inlen -= fill;
+ left = 0;
+ }
+
+#if defined(_OPENMP)
+ omp_set_num_threads(PARALLELISM_DEGREE);
+ #pragma omp parallel shared(S)
+#else
+ for( size_t id__ = 0; id__ < PARALLELISM_DEGREE; ++id__ )
+#endif
+ {
+#if defined(_OPENMP)
+ size_t id__ = ( size_t ) omp_get_thread_num();
+#endif
+ size_t inlen__ = inlen;
+ const uint8_t *in__ = ( const uint8_t * )in;
+ in__ += id__ * BLAKE2B_BLOCKBYTES;
+
+ while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES )
+ {
+ blake2b_update( S->S[id__], in__, BLAKE2B_BLOCKBYTES );
+ in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
+ inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
+ }
+ }
+
+ in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES );
+ inlen %= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
+
+ if( inlen > 0 )
+ memcpy( S->buf + left, in, inlen );
+
+ S->buflen = ( uint32_t ) left + ( uint32_t ) inlen;
+ return 0;
+}
+
+
+
+int blake2bp_final( blake2bp_state *S, uint8_t *out, size_t outlen )
+{
+ uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES];
+
+ if(S->outlen != outlen) return -1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ {
+ if( S->buflen > i * BLAKE2B_BLOCKBYTES )
+ {
+ size_t left = S->buflen - i * BLAKE2B_BLOCKBYTES;
+
+ if( left > BLAKE2B_BLOCKBYTES ) left = BLAKE2B_BLOCKBYTES;
+
+ blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, left );
+ }
+
+ blake2b_final( S->S[i], hash[i], BLAKE2B_OUTBYTES );
+ }
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2b_update( S->R, hash[i], BLAKE2B_OUTBYTES );
+
+ return blake2b_final( S->R, out, outlen );
+}
+
+int blake2bp( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES];
+ blake2b_state S[PARALLELISM_DEGREE][1];
+ blake2b_state FS[1];
+
+ /* Verify parameters */
+ if ( NULL == in && inlen > 0 ) return -1;
+
+ if ( NULL == out ) return -1;
+
+ if ( NULL == key && keylen > 0) return -1;
+
+ if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
+
+ if( keylen > BLAKE2B_KEYBYTES ) return -1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ if( blake2bp_init_leaf( S[i], ( uint8_t ) outlen, ( uint8_t ) keylen, i ) < 0 )
+ return -1;
+
+ S[PARALLELISM_DEGREE - 1]->last_node = 1; // mark last node
+
+ if( keylen > 0 )
+ {
+ uint8_t block[BLAKE2B_BLOCKBYTES];
+ memset( block, 0, BLAKE2B_BLOCKBYTES );
+ memcpy( block, key, keylen );
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2b_update( S[i], block, BLAKE2B_BLOCKBYTES );
+
+ secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
+ }
+
+#if defined(_OPENMP)
+ omp_set_num_threads(PARALLELISM_DEGREE);
+ #pragma omp parallel shared(S,hash)
+#else
+ for( size_t id__ = 0; id__ < PARALLELISM_DEGREE; ++id__ )
+#endif
+ {
+#if defined(_OPENMP)
+ size_t id__ = ( size_t ) omp_get_thread_num();
+#endif
+ size_t inlen__ = inlen;
+ const uint8_t *in__ = ( const uint8_t * )in;
+ in__ += id__ * BLAKE2B_BLOCKBYTES;
+
+ while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES )
+ {
+ blake2b_update( S[id__], in__, BLAKE2B_BLOCKBYTES );
+ in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
+ inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
+ }
+
+ if( inlen__ > id__ * BLAKE2B_BLOCKBYTES )
+ {
+ const size_t left = inlen__ - id__ * BLAKE2B_BLOCKBYTES;
+ const size_t len = left <= BLAKE2B_BLOCKBYTES ? left : BLAKE2B_BLOCKBYTES;
+ blake2b_update( S[id__], in__, len );
+ }
+
+ blake2b_final( S[id__], hash[id__], BLAKE2B_OUTBYTES );
+ }
+
+ if( blake2bp_init_root( FS, ( uint8_t ) outlen, ( uint8_t ) keylen ) < 0 )
+ return -1;
+
+ FS->last_node = 1; // Mark as last node
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2b_update( FS, hash[i], BLAKE2B_OUTBYTES );
+
+ return blake2b_final( FS, out, outlen );
+}
+
+
+
diff --git a/contrib/libs/blake2/src/blake2s-load-sse2.h b/contrib/libs/blake2/src/blake2s-load-sse2.h
new file mode 100644
index 00000000000..b24483cf931
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2s-load-sse2.h
@@ -0,0 +1,59 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2S_LOAD_SSE2_H__
+#define __BLAKE2S_LOAD_SSE2_H__
+
+#define LOAD_MSG_0_1(buf) buf = _mm_set_epi32(m6,m4,m2,m0)
+#define LOAD_MSG_0_2(buf) buf = _mm_set_epi32(m7,m5,m3,m1)
+#define LOAD_MSG_0_3(buf) buf = _mm_set_epi32(m14,m12,m10,m8)
+#define LOAD_MSG_0_4(buf) buf = _mm_set_epi32(m15,m13,m11,m9)
+#define LOAD_MSG_1_1(buf) buf = _mm_set_epi32(m13,m9,m4,m14)
+#define LOAD_MSG_1_2(buf) buf = _mm_set_epi32(m6,m15,m8,m10)
+#define LOAD_MSG_1_3(buf) buf = _mm_set_epi32(m5,m11,m0,m1)
+#define LOAD_MSG_1_4(buf) buf = _mm_set_epi32(m3,m7,m2,m12)
+#define LOAD_MSG_2_1(buf) buf = _mm_set_epi32(m15,m5,m12,m11)
+#define LOAD_MSG_2_2(buf) buf = _mm_set_epi32(m13,m2,m0,m8)
+#define LOAD_MSG_2_3(buf) buf = _mm_set_epi32(m9,m7,m3,m10)
+#define LOAD_MSG_2_4(buf) buf = _mm_set_epi32(m4,m1,m6,m14)
+#define LOAD_MSG_3_1(buf) buf = _mm_set_epi32(m11,m13,m3,m7)
+#define LOAD_MSG_3_2(buf) buf = _mm_set_epi32(m14,m12,m1,m9)
+#define LOAD_MSG_3_3(buf) buf = _mm_set_epi32(m15,m4,m5,m2)
+#define LOAD_MSG_3_4(buf) buf = _mm_set_epi32(m8,m0,m10,m6)
+#define LOAD_MSG_4_1(buf) buf = _mm_set_epi32(m10,m2,m5,m9)
+#define LOAD_MSG_4_2(buf) buf = _mm_set_epi32(m15,m4,m7,m0)
+#define LOAD_MSG_4_3(buf) buf = _mm_set_epi32(m3,m6,m11,m14)
+#define LOAD_MSG_4_4(buf) buf = _mm_set_epi32(m13,m8,m12,m1)
+#define LOAD_MSG_5_1(buf) buf = _mm_set_epi32(m8,m0,m6,m2)
+#define LOAD_MSG_5_2(buf) buf = _mm_set_epi32(m3,m11,m10,m12)
+#define LOAD_MSG_5_3(buf) buf = _mm_set_epi32(m1,m15,m7,m4)
+#define LOAD_MSG_5_4(buf) buf = _mm_set_epi32(m9,m14,m5,m13)
+#define LOAD_MSG_6_1(buf) buf = _mm_set_epi32(m4,m14,m1,m12)
+#define LOAD_MSG_6_2(buf) buf = _mm_set_epi32(m10,m13,m15,m5)
+#define LOAD_MSG_6_3(buf) buf = _mm_set_epi32(m8,m9,m6,m0)
+#define LOAD_MSG_6_4(buf) buf = _mm_set_epi32(m11,m2,m3,m7)
+#define LOAD_MSG_7_1(buf) buf = _mm_set_epi32(m3,m12,m7,m13)
+#define LOAD_MSG_7_2(buf) buf = _mm_set_epi32(m9,m1,m14,m11)
+#define LOAD_MSG_7_3(buf) buf = _mm_set_epi32(m2,m8,m15,m5)
+#define LOAD_MSG_7_4(buf) buf = _mm_set_epi32(m10,m6,m4,m0)
+#define LOAD_MSG_8_1(buf) buf = _mm_set_epi32(m0,m11,m14,m6)
+#define LOAD_MSG_8_2(buf) buf = _mm_set_epi32(m8,m3,m9,m15)
+#define LOAD_MSG_8_3(buf) buf = _mm_set_epi32(m10,m1,m13,m12)
+#define LOAD_MSG_8_4(buf) buf = _mm_set_epi32(m5,m4,m7,m2)
+#define LOAD_MSG_9_1(buf) buf = _mm_set_epi32(m1,m7,m8,m10)
+#define LOAD_MSG_9_2(buf) buf = _mm_set_epi32(m5,m6,m4,m2)
+#define LOAD_MSG_9_3(buf) buf = _mm_set_epi32(m13,m3,m9,m15)
+#define LOAD_MSG_9_4(buf) buf = _mm_set_epi32(m0,m12,m14,m11)
+
+
+#endif
diff --git a/contrib/libs/blake2/src/blake2s-load-sse41.h b/contrib/libs/blake2/src/blake2s-load-sse41.h
new file mode 100644
index 00000000000..3ac12eb6f5d
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2s-load-sse41.h
@@ -0,0 +1,229 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2S_LOAD_SSE41_H__
+#define __BLAKE2S_LOAD_SSE41_H__
+
+#define LOAD_MSG_0_1(buf) \
+buf = TOI(_mm_shuffle_ps(TOF(m0), TOF(m1), _MM_SHUFFLE(2,0,2,0)));
+
+#define LOAD_MSG_0_2(buf) \
+buf = TOI(_mm_shuffle_ps(TOF(m0), TOF(m1), _MM_SHUFFLE(3,1,3,1)));
+
+#define LOAD_MSG_0_3(buf) \
+buf = TOI(_mm_shuffle_ps(TOF(m2), TOF(m3), _MM_SHUFFLE(2,0,2,0)));
+
+#define LOAD_MSG_0_4(buf) \
+buf = TOI(_mm_shuffle_ps(TOF(m2), TOF(m3), _MM_SHUFFLE(3,1,3,1)));
+
+#define LOAD_MSG_1_1(buf) \
+t0 = _mm_blend_epi16(m1, m2, 0x0C); \
+t1 = _mm_slli_si128(m3, 4); \
+t2 = _mm_blend_epi16(t0, t1, 0xF0); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,1,0,3));
+
+#define LOAD_MSG_1_2(buf) \
+t0 = _mm_shuffle_epi32(m2,_MM_SHUFFLE(0,0,2,0)); \
+t1 = _mm_blend_epi16(m1,m3,0xC0); \
+t2 = _mm_blend_epi16(t0, t1, 0xF0); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,3,0,1));
+
+#define LOAD_MSG_1_3(buf) \
+t0 = _mm_slli_si128(m1, 4); \
+t1 = _mm_blend_epi16(m2, t0, 0x30); \
+t2 = _mm_blend_epi16(m0, t1, 0xF0); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,3,0,1));
+
+#define LOAD_MSG_1_4(buf) \
+t0 = _mm_unpackhi_epi32(m0,m1); \
+t1 = _mm_slli_si128(m3, 4); \
+t2 = _mm_blend_epi16(t0, t1, 0x0C); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,3,0,1));
+
+#define LOAD_MSG_2_1(buf) \
+t0 = _mm_unpackhi_epi32(m2,m3); \
+t1 = _mm_blend_epi16(m3,m1,0x0C); \
+t2 = _mm_blend_epi16(t0, t1, 0x0F); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(3,1,0,2));
+
+#define LOAD_MSG_2_2(buf) \
+t0 = _mm_unpacklo_epi32(m2,m0); \
+t1 = _mm_blend_epi16(t0, m0, 0xF0); \
+t2 = _mm_slli_si128(m3, 8); \
+buf = _mm_blend_epi16(t1, t2, 0xC0);
+
+#define LOAD_MSG_2_3(buf) \
+t0 = _mm_blend_epi16(m0, m2, 0x3C); \
+t1 = _mm_srli_si128(m1, 12); \
+t2 = _mm_blend_epi16(t0,t1,0x03); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,0,3,2));
+
+#define LOAD_MSG_2_4(buf) \
+t0 = _mm_slli_si128(m3, 4); \
+t1 = _mm_blend_epi16(m0, m1, 0x33); \
+t2 = _mm_blend_epi16(t1, t0, 0xC0); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(0,1,2,3));
+
+#define LOAD_MSG_3_1(buf) \
+t0 = _mm_unpackhi_epi32(m0,m1); \
+t1 = _mm_unpackhi_epi32(t0, m2); \
+t2 = _mm_blend_epi16(t1, m3, 0x0C); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(3,1,0,2));
+
+#define LOAD_MSG_3_2(buf) \
+t0 = _mm_slli_si128(m2, 8); \
+t1 = _mm_blend_epi16(m3,m0,0x0C); \
+t2 = _mm_blend_epi16(t1, t0, 0xC0); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,0,1,3));
+
+#define LOAD_MSG_3_3(buf) \
+t0 = _mm_blend_epi16(m0,m1,0x0F); \
+t1 = _mm_blend_epi16(t0, m3, 0xC0); \
+buf = _mm_shuffle_epi32(t1, _MM_SHUFFLE(3,0,1,2));
+
+#define LOAD_MSG_3_4(buf) \
+t0 = _mm_unpacklo_epi32(m0,m2); \
+t1 = _mm_unpackhi_epi32(m1,m2); \
+buf = _mm_unpacklo_epi64(t1,t0);
+
+#define LOAD_MSG_4_1(buf) \
+t0 = _mm_unpacklo_epi64(m1,m2); \
+t1 = _mm_unpackhi_epi64(m0,m2); \
+t2 = _mm_blend_epi16(t0,t1,0x33); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,0,1,3));
+
+#define LOAD_MSG_4_2(buf) \
+t0 = _mm_unpackhi_epi64(m1,m3); \
+t1 = _mm_unpacklo_epi64(m0,m1); \
+buf = _mm_blend_epi16(t0,t1,0x33);
+
+#define LOAD_MSG_4_3(buf) \
+t0 = _mm_unpackhi_epi64(m3,m1); \
+t1 = _mm_unpackhi_epi64(m2,m0); \
+buf = _mm_blend_epi16(t1,t0,0x33);
+
+#define LOAD_MSG_4_4(buf) \
+t0 = _mm_blend_epi16(m0,m2,0x03); \
+t1 = _mm_slli_si128(t0, 8); \
+t2 = _mm_blend_epi16(t1,m3,0x0F); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,2,0,3));
+
+#define LOAD_MSG_5_1(buf) \
+t0 = _mm_unpackhi_epi32(m0,m1); \
+t1 = _mm_unpacklo_epi32(m0,m2); \
+buf = _mm_unpacklo_epi64(t0,t1);
+
+#define LOAD_MSG_5_2(buf) \
+t0 = _mm_srli_si128(m2, 4); \
+t1 = _mm_blend_epi16(m0,m3,0x03); \
+buf = _mm_blend_epi16(t1,t0,0x3C);
+
+#define LOAD_MSG_5_3(buf) \
+t0 = _mm_blend_epi16(m1,m0,0x0C); \
+t1 = _mm_srli_si128(m3, 4); \
+t2 = _mm_blend_epi16(t0,t1,0x30); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,2,3,0));
+
+#define LOAD_MSG_5_4(buf) \
+t0 = _mm_unpacklo_epi64(m1,m2); \
+t1= _mm_shuffle_epi32(m3, _MM_SHUFFLE(0,2,0,1)); \
+buf = _mm_blend_epi16(t0,t1,0x33);
+
+#define LOAD_MSG_6_1(buf) \
+t0 = _mm_slli_si128(m1, 12); \
+t1 = _mm_blend_epi16(m0,m3,0x33); \
+buf = _mm_blend_epi16(t1,t0,0xC0);
+
+#define LOAD_MSG_6_2(buf) \
+t0 = _mm_blend_epi16(m3,m2,0x30); \
+t1 = _mm_srli_si128(m1, 4); \
+t2 = _mm_blend_epi16(t0,t1,0x03); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,1,3,0));
+
+#define LOAD_MSG_6_3(buf) \
+t0 = _mm_unpacklo_epi64(m0,m2); \
+t1 = _mm_srli_si128(m1, 4); \
+buf = _mm_shuffle_epi32(_mm_blend_epi16(t0,t1,0x0C), _MM_SHUFFLE(2,3,1,0));
+
+#define LOAD_MSG_6_4(buf) \
+t0 = _mm_unpackhi_epi32(m1,m2); \
+t1 = _mm_unpackhi_epi64(m0,t0); \
+buf = _mm_shuffle_epi32(t1, _MM_SHUFFLE(3,0,1,2));
+
+#define LOAD_MSG_7_1(buf) \
+t0 = _mm_unpackhi_epi32(m0,m1); \
+t1 = _mm_blend_epi16(t0,m3,0x0F); \
+buf = _mm_shuffle_epi32(t1,_MM_SHUFFLE(2,0,3,1));
+
+#define LOAD_MSG_7_2(buf) \
+t0 = _mm_blend_epi16(m2,m3,0x30); \
+t1 = _mm_srli_si128(m0,4); \
+t2 = _mm_blend_epi16(t0,t1,0x03); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,0,2,3));
+
+#define LOAD_MSG_7_3(buf) \
+t0 = _mm_unpackhi_epi64(m0,m3); \
+t1 = _mm_unpacklo_epi64(m1,m2); \
+t2 = _mm_blend_epi16(t0,t1,0x3C); \
+buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(0,2,3,1));
+
+#define LOAD_MSG_7_4(buf) \
+t0 = _mm_unpacklo_epi32(m0,m1); \
+t1 = _mm_unpackhi_epi32(m1,m2); \
+buf = _mm_unpacklo_epi64(t0,t1);
+
+#define LOAD_MSG_8_1(buf) \
+t0 = _mm_unpackhi_epi32(m1,m3); \
+t1 = _mm_unpacklo_epi64(t0,m0); \
+t2 = _mm_blend_epi16(t1,m2,0xC0); \
+buf = _mm_shufflehi_epi16(t2,_MM_SHUFFLE(1,0,3,2));
+
+#define LOAD_MSG_8_2(buf) \
+t0 = _mm_unpackhi_epi32(m0,m3); \
+t1 = _mm_blend_epi16(m2,t0,0xF0); \
+buf = _mm_shuffle_epi32(t1,_MM_SHUFFLE(0,2,1,3));
+
+#define LOAD_MSG_8_3(buf) \
+t0 = _mm_blend_epi16(m2,m0,0x0C); \
+t1 = _mm_slli_si128(t0,4); \
+buf = _mm_blend_epi16(t1,m3,0x0F);
+
+#define LOAD_MSG_8_4(buf) \
+t0 = _mm_blend_epi16(m1,m0,0x30); \
+buf = _mm_shuffle_epi32(t0,_MM_SHUFFLE(1,0,3,2));
+
+#define LOAD_MSG_9_1(buf) \
+t0 = _mm_blend_epi16(m0,m2,0x03); \
+t1 = _mm_blend_epi16(m1,m2,0x30); \
+t2 = _mm_blend_epi16(t1,t0,0x0F); \
+buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(1,3,0,2));
+
+#define LOAD_MSG_9_2(buf) \
+t0 = _mm_slli_si128(m0,4); \
+t1 = _mm_blend_epi16(m1,t0,0xC0); \
+buf = _mm_shuffle_epi32(t1,_MM_SHUFFLE(1,2,0,3));
+
+#define LOAD_MSG_9_3(buf) \
+t0 = _mm_unpackhi_epi32(m0,m3); \
+t1 = _mm_unpacklo_epi32(m2,m3); \
+t2 = _mm_unpackhi_epi64(t0,t1); \
+buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(3,0,2,1));
+
+#define LOAD_MSG_9_4(buf) \
+t0 = _mm_blend_epi16(m3,m2,0xC0); \
+t1 = _mm_unpacklo_epi32(m0,m3); \
+t2 = _mm_blend_epi16(t0,t1,0x0F); \
+buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(0,1,2,3));
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2s-load-xop.h b/contrib/libs/blake2/src/blake2s-load-xop.h
new file mode 100644
index 00000000000..ac591a77d19
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2s-load-xop.h
@@ -0,0 +1,189 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2S_LOAD_XOP_H__
+#define __BLAKE2S_LOAD_XOP_H__
+
+#define TOB(x) ((x)*4*0x01010101 + 0x03020100) // ..or not TOB
+
+/* Basic VPPERM emulation, for testing purposes */
+/*static __m128i _mm_perm_epi8(const __m128i src1, const __m128i src2, const __m128i sel)
+{
+ const __m128i sixteen = _mm_set1_epi8(16);
+ const __m128i t0 = _mm_shuffle_epi8(src1, sel);
+ const __m128i s1 = _mm_shuffle_epi8(src2, _mm_sub_epi8(sel, sixteen));
+ const __m128i mask = _mm_or_si128(_mm_cmpeq_epi8(sel, sixteen),
+ _mm_cmpgt_epi8(sel, sixteen)); // (>=16) = 0xff : 00
+ return _mm_blendv_epi8(t0, s1, mask);
+}*/
+
+#define LOAD_MSG_0_1(buf) \
+buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(6),TOB(4),TOB(2),TOB(0)) );
+
+#define LOAD_MSG_0_2(buf) \
+buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(7),TOB(5),TOB(3),TOB(1)) );
+
+#define LOAD_MSG_0_3(buf) \
+buf = _mm_perm_epi8(m2, m3, _mm_set_epi32(TOB(6),TOB(4),TOB(2),TOB(0)) );
+
+#define LOAD_MSG_0_4(buf) \
+buf = _mm_perm_epi8(m2, m3, _mm_set_epi32(TOB(7),TOB(5),TOB(3),TOB(1)) );
+
+#define LOAD_MSG_1_1(buf) \
+t0 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(0),TOB(5),TOB(0),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(5),TOB(2),TOB(1),TOB(6)) );
+
+#define LOAD_MSG_1_2(buf) \
+t1 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(2),TOB(0),TOB(4),TOB(6)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(7),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_1_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(5),TOB(0),TOB(0),TOB(1)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(7),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_1_4(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(3),TOB(7),TOB(2),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(4)) );
+
+#define LOAD_MSG_2_1(buf) \
+t0 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(0),TOB(1),TOB(0),TOB(7)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(7),TOB(2),TOB(4),TOB(0)) );
+
+#define LOAD_MSG_2_2(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(2),TOB(0),TOB(4)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(5),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_2_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(7),TOB(3),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(5),TOB(2),TOB(1),TOB(6)) );
+
+#define LOAD_MSG_2_4(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(4),TOB(1),TOB(6),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(6)) );
+
+#define LOAD_MSG_3_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(3),TOB(7)) ); \
+t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(7),TOB(2),TOB(1),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(5),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_3_2(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(0),TOB(1),TOB(5)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(6),TOB(4),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_3_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(4),TOB(5),TOB(2)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(7),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_3_4(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(0),TOB(6)) ); \
+buf = _mm_perm_epi8(t1, m2, _mm_set_epi32(TOB(4),TOB(2),TOB(6),TOB(0)) );
+
+#define LOAD_MSG_4_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(2),TOB(5),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(6),TOB(2),TOB(1),TOB(5)) );
+
+#define LOAD_MSG_4_2(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(4),TOB(7),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(7),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_4_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(3),TOB(6),TOB(0),TOB(0)) ); \
+t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(2),TOB(7),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(6)) );
+
+#define LOAD_MSG_4_4(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(4),TOB(0),TOB(1)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(5),TOB(2),TOB(4),TOB(0)) );
+
+#define LOAD_MSG_5_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(6),TOB(2)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(4),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_5_2(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(3),TOB(7),TOB(6),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(4)) );
+
+#define LOAD_MSG_5_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(1),TOB(0),TOB(7),TOB(4)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(7),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_5_4(buf) \
+t1 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(5),TOB(0),TOB(1),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(6),TOB(1),TOB(5)) );
+
+#define LOAD_MSG_6_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(4),TOB(0),TOB(1),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(6),TOB(1),TOB(4)) );
+
+#define LOAD_MSG_6_2(buf) \
+t1 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(6),TOB(0),TOB(0),TOB(1)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(5),TOB(7),TOB(0)) );
+
+#define LOAD_MSG_6_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(6),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(4),TOB(5),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_6_4(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(2),TOB(3),TOB(7)) ); \
+buf = _mm_perm_epi8(t1, m2, _mm_set_epi32(TOB(7),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_7_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(3),TOB(0),TOB(7),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(4),TOB(1),TOB(5)) );
+
+#define LOAD_MSG_7_2(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(5),TOB(1),TOB(0),TOB(7)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(6),TOB(0)) );
+
+#define LOAD_MSG_7_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(2),TOB(0),TOB(0),TOB(5)) ); \
+t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(4),TOB(1),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(7),TOB(0)) );
+
+#define LOAD_MSG_7_4(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(6),TOB(4),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m2, _mm_set_epi32(TOB(6),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_8_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(0),TOB(6)) ); \
+t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(7),TOB(1),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(6),TOB(0)) );
+
+#define LOAD_MSG_8_2(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(4),TOB(3),TOB(5),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(7)) );
+
+#define LOAD_MSG_8_3(buf) \
+t0 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(6),TOB(1),TOB(0),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(5),TOB(4)) ); \
+
+#define LOAD_MSG_8_4(buf) \
+buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(5),TOB(4),TOB(7),TOB(2)) );
+
+#define LOAD_MSG_9_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(1),TOB(7),TOB(0),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(2),TOB(4),TOB(6)) );
+
+#define LOAD_MSG_9_2(buf) \
+buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(5),TOB(6),TOB(4),TOB(2)) );
+
+#define LOAD_MSG_9_3(buf) \
+t0 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(3),TOB(5),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(5),TOB(2),TOB(1),TOB(7)) );
+
+#define LOAD_MSG_9_4(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(0),TOB(0),TOB(7)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(4),TOB(6),TOB(0)) );
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2s-ref.c b/contrib/libs/blake2/src/blake2s-ref.c
new file mode 100644
index 00000000000..db7390f9252
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2s-ref.c
@@ -0,0 +1,375 @@
+/*
+ BLAKE2 reference source code package - reference C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+static const uint32_t blake2s_IV[8] =
+{
+ 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
+ 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
+};
+
+static const uint8_t blake2s_sigma[10][16] =
+{
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
+ { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
+ { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
+ { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
+ { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
+ { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
+ { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
+ { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
+ { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
+};
+
+static inline int blake2s_set_lastnode( blake2s_state *S )
+{
+ S->f[1] = ~0U;
+ return 0;
+}
+
+static inline int blake2s_clear_lastnode( blake2s_state *S )
+{
+ S->f[1] = 0U;
+ return 0;
+}
+
+/* Some helper functions, not necessarily useful */
+static inline int blake2s_set_lastblock( blake2s_state *S )
+{
+ if( S->last_node ) blake2s_set_lastnode( S );
+
+ S->f[0] = ~0U;
+ return 0;
+}
+
+static inline int blake2s_clear_lastblock( blake2s_state *S )
+{
+ if( S->last_node ) blake2s_clear_lastnode( S );
+
+ S->f[0] = 0U;
+ return 0;
+}
+
+static inline int blake2s_increment_counter( blake2s_state *S, const uint32_t inc )
+{
+ S->t[0] += inc;
+ S->t[1] += ( S->t[0] < inc );
+ return 0;
+}
+
+// Parameter-related functions
+static inline int blake2s_param_set_digest_length( blake2s_param *P, const uint8_t digest_length )
+{
+ P->digest_length = digest_length;
+ return 0;
+}
+
+static inline int blake2s_param_set_fanout( blake2s_param *P, const uint8_t fanout )
+{
+ P->fanout = fanout;
+ return 0;
+}
+
+static inline int blake2s_param_set_max_depth( blake2s_param *P, const uint8_t depth )
+{
+ P->depth = depth;
+ return 0;
+}
+
+static inline int blake2s_param_set_leaf_length( blake2s_param *P, const uint32_t leaf_length )
+{
+ store32( &P->leaf_length, leaf_length );
+ return 0;
+}
+
+static inline int blake2s_param_set_node_offset( blake2s_param *P, const uint64_t node_offset )
+{
+ store48( P->node_offset, node_offset );
+ return 0;
+}
+
+static inline int blake2s_param_set_node_depth( blake2s_param *P, const uint8_t node_depth )
+{
+ P->node_depth = node_depth;
+ return 0;
+}
+
+static inline int blake2s_param_set_inner_length( blake2s_param *P, const uint8_t inner_length )
+{
+ P->inner_length = inner_length;
+ return 0;
+}
+
+static inline int blake2s_param_set_salt( blake2s_param *P, const uint8_t salt[BLAKE2S_SALTBYTES] )
+{
+ memcpy( P->salt, salt, BLAKE2S_SALTBYTES );
+ return 0;
+}
+
+static inline int blake2s_param_set_personal( blake2s_param *P, const uint8_t personal[BLAKE2S_PERSONALBYTES] )
+{
+ memcpy( P->personal, personal, BLAKE2S_PERSONALBYTES );
+ return 0;
+}
+
+static inline int blake2s_init0( blake2s_state *S )
+{
+ memset( S, 0, sizeof( blake2s_state ) );
+
+ for( int i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i];
+
+ return 0;
+}
+
+#define blake2s_init BLAKE2_IMPL_NAME(blake2s_init)
+#define blake2s_init_param BLAKE2_IMPL_NAME(blake2s_init_param)
+#define blake2s_init_key BLAKE2_IMPL_NAME(blake2s_init_key)
+#define blake2s_update BLAKE2_IMPL_NAME(blake2s_update)
+#define blake2s_final BLAKE2_IMPL_NAME(blake2s_final)
+#define blake2s BLAKE2_IMPL_NAME(blake2s)
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+ int blake2s_init( blake2s_state *S, size_t outlen );
+ int blake2s_init_param( blake2s_state *S, const blake2s_param *P );
+ int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_update( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+#if defined(__cplusplus)
+}
+#endif
+
+/* init2 xors IV with input parameter block */
+int blake2s_init_param( blake2s_state *S, const blake2s_param *P )
+{
+ blake2s_init0( S );
+ uint32_t *p = ( uint32_t * )( P );
+
+ /* IV XOR ParamBlock */
+ for( size_t i = 0; i < 8; ++i )
+ S->h[i] ^= load32( &p[i] );
+
+ S->outlen = P->digest_length;
+ return 0;
+}
+
+
+// Sequential blake2s initialization
+int blake2s_init( blake2s_state *S, size_t outlen )
+{
+ blake2s_param P[1];
+
+ /* Move interval verification here? */
+ if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;
+
+ P->digest_length = ( uint8_t) outlen;
+ P->key_length = 0;
+ P->fanout = 1;
+ P->depth = 1;
+ store32( &P->leaf_length, 0 );
+ store48( &P->node_offset, 0 );
+ P->node_depth = 0;
+ P->inner_length = 0;
+ // memset(P->reserved, 0, sizeof(P->reserved) );
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+ return blake2s_init_param( S, P );
+}
+
+int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ blake2s_param P[1];
+
+ if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;
+
+ if ( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1;
+
+ P->digest_length = ( uint8_t ) outlen;
+ P->key_length = ( uint8_t ) keylen;
+ P->fanout = 1;
+ P->depth = 1;
+ store32( &P->leaf_length, 0 );
+ store48( &P->node_offset, 0 );
+ P->node_depth = 0;
+ P->inner_length = 0;
+ // memset(P->reserved, 0, sizeof(P->reserved) );
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+
+ if( blake2s_init_param( S, P ) < 0 ) return -1;
+
+ {
+ uint8_t block[BLAKE2S_BLOCKBYTES];
+ memset( block, 0, BLAKE2S_BLOCKBYTES );
+ memcpy( block, key, keylen );
+ blake2s_update( S, block, BLAKE2S_BLOCKBYTES );
+ secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
+ }
+ return 0;
+}
+
+static int blake2s_compress( blake2s_state *S, const uint8_t block[BLAKE2S_BLOCKBYTES] )
+{
+ uint32_t m[16];
+ uint32_t v[16];
+
+ for( size_t i = 0; i < 16; ++i )
+ m[i] = load32( block + i * sizeof( m[i] ) );
+
+ for( size_t i = 0; i < 8; ++i )
+ v[i] = S->h[i];
+
+ v[ 8] = blake2s_IV[0];
+ v[ 9] = blake2s_IV[1];
+ v[10] = blake2s_IV[2];
+ v[11] = blake2s_IV[3];
+ v[12] = S->t[0] ^ blake2s_IV[4];
+ v[13] = S->t[1] ^ blake2s_IV[5];
+ v[14] = S->f[0] ^ blake2s_IV[6];
+ v[15] = S->f[1] ^ blake2s_IV[7];
+#define G(r,i,a,b,c,d) \
+ do { \
+ a = a + b + m[blake2s_sigma[r][2*i+0]]; \
+ d = rotr32(d ^ a, 16); \
+ c = c + d; \
+ b = rotr32(b ^ c, 12); \
+ a = a + b + m[blake2s_sigma[r][2*i+1]]; \
+ d = rotr32(d ^ a, 8); \
+ c = c + d; \
+ b = rotr32(b ^ c, 7); \
+ } while(0)
+#define ROUND(r) \
+ do { \
+ G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
+ G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
+ G(r,2,v[ 2],v[ 6],v[10],v[14]); \
+ G(r,3,v[ 3],v[ 7],v[11],v[15]); \
+ G(r,4,v[ 0],v[ 5],v[10],v[15]); \
+ G(r,5,v[ 1],v[ 6],v[11],v[12]); \
+ G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
+ G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
+ } while(0)
+ ROUND( 0 );
+ ROUND( 1 );
+ ROUND( 2 );
+ ROUND( 3 );
+ ROUND( 4 );
+ ROUND( 5 );
+ ROUND( 6 );
+ ROUND( 7 );
+ ROUND( 8 );
+ ROUND( 9 );
+
+ for( size_t i = 0; i < 8; ++i )
+ S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
+
+#undef G
+#undef ROUND
+ return 0;
+}
+
+
+int blake2s_update( blake2s_state *S, const uint8_t *in, size_t inlen )
+{
+ while( inlen > 0 )
+ {
+ uint32_t left = S->buflen;
+ uint32_t fill = 2 * BLAKE2S_BLOCKBYTES - left;
+
+ if( inlen > fill )
+ {
+ memcpy( S->buf + left, in, fill ); // Fill buffer
+ S->buflen += fill;
+ blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
+ blake2s_compress( S, S->buf ); // Compress
+ memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); // Shift buffer left
+ S->buflen -= BLAKE2S_BLOCKBYTES;
+ in += fill;
+ inlen -= fill;
+ }
+ else // inlen <= fill
+ {
+ memcpy( S->buf + left, in, inlen );
+ S->buflen += ( uint32_t ) inlen; // Be lazy, do not compress
+ in += inlen;
+ inlen -= inlen;
+ }
+ }
+
+ return 0;
+}
+
+int blake2s_final( blake2s_state *S, uint8_t *out, size_t outlen )
+{
+ uint8_t buffer[BLAKE2S_OUTBYTES];
+ size_t i;
+
+ if(S->outlen != outlen) return -1;
+
+ if( S->buflen > BLAKE2S_BLOCKBYTES )
+ {
+ blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
+ blake2s_compress( S, S->buf );
+ S->buflen -= BLAKE2S_BLOCKBYTES;
+ memmove( S->buf, S->buf + BLAKE2S_BLOCKBYTES, S->buflen );
+ }
+
+ blake2s_increment_counter( S, ( uint32_t )S->buflen );
+ blake2s_set_lastblock( S );
+ memset( S->buf + S->buflen, 0, 2 * BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */
+ blake2s_compress( S, S->buf );
+
+ for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */
+ store32( buffer + sizeof( S->h[i] ) * i, S->h[i] );
+
+ memcpy( out, buffer, outlen );
+ return 0;
+}
+
+int blake2s( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ blake2s_state S[1];
+
+ /* Verify parameters */
+ if ( NULL == in && inlen > 0 ) return -1;
+
+ if ( NULL == out ) return -1;
+
+ if ( NULL == key && keylen > 0 ) return -1;
+
+ if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+ if( keylen > BLAKE2S_KEYBYTES ) return -1;
+
+ if( keylen > 0 )
+ {
+ if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1;
+ }
+ else
+ {
+ if( blake2s_init( S, outlen ) < 0 ) return -1;
+ }
+
+ if( blake2s_update( S, ( uint8_t * )in, inlen ) < 0) return -1;
+ return blake2s_final( S, out, outlen );
+}
+
diff --git a/contrib/libs/blake2/src/blake2s-round.h b/contrib/libs/blake2/src/blake2s-round.h
new file mode 100644
index 00000000000..1e2f2b7f59b
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2s-round.h
@@ -0,0 +1,91 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2S_ROUND_H__
+#define __BLAKE2S_ROUND_H__
+
+#define LOAD(p) _mm_load_si128( (__m128i *)(p) )
+#define STORE(p,r) _mm_store_si128((__m128i *)(p), r)
+
+#define LOADU(p) _mm_loadu_si128( (__m128i *)(p) )
+#define STOREU(p,r) _mm_storeu_si128((__m128i *)(p), r)
+
+#define TOF(reg) _mm_castsi128_ps((reg))
+#define TOI(reg) _mm_castps_si128((reg))
+
+#define LIKELY(x) __builtin_expect((x),1)
+
+
+/* Microarchitecture-specific macros */
+#ifndef HAVE_XOP
+#ifdef HAVE_SSSE3
+#define _mm_roti_epi32(r, c) ( \
+ (8==-(c)) ? _mm_shuffle_epi8(r,r8) \
+ : (16==-(c)) ? _mm_shuffle_epi8(r,r16) \
+ : _mm_xor_si128(_mm_srli_epi32( (r), -(c) ),_mm_slli_epi32( (r), 32-(-(c)) )) )
+#else
+#define _mm_roti_epi32(r, c) _mm_xor_si128(_mm_srli_epi32( (r), -(c) ),_mm_slli_epi32( (r), 32-(-(c)) ))
+#endif
+#else
+/* ... */
+#endif
+
+
+#define G1(row1,row2,row3,row4,buf) \
+ row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
+ row4 = _mm_xor_si128( row4, row1 ); \
+ row4 = _mm_roti_epi32(row4, -16); \
+ row3 = _mm_add_epi32( row3, row4 ); \
+ row2 = _mm_xor_si128( row2, row3 ); \
+ row2 = _mm_roti_epi32(row2, -12);
+
+#define G2(row1,row2,row3,row4,buf) \
+ row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
+ row4 = _mm_xor_si128( row4, row1 ); \
+ row4 = _mm_roti_epi32(row4, -8); \
+ row3 = _mm_add_epi32( row3, row4 ); \
+ row2 = _mm_xor_si128( row2, row3 ); \
+ row2 = _mm_roti_epi32(row2, -7);
+
+#define DIAGONALIZE(row1,row2,row3,row4) \
+ row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(2,1,0,3) ); \
+ row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
+ row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(0,3,2,1) );
+
+#define UNDIAGONALIZE(row1,row2,row3,row4) \
+ row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(0,3,2,1) ); \
+ row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
+ row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(2,1,0,3) );
+
+#if defined(HAVE_XOP)
+#include "blake2s-load-xop.h"
+#elif defined(HAVE_SSE4_1)
+#include "blake2s-load-sse41.h"
+#else
+#include "blake2s-load-sse2.h"
+#endif
+
+#define ROUND(r) \
+ LOAD_MSG_ ##r ##_1(buf1); \
+ G1(row1,row2,row3,row4,buf1); \
+ LOAD_MSG_ ##r ##_2(buf2); \
+ G2(row1,row2,row3,row4,buf2); \
+ DIAGONALIZE(row1,row2,row3,row4); \
+ LOAD_MSG_ ##r ##_3(buf3); \
+ G1(row1,row2,row3,row4,buf3); \
+ LOAD_MSG_ ##r ##_4(buf4); \
+ G2(row1,row2,row3,row4,buf4); \
+ UNDIAGONALIZE(row1,row2,row3,row4); \
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2s.c b/contrib/libs/blake2/src/blake2s.c
new file mode 100644
index 00000000000..db979748950
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2s.c
@@ -0,0 +1,422 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+#include "blake2-config.h"
+
+#if defined(_MSC_VER)
+#include <intrin.h>
+#endif
+
+#if defined(HAVE_SSE2)
+#include <emmintrin.h>
+// MSVC only defines _mm_set_epi64x for x86_64...
+#if defined(_MSC_VER) && !defined(_M_X64) && !defined(__clang__)
+static inline __m128i _mm_set_epi64x( const uint64_t u1, const uint64_t u0 )
+{
+ return _mm_set_epi32( u1 >> 32, u1, u0 >> 32, u0 );
+}
+#endif
+#endif
+
+
+#if defined(HAVE_SSSE3)
+#include <tmmintrin.h>
+#endif
+#if defined(HAVE_SSE4_1)
+#include <smmintrin.h>
+#endif
+#if defined(HAVE_AVX)
+#include <immintrin.h>
+#endif
+#if defined(HAVE_XOP) && !defined(_MSC_VER)
+#include <x86intrin.h>
+#endif
+
+#include "blake2s-round.h"
+
+static const uint32_t blake2s_IV[8] =
+{
+ 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
+ 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
+};
+
+static const uint8_t blake2s_sigma[10][16] =
+{
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
+ { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
+ { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
+ { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
+ { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
+ { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
+ { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
+ { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
+ { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
+};
+
+
+/* Some helper functions, not necessarily useful */
+static inline int blake2s_set_lastnode( blake2s_state *S )
+{
+ S->f[1] = ~0U;
+ return 0;
+}
+
+static inline int blake2s_clear_lastnode( blake2s_state *S )
+{
+ S->f[1] = 0U;
+ return 0;
+}
+
+static inline int blake2s_set_lastblock( blake2s_state *S )
+{
+ if( S->last_node ) blake2s_set_lastnode( S );
+
+ S->f[0] = ~0U;
+ return 0;
+}
+
+static inline int blake2s_clear_lastblock( blake2s_state *S )
+{
+ if( S->last_node ) blake2s_clear_lastnode( S );
+
+ S->f[0] = 0U;
+ return 0;
+}
+
+static inline int blake2s_increment_counter( blake2s_state *S, const uint32_t inc )
+{
+ uint64_t t = ( ( uint64_t )S->t[1] << 32 ) | S->t[0];
+ t += inc;
+ S->t[0] = ( uint32_t )( t >> 0 );
+ S->t[1] = ( uint32_t )( t >> 32 );
+ return 0;
+}
+
+
+// Parameter-related functions
+static inline int blake2s_param_set_digest_length( blake2s_param *P, const uint8_t digest_length )
+{
+ P->digest_length = digest_length;
+ return 0;
+}
+
+static inline int blake2s_param_set_fanout( blake2s_param *P, const uint8_t fanout )
+{
+ P->fanout = fanout;
+ return 0;
+}
+
+static inline int blake2s_param_set_max_depth( blake2s_param *P, const uint8_t depth )
+{
+ P->depth = depth;
+ return 0;
+}
+
+static inline int blake2s_param_set_leaf_length( blake2s_param *P, const uint32_t leaf_length )
+{
+ P->leaf_length = leaf_length;
+ return 0;
+}
+
+static inline int blake2s_param_set_node_offset( blake2s_param *P, const uint64_t node_offset )
+{
+ store48( P->node_offset, node_offset );
+ return 0;
+}
+
+static inline int blake2s_param_set_node_depth( blake2s_param *P, const uint8_t node_depth )
+{
+ P->node_depth = node_depth;
+ return 0;
+}
+
+static inline int blake2s_param_set_inner_length( blake2s_param *P, const uint8_t inner_length )
+{
+ P->inner_length = inner_length;
+ return 0;
+}
+
+static inline int blake2s_param_set_salt( blake2s_param *P, const uint8_t salt[BLAKE2S_SALTBYTES] )
+{
+ memcpy( P->salt, salt, BLAKE2S_SALTBYTES );
+ return 0;
+}
+
+static inline int blake2s_param_set_personal( blake2s_param *P, const uint8_t personal[BLAKE2S_PERSONALBYTES] )
+{
+ memcpy( P->personal, personal, BLAKE2S_PERSONALBYTES );
+ return 0;
+}
+
+static inline int blake2s_init0( blake2s_state *S )
+{
+ memset( S, 0, sizeof( blake2s_state ) );
+
+ for( int i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i];
+
+ return 0;
+}
+
+#define blake2s_init BLAKE2_IMPL_NAME(blake2s_init)
+#define blake2s_init_param BLAKE2_IMPL_NAME(blake2s_init_param)
+#define blake2s_init_key BLAKE2_IMPL_NAME(blake2s_init_key)
+#define blake2s_update BLAKE2_IMPL_NAME(blake2s_update)
+#define blake2s_final BLAKE2_IMPL_NAME(blake2s_final)
+#define blake2s BLAKE2_IMPL_NAME(blake2s)
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+ int blake2s_init( blake2s_state *S, size_t outlen );
+ int blake2s_init_param( blake2s_state *S, const blake2s_param *P );
+ int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_update( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+#if defined(__cplusplus)
+}
+#endif
+
+
+/* init2 xors IV with input parameter block */
+int blake2s_init_param( blake2s_state *S, const blake2s_param *P )
+{
+ uint8_t *p, *h, *v;
+ //blake2s_init0( S );
+ v = ( uint8_t * )( blake2s_IV );
+ h = ( uint8_t * )( S->h );
+ p = ( uint8_t * )( P );
+ /* IV XOR ParamBlock */
+ memset( S, 0, sizeof( blake2s_state ) );
+
+ for( int i = 0; i < BLAKE2S_OUTBYTES; ++i ) h[i] = v[i] ^ p[i];
+
+ S->outlen = P->digest_length;
+ return 0;
+}
+
+
+/* Some sort of default parameter block initialization, for sequential blake2s */
+int blake2s_init( blake2s_state *S, size_t outlen )
+{
+ if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;
+
+ const blake2s_param P =
+ {
+ outlen,
+ 0,
+ 1,
+ 1,
+ 0,
+ {0},
+ 0,
+ 0,
+ {0},
+ {0}
+ };
+ return blake2s_init_param( S, &P );
+}
+
+
+int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;
+
+ if ( ( !key ) || ( !keylen ) || keylen > BLAKE2S_KEYBYTES ) return -1;
+
+ const blake2s_param P =
+ {
+ outlen,
+ keylen,
+ 1,
+ 1,
+ 0,
+ {0},
+ 0,
+ 0,
+ {0},
+ {0}
+ };
+
+ if( blake2s_init_param( S, &P ) < 0 )
+ return -1;
+
+ {
+ uint8_t block[BLAKE2S_BLOCKBYTES];
+ memset( block, 0, BLAKE2S_BLOCKBYTES );
+ memcpy( block, key, keylen );
+ blake2s_update( S, block, BLAKE2S_BLOCKBYTES );
+ secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
+ }
+ return 0;
+}
+
+
+static inline int blake2s_compress( blake2s_state *S, const uint8_t block[BLAKE2S_BLOCKBYTES] )
+{
+ __m128i row1, row2, row3, row4;
+ __m128i buf1, buf2, buf3, buf4;
+#if defined(HAVE_SSE4_1)
+ __m128i t0, t1;
+#if !defined(HAVE_XOP)
+ __m128i t2;
+#endif
+#endif
+ __m128i ff0, ff1;
+#if defined(HAVE_SSSE3) && !defined(HAVE_XOP)
+ const __m128i r8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 );
+ const __m128i r16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 );
+#endif
+#if defined(HAVE_SSE4_1)
+ const __m128i m0 = LOADU( block + 00 );
+ const __m128i m1 = LOADU( block + 16 );
+ const __m128i m2 = LOADU( block + 32 );
+ const __m128i m3 = LOADU( block + 48 );
+#else
+ const uint32_t m0 = ( ( uint32_t * )block )[ 0];
+ const uint32_t m1 = ( ( uint32_t * )block )[ 1];
+ const uint32_t m2 = ( ( uint32_t * )block )[ 2];
+ const uint32_t m3 = ( ( uint32_t * )block )[ 3];
+ const uint32_t m4 = ( ( uint32_t * )block )[ 4];
+ const uint32_t m5 = ( ( uint32_t * )block )[ 5];
+ const uint32_t m6 = ( ( uint32_t * )block )[ 6];
+ const uint32_t m7 = ( ( uint32_t * )block )[ 7];
+ const uint32_t m8 = ( ( uint32_t * )block )[ 8];
+ const uint32_t m9 = ( ( uint32_t * )block )[ 9];
+ const uint32_t m10 = ( ( uint32_t * )block )[10];
+ const uint32_t m11 = ( ( uint32_t * )block )[11];
+ const uint32_t m12 = ( ( uint32_t * )block )[12];
+ const uint32_t m13 = ( ( uint32_t * )block )[13];
+ const uint32_t m14 = ( ( uint32_t * )block )[14];
+ const uint32_t m15 = ( ( uint32_t * )block )[15];
+#endif
+ row1 = ff0 = LOADU( &S->h[0] );
+ row2 = ff1 = LOADU( &S->h[4] );
+ row3 = _mm_setr_epi32( 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A );
+ row4 = _mm_xor_si128( _mm_setr_epi32( 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ), LOADU( &S->t[0] ) );
+ ROUND( 0 );
+ ROUND( 1 );
+ ROUND( 2 );
+ ROUND( 3 );
+ ROUND( 4 );
+ ROUND( 5 );
+ ROUND( 6 );
+ ROUND( 7 );
+ ROUND( 8 );
+ ROUND( 9 );
+ STOREU( &S->h[0], _mm_xor_si128( ff0, _mm_xor_si128( row1, row3 ) ) );
+ STOREU( &S->h[4], _mm_xor_si128( ff1, _mm_xor_si128( row2, row4 ) ) );
+ return 0;
+}
+
+
+int blake2s_update( blake2s_state *S, const uint8_t *in, size_t inlen )
+{
+ while( inlen > 0 )
+ {
+ size_t left = S->buflen;
+ size_t fill = 2 * BLAKE2S_BLOCKBYTES - left;
+
+ if( inlen > fill )
+ {
+ memcpy( S->buf + left, in, fill ); // Fill buffer
+ S->buflen += fill;
+ blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
+ blake2s_compress( S, S->buf ); // Compress
+ memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); // Shift buffer left
+ S->buflen -= BLAKE2S_BLOCKBYTES;
+ in += fill;
+ inlen -= fill;
+ }
+ else /* inlen <= fill */
+ {
+ memcpy( S->buf + left, in, inlen );
+ S->buflen += inlen; // Be lazy, do not compress
+ in += inlen;
+ inlen -= inlen;
+ }
+ }
+
+ return 0;
+}
+
+
+int blake2s_final( blake2s_state *S, uint8_t *out, size_t outlen )
+{
+ uint8_t buffer[BLAKE2S_OUTBYTES];
+
+ if(outlen != S->outlen ) return -1;
+
+ if( S->buflen > BLAKE2S_BLOCKBYTES )
+ {
+ blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
+ blake2s_compress( S, S->buf );
+ S->buflen -= BLAKE2S_BLOCKBYTES;
+ memmove( S->buf, S->buf + BLAKE2S_BLOCKBYTES, S->buflen );
+ }
+
+ blake2s_increment_counter( S, ( uint32_t )S->buflen );
+ blake2s_set_lastblock( S );
+ memset( S->buf + S->buflen, 0, 2 * BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */
+ blake2s_compress( S, S->buf );
+
+ for( int i = 0; i < 8; ++i ) /* Output full hash to temp buffer */
+ store32( buffer + sizeof( S->h[i] ) * i, S->h[i] );
+
+ memcpy( out, buffer, outlen );
+ return 0;
+}
+
+int blake2s( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ blake2s_state S[1];
+
+ /* Verify parameters */
+ if ( NULL == in && inlen > 0 ) return -1;
+
+ if ( NULL == out ) return -1;
+
+ if ( NULL == key && keylen > 0) return -1;
+
+ if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+ if( keylen > BLAKE2S_KEYBYTES ) return -1;
+
+ if( keylen > 0 )
+ {
+ if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1;
+ }
+ else
+ {
+ if( blake2s_init( S, outlen ) < 0 ) return -1;
+ }
+
+ if( blake2s_update( S, ( uint8_t * )in, inlen ) < 0) return -1;
+ return blake2s_final( S, out, outlen );
+}
+
+#if defined(SUPERCOP)
+int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen )
+{
+ return blake2s( out, in, NULL, BLAKE2S_OUTBYTES, (size_t)inlen, 0 );
+}
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2sp.c b/contrib/libs/blake2/src/blake2sp.c
new file mode 100644
index 00000000000..2f32bf3a226
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2sp.c
@@ -0,0 +1,274 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#if defined(_OPENMP)
+#include <omp.h>
+#endif
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+#define PARALLELISM_DEGREE 8
+
+static int blake2sp_init_leaf( blake2s_state *S, uint8_t outlen, uint8_t keylen, uint64_t offset )
+{
+ blake2s_param P[1];
+ P->digest_length = outlen;
+ P->key_length = keylen;
+ P->fanout = PARALLELISM_DEGREE;
+ P->depth = 2;
+ P->leaf_length = 0;
+ store48( P->node_offset, offset );
+ P->node_depth = 0;
+ P->inner_length = BLAKE2S_OUTBYTES;
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+ blake2s_init_param( S, P );
+ S->outlen = P->inner_length;
+ return 0;
+}
+
+static int blake2sp_init_root( blake2s_state *S, uint8_t outlen, uint8_t keylen )
+{
+ blake2s_param P[1];
+ P->digest_length = outlen;
+ P->key_length = keylen;
+ P->fanout = PARALLELISM_DEGREE;
+ P->depth = 2;
+ P->leaf_length = 0;
+ store48( P->node_offset, 0ULL );
+ P->node_depth = 1;
+ P->inner_length = BLAKE2S_OUTBYTES;
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+ blake2s_init_param( S, P );
+ S->outlen = P->digest_length;
+ return 0;
+}
+
+
+int blake2sp_init( blake2sp_state *S, size_t outlen )
+{
+ if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+ memset( S->buf, 0, sizeof( S->buf ) );
+ S->buflen = 0;
+
+ if( blake2sp_init_root( S->R, ( uint8_t ) outlen, 0 ) < 0 )
+ return -1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ if( blake2sp_init_leaf( S->S[i], ( uint8_t ) outlen, 0, i ) < 0 ) return -1;
+
+ S->R->last_node = 1;
+ S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
+ S->outlen = ( uint8_t ) outlen;
+ return 0;
+}
+
+int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+ if( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1;
+
+ memset( S->buf, 0, sizeof( S->buf ) );
+ S->buflen = 0;
+
+ if( blake2sp_init_root( S->R, ( uint8_t ) outlen, ( uint8_t ) keylen ) < 0 )
+ return -1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ if( blake2sp_init_leaf( S->S[i], ( uint8_t ) outlen, ( uint8_t ) keylen, i ) < 0 )
+ return -1;
+
+ S->R->last_node = 1;
+ S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
+ S->outlen = ( uint8_t ) outlen;
+ {
+ uint8_t block[BLAKE2S_BLOCKBYTES];
+ memset( block, 0, BLAKE2S_BLOCKBYTES );
+ memcpy( block, key, keylen );
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_update( S->S[i], block, BLAKE2S_BLOCKBYTES );
+
+ secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
+ }
+ return 0;
+}
+
+
+int blake2sp_update( blake2sp_state *S, const uint8_t *in, size_t inlen )
+{
+ size_t left = S->buflen;
+ size_t fill = sizeof( S->buf ) - left;
+
+ if( left && inlen >= fill )
+ {
+ memcpy( S->buf + left, in, fill );
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES );
+
+ in += fill;
+ inlen -= fill;
+ left = 0;
+ }
+
+#if defined(_OPENMP)
+ omp_set_num_threads(PARALLELISM_DEGREE);
+ #pragma omp parallel shared(S)
+#else
+ for( size_t id__ = 0; id__ < PARALLELISM_DEGREE; ++id__ )
+#endif
+ {
+#if defined(_OPENMP)
+ size_t id__ = ( size_t ) omp_get_thread_num();
+#endif
+ size_t inlen__ = inlen;
+ const uint8_t *in__ = ( const uint8_t * )in;
+ in__ += id__ * BLAKE2S_BLOCKBYTES;
+
+ while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
+ {
+ blake2s_update( S->S[id__], in__, BLAKE2S_BLOCKBYTES );
+ in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+ inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+ }
+ }
+
+ in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES );
+ inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+
+ if( inlen > 0 )
+ memcpy( S->buf + left, in, inlen );
+
+ S->buflen = ( uint32_t ) left + ( uint32_t ) inlen;
+ return 0;
+}
+
+
+int blake2sp_final( blake2sp_state *S, uint8_t *out, size_t outlen )
+{
+ uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
+
+ if(S->outlen != outlen) return -1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ {
+ if( S->buflen > i * BLAKE2S_BLOCKBYTES )
+ {
+ size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES;
+
+ if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES;
+
+ blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left );
+ }
+
+ blake2s_final( S->S[i], hash[i], BLAKE2S_OUTBYTES );
+ }
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_update( S->R, hash[i], BLAKE2S_OUTBYTES );
+
+ blake2s_final( S->R, out, outlen );
+ return 0;
+}
+
+
+int blake2sp( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
+ blake2s_state S[PARALLELISM_DEGREE][1];
+ blake2s_state FS[1];
+
+ /* Verify parameters */
+ if ( NULL == in && inlen > 0 ) return -1;
+
+ if ( NULL == out ) return -1;
+
+ if ( NULL == key && keylen > 0 ) return -1;
+
+ if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+ if( keylen > BLAKE2S_KEYBYTES ) return -1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ if( blake2sp_init_leaf( S[i], ( uint8_t ) outlen, ( uint8_t ) keylen, i ) < 0 )
+ return -1;
+
+ S[PARALLELISM_DEGREE - 1]->last_node = 1; // mark last node
+
+ if( keylen > 0 )
+ {
+ uint8_t block[BLAKE2S_BLOCKBYTES];
+ memset( block, 0, BLAKE2S_BLOCKBYTES );
+ memcpy( block, key, keylen );
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_update( S[i], block, BLAKE2S_BLOCKBYTES );
+
+ secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
+ }
+
+#if defined(_OPENMP)
+ omp_set_num_threads(PARALLELISM_DEGREE);
+ #pragma omp parallel shared(S,hash)
+#else
+
+ for( size_t id__ = 0; id__ < PARALLELISM_DEGREE; ++id__ )
+#endif
+ {
+#if defined(_OPENMP)
+ size_t id__ = ( size_t ) omp_get_thread_num();
+#endif
+ size_t inlen__ = inlen;
+ const uint8_t *in__ = ( const uint8_t * )in;
+ in__ += id__ * BLAKE2S_BLOCKBYTES;
+
+ while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
+ {
+ blake2s_update( S[id__], in__, BLAKE2S_BLOCKBYTES );
+ in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+ inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+ }
+
+ if( inlen__ > id__ * BLAKE2S_BLOCKBYTES )
+ {
+ const size_t left = inlen__ - id__ * BLAKE2S_BLOCKBYTES;
+ const size_t len = left <= BLAKE2S_BLOCKBYTES ? left : BLAKE2S_BLOCKBYTES;
+ blake2s_update( S[id__], in__, len );
+ }
+
+ blake2s_final( S[id__], hash[id__], BLAKE2S_OUTBYTES );
+ }
+
+ if( blake2sp_init_root( FS, ( uint8_t ) outlen, ( uint8_t ) keylen ) < 0 )
+ return -1;
+
+ FS->last_node = 1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_update( FS, hash[i], BLAKE2S_OUTBYTES );
+
+ return blake2s_final( FS, out, outlen );
+}
+
+
+
+
diff --git a/contrib/libs/blake2/src/config.h b/contrib/libs/blake2/src/config.h
new file mode 100644
index 00000000000..fd863ba01ae
--- /dev/null
+++ b/contrib/libs/blake2/src/config.h
@@ -0,0 +1,157 @@
+/* src/config.h. Generated from config.h.in by configure. */
+/* src/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Support Altivec instructions */
+/* #undef HAVE_ALTIVEC */
+
+/* Support AVX (Advanced Vector Extensions) instructions */
+/* #undef HAVE_AVX */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the `explicit_bzero' function. */
+#define HAVE_EXPLICIT_BZERO 1
+
+/* Define to 1 if you have the `explicit_memset' function. */
+/* #undef HAVE_EXPLICIT_MEMSET */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `memset' function. */
+#define HAVE_MEMSET 1
+
+/* Define to 1 if you have the `memset_s' function. */
+/* #undef HAVE_MEMSET_S */
+
+/* Support mmx instructions */
+/* #undef HAVE_MMX */
+
+/* Support SSE (Streaming SIMD Extensions) instructions */
+/* #undef HAVE_SSE */
+
+/* Support SSE2 (Streaming SIMD Extensions 2) instructions */
+/* #undef HAVE_SSE2 */
+
+/* Support SSE3 (Streaming SIMD Extensions 3) instructions */
+/* #undef HAVE_SSE3 */
+
+/* Support SSSE4.1 (Streaming SIMD Extensions 4.1) instructions */
+/* #undef HAVE_SSE4_1 */
+
+/* Support SSSE4.2 (Streaming SIMD Extensions 4.2) instructions */
+/* #undef HAVE_SSE4_2 */
+
+/* Support SSSE3 (Supplemental Streaming SIMD Extensions 3) instructions */
+/* #undef HAVE_SSSE3 */
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#define HAVE_STDDEF_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* machine is little-endian */
+#define NATIVE_LITTLE_ENDIAN 1
+
+/* Name of package */
+#define PACKAGE "libb2"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "contact@blake2.net"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libb2"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libb2 0.98.1"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libb2"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL "https://blake2.net"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "0.98.1"
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+ required in a freestanding environment). This macro is provided for
+ backward compatibility; new code need not use it. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "0.98.1"
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+/* # undef WORDS_BIGENDIAN */
+# endif
+#endif
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+/* #undef _UINT32_T */
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+/* #undef _UINT64_T */
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+/* #undef _UINT8_T */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint32_t */
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint64_t */
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint8_t */
diff --git a/contrib/libs/blake2/ya.make b/contrib/libs/blake2/ya.make
new file mode 100644
index 00000000000..82158e6ab34
--- /dev/null
+++ b/contrib/libs/blake2/ya.make
@@ -0,0 +1,49 @@
+# Generated by devtools/yamaker from nixpkgs 22.11.
+
+LIBRARY()
+
+LICENSE(CC0-1.0)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+VERSION(0.98.1)
+
+ORIGINAL_SOURCE(https://github.com/BLAKE2/libb2/archive/v0.98.1.tar.gz)
+
+ADDINCL(
+ contrib/libs/blake2/src
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DHAVE_CONFIG_H
+ -DSUFFIX=
+)
+
+SRCS(
+ src/blake2-dispatch.c
+ src/blake2bp.c
+ src/blake2sp.c
+)
+
+SRC(src/blake2b-ref.c -DSUFFIX=_ref)
+
+SRC(src/blake2s-ref.c -DSUFFIX=_ref)
+
+IF (ARCH_X86_64)
+ SRC_C_AVX(src/blake2b.c -DSUFFIX=_avx)
+ SRC_C_SSE2(src/blake2b.c -DSUFFIX=_sse2)
+ SRC_C_SSE41(src/blake2b.c -DSUFFIX=_sse41)
+ SRC_C_SSSE3(src/blake2b.c -DSUFFIX=_ssse3)
+ SRC_C_XOP(src/blake2b.c -DSUFFIX=_xop)
+ SRC_C_AVX(src/blake2s.c -DSUFFIX=_avx)
+ SRC_C_SSE2(src/blake2s.c -DSUFFIX=_sse2)
+ SRC_C_SSE41(src/blake2s.c -DSUFFIX=_sse41)
+ SRC_C_SSSE3(src/blake2s.c -DSUFFIX=_ssse3)
+ SRC_C_XOP(src/blake2s.c -DSUFFIX=_xop)
+ENDIF()
+
+END()
diff --git a/contrib/libs/libarchive/CONTRIBUTING.md b/contrib/libs/libarchive/CONTRIBUTING.md
new file mode 100644
index 00000000000..9ccc45c3d18
--- /dev/null
+++ b/contrib/libs/libarchive/CONTRIBUTING.md
@@ -0,0 +1,98 @@
+Thank you for helping us improve libarchive.
+The following guidelines will help ensure your contribution gets prompt attention.
+
+# Bugs and other Issues
+
+If you encounter any problems with libarchive,
+[please file an issue on our issue tracker](https://github.com/libarchive/libarchive/issues).
+
+All bug reports should include the following information. You can copy the text below directly into the issue tracker to get started:
+
+```
+Basic Information
+ Version of libarchive:
+ How you obtained it: (build from source, pre-packaged binary, etc)
+ Operating system and version:
+ What compiler and/or IDE you are using (include version):
+
+If you are using a pre-packaged binary
+ Exact package name and version:
+ Repository you obtained it from:
+
+Description of the problem you are seeing:
+ What did you do?
+ What did you expect to happen?
+ What actually happened?
+ What log files or error messages were produced?
+
+How the libarchive developers can reproduce your problem:
+ What other software was involved?
+ What other files were involved?
+ How can we obtain any of the above?
+```
+
+Depending on the specific type of issue, other information will be helpful:
+
+## Test Failures
+
+If you see any test failures, please include the information above and also:
+
+* Names of the tests that failed.
+
+* Look for the .log files in the /tmp/libarchive_test_*date-and-time* directories. (On Mac OS, look in $TMPDIR which is different than /tmp.)
+
+Please paste the .log files you will find there directly into your report.
+
+
+## Problems using libarchive in a program
+
+If you are trying to write a program using libarchive, please include the information above and also:
+
+* It will help us if we can actually run the program. This is easiest if you can provide source to a short program that illustrates your problem.
+
+* If you have a sufficiently short program that shows the problem, you can either paste it into the report or [put it into a gist](https://gist.github.com).
+
+
+## Libarchive produced incorrect output
+
+Please tell us what program you ran, any command-line arguments you provided, and details of the input files (`ls -l` output is helpful here). If the problem involved a command-line program, please copy the full terminal text into the report, including the command line and any error messages.
+
+Please try to make the output file available to us. Unless it is very large, you can upload it into a fresh github repository and provide a link in your issue report.
+
+
+## Libarchive could not read a particular input file
+
+Note: If you can provide a **very small** input file that reproduces the problem, we can add that to our test suite. This will ensure that the bug does not reappear in the future.
+
+A link to the relevant file is usually sufficient.
+
+If you cannot provide the input file or a link to the file, please let us know if there is some other way to obtain it.
+
+
+## Documentation improvements
+
+We are always interested in improving the libarchive documentation. Please tell us about any errors you find, including:
+
+* Typos or errors in the manpages provided with libarchive source.
+
+* Mistakes in the [libarchive Wiki](https://github.com/libarchive/libarchive/wiki)
+
+* Problems with the PDF or Wiki files that are automatically generated from the manpages.
+
+
+# Code Submissions
+
+We welcome all code submissions. But of course, some code submissions are easier for us to respond to than others. The best code submissions:
+
+* Address a single issue. There have been many cases where a simple fix to an obvious problem did not get handled for months because the patch that was provided also included an unrelated change affecting an especially complex area of the code.
+
+* Follow existing libarchive code style and conventions. Libarchive generally follows [BSD KNF](https://www.freebsd.org/cgi/man.cgi?query=style&sektion=9) for formatting code.
+
+* Do not make unnecessary changes to existing whitespace, capitalization, or spelling.
+
+* Include detailed instructions for reproducing the problem you're fixing. We do try to verify that a submission actually fixes a real problem. If we can't reproduce the problem, it will take us longer to evaluate the fix. For this reason, we encourage you to file an issue report first with details on reproducing the problem, then refer to that issue in your pull request.
+
+* Includes a test case. The libarchive Wiki has [detailed documentation for adding new test cases](https://github.com/libarchive/libarchive/wiki/LibarchiveAddingTest).
+
+* Are provided via Github pull requests. We welcome patches in almost any format, but github's pull request management makes it significantly easier for us to evaluate and test changes.
+
diff --git a/contrib/libs/libarchive/COPYING b/contrib/libs/libarchive/COPYING
new file mode 100644
index 00000000000..1b9723574a7
--- /dev/null
+++ b/contrib/libs/libarchive/COPYING
@@ -0,0 +1,65 @@
+The libarchive distribution as a whole is Copyright by Tim Kientzle
+and is subject to the copyright notice reproduced at the bottom of
+this file.
+
+Each individual file in this distribution should have a clear
+copyright/licensing statement at the beginning of the file. If any do
+not, please let me know and I will rectify it. The following is
+intended to summarize the copyright status of the individual files;
+the actual statements in the files are controlling.
+
+* Except as listed below, all C sources (including .c and .h files)
+ and documentation files are subject to the copyright notice reproduced
+ at the bottom of this file.
+
+* The following source files are also subject in whole or in part to
+ a 3-clause UC Regents copyright; please read the individual source
+ files for details:
+ libarchive/archive_read_support_filter_compress.c
+ libarchive/archive_write_add_filter_compress.c
+ libarchive/mtree.5
+
+* The following source files are in the public domain:
+ libarchive/archive_getdate.c
+
+* The following source files are triple-licensed with the ability to choose
+ from CC0 1.0 Universal, OpenSSL or Apache 2.0 licenses:
+ libarchive/archive_blake2.h
+ libarchive/archive_blake2_impl.h
+ libarchive/archive_blake2s_ref.c
+ libarchive/archive_blake2sp_ref.c
+
+* The build files---including Makefiles, configure scripts,
+ and auxiliary scripts used as part of the compile process---have
+ widely varying licensing terms. Please check individual files before
+ distributing them to see if those restrictions apply to you.
+
+I intend for all new source code to use the license below and hope over
+time to replace code with other licenses with new implementations that
+do use the license below. The varying licensing of the build scripts
+seems to be an unavoidable mess.
+
+
+Copyright (c) 2003-2018 <author(s)>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer
+ in this position and unchanged.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/contrib/libs/libarchive/INSTALL b/contrib/libs/libarchive/INSTALL
new file mode 100644
index 00000000000..2fafbd5046d
--- /dev/null
+++ b/contrib/libs/libarchive/INSTALL
@@ -0,0 +1,35 @@
+More complete build documentation is available on the libarchive
+Wiki: https://github.com/libarchive/libarchive/wiki
+
+On most Unix-like systems, you should be able to install libarchive,
+bsdtar, and bsdcpio using the following common steps:
+ ./configure
+ make
+ make install
+
+If you need to customize the target directories or otherwise adjust
+the build setting, use
+ ./configure --help
+to list the configure options.
+
+If you are developing libarchive and need to update the
+configure script and other build files:
+ /bin/sh build/autogen.sh
+
+To create a distribution, please use the 'distcheck' target:
+ /bin/sh build/autogen.sh && ./configure && make distcheck
+
+On Unix-like and non-Unix-like systems, use the "cmake" utility (available from
+http://cmake.org/) to generate suitable build files for your platform.
+Cmake requires the name of the directory containing CmakeLists.txt and
+the "generator" to use for your build environment. For example, to
+build with Xcode on Mac OS, you can use the following command:
+ cmake -G "Xcode" ~/libarchive-download-dir/
+The result will be appropriate makefiles, solution files, or project
+files that can be used with the corresponding development tool.
+The default on Unix-like systems is to generate Makefiles, so you
+can also use cmake instead of the configure script:
+ cmake ~/libarchive-download-dir/
+ make
+ make install
+See the libarchive Wiki or the cmake site for further documentation.
diff --git a/contrib/libs/libarchive/NEWS b/contrib/libs/libarchive/NEWS
new file mode 100644
index 00000000000..7509c9ce5fa
--- /dev/null
+++ b/contrib/libs/libarchive/NEWS
@@ -0,0 +1,755 @@
+Jul 29, 2023: libarchive 3.7.1 released
+
+Jul 18, 2023: libarchive 3.7.0 released
+
+Jul 14, 2023: bsdunzip port from FreeBSD
+
+Dec 07, 2022: libarchive 3.6.2 released
+
+Apr 08, 2022: libarchive 3.6.1 released
+
+Feb 09, 2022: libarchive 3.6.0 released
+
+Feb 08, 2022: libarchive 3.5.3 released
+
+Aug 22, 2021: libarchive 3.5.2 released
+
+Dec 26, 2020: libarchive 3.5.1 released
+
+Dec 01, 2020: libarchive 3.5.0 released
+
+Oct 14, 2020: Support for system extended attributes
+
+May 20, 2020: libarchive 3.4.3 released
+
+Apr 30, 2020: Support for pzstd compressed files
+
+Apr 16, 2020: Support for RHT.security.selinux tar extended attribute
+
+Feb 11, 2020: libarchive 3.4.2 released
+
+Jan 23, 2020: Important fixes for writing XAR archives
+
+Jan 20, 2020: New tar option: --safe-writes (atomical file extraction)
+
+Jan 03, 2020: Support mbed TLS (PolarSSL) as optional crypto provider
+
+Dec 30, 2019: libarchive 3.4.1 released
+
+Dec 11, 2019: New pax write option "xattrhdr"
+
+Nov 17, 2019: Unicode filename support for reading lha/lzh archives
+
+Jun 11, 2019: libarchive 3.4.0 released
+
+May 18, 2019: Fixes for reading Android APK and JAR archives
+
+Apr 16, 2019: Support for non-recursive list and extract
+
+Apr 14, 2019: New tar option: --exclude-vcs
+
+Mar 27, 2019: Support for file and directory symlinks on Windows
+
+Mar 12, 2019: Important fixes for storing file attributes and flags
+
+Jan 20, 2019: Support for xz, lzma, ppmd8 and bzip2 decompression in ZIP files
+
+Oct 06, 2018: RAR 5.0 reader
+
+Sep 03, 2018: libarchive 3.3.3 released
+
+Jul 19, 2018: Avoid super-linear slowdown on malformed mtree files
+
+Jan 27, 2018: Many fixes for building with Visual Studio
+
+Oct 19, 2017: NO_OVERWRITE doesn't change existing directory attributes
+
+Aug 12, 2017: New support for Zstandard read and write filters
+
+Jul 09, 2017: libarchive 3.3.2 released
+
+Mar 16, 2017: NFSv4 ACL support for Linux (librichacl)
+
+Feb 26, 2017: libarchive 3.3.1 released
+ Security & Feature release
+
+Feb 19, 2017: libarchive 3.3.0 released
+ Security & Feature release
+
+Jan 29, 2017: Limited NFSv4 ACL support for Mac OS (Darwin)
+
+Jan 10, 2017: POSIX.1e and NFSv4 ACL support for Solaris and derivates
+
+Dec 27, 2016: NFSv4 ACL read and write support for pax
+ Deprecated functions: archive_entry_acl_text(), archive_entry_acl_text_w()
+
+Nov, 2016: libarchive is now being tested by the OSS-Fuzz project
+
+Oct 26, 2016: Remove liblzmadec support
+
+Oct 23, 2016: libarchive 3.2.2 released
+ Security release
+
+Jun 20, 2016: libarchive 3.2.1 released
+ This fixes a handful of security and other critical issues with 3.2.0
+
+May 01, 2016: libarchive 3.2.0 released
+
+Apr 09, 2016: libarchive 3.1.901a released
+ Another test release in preparation for 3.2.0
+
+Feb 13, 2016: libarchive 3.1.900a released
+ This is a test release in preparation for 3.2.0
+
+Oct 21, 2015: Preliminary port to OSF
+
+Apr 11, 2015: libarchive's issue tracker is now hosted at GitHub.
+ https://github.com/libarchive/libarchive/issues
+
+Early 2015: Many fixes to crash and overflow bugs thanks to Hanno Boeck
+
+Oct 13, 2014: Zip encryption and decryption support
+
+Aug 13, 2014: Add support for lz4 compression.
+
+Jun 10, 2014: Add warc format support
+
+May 3, 2014: Add experimental Zip streaming extension
+
+Apr 6, 2014: Add bsdcat command-line tool
+
+Jan 12, 2014: Add Zip64 support
+
+Dec 1, 2013: Rewrite Zip write logic
+
+Jul 1, 2013: Add ability to detect encrypted entries for many formats
+ (This does not add the ability to *decrypt* those entries, however)
+
+Feb 23, 2013: "raw" write support added
+
+Feb 09, 2013: libarchive 3.1.2 released
+
+Jan 28, 2013: libarchive's new website moved to http://www.libarchive.org.
+
+Jan 13, 2013: libarchive 3.1.1 released
+
+Jan 13, 2013: libarchive 3.1.0 released
+
+Dec 07, 2012: Implement functions to manually set the format and filters used.
+
+Nov 11, 2012: Add support for __MACOSX directory in Zip archives, which resource
+ forks are stored in.
+
+Oct 20, 2012: Add support for writing v7 tar format.
+
+Oct 09, 2012: Add support for grzip compression.
+
+Oct 07, 2012: Introduce b64encode filter.
+Oct 07, 2012: Introduce uuencode filter.
+
+Oct 06, 2012: Add support for lzop.
+
+Sep 27, 2012: Implement function used to seek within data blocks.
+ (Currently only supported for uncompressed RAR archives).
+
+Apr 22, 2012: Add basic archive read and write filter support for lrzip.
+
+Mar 27, 2012: libarchive 3.0.4 released
+
+Feb 05, 2012: libarchive development now hosted at GitHub.
+ http://libarchive.github.com/
+Feb 05, 2012: libarchive's issue tracker remains at Google Code.
+ http://code.google.com/p/libarchive/issues/list
+Feb 05, 2012: libarchive's mailing lists remain at Google Groups.
+
+Dec 24, 2011: libarchive 3.0.2 released
+Dec 23, 2011: Various fixes merged from FreeBSD
+Dec 23, 2011: Symlink support in Zip reader and writer
+Dec 23, 2011: Robustness fixes to 7Zip reader
+
+Nov 27, 2011: libarchive 3.0.1b released
+
+Nov 26, 2011: 7Zip reader
+Nov 26, 2011: Small fixes to ISO and Zip to improve robustness with corrupted input
+Nov 24, 2011: Improve streaming Zip reader's support for uncompressed entries
+Nov 20, 2011: New seeking Zip reader supports SFX Zip archives
+Nov 20, 2011: Build fixes on Windows
+
+Nov 13, 2011: libarchive 3.0.0a released
+
+Nov 06, 2011: Update shared-library version calculations for libarchive 3.x
+Sep 04, 2011: Fix tar -s; follow GNU tar for controlling hardlink/symlink substitutions
+Aug 18, 2011: Fix reading ISO images built by NetBSD's mkisofs
+Aug 15, 2011: Old archive_read_support_compression_XXX functions are deprecated and
+ will disappear in libarchive 4.0.
+Jun 26, 2011: RAR reader
+Jun 16, 2011: Add tar:compat-2x option to emulate broken libarchive 2.x
+ handling of pax UTF-8 headers
+Apr 25, 2011: Refactor read_open() into a collection of single-item setters;
+ support the old interfaces as wrappers
+Apr 12, 2011: Split disk writer into separate POSIX and Windows implementations
+Apr 10, 2011: Improvements to character translations on Windows.
+Mar 30, 2011: More work to return errors instead of calling abort()
+Mar 23, 2011: Add charset option to many writers to control MBCS filenames
+Mar 17, 2011: Overhauled support for per-format extension options
+Mar 17, 2011: Track character set used for mbcs strings, support
+ translating to/from user-specified locale
+Mar 09, 2011: Recognize mtree files without requiring a signature
+Mar 06, 2011: Use iconv to convert to/from Unicode instead of making bad
+ assumptions about the C90 character set translation functions
+Feb 17, 2011: Fixes for AIX, TRU64, and other platforms
+Dec 22, 2010: CAB reader
+Dec 20, 2010: LHA/LZH reader
+Jul 03, 2010: minitar example demonstrates archive_read_disk directory traversal
+Jun 29, 2010: Many improvements to ISO reader compatibility
+Jun 26, 2010: Use larger buffers when copy files into an archive
+Jun 18, 2010: Reimplement Mac OS extensions in libarchive
+Jun 09, 2010: archive_read_disk now supports traversals
+May 28, 2010: XAR writer
+May 16, 2010: Fix ^T handling; don't exit on interrupted reads and writes
+May 09, 2010: Improved detection of platform-specific crypto support
+May 04, 2010: lzip read and write filters
+May 01, 2010: New options: tar --gid --gname --uid --uname
+Apr 28, 2010: Use Red-black tree for ISO reader/writer to improve performance
+Apr 17, 2010: Minimal writer for legacy GNU tar format
+Mar 12, 2010: Don't dereference symlinks on Linux when reading ACLs.
+Mar 06, 2010: Fix build when an older libarchive is already installed
+Feb 28, 2010: Relax handling of state failures; misuse by clients now generally
+ results in a sticky ARCHIVE_FATAL rather than a visit to abort()
+Feb 25, 2010: ISO writer
+Feb 21, 2010: Split many man pages into smaller chunks.
+Feb 21, 2010: Performance: Cheat on block sizes when reading archives from disk.
+Feb 21, 2010: Use int64_t instead of off_t, dev_t, ino_t, uid_t, and gid_t
+Feb 20, 2010: Document new ACL functions.
+Feb 19, 2010: Support multiple write filters
+Feb 07, 2010: Remove some legacy libarchive 1.x APIs
+Feb 04, 2010: Read afio headers
+Feb 02, 2010: Archive sparse files compatibly with GNU tar
+Feb 01, 2010: Integrate Apple extensions for Mac OS extended attributes into bsdtar
+Jan 31, 2010: Support cpio -V
+
+Feb 04, 2010: libarchive 2.8.0 released
+Jan 17, 2010: Fix error handling for 'echo nonexistent | cpio -o'
+Jan 17, 2010: Don't use futimes() on Cygwin
+
+Jan 02, 2010: libarchive 2.7.902a released (test release for 2.8)
+Jan 02, 2010: Fix tar/test/test_windows on MinGW
+Jan 02, 2010: Fix memory leaks in libarchive tests
+Jan 01, 2010: Fix memory leak when filter startup fails
+
+Dec 27, 2009: libarchive 2.7.901a released (test release for 2.8)
+
+Aug 04, 2009: libarchive 2.7.1 released
+Jul 20, 2009: Suppress bogus warning about unxz
+Jul 19, 2009: Support Cygwin 1.7
+Jun 11, 2009: Support lzma/xz files compressed with larger buffer sizes.
+May 24, 2009: Handle gzip files signed with OpenBSD "gzsig" program.
+May 07, 2009: Avoid false failures when reading from pipe.
+
+Apr 16, 2009: libarchive 2.7.0 released
+
+Apr 10, 2009: libarchive 2.6.992a released
+Apr 09, 2009: Fix SIGPIPE issue building with MSVC.
+Apr 09, 2009: Fix several minor memory leaks in libarchive and libarchive_test
+
+Apr 08, 2009: libarchive 2.6.991a released
+Apr 07, 2009: Additional tests added to bsdcpio_test
+
+Apr 01, 2009: libarchive 2.6.990a released
+Apr 01, 2009: Use command-line gunzip, bunzip2, unxz, unlzma for
+ decompression if the library is built without suitable
+ libraries. The setup functions return ARCHIVE_WARN
+ in this case so clients can adapt if necessary.
+Apr 01, 2009: Use getpw*_r and getgr*_r functions for thread-safety.
+Mar 24, 2009: Add archive_read_next_header2(), which is up to 25%
+ more efficient for some clients; from Brian Harring.
+Mar 22, 2009: PDF versions of manpages are now included in the distribution.
+Mar, 2009: Major work to improve Cygwin build by Charles Wilson.
+Feb/Mar, 2009: Major work on cmake build support, mostly by Michihiro NAKAJIMA.
+Feb/Mar, 2009: Major work on Visual Studio support by Michihiro NAKAJIMA.
+ All tests now pass.
+Feb 25, 2009: Fix Debian Bug #516577
+Feb 21, 2009: Yacc is no longer needed to build; date parser rewritten in C.
+Jan/Feb, 2009: Mtree work by Michihiro.
+Feb, 2009: Joliet support by Andreas Henriksson.
+Jan/Feb, 2009: New options framework by Michihiro.
+Feb, 2009: High-res timestamps on Tru64, AIX, and GNU Hurd, by Björn Jacke.
+Jan 18, 2009: Extended attributes work on FreeBSD and Linux now with pax format.
+Jan 07, 2009: New archive_read_disk_entry_from_file() knows about ACLs,
+ extended attributes, etc so that bsdtar and bsdcpio don't require
+ such system-specific knowledge.
+Jan 03, 2009: Read filter system extensively refactored. In particular,
+ read filter pipelines are now built out automatically and individual
+ filters should be much easier to implement. Documentation on the
+ Googlecode Wiki explains how to implement new filters.
+Dec 28, 2008: Many Windows/Visual Studio fixes from Michihiro NAKAJIMA.
+
+Dec 28, 2008: Main libarchive development moved from FreeBSD Perforce
+ server to Google Code. This should make it easier for more
+ people to participate in libarchive development.
+
+Dec 28, 2008: libarchive 2.6.0 released
+Dec 25, 2008: libarchive 2.5.905a released
+Dec 10, 2008: libarchive 2.5.904a released
+Dec 04, 2008: libarchive 2.5.903a released
+Nov 09, 2008: libarchive 2.5.902a released
+Nov 08, 2008: libarchive 2.5.901a released
+Nov 08, 2008: Start of pre-release testing for libarchive 2.6
+
+Nov 07, 2008: Read filter refactor: The decompression routines just
+ consume and produce arbitrarily-sized blocks. The reblocking
+ from read_support_compression_none() has been pulled into the
+ read core. Also, the decompression bid now makes multiple
+ passes and stacks read filters.
+Oct 21, 2008: bsdcpio: New command-line parser.
+Oct 19, 2008: Internal read_ahead change: short reads are now an error
+Oct 06, 2008: bsdtar: option parser no longer uses getopt_long(),
+ gives consistent option parsing on all platforms.
+Sep 19, 2008: Jaakko Heinonen: shar utility built on libarchive
+Sep 17, 2008: Pedro Giffuni: birthtime support
+Sep 17, 2008: Miklos Vajna: lzma reader and test. Note: I still have
+ some concerns about the auto-detection (LZMA file format
+ doesn't support auto-detection well), so this is not yet
+ enabled under archive_read_support_compression_all(). For
+ now, you must call archive_read_support_compression_lzma() if
+ you want LZMA read support.
+Sep 11, 2008: Ivailo Petrov: Many fixes to Windows build, new solution files
+Jul 26, 2008: archive_entry now tracks which values have not been set.
+ This helps zip extraction (file size is often "unknown") and
+ time restores (tar usually doesn't know atime).
+Jul 26, 2008: Joerg Sonnenberger: Performance improvements to shar writer
+Jul 25, 2008: Joerg Sonnenberger: mtree write support
+
+Jul 02, 2008: libarchive 2.5.5 released
+
+Jul 02, 2008: libarchive 2.5.5b released
+Jul 01, 2008: bsdcpio is being used by enough people, we can call it 1.0.0 now
+Jun 20, 2008: bsdcpio: If a -l link fails with EXDEV, copy the file instead
+Jun 19, 2008: bsdcpio: additional long options for better GNU cpio compat
+Jun 15, 2008: Many small portability and bugfixes since 2.5.4b.
+
+May 25, 2008: libarchive 2.5.4b released
+May 21, 2008: Joerg Sonnenberger: fix bsdtar hardlink handling for newc format
+
+May 21, 2008: More progress on Windows building. Thanks to "Scott"
+ for the Windows makefiles, thanks to Kees Zeelenberg for
+ code contributions.
+
+May 21, 2008: Fix a number of non-exploitable integer and buffer overflows,
+ thanks to David Remahl at Apple for pointing these out.
+
+May 21, 2008: Colin Percival: SIGINFO or SIGUSR1 to bsdtar prints progress info
+
+May 16, 2008: bsdtar's test harness no longer depends on file ordering.
+ This was causing spurious test failures on a lot of systems.
+ Thanks to Bernhard R. Link for the diagnosis.
+
+May 14, 2008: Joerg Sonnenberger: -s substitution support for bsdtar
+
+May 13, 2008: Joerg Sonnenberger: Many mtree improvements
+
+May 11, 2008: Joerg Sonnenberger: fix hardlink extraction when
+ hardlinks have different permissions from original file
+
+April 30, 2008: Primary libarchive work has been moved into the FreeBSD
+ project's Perforce repository: http://perforce.freebsd.org/
+ The libarchive project can be browsed at
+ //depot/user/kientzle/libarchive-portable
+ Direct link: http://preview.tinyurl.com/46mdgr
+
+May 04, 2008: libarchive 2.5.3b released
+ * libarchive: Several fixes to link resolver to address bsdcpio crashes
+ * bsdcpio: -p hardlink handling fixes
+ * tar/pax: Ensure ustar dirnames end in '/'; be more careful about
+ measuring filenames when deciding what pathname fields to use
+ * libarchive: Mark which entry strings are set; be accurate about
+ distinguishing empty strings ("") from unset ones (NULL)
+ * tar: Don't crash reading entries with empty filenames
+ * libarchive_test, bsdtar_test, bsdcpio_test: Better defaults:
+ run all tests, delete temp dirs, summarize repeated failures
+ * -no-undefined to libtool for Cygwin
+ * libarchive_test: Skip large file tests on systems with 32-bit off_t
+ * iso9660: Don't bother trying to find the body of an empty file;
+ this works around strange behavior from some ISO9660 writers
+ * tar: allow -r -T to be used together
+ * tar: allow --format with -r or -u
+ * libarchive: Don't build archive.h
+
+May 04, 2008: Simplified building: archive.h is no longer constructed
+ This may require additional #if conditionals on some platforms.
+
+Mar 30, 2008: libarchive 2.5.1b released
+
+Mar 15, 2008: libarchive 2.5.0b released
+Mar 15, 2008: bsdcpio now seems to correctly write hardlinks into newc,
+ ustar, and old cpio archives. Just a little more testing before
+ bsdcpio 1.0 becomes a reality.
+Mar 15, 2008: I think the new linkify() interface is finally handling
+ all known hardlink strategies.
+Mar 15, 2008: Mtree read fixes from Joerg Sonnenberger.
+Mar 15, 2008: Many new bsdtar and bsdcpio options from Joerg Sonnenberger.
+Mar 15, 2008: test harnesses no longer require uudecode; they
+ now have built-in decoding logic that decodes the reference
+ files as they are needed.
+
+Mar 14, 2008: libarchive 2.4.14 released; identical to 2.4.13 except for
+ a point fix for gname/uname mixup in pax format that was introduced
+ with the UTF-8 fixes.
+
+Feb 26, 2008: libarchive 2.4.13 released
+Feb 25, 2008: Handle path, linkname, gname, or uname that can't be converted
+ to/from UTF-8. Implement "hdrcharset" attribute from SUS-2008.
+Feb 25, 2008: Fix name clash on NetBSD.
+Feb 18, 2008: Fix writing empty 'ar' archives, per Kai Wang
+Feb 18, 2008: [bsdtar] Permit appending on block devices.
+Feb 09, 2008: New "linkify" resolver to help with newc hardlink writing;
+ bsdcpio still needs to be converted to use this.
+Feb 02, 2008: Windows compatibility fixes from Ivailo Petrov, Kees Zeelenberg
+Jan 30, 2008: Ignore hardlink size for non-POSIX tar archives.
+
+Jan 22, 2008: libarchive 2.4.12 released
+Jan 22, 2008: Fix bad padding when writing symlinks to newc cpio archives.
+Jan 22, 2008: Verify bsdcpio_test by getting it to work against GNU cpio 2.9.
+ bsdcpio_test complains about missing options (-y and -z), format
+ of informational messages (--version, --help), and a minor formatting
+ issue in odc format output. After this update, bsdcpio_test uncovered
+ several more cosmetic issues in bsdcpio, all now fixed.
+Jan 22, 2008: Experimental support for self-extracting Zip archives.
+Jan 22, 2008: Extend hardlink restore strategy to work correctly with
+ hardlinks extracted from newc cpio files. (Which store the body
+ only with the last occurrence of a link.)
+
+Dec 30, 2007: libarchive 2.4.11 released
+Dec 30, 2007: Fixed a compile error in bsdcpio on some systems.
+
+Dec 29, 2007: libarchive 2.4.10 released
+Dec 29, 2007: bsdcpio 0.9.0 is ready for wider use.
+Dec 29, 2007: Completed initial test harness for bsdcpio.
+
+Dec 22, 2007: libarchive 2.4.9 released
+Dec 22, 2007: Implement the remaining options for bsdcpio: -a, -q, -L, -f,
+ pattern selection for -i and -it.
+
+Dec 13, 2007: libarchive 2.4.8 released
+Dec 13, 2007: gzip and bzip2 compression now handle zero-byte writes correctly,
+ Thanks to Damien Golding for bringing this to my attention.
+
+Dec 12, 2007: libarchive 2.4.7 released
+
+Dec 10, 2007: libarchive 2.4.6 released
+Dec 09, 2007: tar/test/test_copy.c verifies "tar -c | tar -x" copy pipeline
+Dec 07, 2007: Fix a couple of minor memory leaks.
+
+Dec 04, 2007: libarchive 2.4.5 released
+Dec 04, 2007: Fix cpio/test/test_write_odc by setting the umask first.
+
+Dec 03, 2007: libarchive 2.4.4 released
+Dec 03, 2007: New configure options --disable-xattr and --disable-acl,
+ thanks to Samuli Suominen.
+
+Dec 03, 2007: libarchive 2.4.3 released
+Dec 03, 2007: Thanks to Lapo Luchini for sending me a ZIP file that
+ libarchive couldn't handle. Fixed a bug in handling of
+ "length at end" flags in ZIP files.
+Dec 03, 2007: Fixed bsdcpio -help, bsdtar -help tests.
+Dec 02, 2007: First cut at real bsdtar test harness.
+
+Dec 02, 2007: libarchive 2.4.2 released
+
+Dec 02, 2007: libarchive 2.4.1 released
+Dec 02, 2007: Minor fixes, rough cut of mdoc-to-man conversion for
+ man pages.
+
+Oct 30, 2007: libarchive 2.4.0 released
+Oct 30, 2007: Minor compile fix thanks to Joerg Schilling.
+Oct 30, 2007: Only run the format auction once at the beginning of the
+ archive. This is simpler and supports better error recovery.
+Oct 29, 2007: Test support for very large entries in tar archives:
+ libarchive_test now exercises entries from 2GB up to 1TB.
+
+Oct 27, 2007: libarchive 2.3.5 released
+Oct 27, 2007: Correct some unnecessary internal data copying in the
+ "compression none" reader and writer; this reduces user time
+ by up to 2/3 in some tests. (Thanks to Jan Psota for
+ publishing his performance test results to GNU tar's bug-tar
+ mailing list; those results pointed me towards this problem.)
+Oct 27, 2007: Fix for skipping archive entries that are exactly
+ a multiple of 4G on 32-bit platforms.
+Oct 25, 2007: Fix for reading very large (>8G) tar archives; this was
+ broken when I put in support for new GNU tar sparse formats.
+Oct 20, 2007: Initial work on new pattern-matching code for cpio; I
+ hope this eventually replaces the code currently in bsdtar.
+
+Oct 08, 2007: libarchive 2.3.4 released
+Oct 05, 2007: Continuing work on bsdcpio test suite.
+Oct 05, 2007: New cpio.5 manpage, updates to "History" of bsdcpio.1 and
+ bsdtar.1 manpages.
+Oct 05, 2007: Fix zip reader to immediately return EOF if you try
+ to read body of non-regular file. In particular, this fixes
+ bsdtar extraction of zip archives.
+
+Sep 30, 2007: libarchive 2.3.3 released
+Sep 26, 2007: Rework Makefile.am so that the enable/disable options
+ actually do the right things.
+Sep 26, 2007: cpio-odc and cpio-newc archives no longer write bodies
+ for non-regular files.
+Sep 26, 2007: Test harness for bsdcpio is in place, needs more tests written.
+ This is much nicer than the ragtag collection of test scripts
+ that bsdtar has.
+
+Sep 20, 2007: libarchive 2.3.2 released
+Sep 20, 2007: libarchive 2.3.1 broke bsdtar because the archive_write_data()
+ fix was implemented incorrectly.
+
+Sep 16, 2007: libarchive 2.3.1 released
+Sep 16, 2007: Many fixes to bsdcpio 0.3: handle hardlinks with -p, recognize
+ block size on writing, fix a couple of segfaults.
+Sep 16, 2007: Fixed return value from archive_write_data() when used
+ with archive_write_disk() to match the documentation and other
+ instances of this same function.
+Sep 15, 2007: Add archive_entry_link_resolver, archive_entry_strmode
+
+Sep 11, 2007: libarchive 2.2.8 released
+Sep 09, 2007: bsdcpio 0.2 supports most (not yet all) of the old POSIX spec.
+
+Sep 01, 2007: libarchive 2.2.7 released
+Aug 31, 2007: Support for reading mtree files, including an mtree.5 manpage
+ (A little experimental still.)
+Aug 18, 2007: Read gtar 1.17 --posix --sparse entries.
+Aug 13, 2007: Refined suid/sgid restore handling; it is no longer
+ an error if suid/sgid bits are dropped when you request
+ perm restore but don't request owner restore.
+Aug 06, 2007: Use --enable-bsdcpio if you want to try bsdcpio
+
+Aug 05, 2007: libarchive 2.2.6 released
+Aug 05, 2007: New configure option --disable-bsdtar, thanks to Joerg
+ Sonnenberger.
+Aug 05, 2007: Several bug fixes from FreeBSD CVS repo.
+
+Jul 13, 2007: libarchive 2.2.5 released
+
+Jul 12, 2007: libarchive 2.2.4 released
+Jul 12, 2007: Thanks to Colin Percival's help in diagnosing and
+ fixing several critical security bugs. Details available at
+ http://security.freebsd.org/advisories/FreeBSD-SA-07:05.libarchive.asc
+
+May 26, 2007: libarchive 2.2.3 released
+May 26, 2007: Fix memory leaks in ZIP reader and shar writer, add some
+ missing system headers to archive_entry.h, dead code cleanup
+ from Colin Percival, more tests for gzip/bzip2, fix an
+ EOF anomaly in bzip2 decompression.
+
+May 12, 2007: libarchive 2.2.2 released
+May 12, 2007: Fix archive_write_disk permission restore by cloning
+ entry passed into write_header so that permission info is
+ still available at finish_entry time. (archive_read_extract()
+ worked okay because it held onto the passed-in entry, but
+ direct consumers of archive_write_disk would break). This
+ required fixing archive_entry_clone(), which now works and has
+ a reasonably complete test case.
+May 10, 2007: Skeletal cpio implementation.
+
+May 06, 2007: libarchive 2.2.1 released
+May 06, 2007: Flesh out a lot more of test_entry.c so as to catch
+ problems such as the device node breakage before releasing <sigh>.
+May 05, 2007: Fix a bad bug introduced in 2.1.9 that broke device
+ node entries in tar archives.
+May 03, 2007: Move 'struct stat' out of archive_entry core as well.
+ This removes some portability headaches and fixes a bunch
+ of corner cases that arise when manipulating archives on
+ dissimilar systems.
+
+Apr 30, 2007: libarchive 2.1.10 released
+Apr 31, 2007: Minor code cleanup.
+
+Apr 24, 2007: libarchive 2.1.9 released
+Apr 24, 2007: Fix some recently-introduced problems with libraries
+ (Just let automake handle it and it all works much better.)
+ Finish isolating major()/minor()/makedev() in archive_entry.c.
+
+Apr 23, 2007: libarchive 2.1.8 released
+Apr 23, 2007: Minor fixes found from building on MacOS X
+
+Apr 22, 2007: libarchive 2.1.7 released
+Apr 22, 2007: Eliminated all uses of 'struct stat' from the
+ format readers/writers. This should improve portability;
+ 'struct stat' is now only used in archive_entry and in
+ code that actually touches the disk.
+
+Apr 17, 2007: libarchive 2.1.6 released
+ Libarchive now compiles and passes all tests on Interix.
+
+Apr 16, 2007: libarchive 2.1.5 released
+
+Apr 15, 2007: libarchive 2.1b2 released
+Apr 15, 2007: New libarchive_internals.3 documentation of internal APIs.
+ Not complete, but should prove helpful.
+Apr 15, 2007: Experimental "read_compress_program" and "write_compress_program"
+ for using libarchive with external compression. Not yet
+ well tested, and likely has portability issues. Feedback
+ appreciated.
+
+Apr 14, 2007: libarchive 2.0.31 released
+Apr 14, 2007: More fixes for Interix, more 'ar' work
+
+Apr 14, 2007: libarchive 2.0.30 released
+Apr 13, 2007: libarchive now enforces trailing '/' on dirs
+ written to tar archives
+
+Apr 11, 2007: libarchive 2.0.29 released
+Apr 11, 2007: Make it easier to statically configure for different platforms.
+Apr 11, 2007: Updated config.guess, config.sub, libtool
+
+Apr 06, 2007: libarchive 2.0.28 released
+Apr 06, 2007: 'ar' format read/write support thanks to Kai Wang.
+
+Apr 01, 2007: libarchive 2.0.27 released
+Mar 31, 2007: Several minor fixes from Colin Percival and Joerg Sonnenberger.
+
+Mar 12, 2007: libarchive 2.0.25 released
+Mar 12, 2007: Fix broken --unlink flag.
+
+Mar 11, 2007: libarchive 2.0.24 released
+Mar 10, 2007: Correct an ACL blunder that causes any ACL with an entry
+ that refers to a non-existent user or group to not be restored correctly.
+ The fix both makes the parser more tolerant (so that archives created
+ with the buggy ACLs can be read now) and corrects the ACL formatter.
+Mar 10, 2007: More work on test portability to Linux.
+
+Mar 10, 2007: libarchive 2.0.22 released
+Mar 10, 2007: Header cleanups; added linux/fs.h, removed
+ some unnecessary headers, added #include guards in bsdtar.
+ If you see any obvious compile failures from this, let me know.
+Mar 10, 2007: Work on bsdtar test scripts: not yet robust enough
+ to enable as part of "make check", but getting better.
+Mar 10, 2007: libarchive now returns ARCHIVE_FAILED when
+ a header write fails in a way that only affects this item.
+ Less bad than ARCHIVE_FATAL, but worse than ARCHIVE_WARN.
+
+Mar 07, 2007: libarchive 2.0.21 released
+Mar 07, 2007: Add some ACL tests (only for the system-independent
+ portion of the ACL support for now).
+Mar 07, 2007: tar's ability to read ACLs off disk got
+ turned off for FreeBSD; re-enable it. (ACL restores and
+ libarchive support for storing/reading ACLs from pax
+ archives was unaffected.)
+
+Mar 02, 2007: libarchive 2.0.20 released
+Mar 2, 2007: It's not perfect, but it's pretty good.
+ Libarchive 2.0 is officially out of beta.
+
+Feb 28, 2007: libarchive 2.0b17 released
+Feb 27, 2007: Make the GID restore checks more robust by checking
+ whether the current user has too few or too many privileges.
+
+Feb 26, 2007: libarchive 2.0b15 released
+Feb 26, 2007: Don't lose symlinks when extracting from ISOs.
+ Thanks to Diego "Flameeyes" Pettenò for telling me about the
+ broken testcase on Gentoo that (finally!) led me to the cause
+ of this long-standing bug.
+
+Feb 26, 2007: libarchive 2.0b14 released
+Feb 26, 2007: Fix a broken test on platforms that lack lchmod().
+
+Feb 25, 2007: libarchive 2.0b13 released
+Feb 25, 2007: Empty archives were being written as empty files,
+ without a proper end-of-archive marker. Fixed.
+
+Feb 23, 2007: libarchive 2.0b12 released
+Feb 22, 2007: Basic security checks added: _EXTRACT_SECURE_NODOTDOT
+ and _EXTRACT_SECURE_SYMLINK. These checks used to be in bsdtar,
+ but they belong down in libarchive where they can be used by
+ other tools and where they can be better optimized.
+
+Feb 11, 2007: libarchive 2.0b11 released
+Feb 10, 2007: Fixed a bunch of errors in libarchive's handling
+ of EXTRACT_PERM and EXTRACT_OWNER, especially relating
+ to SUID and SGID bits.
+
+Jan 31, 2007: libarchive 2.0b9 released
+Jan 31, 2007: Added read support for "empty" archives as a
+ distinct archive format. Bsdtar uses this to handle, e.g.,
+ "touch foo.tar; tar -rf foo.tar"
+
+Jan 22, 2007: libarchive 2.0b6 released
+Jan 22, 2007: archive_write_disk API is now in place. It provides
+ a finer-grained interface than archive_read_extract. In particular,
+ you can use it to create objects on disk without having an archive
+ around (just feed it archive_entry objects describing what you
+ want to create), you can override the uname/gname-to-uid/gid lookups
+ (minitar uses this to avoid getpwXXX() and getgrXXX() bloat).
+
+Jan 09, 2007: libarchive 2.0a3 released
+Jan 9, 2007: archive_extract is now much better; it handles the
+ most common cases with a minimal number of system calls.
+ Some features still need a lot of testing, especially corner
+ cases involving objects that already exist on disk. I expect
+ the next round of API overhaul will simplify building test cases.
+Jan 9, 2007: a number of fixes thanks to Colin Percival, especially
+ corrections to the skip() framework and handling of large files.
+Jan 9, 2007: Fixes for large ISOs. The code should correctly handle
+ very large ISOs with entries up to 4G. Thanks to Robert Sciuk
+ for pointing out these issues.
+
+Sep 05, 2006: libarchive 1.3.1 released
+Sep 5, 2006: Bump version to 1.3 for new I/O wrappers.
+Sep 4, 2006: New memory and FILE read/write wrappers.
+Sep 4, 2006: libarchive test harness is now minimally functional;
+ it's located a few minor bugs in error-handling logic
+
+Aug 17, 2006: libarchive 1.2.54 released
+Aug 17, 2006: Outline ABI changes for libarchive 2.0; these
+ are protected behind #ifdef's until I think I've found everything
+ that needs to change.
+Aug 17, 2006: Fix error-handling in archive_read/write_close()
+ They weren't returning any errors before.
+Aug 17, 2006: Fix recursive-add logic to not trigger if it's not set
+ Fixes a bug adding files when writing archive to pipe or when
+ using archive_write_open() directly.
+Jul 2006: New "skip" handling improves performance extracting
+ single files from large uncompressed archives.
+
+Mar 21, 2006: 1.2.52 released
+Mar 21, 2006: Fix -p on platforms that don't have platform-specific
+ extended attribute code.
+Mar 20, 2006: Add NEWS file; fill in some older history from other
+ files. I'll try to keep this file up-to-date from now on.
+
+OLDER NEWS SUMMARIES
+
+Mar 19, 2006: libarchive 1.2.51 released
+Mar 18, 2006: Many fixes to extended attribute support, including a redesign
+ of the storage format to simplify debugging.
+Mar 12, 2006: Remove 'tp' support; it was a fun idea, but not worth
+ spending much time on.
+Mar 11, 2006: Incorporated Jaakko Heinonen's still-experimental support
+ for extended attributes (Currently Linux-only.).
+Mar 11, 2006: Reorganized distribution package: There is now one tar.gz
+ file that builds both libarchive and bsdtar.
+Feb 13, 2006: Minor bug fixes: correctly read cpio device entries, write
+ Pax attribute entry names.
+Nov 7, 2005: Experimental 'tp' format support in libarchive. Feedback
+ appreciated; this is not enabled by archive_read_support_format_all()
+ yet as I'm not quite content with the format detection heuristics.
+Nov 7, 2005: Some more portability improvements thanks to Darin Broady,
+ minor bugfixes.
+Oct 12, 2005: Use GNU libtool to build shared libraries on many systems.
+Aug 9, 2005: Correctly detect that MacOS X does not have POSIX ACLs.
+Apr 17, 2005: Kees Zeelenberg has ported libarchive and bsdtar to Windows:
+ http://gnuwin32.sourceforge.net/
+Apr 11, 2005: Extended Zip/Zip64 support thanks to Dan Nelson. -L/-h
+ fix from Jaakko Heinonen.
+Mar 12, 2005: archive_read_extract can now handle very long
+ pathnames (I've tested with pathnames up to 1MB).
+Mar 12, 2005: Marcus Geiger has written an article about libarchive
+ http://xsnil.antbear.org/2005/02/05/archive-mit-libarchive-verarbeiten/
+ including examples of using it from Objective-C. His MoinX
+ http://moinx.antbear.org/ desktop Wiki uses
+ libarchive for archiving and restoring Wiki pages.
+Jan 22, 2005: Preliminary ZIP extraction support,
+ new directory-walking code for bsdtar.
+Jan 16, 2005: ISO9660 extraction code added; manpage corrections.
+May 22, 2004: Many gtar-compatible long options have been added; almost
+ all FreeBSD ports extract correctly with bsdtar.
+May 18, 2004: bsdtar can read Solaris, HP-UX, Unixware, star, gtar,
+ and pdtar archives.
diff --git a/contrib/libs/libarchive/README.md b/contrib/libs/libarchive/README.md
new file mode 100644
index 00000000000..727ed49856b
--- /dev/null
+++ b/contrib/libs/libarchive/README.md
@@ -0,0 +1,244 @@
+# Welcome to libarchive!
+
+The libarchive project develops a portable, efficient C library that
+can read and write streaming archives in a variety of formats. It
+also includes implementations of the common `tar`, `cpio`, and `zcat`
+command-line tools that use the libarchive library.
+
+## Questions? Issues?
+
+* https://www.libarchive.org is the home for ongoing
+ libarchive development, including documentation,
+ and links to the libarchive mailing lists.
+* To report an issue, use the issue tracker at
+ https://github.com/libarchive/libarchive/issues
+* To submit an enhancement to libarchive, please
+ submit a pull request via GitHub: https://github.com/libarchive/libarchive/pulls
+
+## Contents of the Distribution
+
+This distribution bundle includes the following major components:
+
+* **libarchive**: a library for reading and writing streaming archives
+* **tar**: the 'bsdtar' program is a full-featured 'tar' implementation built on libarchive
+* **cpio**: the 'bsdcpio' program is a different interface to essentially the same functionality
+* **cat**: the 'bsdcat' program is a simple replacement tool for zcat, bzcat, xzcat, and such
+* **unzip**: the 'bsdunzip' program is a simple replacement tool for Info-ZIP's unzip
+* **examples**: Some small example programs that you may find useful.
+* **examples/minitar**: a compact sample demonstrating use of libarchive.
+* **contrib**: Various items sent to me by third parties; please contact the authors with any questions.
+
+The top-level directory contains the following information files:
+
+* **NEWS** - highlights of recent changes
+* **COPYING** - what you can do with this
+* **INSTALL** - installation instructions
+* **README** - this file
+* **CMakeLists.txt** - input for "cmake" build tool, see INSTALL
+* **configure** - configuration script, see INSTALL for details. If your copy of the source lacks a `configure` script, you can try to construct it by running the script in `build/autogen.sh` (or use `cmake`).
+
+The following files in the top-level directory are used by the 'configure' script:
+
+* `Makefile.am`, `aclocal.m4`, `configure.ac` - used to build this distribution, only needed by maintainers
+* `Makefile.in`, `config.h.in` - templates used by configure script
+
+## Documentation
+
+In addition to the informational articles and documentation
+in the online [libarchive Wiki](https://github.com/libarchive/libarchive/wiki),
+the distribution also includes a number of manual pages:
+
+ * bsdtar.1 explains the use of the bsdtar program
+ * bsdcpio.1 explains the use of the bsdcpio program
+ * bsdcat.1 explains the use of the bsdcat program
+ * libarchive.3 gives an overview of the library as a whole
+ * archive_read.3, archive_write.3, archive_write_disk.3, and
+ archive_read_disk.3 provide detailed calling sequences for the read
+ and write APIs
+ * archive_entry.3 details the "struct archive_entry" utility class
+ * archive_internals.3 provides some insight into libarchive's
+ internal structure and operation.
+ * libarchive-formats.5 documents the file formats supported by the library
+ * cpio.5, mtree.5, and tar.5 provide detailed information about these
+ popular archive formats, including hard-to-find details about
+ modern cpio and tar variants.
+
+The manual pages above are provided in the 'doc' directory in
+a number of different formats.
+
+You should also read the copious comments in `archive.h` and the
+source code for the sample programs for more details. Please let us
+know about any errors or omissions you find.
+
+## Supported Formats
+
+Currently, the library automatically detects and reads the following formats:
+
+ * Old V7 tar archives
+ * POSIX ustar
+ * GNU tar format (including GNU long filenames, long link names, and sparse files)
+ * Solaris 9 extended tar format (including ACLs)
+ * POSIX pax interchange format
+ * POSIX octet-oriented cpio
+ * SVR4 ASCII cpio
+ * Binary cpio (big-endian or little-endian)
+ * PWB binary cpio
+ * ISO9660 CD-ROM images (with optional Rockridge or Joliet extensions)
+ * ZIP archives (with uncompressed or "deflate" compressed entries, including support for encrypted Zip archives)
+ * ZIPX archives (with support for bzip2, ppmd8, lzma and xz compressed entries)
+ * GNU and BSD 'ar' archives
+ * 'mtree' format
+ * 7-Zip archives (including archives that use zstandard compression)
+ * Microsoft CAB format
+ * LHA and LZH archives
+ * RAR and RAR 5.0 archives (with some limitations due to RAR's proprietary status)
+ * XAR archives
+
+The library also detects and handles any of the following before evaluating the archive:
+
+ * uuencoded files
+ * files with RPM wrapper
+ * gzip compression
+ * bzip2 compression
+ * compress/LZW compression
+ * lzma, lzip, and xz compression
+ * lz4 compression
+ * lzop compression
+ * zstandard compression
+
+The library can create archives in any of the following formats:
+
+ * POSIX ustar
+ * POSIX pax interchange format
+ * "restricted" pax format, which will create ustar archives except for
+ entries that require pax extensions (for long filenames, ACLs, etc).
+ * Old GNU tar format
+ * Old V7 tar format
+ * POSIX octet-oriented cpio
+ * SVR4 "newc" cpio
+ * Binary cpio (little-endian)
+ * PWB binary cpio
+ * shar archives
+ * ZIP archives (with uncompressed or "deflate" compressed entries)
+ * GNU and BSD 'ar' archives
+ * 'mtree' format
+ * ISO9660 format
+ * 7-Zip archives
+ * XAR archives
+
+When creating archives, the result can be filtered with any of the following:
+
+ * uuencode
+ * gzip compression
+ * bzip2 compression
+ * compress/LZW compression
+ * lzma, lzip, and xz compression
+ * lz4 compression
+ * lzop compression
+ * zstandard compression
+
+## Notes about the Library Design
+
+The following notes address many of the most common
+questions we are asked about libarchive:
+
+* This is a heavily stream-oriented system. That means that
+ it is optimized to read or write the archive in a single
+ pass from beginning to end. For example, this allows
+ libarchive to process archives too large to store on disk
+ by processing them on-the-fly as they are read from or
+ written to a network or tape drive. This also makes
+ libarchive useful for tools that need to produce
+ archives on-the-fly (such as webservers that provide
+ archived contents of a users account).
+
+* In-place modification and random access to the contents
+ of an archive are not directly supported. For some formats,
+ this is not an issue: For example, tar.gz archives are not
+ designed for random access. In some other cases, libarchive
+ can re-open an archive and scan it from the beginning quickly
+ enough to provide the needed abilities even without true
+ random access. Of course, some applications do require true
+ random access; those applications should consider alternatives
+ to libarchive.
+
+* The library is designed to be extended with new compression and
+ archive formats. The only requirement is that the format be
+ readable or writable as a stream and that each archive entry be
+ independent. There are articles on the libarchive Wiki explaining
+ how to extend libarchive.
+
+* On read, compression and format are always detected automatically.
+
+* The same API is used for all formats; it should be very
+ easy for software using libarchive to transparently handle
+ any of libarchive's archiving formats.
+
+* Libarchive's automatic support for decompression can be used
+ without archiving by explicitly selecting the "raw" and "empty"
+ formats.
+
+* I've attempted to minimize static link pollution. If you don't
+ explicitly invoke a particular feature (such as support for a
+ particular compression or format), it won't get pulled in to
+ statically-linked programs. In particular, if you don't explicitly
+ enable a particular compression or decompression support, you won't
+ need to link against the corresponding compression or decompression
+ libraries. This also reduces the size of statically-linked
+ binaries in environments where that matters.
+
+* The library is generally _thread safe_ depending on the platform:
+ it does not define any global variables of its own. However, some
+ platforms do not provide fully thread-safe versions of key C library
+ functions. On those platforms, libarchive will use the non-thread-safe
+ functions. Patches to improve this are of great interest to us.
+
+* The function `archive_write_disk_header()` is _not_ thread safe on
+ POSIX machines and could lead to security issue resulting in world
+ writeable directories. Thus it must be mutexed by the calling code.
+ This is due to calling `umask(oldumask = umask(0))`, which sets the
+ umask for the whole process to 0 for a short time frame.
+ In case other thread calls the same function in parallel, it might
+ get interrupted by it and cause the executable to use umask=0 for the
+ remaining execution.
+ This will then lead to implicitely created directories to have 777
+ permissions without sticky bit.
+
+* In particular, libarchive's modules to read or write a directory
+ tree do use `chdir()` to optimize the directory traversals. This
+ can cause problems for programs that expect to do disk access from
+ multiple threads. Of course, those modules are completely
+ optional and you can use the rest of libarchive without them.
+
+* The library is _not_ thread aware, however. It does no locking
+ or thread management of any kind. If you create a libarchive
+ object and need to access it from multiple threads, you will
+ need to provide your own locking.
+
+* On read, the library accepts whatever blocks you hand it.
+ Your read callback is free to pass the library a byte at a time
+ or mmap the entire archive and give it to the library at once.
+ On write, the library always produces correctly-blocked output.
+
+* The object-style approach allows you to have multiple archive streams
+ open at once. bsdtar uses this in its "@archive" extension.
+
+* The archive itself is read/written using callback functions.
+ You can read an archive directly from an in-memory buffer or
+ write it to a socket, if you wish. There are some utility
+ functions to provide easy-to-use "open file," etc, capabilities.
+
+* The read/write APIs are designed to allow individual entries
+ to be read or written to any data source: You can create
+ a block of data in memory and add it to a tar archive without
+ first writing a temporary file. You can also read an entry from
+ an archive and write the data directly to a socket. If you want
+ to read/write entries to disk, there are convenience functions to
+ make this especially easy.
+
+* Note: The "pax interchange format" is a POSIX standard extended tar
+ format that should be used when the older _ustar_ format is not
+ appropriate. It has many advantages over other tar formats
+ (including the legacy GNU tar format) and is widely supported by
+ current tar implementations.
+
diff --git a/contrib/libs/libarchive/SECURITY.md b/contrib/libs/libarchive/SECURITY.md
new file mode 100644
index 00000000000..6ca188b603f
--- /dev/null
+++ b/contrib/libs/libarchive/SECURITY.md
@@ -0,0 +1,19 @@
+# Security Policy
+
+If you have discovered a security vulnerability in this project, please report it
+privately. **Do not disclose it as a public issue.** This gives us time to work with you
+to fix the issue before public exposure, reducing the chance that the exploit will be
+used before a patch is released.
+
+You may submit the report in the following ways:
+
+- send an email to security@libarchive.de; and/or
+- send us a [private vulnerability report](https://github.com/libarchive/libarchive/security/advisories/new)
+
+Please provide the following information in your report:
+
+- A description of the vulnerability and its impact
+- How to reproduce the issue
+
+This project is maintained by volunteers on a reasonable-effort basis. As such, we ask
+that you give me 90 days to work on a fix before public exposure.
diff --git a/contrib/libs/libarchive/config-linux.h b/contrib/libs/libarchive/config-linux.h
new file mode 100644
index 00000000000..50d5a3fc663
--- /dev/null
+++ b/contrib/libs/libarchive/config-linux.h
@@ -0,0 +1,1396 @@
+/* config.h. Generated from build/cmake/config.h.in by cmake configure */
+#define __LIBARCHIVE_CONFIG_H_INCLUDED 1
+
+/*
+ * Ensure we have C99-style int64_t, etc, all defined.
+ */
+
+/* First, we need to know if the system has already defined them. */
+#define HAVE_INT16_T
+#define HAVE_INT32_T
+#define HAVE_INT64_T
+#define HAVE_INTMAX_T
+
+#define HAVE_UINT8_T
+#define HAVE_UINT16_T
+#define HAVE_UINT32_T
+#define HAVE_UINT64_T
+#define HAVE_UINTMAX_T
+
+/* We might have the types we want under other spellings. */
+/* #undef HAVE___INT64 */
+/* #undef HAVE_U_INT64_T */
+/* #undef HAVE_UNSIGNED___INT64 */
+
+/* The sizes of various standard integer types. */
+#define SIZEOF_SHORT 2
+#define SIZEOF_INT 4
+#define SIZEOF_LONG 8
+#define SIZEOF_LONG_LONG 8
+#define SIZEOF_UNSIGNED_SHORT 2
+#define SIZEOF_UNSIGNED 4
+#define SIZEOF_UNSIGNED_LONG 8
+#define SIZEOF_UNSIGNED_LONG_LONG 8
+
+/*
+ * If we lack int64_t, define it to the first of __int64, int, long, and long long
+ * that exists and is the right size.
+ */
+#if !defined(HAVE_INT64_T) && defined(HAVE___INT64)
+typedef __int64 int64_t;
+#define HAVE_INT64_T
+#endif
+
+#if !defined(HAVE_INT64_T) && SIZEOF_INT == 8
+typedef int int64_t;
+#define HAVE_INT64_T
+#endif
+
+#if !defined(HAVE_INT64_T) && SIZEOF_LONG == 8
+typedef long int64_t;
+#define HAVE_INT64_T
+#endif
+
+#if !defined(HAVE_INT64_T) && SIZEOF_LONG_LONG == 8
+typedef long long int64_t;
+#define HAVE_INT64_T
+#endif
+
+#if !defined(HAVE_INT64_T)
+#error No 64-bit integer type was found.
+#endif
+
+/*
+ * Similarly for int32_t
+ */
+#if !defined(HAVE_INT32_T) && SIZEOF_INT == 4
+typedef int int32_t;
+#define HAVE_INT32_T
+#endif
+
+#if !defined(HAVE_INT32_T) && SIZEOF_LONG == 4
+typedef long int32_t;
+#define HAVE_INT32_T
+#endif
+
+#if !defined(HAVE_INT32_T)
+#error No 32-bit integer type was found.
+#endif
+
+/*
+ * Similarly for int16_t
+ */
+#if !defined(HAVE_INT16_T) && SIZEOF_INT == 2
+typedef int int16_t;
+#define HAVE_INT16_T
+#endif
+
+#if !defined(HAVE_INT16_T) && SIZEOF_SHORT == 2
+typedef short int16_t;
+#define HAVE_INT16_T
+#endif
+
+#if !defined(HAVE_INT16_T)
+#error No 16-bit integer type was found.
+#endif
+
+/*
+ * Similarly for uint64_t
+ */
+#if !defined(HAVE_UINT64_T) && defined(HAVE_UNSIGNED___INT64)
+typedef unsigned __int64 uint64_t;
+#define HAVE_UINT64_T
+#endif
+
+#if !defined(HAVE_UINT64_T) && SIZEOF_UNSIGNED == 8
+typedef unsigned uint64_t;
+#define HAVE_UINT64_T
+#endif
+
+#if !defined(HAVE_UINT64_T) && SIZEOF_UNSIGNED_LONG == 8
+typedef unsigned long uint64_t;
+#define HAVE_UINT64_T
+#endif
+
+#if !defined(HAVE_UINT64_T) && SIZEOF_UNSIGNED_LONG_LONG == 8
+typedef unsigned long long uint64_t;
+#define HAVE_UINT64_T
+#endif
+
+#if !defined(HAVE_UINT64_T)
+#error No 64-bit unsigned integer type was found.
+#endif
+
+
+/*
+ * Similarly for uint32_t
+ */
+#if !defined(HAVE_UINT32_T) && SIZEOF_UNSIGNED == 4
+typedef unsigned uint32_t;
+#define HAVE_UINT32_T
+#endif
+
+#if !defined(HAVE_UINT32_T) && SIZEOF_UNSIGNED_LONG == 4
+typedef unsigned long uint32_t;
+#define HAVE_UINT32_T
+#endif
+
+#if !defined(HAVE_UINT32_T)
+#error No 32-bit unsigned integer type was found.
+#endif
+
+/*
+ * Similarly for uint16_t
+ */
+#if !defined(HAVE_UINT16_T) && SIZEOF_UNSIGNED == 2
+typedef unsigned uint16_t;
+#define HAVE_UINT16_T
+#endif
+
+#if !defined(HAVE_UINT16_T) && SIZEOF_UNSIGNED_SHORT == 2
+typedef unsigned short uint16_t;
+#define HAVE_UINT16_T
+#endif
+
+#if !defined(HAVE_UINT16_T)
+#error No 16-bit unsigned integer type was found.
+#endif
+
+/*
+ * Similarly for uint8_t
+ */
+#if !defined(HAVE_UINT8_T)
+typedef unsigned char uint8_t;
+#define HAVE_UINT8_T
+#endif
+
+#if !defined(HAVE_UINT8_T)
+#error No 8-bit unsigned integer type was found.
+#endif
+
+/* Define intmax_t and uintmax_t if they are not already defined. */
+#if !defined(HAVE_INTMAX_T)
+typedef int64_t intmax_t;
+#endif
+
+#if !defined(HAVE_UINTMAX_T)
+typedef uint64_t uintmax_t;
+#endif
+
+/* Define ZLIB_WINAPI if zlib was built on Visual Studio. */
+/* #undef ZLIB_WINAPI */
+
+/* Darwin ACL support */
+/* #undef ARCHIVE_ACL_DARWIN */
+
+/* FreeBSD ACL support */
+/* #undef ARCHIVE_ACL_FREEBSD */
+
+/* FreeBSD NFSv4 ACL support */
+/* #undef ARCHIVE_ACL_FREEBSD_NFS4 */
+
+/* Linux POSIX.1e ACL support via libacl */
+/* #undef ARCHIVE_ACL_LIBACL */
+
+/* Linux NFSv4 ACL support via librichacl */
+/* #undef ARCHIVE_ACL_LIBRICHACL */
+
+/* Solaris ACL support */
+/* #undef ARCHIVE_ACL_SUNOS */
+
+/* Solaris NFSv4 ACL support */
+/* #undef ARCHIVE_ACL_SUNOS_NFS4 */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_LIBC */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_LIBSYSTEM */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_MBEDTLS */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_NETTLE */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_OPENSSL supported. */
+#define ARCHIVE_CRYPTO_MD5_OPENSSL 1
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_WIN */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_LIBC */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_NETTLE */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_MBEDTLS */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_OPENSSL supported. */
+#define ARCHIVE_CRYPTO_RMD160_OPENSSL 1
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_LIBC */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_LIBSYSTEM */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_MBEDTLS */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_NETTLE */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_OPENSSL supported. */
+#define ARCHIVE_CRYPTO_SHA1_OPENSSL 1
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_WIN */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC2 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC2 */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC3 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC3 */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBSYSTEM */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_MBEDTLS */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_NETTLE */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_OPENSSL supported. */
+#define ARCHIVE_CRYPTO_SHA256_OPENSSL 1
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_WIN */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC2 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC2 */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC3 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC3 */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBSYSTEM */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_MBEDTLS */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_NETTLE */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_OPENSSL supported. */
+#define ARCHIVE_CRYPTO_SHA384_OPENSSL 1
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_WIN */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC2 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC2 */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC3 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC3 */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBSYSTEM */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_MBEDTLS */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_NETTLE */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_OPENSSL supported. */
+#define ARCHIVE_CRYPTO_SHA512_OPENSSL 1
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_WIN */
+
+/* AIX xattr support */
+/* #undef ARCHIVE_XATTR_AIX */
+
+/* Darwin xattr support */
+/* #undef ARCHIVE_XATTR_DARWIN */
+
+/* FreeBSD xattr support */
+/* #undef ARCHIVE_XATTR_FREEBSD */
+
+/* Linux xattr support */
+/* #undef ARCHIVE_XATTR_LINUX */
+
+/* Version number of bsdcpio */
+#define BSDCPIO_VERSION_STRING "3.7.1"
+
+/* Version number of bsdtar */
+#define BSDTAR_VERSION_STRING "3.7.1"
+
+/* Version number of bsdcat */
+#define BSDCAT_VERSION_STRING "3.7.1"
+
+/* Define to 1 if you have the `acl_create_entry' function. */
+/* #undef HAVE_ACL_CREATE_ENTRY */
+
+/* Define to 1 if you have the `acl_get_fd_np' function. */
+/* #undef HAVE_ACL_GET_FD_NP */
+
+/* Define to 1 if you have the `acl_get_link' function. */
+/* #undef HAVE_ACL_GET_LINK */
+
+/* Define to 1 if you have the `acl_get_link_np' function. */
+/* #undef HAVE_ACL_GET_LINK_NP */
+
+/* Define to 1 if you have the `acl_get_perm' function. */
+/* #undef HAVE_ACL_GET_PERM */
+
+/* Define to 1 if you have the `acl_get_perm_np' function. */
+/* #undef HAVE_ACL_GET_PERM_NP */
+
+/* Define to 1 if you have the `acl_init' function. */
+/* #undef HAVE_ACL_INIT */
+
+/* Define to 1 if you have the <acl/libacl.h> header file. */
+/* #undef HAVE_ACL_LIBACL_H */
+
+/* Define to 1 if the system has the type `acl_permset_t'. */
+/* #undef HAVE_ACL_PERMSET_T */
+
+/* Define to 1 if you have the `acl_set_fd' function. */
+/* #undef HAVE_ACL_SET_FD */
+
+/* Define to 1 if you have the `acl_set_fd_np' function. */
+/* #undef HAVE_ACL_SET_FD_NP */
+
+/* Define to 1 if you have the `acl_set_file' function. */
+/* #undef HAVE_ACL_SET_FILE */
+
+/* Define to 1 if you have the `arc4random_buf' function. */
+/* #undef HAVE_ARC4RANDOM_BUF */
+
+/* Define to 1 if you have the <attr/xattr.h> header file. */
+/* #undef HAVE_ATTR_XATTR_H */
+
+/* Define to 1 if you have the <bcrypt.h> header file. */
+/* #undef HAVE_BCRYPT_H */
+
+/* Define to 1 if you have the <bsdxml.h> header file. */
+/* #undef HAVE_BSDXML_H */
+
+/* Define to 1 if you have the <bzlib.h> header file. */
+#define HAVE_BZLIB_H 1
+
+/* Define to 1 if you have the `chflags' function. */
+/* #undef HAVE_CHFLAGS */
+
+/* Define to 1 if you have the `chown' function. */
+#define HAVE_CHOWN 1
+
+/* Define to 1 if you have the `chroot' function. */
+#define HAVE_CHROOT 1
+
+/* Define to 1 if you have the <copyfile.h> header file. */
+/* #undef HAVE_COPYFILE_H */
+
+/* Define to 1 if you have the `ctime_r' function. */
+#define HAVE_CTIME_R 1
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define to 1 if you have the `cygwin_conv_path' function. */
+/* #undef HAVE_CYGWIN_CONV_PATH */
+
+/* Define to 1 if you have the declaration of `ACE_GETACL', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_ACE_GETACL */
+
+/* Define to 1 if you have the declaration of `ACE_GETACLCNT', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_ACE_GETACLCNT */
+
+/* Define to 1 if you have the declaration of `ACE_SETACL', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_ACE_SETACL */
+
+/* Define to 1 if you have the declaration of `ACL_SYNCHRONIZE', and to 0 if
+ you don't. */
+/* #undef HAVE_DECL_ACL_SYNCHRONIZE */
+
+/* Define to 1 if you have the declaration of `ACL_TYPE_EXTENDED', and to 0 if
+ you don't. */
+/* #undef HAVE_DECL_ACL_TYPE_EXTENDED */
+
+/* Define to 1 if you have the declaration of `ACL_TYPE_NFS4', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_ACL_TYPE_NFS4 */
+
+/* Define to 1 if you have the declaration of `ACL_USER', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_ACL_USER */
+
+/* Define to 1 if you have the declaration of `INT32_MAX', and to 0 if you
+ don't. */
+#define HAVE_DECL_INT32_MAX 1
+
+/* Define to 1 if you have the declaration of `INT32_MIN', and to 0 if you
+ don't. */
+#define HAVE_DECL_INT32_MIN 1
+
+/* Define to 1 if you have the declaration of `INT64_MAX', and to 0 if you
+ don't. */
+#define HAVE_DECL_INT64_MAX 1
+
+/* Define to 1 if you have the declaration of `INT64_MIN', and to 0 if you
+ don't. */
+#define HAVE_DECL_INT64_MIN 1
+
+/* Define to 1 if you have the declaration of `INTMAX_MAX', and to 0 if you
+ don't. */
+#define HAVE_DECL_INTMAX_MAX 1
+
+/* Define to 1 if you have the declaration of `INTMAX_MIN', and to 0 if you
+ don't. */
+#define HAVE_DECL_INTMAX_MIN 1
+
+/* Define to 1 if you have the declaration of `SETACL', and to 0 if you don't.
+ */
+/* #undef HAVE_DECL_SETACL */
+
+/* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you
+ don't. */
+#define HAVE_DECL_SIZE_MAX 1
+
+/* Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you
+ don't. */
+#define HAVE_DECL_SSIZE_MAX 1
+
+/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you
+ don't. */
+#define HAVE_DECL_STRERROR_R 1
+
+/* Define to 1 if you have the declaration of `UINT32_MAX', and to 0 if you
+ don't. */
+#define HAVE_DECL_UINT32_MAX 1
+
+/* Define to 1 if you have the declaration of `UINT64_MAX', and to 0 if you
+ don't. */
+#define HAVE_DECL_UINT64_MAX 1
+
+/* Define to 1 if you have the declaration of `UINTMAX_MAX', and to 0 if you
+ don't. */
+#define HAVE_DECL_UINTMAX_MAX 1
+
+/* Define to 1 if you have the declaration of `XATTR_NOFOLLOW', and to 0 if
+ you don't. */
+/* #undef HAVE_DECL_XATTR_NOFOLLOW */
+
+/* Define to 1 if you have the <direct.h> header file. */
+/* #undef HAVE_DIRECT_H */
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+ */
+#define HAVE_DIRENT_H 1
+
+/* Define to 1 if you have the `dirfd' function. */
+#define HAVE_DIRFD 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+/* #undef HAVE_DOPRNT */
+
+/* Define to 1 if nl_langinfo supports D_MD_ORDER */
+/* #undef HAVE_D_MD_ORDER */
+
+/* A possible errno value for invalid file format errors */
+/* #undef HAVE_EFTYPE */
+
+/* A possible errno value for invalid file format errors */
+#define HAVE_EILSEQ 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <expat.h> header file. */
+/* #undef HAVE_EXPAT_H */
+
+/* Define to 1 if you have the <ext2fs/ext2_fs.h> header file. */
+/* #undef HAVE_EXT2FS_EXT2_FS_H */
+
+/* Define to 1 if you have the `extattr_get_file' function. */
+/* #undef HAVE_EXTATTR_GET_FILE */
+
+/* Define to 1 if you have the `extattr_list_file' function. */
+/* #undef HAVE_EXTATTR_LIST_FILE */
+
+/* Define to 1 if you have the `extattr_set_fd' function. */
+/* #undef HAVE_EXTATTR_SET_FD */
+
+/* Define to 1 if you have the `extattr_set_file' function. */
+/* #undef HAVE_EXTATTR_SET_FILE */
+
+/* Define to 1 if EXTATTR_NAMESPACE_USER is defined in sys/extattr.h. */
+/* #undef HAVE_DECL_EXTATTR_NAMESPACE_USER */
+
+/* Define to 1 if you have the declaration of `GETACL', and to 0 if you don't.
+ */
+/* #undef HAVE_DECL_GETACL */
+
+/* Define to 1 if you have the declaration of `GETACLCNT', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_GETACLCNT */
+
+/* Define to 1 if you have the `fchdir' function. */
+#define HAVE_FCHDIR 1
+
+/* Define to 1 if you have the `fchflags' function. */
+/* #undef HAVE_FCHFLAGS */
+
+/* Define to 1 if you have the `fchmod' function. */
+#define HAVE_FCHMOD 1
+
+/* Define to 1 if you have the `fchown' function. */
+#define HAVE_FCHOWN 1
+
+/* Define to 1 if you have the `fcntl' function. */
+#define HAVE_FCNTL 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `fdopendir' function. */
+#define HAVE_FDOPENDIR 1
+
+/* Define to 1 if you have the `fgetea' function. */
+/* #undef HAVE_FGETEA */
+
+/* Define to 1 if you have the `fgetxattr' function. */
+/* #undef HAVE_FGETXATTR */
+
+/* Define to 1 if you have the `flistea' function. */
+/* #undef HAVE_FLISTEA */
+
+/* Define to 1 if you have the `flistxattr' function. */
+/* #undef HAVE_FLISTXATTR */
+
+/* Define to 1 if you have the `fnmatch' function. */
+#define HAVE_FNMATCH 1
+
+/* Define to 1 if you have the <fnmatch.h> header file. */
+#define HAVE_FNMATCH_H 1
+
+/* Define to 1 if you have the `fork' function. */
+#define HAVE_FORK 1
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#define HAVE_FSEEKO 1
+
+/* Define to 1 if you have the `fsetea' function. */
+/* #undef HAVE_FSETEA */
+
+/* Define to 1 if you have the `fsetxattr' function. */
+/* #undef HAVE_FSETXATTR */
+
+/* Define to 1 if you have the `fstat' function. */
+#define HAVE_FSTAT 1
+
+/* Define to 1 if you have the `fstatat' function. */
+#define HAVE_FSTATAT 1
+
+/* Define to 1 if you have the `fstatfs' function. */
+#define HAVE_FSTATFS 1
+
+/* Define to 1 if you have the `fstatvfs' function. */
+/* #undef HAVE_FSTATVFS */
+
+/* Define to 1 if you have the `ftruncate' function. */
+#define HAVE_FTRUNCATE 1
+
+/* Define to 1 if you have the `futimens' function. */
+#define HAVE_FUTIMENS 1
+
+/* Define to 1 if you have the `futimes' function. */
+#define HAVE_FUTIMES 1
+
+/* Define to 1 if you have the `futimesat' function. */
+#define HAVE_FUTIMESAT 1
+
+/* Define to 1 if you have the `getea' function. */
+/* #undef HAVE_GETEA */
+
+/* Define to 1 if you have the `geteuid' function. */
+#define HAVE_GETEUID 1
+
+/* Define to 1 if you have the `getgrgid_r' function. */
+#define HAVE_GETGRGID_R 1
+
+/* Define to 1 if you have the `getgrnam_r' function. */
+#define HAVE_GETGRNAM_R 1
+
+/* Define to 1 if you have the `getline' function. */
+#define HAVE_GETLINE 1
+
+/* Define to 1 if platform uses `optreset` to reset `getopt` */
+/* #undef HAVE_GETOPT_OPTRESET */
+
+/* Define to 1 if you have the `getpid' function. */
+#define HAVE_GETPID 1
+
+/* Define to 1 if you have the `getpwnam_r' function. */
+#define HAVE_GETPWNAM_R 1
+
+/* Define to 1 if you have the `getpwuid_r' function. */
+#define HAVE_GETPWUID_R 1
+
+/* Define to 1 if you have the `getvfsbyname' function. */
+/* #undef HAVE_GETVFSBYNAME */
+
+/* Define to 1 if you have the `getxattr' function. */
+/* #undef HAVE_GETXATTR */
+
+/* Define to 1 if you have the `gmtime_r' function. */
+#define HAVE_GMTIME_R 1
+
+/* Define to 1 if you have the <grp.h> header file. */
+#define HAVE_GRP_H 1
+
+/* Define to 1 if you have the `iconv' function. */
+/* #undef HAVE_ICONV */
+
+/* Define to 1 if you have the <iconv.h> header file. */
+/* #undef HAVE_ICONV_H */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <io.h> header file. */
+/* #undef HAVE_IO_H */
+
+/* Define to 1 if you have the <langinfo.h> header file. */
+#define HAVE_LANGINFO_H 1
+
+/* Define to 1 if you have the `lchflags' function. */
+/* #undef HAVE_LCHFLAGS */
+
+/* Define to 1 if you have the `lchmod' function. */
+#define HAVE_LCHMOD 1
+
+/* Define to 1 if you have the `lchown' function. */
+#define HAVE_LCHOWN 1
+
+/* Define to 1 if you have the `lgetea' function. */
+/* #undef HAVE_LGETEA */
+
+/* Define to 1 if you have the `lgetxattr' function. */
+/* #undef HAVE_LGETXATTR */
+
+/* Define to 1 if you have the `acl' library (-lacl). */
+/* #undef HAVE_LIBACL */
+
+/* Define to 1 if you have the `attr' library (-lattr). */
+/* #undef HAVE_LIBATTR */
+
+/* Define to 1 if you have the `bsdxml' library (-lbsdxml). */
+/* #undef HAVE_LIBBSDXML */
+
+/* Define to 1 if you have the `bz2' library (-lbz2). */
+#define HAVE_LIBBZ2 1
+
+/* Define to 1 if you have the `b2' library (-lb2). */
+#define HAVE_LIBB2 1
+
+/* Define to 1 if you have the <blake2.h> header file. */
+#define HAVE_BLAKE2_H 1
+
+/* Define to 1 if you have the `charset' library (-lcharset). */
+/* #undef HAVE_LIBCHARSET */
+
+/* Define to 1 if you have the `crypto' library (-lcrypto). */
+#define HAVE_LIBCRYPTO 1
+
+/* Define to 1 if you have the `expat' library (-lexpat). */
+/* #undef HAVE_LIBEXPAT */
+
+/* Define to 1 if you have the `gcc' library (-lgcc). */
+/* #undef HAVE_LIBGCC */
+
+/* Define to 1 if you have the `lz4' library (-llz4). */
+#define HAVE_LIBLZ4 1
+
+/* Define to 1 if you have the `lzma' library (-llzma). */
+/* #undef HAVE_LIBLZMA */
+
+/* Define to 1 if you have the `lzmadec' library (-llzmadec). */
+/* #undef HAVE_LIBLZMADEC */
+
+/* Define to 1 if you have the `lzo2' library (-llzo2). */
+/* #undef HAVE_LIBLZO2 */
+
+/* Define to 1 if you have the `mbedcrypto' library (-lmbedcrypto). */
+/* #undef HAVE_LIBMBEDCRYPTO */
+
+/* Define to 1 if you have the `nettle' library (-lnettle). */
+/* #undef HAVE_LIBNETTLE */
+
+/* Define to 1 if you have the `pcre' library (-lpcre). */
+/* #undef HAVE_LIBPCRE */
+
+/* Define to 1 if you have the `pcreposix' library (-lpcreposix). */
+/* #undef HAVE_LIBPCREPOSIX */
+
+/* Define to 1 if you have the `xml2' library (-lxml2). */
+/* #undef HAVE_LIBXML2 */
+
+/* Define to 1 if you have the <libxml/xmlreader.h> header file. */
+/* #undef HAVE_LIBXML_XMLREADER_H */
+
+/* Define to 1 if you have the <libxml/xmlwriter.h> header file. */
+/* #undef HAVE_LIBXML_XMLWRITER_H */
+
+/* Define to 1 if you have the `z' library (-lz). */
+#define HAVE_LIBZ 1
+
+/* Define to 1 if you have the `zstd' library (-lzstd). */
+#define HAVE_LIBZSTD 1
+
+/* Define to 1 if you have the `zstd' library (-lzstd) with compression
+ support. */
+#define HAVE_LIBZSTD_COMPRESSOR 1
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the `link' function. */
+#define HAVE_LINK 1
+
+/* Define to 1 if you have the `linkat' function. */
+#define HAVE_LINKAT 1
+
+/* Define to 1 if you have the <linux/fiemap.h> header file. */
+#define HAVE_LINUX_FIEMAP_H 1
+
+/* Define to 1 if you have the <linux/fs.h> header file. */
+#define HAVE_LINUX_FS_H 1
+
+/* Define to 1 if you have the <linux/magic.h> header file. */
+#define HAVE_LINUX_MAGIC_H 1
+
+/* Define to 1 if you have the <linux/types.h> header file. */
+#define HAVE_LINUX_TYPES_H 1
+
+/* Define to 1 if you have the `listea' function. */
+/* #undef HAVE_LISTEA */
+
+/* Define to 1 if you have the `listxattr' function. */
+/* #undef HAVE_LISTXATTR */
+
+/* Define to 1 if you have the `llistea' function. */
+/* #undef HAVE_LLISTEA */
+
+/* Define to 1 if you have the `llistxattr' function. */
+/* #undef HAVE_LLISTXATTR */
+
+/* Define to 1 if you have the <localcharset.h> header file. */
+/* #undef HAVE_LOCALCHARSET_H */
+
+/* Define to 1 if you have the `locale_charset' function. */
+/* #undef HAVE_LOCALE_CHARSET */
+
+/* Define to 1 if you have the <locale.h> header file. */
+#define HAVE_LOCALE_H 1
+
+/* Define to 1 if you have the `localtime_r' function. */
+#define HAVE_LOCALTIME_R 1
+
+/* Define to 1 if the system has the type `long long int'. */
+/* #undef HAVE_LONG_LONG_INT */
+
+/* Define to 1 if you have the `lsetea' function. */
+/* #undef HAVE_LSETEA */
+
+/* Define to 1 if you have the `lsetxattr' function. */
+/* #undef HAVE_LSETXATTR */
+
+/* Define to 1 if you have the `lstat' function. */
+#define HAVE_LSTAT 1
+
+/* Define to 1 if `lstat' has the bug that it succeeds when given the
+ zero-length file name argument. */
+/* #undef HAVE_LSTAT_EMPTY_STRING_BUG */
+
+/* Define to 1 if you have the `lutimes' function. */
+#define HAVE_LUTIMES 1
+
+/* Define to 1 if you have the <lz4hc.h> header file. */
+#define HAVE_LZ4HC_H 1
+
+/* Define to 1 if you have the <lz4.h> header file. */
+#define HAVE_LZ4_H 1
+
+/* Define to 1 if you have the <lzmadec.h> header file. */
+/* #undef HAVE_LZMADEC_H */
+
+/* Define to 1 if you have the <lzma.h> header file. */
+/* #undef HAVE_LZMA_H */
+
+/* Define to 1 if you have a working `lzma_stream_encoder_mt' function. */
+/* #undef HAVE_LZMA_STREAM_ENCODER_MT */
+
+/* Define to 1 if you have the <lzo/lzo1x.h> header file. */
+/* #undef HAVE_LZO_LZO1X_H */
+
+/* Define to 1 if you have the <lzo/lzoconf.h> header file. */
+/* #undef HAVE_LZO_LZOCONF_H */
+
+/* Define to 1 if you have the <mbedtls/aes.h> header file. */
+/* #undef HAVE_MBEDTLS_AES_H */
+
+/* Define to 1 if you have the <mbedtls/md.h> header file. */
+/* #undef HAVE_MBEDTLS_MD_H */
+
+/* Define to 1 if you have the <mbedtls/pkcs5.h> header file. */
+/* #undef HAVE_MBEDTLS_PKCS5_H */
+
+/* Define to 1 if you have the `mbrtowc' function. */
+#define HAVE_MBRTOWC 1
+
+/* Define to 1 if you have the <membership.h> header file. */
+/* #undef HAVE_MEMBERSHIP_H */
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `mkdir' function. */
+#define HAVE_MKDIR 1
+
+/* Define to 1 if you have the `mkfifo' function. */
+#define HAVE_MKFIFO 1
+
+/* Define to 1 if you have the `mknod' function. */
+#define HAVE_MKNOD 1
+
+/* Define to 1 if you have the `mkstemp' function. */
+#define HAVE_MKSTEMP 1
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+/* #undef HAVE_NDIR_H */
+
+/* Define to 1 if you have the <nettle/aes.h> header file. */
+/* #undef HAVE_NETTLE_AES_H */
+
+/* Define to 1 if you have the <nettle/hmac.h> header file. */
+/* #undef HAVE_NETTLE_HMAC_H */
+
+/* Define to 1 if you have the <nettle/md5.h> header file. */
+/* #undef HAVE_NETTLE_MD5_H */
+
+/* Define to 1 if you have the <nettle/pbkdf2.h> header file. */
+/* #undef HAVE_NETTLE_PBKDF2_H */
+
+/* Define to 1 if you have the <nettle/ripemd160.h> header file. */
+/* #undef HAVE_NETTLE_RIPEMD160_H */
+
+/* Define to 1 if you have the <nettle/sha.h> header file. */
+/* #undef HAVE_NETTLE_SHA_H */
+
+/* Define to 1 if you have the `nl_langinfo' function. */
+#define HAVE_NL_LANGINFO 1
+
+/* Define to 1 if you have the `openat' function. */
+#define HAVE_OPENAT 1
+
+/* Define to 1 if you have the <openssl/evp.h> header file. */
+#define HAVE_OPENSSL_EVP_H 1
+
+/* Define to 1 if you have the <paths.h> header file. */
+#define HAVE_PATHS_H 1
+
+/* Define to 1 if you have the <pcreposix.h> header file. */
+/* #undef HAVE_PCREPOSIX_H */
+
+/* Define to 1 if you have the `pipe' function. */
+#define HAVE_PIPE 1
+
+/* Define to 1 if you have the `PKCS5_PBKDF2_HMAC_SHA1' function. */
+#define HAVE_PKCS5_PBKDF2_HMAC_SHA1 1
+
+/* Define to 1 if you have the `poll' function. */
+#define HAVE_POLL 1
+
+/* Define to 1 if you have the <poll.h> header file. */
+#define HAVE_POLL_H 1
+
+/* Define to 1 if you have the `posix_spawnp' function. */
+#define HAVE_POSIX_SPAWNP 0
+
+/* Define to 1 if you have the <process.h> header file. */
+/* #undef HAVE_PROCESS_H */
+
+/* Define to 1 if you have the <pthread.h> header file. */
+#define HAVE_PTHREAD_H 1
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#define HAVE_PWD_H 1
+
+/* Define to 1 if you have the `readdir_r' function. */
+#define HAVE_READDIR_R 1
+
+/* Define to 1 if you have the `readlink' function. */
+#define HAVE_READLINK 1
+
+/* Define to 1 if you have the `readlinkat' function. */
+#define HAVE_READLINKAT 1
+
+/* Define to 1 if you have the `readpassphrase' function. */
+/* #undef HAVE_READPASSPHRASE */
+
+/* Define to 1 if you have the <readpassphrase.h> header file. */
+/* #undef HAVE_READPASSPHRASE_H */
+
+/* Define to 1 if you have the <regex.h> header file. */
+#define HAVE_REGEX_H 1
+
+/* Define to 1 if you have the `select' function. */
+#define HAVE_SELECT 1
+
+/* Define to 1 if you have the `setenv' function. */
+#define HAVE_SETENV 1
+
+/* Define to 1 if you have the `setlocale' function. */
+#define HAVE_SETLOCALE 1
+
+/* Define to 1 if you have the `sigaction' function. */
+#define HAVE_SIGACTION 1
+
+/* Define to 1 if you have the <signal.h> header file. */
+#define HAVE_SIGNAL_H 1
+
+/* Define to 1 if you have the <spawn.h> header file. */
+#define HAVE_SPAWN_H 1
+
+/* Define to 1 if you have the `statfs' function. */
+#define HAVE_STATFS 1
+
+/* Define to 1 if you have the `statvfs' function. */
+/* #undef HAVE_STATVFS */
+
+/* Define to 1 if `stat' has the bug that it succeeds when given the
+ zero-length file name argument. */
+/* #undef HAVE_STAT_EMPTY_STRING_BUG */
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strchr' function. */
+#define HAVE_STRCHR 1
+
+/* Define to 1 if you have the `strnlen' function. */
+#define HAVE_STRNLEN 1
+
+/* Define to 1 if you have the `strdup' function. */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the `strerror' function. */
+#define HAVE_STRERROR 1
+
+/* Define to 1 if you have the `strerror_r' function. */
+#define HAVE_STRERROR_R 1
+
+/* Define to 1 if you have the `strftime' function. */
+#define HAVE_STRFTIME 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strrchr' function. */
+#define HAVE_STRRCHR 1
+
+/* Define to 1 if `f_namemax' is a member of `struct statfs'. */
+/* #undef HAVE_STRUCT_STATFS_F_NAMEMAX */
+
+/* Define to 1 if `f_iosize' is a member of `struct statvfs'. */
+/* #undef HAVE_STRUCT_STATVFS_F_IOSIZE */
+
+/* Define to 1 if `st_birthtime' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_BIRTHTIME */
+
+/* Define to 1 if `st_birthtimespec.tv_nsec' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC */
+
+/* Define to 1 if `st_blksize' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_BLKSIZE 1
+
+/* Define to 1 if `st_flags' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_FLAGS */
+
+/* Define to 1 if `st_mtimespec.tv_nsec' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC */
+
+/* Define to 1 if `st_mtime_n' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_MTIME_N */
+
+/* Define to 1 if `st_mtime_usec' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_MTIME_USEC */
+
+/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1
+
+/* Define to 1 if `st_umtime' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_UMTIME */
+
+/* Define to 1 if `tm_gmtoff' is a member of `struct tm'. */
+#define HAVE_STRUCT_TM_TM_GMTOFF 1
+
+/* Define to 1 if `__tm_gmtoff' is a member of `struct tm'. */
+/* #undef HAVE_STRUCT_TM___TM_GMTOFF */
+
+/* Define to 1 if you have `struct vfsconf'. */
+/* #undef HAVE_STRUCT_VFSCONF */
+
+/* Define to 1 if you have `struct xvfsconf'. */
+/* #undef HAVE_STRUCT_XVFSCONF */
+
+/* Define to 1 if you have the `symlink' function. */
+#define HAVE_SYMLINK 1
+
+/* Define to 1 if you have the <sys/acl.h> header file. */
+/* #undef HAVE_SYS_ACL_H */
+
+/* Define to 1 if you have the <sys/cdefs.h> header file. */
+#define HAVE_SYS_CDEFS_H 1
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+ */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define to 1 if you have the <sys/ea.h> header file. */
+/* #undef HAVE_SYS_EA_H */
+
+/* Define to 1 if you have the <sys/extattr.h> header file. */
+/* #undef HAVE_SYS_EXTATTR_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/mkdev.h> header file. */
+/* #undef HAVE_SYS_MKDEV_H */
+
+/* Define to 1 if you have the <sys/mount.h> header file. */
+/* #undef HAVE_SYS_MOUNT_H */
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+ */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+#define HAVE_SYS_POLL_H 1
+
+/* Define to 1 if you have the <sys/queue.h> header file. */
+#define HAVE_SYS_QUEUE_H 1
+
+/* Define to 1 if you have the <sys/richacl.h> header file. */
+/* #undef HAVE_SYS_RICHACL_H */
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#define HAVE_SYS_SELECT_H 1
+
+/* Define to 1 if you have the <sys/statfs.h> header file. */
+#define HAVE_SYS_STATFS_H 1
+
+/* Define to 1 if you have the <sys/statvfs.h> header file. */
+#define HAVE_SYS_STATVFS_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+
+/* Define to 1 if you have the <sys/sysmacros.h> header file. */
+#define HAVE_SYS_SYSMACROS_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/utime.h> header file. */
+/* #undef HAVE_SYS_UTIME_H */
+
+/* Define to 1 if you have the <sys/utsname.h> header file. */
+#define HAVE_SYS_UTSNAME_H 1
+
+/* Define to 1 if you have the <sys/vfs.h> header file. */
+#define HAVE_SYS_VFS_H 1
+
+/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define to 1 if you have the <sys/xattr.h> header file. */
+#define HAVE_SYS_XATTR_H 1
+
+/* Define to 1 if you have the `timegm' function. */
+#define HAVE_TIMEGM 1
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the `tzset' function. */
+#define HAVE_TZSET 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `unlinkat' function. */
+#define HAVE_UNLINKAT 1
+
+/* Define to 1 if you have the `unsetenv' function. */
+#define HAVE_UNSETENV 1
+
+/* Define to 1 if the system has the type `unsigned long long'. */
+/* #undef HAVE_UNSIGNED_LONG_LONG */
+
+/* Define to 1 if the system has the type `unsigned long long int'. */
+/* #undef HAVE_UNSIGNED_LONG_LONG_INT */
+
+/* Define to 1 if you have the `utime' function. */
+#define HAVE_UTIME 1
+
+/* Define to 1 if you have the `utimensat' function. */
+#define HAVE_UTIMENSAT 1
+
+/* Define to 1 if you have the `utimes' function. */
+#define HAVE_UTIMES 1
+
+/* Define to 1 if you have the <utime.h> header file. */
+#define HAVE_UTIME_H 1
+
+/* Define to 1 if you have the `vfork' function. */
+#define HAVE_VFORK 1
+
+/* Define to 1 if you have the `vprintf' function. */
+#define HAVE_VPRINTF 1
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#define HAVE_WCHAR_H 1
+
+/* Define to 1 if the system has the type `wchar_t'. */
+#define HAVE_WCHAR_T 1
+
+/* Define to 1 if you have the `wcrtomb' function. */
+#define HAVE_WCRTOMB 1
+
+/* Define to 1 if you have the `wcscmp' function. */
+#define HAVE_WCSCMP 1
+
+/* Define to 1 if you have the `wcscpy' function. */
+#define HAVE_WCSCPY 1
+
+/* Define to 1 if you have the `wcslen' function. */
+#define HAVE_WCSLEN 1
+
+/* Define to 1 if you have the `wctomb' function. */
+#define HAVE_WCTOMB 1
+
+/* Define to 1 if you have the <wctype.h> header file. */
+#define HAVE_WCTYPE_H 1
+
+/* Define to 1 if you have the <wincrypt.h> header file. */
+/* #undef HAVE_WINCRYPT_H */
+
+/* Define to 1 if you have the <windows.h> header file. */
+/* #undef HAVE_WINDOWS_H */
+
+/* Define to 1 if you have the <winioctl.h> header file. */
+/* #undef HAVE_WINIOCTL_H */
+
+/* Define to 1 if you have _CrtSetReportMode in <crtdbg.h> */
+/* #undef HAVE__CrtSetReportMode */
+
+/* Define to 1 if you have the `wmemcmp' function. */
+#define HAVE_WMEMCMP 1
+
+/* Define to 1 if you have the `wmemcpy' function. */
+#define HAVE_WMEMCPY 1
+
+/* Define to 1 if you have the `wmemmove' function. */
+#define HAVE_WMEMMOVE 1
+
+/* Define to 1 if you have a working EXT2_IOC_GETFLAGS */
+/* #undef HAVE_WORKING_EXT2_IOC_GETFLAGS */
+
+/* Define to 1 if you have a working FS_IOC_GETFLAGS */
+#define HAVE_WORKING_FS_IOC_GETFLAGS 1
+
+/* Define to 1 if you have the <zlib.h> header file. */
+#define HAVE_ZLIB_H 1
+
+/* Define to 1 if you have the <zstd.h> header file. */
+#define HAVE_ZSTD_H 1
+
+/* Define to 1 if you have the `ctime_s' function. */
+/* #undef HAVE_CTIME_S */
+
+/* Define to 1 if you have the `_fseeki64' function. */
+/* #undef HAVE__FSEEKI64 */
+
+/* Define to 1 if you have the `_get_timezone' function. */
+/* #undef HAVE__GET_TIMEZONE */
+
+/* Define to 1 if you have the `gmtime_s' function. */
+/* #undef HAVE_GMTIME_S */
+
+/* Define to 1 if you have the `localtime_s' function. */
+/* #undef HAVE_LOCALTIME_S */
+
+/* Define to 1 if you have the `_mkgmtime' function. */
+/* #undef HAVE__MKGMTIME */
+
+/* Define as const if the declaration of iconv() needs const. */
+#define ICONV_CONST
+
+/* Version number of libarchive as a single integer */
+#define LIBARCHIVE_VERSION_NUMBER "3007001"
+
+/* Version number of libarchive */
+#define LIBARCHIVE_VERSION_STRING "3.7.1"
+
+/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
+ slash. */
+/* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.
+ */
+/* #undef MAJOR_IN_MKDEV */
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in
+ <sysmacros.h>. */
+#define MAJOR_IN_SYSMACROS 1
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* The size of `wchar_t', as computed by sizeof. */
+#define SIZEOF_WCHAR_T 4
+
+/* Define to 1 if strerror_r returns char *. */
+/* #undef STRERROR_R_CHAR_P */
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+/* #undef TIME_WITH_SYS_TIME */
+
+/*
+ * Some platform requires a macro to use extension functions.
+ */
+#define SAFE_TO_DEFINE_EXTENSIONS 1
+#ifdef SAFE_TO_DEFINE_EXTENSIONS
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable threading extensions on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+#endif /* SAFE_TO_DEFINE_EXTENSIONS */
+
+/* Version number of package */
+#define VERSION "3.7.1"
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+/* #undef _LARGEFILE_SOURCE */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to control Windows SDK version */
+#ifndef NTDDI_VERSION
+/* #undef NTDDI_VERSION */
+#endif // NTDDI_VERSION
+
+#ifndef _WIN32_WINNT
+/* #undef _WIN32_WINNT */
+#endif // _WIN32_WINNT
+
+#ifndef WINVER
+/* #undef WINVER */
+#endif // WINVER
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef gid_t */
+
+/* Define to `unsigned long' if <sys/types.h> does not define. */
+/* #undef id_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef mode_t */
+
+/* Define to `long long' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef pid_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef ssize_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef uid_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef intptr_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef uintptr_t */
+
+#define HAVE_STRUCT_STATFS
diff --git a/contrib/libs/libarchive/config-osx.h b/contrib/libs/libarchive/config-osx.h
new file mode 100644
index 00000000000..be6d950a9e1
--- /dev/null
+++ b/contrib/libs/libarchive/config-osx.h
@@ -0,0 +1,42 @@
+#include "config-linux.h"
+
+#define ARCHIVE_CRYPTO_MD5_LIBSYSTEM 1
+#define ARCHIVE_CRYPTO_SHA1_LIBSYSTEM 1
+#define ARCHIVE_CRYPTO_SHA256_LIBSYSTEM 1
+#define ARCHIVE_CRYPTO_SHA384_LIBSYSTEM 1
+#define ARCHIVE_CRYPTO_SHA512_LIBSYSTEM 1
+
+#define HAVE_ARC4RANDOM_BUF 1
+#define HAVE_CHFLAGS 1
+#define HAVE_COPYFILE_H 1
+#define HAVE_D_MD_ORDER 1
+#define HAVE_EFTYPE 1
+#define HAVE_FCHFLAGS 1
+#define HAVE_GETVFSBYNAME 1
+#define HAVE_LCHFLAGS 1
+#define HAVE_READPASSPHRASE 1
+#define HAVE_READPASSPHRASE_H 1
+#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1
+#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1
+#define HAVE_STRUCT_STAT_ST_FLAGS 1
+#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
+#define HAVE_STRUCT_VFSCONF 1
+
+#undef HAVE_FUTIMENS
+#undef HAVE_FUTIMESAT
+#undef HAVE_LINUX_FIEMAP_H
+#undef HAVE_LINUX_FS_H
+#undef HAVE_LINUX_MAGIC_H
+#undef HAVE_LINUX_TYPES_H
+#undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+#undef HAVE_SYS_STATFS_H
+#undef HAVE_SYS_SYSMACROS_H
+#undef HAVE_SYS_VFS_H
+#undef HAVE_SYS_XATTR_H
+#undef HAVE_UTIMENSAT
+#undef MAJOR_IN_SYSMACROS
+
+#undef HAVE_POSIX_SPAWNP
+#define HAVE_POSIX_SPAWNP 1
+
+#undef HAVE_STRUCT_STATFS
diff --git a/contrib/libs/libarchive/config-win.h b/contrib/libs/libarchive/config-win.h
new file mode 100644
index 00000000000..794a9c46a6b
--- /dev/null
+++ b/contrib/libs/libarchive/config-win.h
@@ -0,0 +1,121 @@
+#include "config-linux.h"
+
+#define HAVE___INT64
+#define HAVE_UNSIGNED___INT64
+
+#undef SIZEOF_LONG
+#define SIZEOF_LONG 4
+
+#undef SIZEOF_UNSIGNED_LONG
+#define SIZEOF_UNSIGNED_LONG 4
+
+#define ARCHIVE_CRYPTO_MD5_WIN 1
+#define ARCHIVE_CRYPTO_SHA1_WIN 1
+#define ARCHIVE_CRYPTO_SHA256_WIN 1
+#define ARCHIVE_CRYPTO_SHA384_WIN 1
+#define ARCHIVE_CRYPTO_SHA512_WIN 1
+
+#define HAVE_BCRYPT_H 1
+#define HAVE_DIRECT_H 1
+#define HAVE__GET_TIMEZONE 1
+#define HAVE_IO_H 1
+#define HAVE_PROCESS_H 1
+#define HAVE_SYS_UTIME_H 1
+#define HAVE_WINCRYPT_H 1
+
+#undef HAVE_CHOWN
+#undef HAVE_CHROOT
+#undef HAVE_CTIME_R
+#undef HAVE_DECL_SSIZE_MAX
+#undef HAVE_DECL_STRERROR_R
+#undef HAVE_DIRENT_H
+#undef HAVE_DIRFD
+#undef HAVE_DLFCN_H
+#undef HAVE_FCHDIR
+#undef HAVE_FCHMOD
+#undef HAVE_FCHOWN
+#undef HAVE_FCNTL
+#undef HAVE_FDOPENDIR
+#undef HAVE_FORK
+#undef HAVE_FSEEK
+#undef HAVE_FSTATAT
+#undef HAVE_FSTATFS
+#undef HAVE_FSTATVFS
+#undef HAVE_FTRUNCATE
+#undef HAVE_FUTIMENS
+#undef HAVE_FUTIMES
+#undef HAVE_FUTIMESAT
+#undef HAVE_GETEUID
+#undef HAVE_GETGRGID_R
+#undef HAVE_GETGRNAM_R
+#undef HAVE_GETPWNAM_R
+#undef HAVE_GETPWUID_R
+#undef HAVE_GMTIME_R
+#undef HAVE_GRP_H
+#undef HAVE_LANGINFO_H
+#undef HAVE_LCHMOD
+#undef HAVE_LCHOW
+#undef HAVE_LINK
+#undef HAVE_LINUX_FIEMAP_H
+#undef HAVE_LINUX_FS_H
+#undef HAVE_LINUX_MAGIC_H
+#undef HAVE_LINUX_TYPES_H
+#undef HAVE_LOCALTIME_R
+#undef HAVE_LSTAT
+#undef HAVE_LUTIMES
+#undef HAVE_MKFIFO
+#undef HAVE_MKNOD
+#undef HAVE_MKSTEMP
+#undef HAVE_NL_LANGINFO
+#undef HAVE_OPENAT
+#undef HAVE_PATHS_H
+#undef HAVE_PIPE
+#undef HAVE_POLL
+#undef HAVE_POLL_H
+#undef HAVE_POSIX_SPAWN
+#undef HAVE_PTHREAD_H
+#undef HAVE_PWD_H
+#undef HAVE_READDIR_R
+#undef HAVE_READLINK
+#undef HAVE_READLINKAT
+#undef HAVE_REGEX_H
+#undef HAVE_SELECT
+#undef HAVE_SETENV
+#undef HAVE_SIGACTION
+#undef HAVE_SPAWN_H
+#undef HAVE_STATFS
+#undef HAVE_STATVFS
+#undef HAVE_STRERROR_R
+#undef HAVE_STRINGS_H
+#undef HAVE_STRUCT_STAT_ST_BLKSIZE
+#undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+#undef HAVE_STRUCT_TM_TM_GMTOFF
+#undef HAVE_SYMLINK
+#undef HAVE_SYS_CDEFS_H
+#undef HAVE_SYS_IOCTL_H
+#undef HAVE_SYS_PARAM_H
+#undef HAVE_SYS_POLL_H
+#undef HAVE_SYS_SELECT_H
+#undef HAVE_SYS_STATFS_H
+#undef HAVE_SYS_STATVFS_H
+#undef HAVE_SYS_SYSMACROS_H
+#undef HAVE_SYS_TIME_H
+#undef HAVE_SYS_UTSNAME_H
+#undef HAVE_SYS_VFS_H
+#undef HAVE_SYS_VFS_H
+#undef HAVE_SYS_WAIT_H
+#undef HAVE_SYS_XATTR_H
+#undef HAVE_TIMEGM
+#undef HAVE_UNISTD_H
+#undef HAVE_UNSETENV
+#undef HAVE_UTIMENSAT
+#undef HAVE_UTIMES
+#undef HAVE_UTIME_H
+#undef MAJOR_IN_SYSMACROS
+
+typedef __int64 ssize_t;
+typedef int pid_t;
+typedef short uid_t;
+typedef short gid_t;
+typedef short id_t;
+typedef unsigned short mode_t;
diff --git a/contrib/libs/libarchive/config.h b/contrib/libs/libarchive/config.h
new file mode 100644
index 00000000000..1614a4f2b65
--- /dev/null
+++ b/contrib/libs/libarchive/config.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#if defined(__APPLE__)
+# include "config-osx.h"
+#elif defined(_MSC_VER)
+# include "config-win.h"
+#else
+# include "config-linux.h"
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive.h b/contrib/libs/libarchive/libarchive/archive.h
new file mode 100644
index 00000000000..cdb4f3ad8c3
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive.h
@@ -0,0 +1,1212 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libarchive/archive.h.in,v 1.50 2008/05/26 17:00:22 kientzle Exp $
+ */
+
+#ifndef ARCHIVE_H_INCLUDED
+#define ARCHIVE_H_INCLUDED
+
+/*
+ * The version number is expressed as a single integer that makes it
+ * easy to compare versions at build time: for version a.b.c, the
+ * version number is printf("%d%03d%03d",a,b,c). For example, if you
+ * know your application requires version 2.12.108 or later, you can
+ * assert that ARCHIVE_VERSION_NUMBER >= 2012108.
+ */
+/* Note: Compiler will complain if this does not match archive_entry.h! */
+#define ARCHIVE_VERSION_NUMBER 3007001
+
+#include <sys/stat.h>
+#include <stddef.h> /* for wchar_t */
+#include <stdio.h> /* For FILE * */
+#include <time.h> /* For time_t */
+
+/*
+ * Note: archive.h is for use outside of libarchive; the configuration
+ * headers (config.h, archive_platform.h, etc.) are purely internal.
+ * Do NOT use HAVE_XXX configuration macros to control the behavior of
+ * this header! If you must conditionalize, use predefined compiler and/or
+ * platform macros.
+ */
+#if defined(__BORLANDC__) && __BORLANDC__ >= 0x560
+# include <stdint.h>
+#elif !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) && !defined(__BORLANDC__) && !defined(_SCO_DS) && !defined(__osf__) && !defined(__CLANG_INTTYPES_H)
+# include <inttypes.h>
+#endif
+
+/* Get appropriate definitions of 64-bit integer */
+#if !defined(__LA_INT64_T_DEFINED)
+/* Older code relied on the __LA_INT64_T macro; after 4.0 we'll switch to the typedef exclusively. */
+# if ARCHIVE_VERSION_NUMBER < 4000000
+#define __LA_INT64_T la_int64_t
+# endif
+#define __LA_INT64_T_DEFINED
+# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
+typedef __int64 la_int64_t;
+# else
+# include <unistd.h> /* ssize_t */
+# if defined(_SCO_DS) || defined(__osf__)
+typedef long long la_int64_t;
+# else
+typedef int64_t la_int64_t;
+# endif
+# endif
+#endif
+
+/* The la_ssize_t should match the type used in 'struct stat' */
+#if !defined(__LA_SSIZE_T_DEFINED)
+/* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */
+# if ARCHIVE_VERSION_NUMBER < 4000000
+#define __LA_SSIZE_T la_ssize_t
+# endif
+#define __LA_SSIZE_T_DEFINED
+# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
+# if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)
+typedef ssize_t la_ssize_t;
+# elif defined(_WIN64)
+typedef __int64 la_ssize_t;
+# else
+typedef long la_ssize_t;
+# endif
+# else
+# include <unistd.h> /* ssize_t */
+typedef ssize_t la_ssize_t;
+# endif
+#endif
+
+/* Large file support for Android */
+#if defined(__LIBARCHIVE_BUILD) && defined(__ANDROID__)
+#error #include "android_lf.h"
+#endif
+
+/*
+ * On Windows, define LIBARCHIVE_STATIC if you're building or using a
+ * .lib. The default here assumes you're building a DLL. Only
+ * libarchive source should ever define __LIBARCHIVE_BUILD.
+ */
+#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC)
+# ifdef __LIBARCHIVE_BUILD
+# ifdef __GNUC__
+# define __LA_DECL __attribute__((dllexport)) extern
+# else
+# define __LA_DECL __declspec(dllexport)
+# endif
+# else
+# ifdef __GNUC__
+# define __LA_DECL
+# else
+# define __LA_DECL __declspec(dllimport)
+# endif
+# endif
+#elif defined __LIBARCHIVE_ENABLE_VISIBILITY
+# define __LA_DECL __attribute__((visibility("default")))
+#else
+/* Static libraries or non-Windows needs no special declaration. */
+# define __LA_DECL
+#endif
+
+#if defined(__GNUC__) && __GNUC__ >= 3 && !defined(__MINGW32__)
+#define __LA_PRINTF(fmtarg, firstvararg) \
+ __attribute__((__format__ (__printf__, fmtarg, firstvararg)))
+#else
+#define __LA_PRINTF(fmtarg, firstvararg) /* nothing */
+#endif
+
+#if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1
+# define __LA_DEPRECATED __attribute__((deprecated))
+#else
+# define __LA_DEPRECATED
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The version number is provided as both a macro and a function.
+ * The macro identifies the installed header; the function identifies
+ * the library version (which may not be the same if you're using a
+ * dynamically-linked version of the library). Of course, if the
+ * header and library are very different, you should expect some
+ * strangeness. Don't do that.
+ */
+__LA_DECL int archive_version_number(void);
+
+/*
+ * Textual name/version of the library, useful for version displays.
+ */
+#define ARCHIVE_VERSION_ONLY_STRING "3.7.1"
+#define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING
+__LA_DECL const char * archive_version_string(void);
+
+/*
+ * Detailed textual name/version of the library and its dependencies.
+ * This has the form:
+ * "libarchive x.y.z zlib/a.b.c liblzma/d.e.f ... etc ..."
+ * the list of libraries described here will vary depending on how
+ * libarchive was compiled.
+ */
+__LA_DECL const char * archive_version_details(void);
+
+/*
+ * Returns NULL if libarchive was compiled without the associated library.
+ * Otherwise, returns the version number that libarchive was compiled
+ * against.
+ */
+__LA_DECL const char * archive_zlib_version(void);
+__LA_DECL const char * archive_liblzma_version(void);
+__LA_DECL const char * archive_bzlib_version(void);
+__LA_DECL const char * archive_liblz4_version(void);
+__LA_DECL const char * archive_libzstd_version(void);
+
+/* Declare our basic types. */
+struct archive;
+struct archive_entry;
+
+/*
+ * Error codes: Use archive_errno() and archive_error_string()
+ * to retrieve details. Unless specified otherwise, all functions
+ * that return 'int' use these codes.
+ */
+#define ARCHIVE_EOF 1 /* Found end of archive. */
+#define ARCHIVE_OK 0 /* Operation was successful. */
+#define ARCHIVE_RETRY (-10) /* Retry might succeed. */
+#define ARCHIVE_WARN (-20) /* Partial success. */
+/* For example, if write_header "fails", then you can't push data. */
+#define ARCHIVE_FAILED (-25) /* Current operation cannot complete. */
+/* But if write_header is "fatal," then this archive is dead and useless. */
+#define ARCHIVE_FATAL (-30) /* No more operations are possible. */
+
+/*
+ * As far as possible, archive_errno returns standard platform errno codes.
+ * Of course, the details vary by platform, so the actual definitions
+ * here are stored in "archive_platform.h". The symbols are listed here
+ * for reference; as a rule, clients should not need to know the exact
+ * platform-dependent error code.
+ */
+/* Unrecognized or invalid file format. */
+/* #define ARCHIVE_ERRNO_FILE_FORMAT */
+/* Illegal usage of the library. */
+/* #define ARCHIVE_ERRNO_PROGRAMMER_ERROR */
+/* Unknown or unclassified error. */
+/* #define ARCHIVE_ERRNO_MISC */
+
+/*
+ * Callbacks are invoked to automatically read/skip/write/open/close the
+ * archive. You can provide your own for complex tasks (like breaking
+ * archives across multiple tapes) or use standard ones built into the
+ * library.
+ */
+
+/* Returns pointer and size of next block of data from archive. */
+typedef la_ssize_t archive_read_callback(struct archive *,
+ void *_client_data, const void **_buffer);
+
+/* Skips at most request bytes from archive and returns the skipped amount.
+ * This may skip fewer bytes than requested; it may even skip zero bytes.
+ * If you do skip fewer bytes than requested, libarchive will invoke your
+ * read callback and discard data as necessary to make up the full skip.
+ */
+typedef la_int64_t archive_skip_callback(struct archive *,
+ void *_client_data, la_int64_t request);
+
+/* Seeks to specified location in the file and returns the position.
+ * Whence values are SEEK_SET, SEEK_CUR, SEEK_END from stdio.h.
+ * Return ARCHIVE_FATAL if the seek fails for any reason.
+ */
+typedef la_int64_t archive_seek_callback(struct archive *,
+ void *_client_data, la_int64_t offset, int whence);
+
+/* Returns size actually written, zero on EOF, -1 on error. */
+typedef la_ssize_t archive_write_callback(struct archive *,
+ void *_client_data,
+ const void *_buffer, size_t _length);
+
+typedef int archive_open_callback(struct archive *, void *_client_data);
+
+typedef int archive_close_callback(struct archive *, void *_client_data);
+
+typedef int archive_free_callback(struct archive *, void *_client_data);
+
+/* Switches from one client data object to the next/prev client data object.
+ * This is useful for reading from different data blocks such as a set of files
+ * that make up one large file.
+ */
+typedef int archive_switch_callback(struct archive *, void *_client_data1,
+ void *_client_data2);
+
+/*
+ * Returns a passphrase used for encryption or decryption, NULL on nothing
+ * to do and give it up.
+ */
+typedef const char *archive_passphrase_callback(struct archive *,
+ void *_client_data);
+
+/*
+ * Codes to identify various stream filters.
+ */
+#define ARCHIVE_FILTER_NONE 0
+#define ARCHIVE_FILTER_GZIP 1
+#define ARCHIVE_FILTER_BZIP2 2
+#define ARCHIVE_FILTER_COMPRESS 3
+#define ARCHIVE_FILTER_PROGRAM 4
+#define ARCHIVE_FILTER_LZMA 5
+#define ARCHIVE_FILTER_XZ 6
+#define ARCHIVE_FILTER_UU 7
+#define ARCHIVE_FILTER_RPM 8
+#define ARCHIVE_FILTER_LZIP 9
+#define ARCHIVE_FILTER_LRZIP 10
+#define ARCHIVE_FILTER_LZOP 11
+#define ARCHIVE_FILTER_GRZIP 12
+#define ARCHIVE_FILTER_LZ4 13
+#define ARCHIVE_FILTER_ZSTD 14
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+#define ARCHIVE_COMPRESSION_NONE ARCHIVE_FILTER_NONE
+#define ARCHIVE_COMPRESSION_GZIP ARCHIVE_FILTER_GZIP
+#define ARCHIVE_COMPRESSION_BZIP2 ARCHIVE_FILTER_BZIP2
+#define ARCHIVE_COMPRESSION_COMPRESS ARCHIVE_FILTER_COMPRESS
+#define ARCHIVE_COMPRESSION_PROGRAM ARCHIVE_FILTER_PROGRAM
+#define ARCHIVE_COMPRESSION_LZMA ARCHIVE_FILTER_LZMA
+#define ARCHIVE_COMPRESSION_XZ ARCHIVE_FILTER_XZ
+#define ARCHIVE_COMPRESSION_UU ARCHIVE_FILTER_UU
+#define ARCHIVE_COMPRESSION_RPM ARCHIVE_FILTER_RPM
+#define ARCHIVE_COMPRESSION_LZIP ARCHIVE_FILTER_LZIP
+#define ARCHIVE_COMPRESSION_LRZIP ARCHIVE_FILTER_LRZIP
+#endif
+
+/*
+ * Codes returned by archive_format.
+ *
+ * Top 16 bits identifies the format family (e.g., "tar"); lower
+ * 16 bits indicate the variant. This is updated by read_next_header.
+ * Note that the lower 16 bits will often vary from entry to entry.
+ * In some cases, this variation occurs as libarchive learns more about
+ * the archive (for example, later entries might utilize extensions that
+ * weren't necessary earlier in the archive; in this case, libarchive
+ * will change the format code to indicate the extended format that
+ * was used). In other cases, it's because different tools have
+ * modified the archive and so different parts of the archive
+ * actually have slightly different formats. (Both tar and cpio store
+ * format codes in each entry, so it is quite possible for each
+ * entry to be in a different format.)
+ */
+#define ARCHIVE_FORMAT_BASE_MASK 0xff0000
+#define ARCHIVE_FORMAT_CPIO 0x10000
+#define ARCHIVE_FORMAT_CPIO_POSIX (ARCHIVE_FORMAT_CPIO | 1)
+#define ARCHIVE_FORMAT_CPIO_BIN_LE (ARCHIVE_FORMAT_CPIO | 2)
+#define ARCHIVE_FORMAT_CPIO_BIN_BE (ARCHIVE_FORMAT_CPIO | 3)
+#define ARCHIVE_FORMAT_CPIO_SVR4_NOCRC (ARCHIVE_FORMAT_CPIO | 4)
+#define ARCHIVE_FORMAT_CPIO_SVR4_CRC (ARCHIVE_FORMAT_CPIO | 5)
+#define ARCHIVE_FORMAT_CPIO_AFIO_LARGE (ARCHIVE_FORMAT_CPIO | 6)
+#define ARCHIVE_FORMAT_CPIO_PWB (ARCHIVE_FORMAT_CPIO | 7)
+#define ARCHIVE_FORMAT_SHAR 0x20000
+#define ARCHIVE_FORMAT_SHAR_BASE (ARCHIVE_FORMAT_SHAR | 1)
+#define ARCHIVE_FORMAT_SHAR_DUMP (ARCHIVE_FORMAT_SHAR | 2)
+#define ARCHIVE_FORMAT_TAR 0x30000
+#define ARCHIVE_FORMAT_TAR_USTAR (ARCHIVE_FORMAT_TAR | 1)
+#define ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE (ARCHIVE_FORMAT_TAR | 2)
+#define ARCHIVE_FORMAT_TAR_PAX_RESTRICTED (ARCHIVE_FORMAT_TAR | 3)
+#define ARCHIVE_FORMAT_TAR_GNUTAR (ARCHIVE_FORMAT_TAR | 4)
+#define ARCHIVE_FORMAT_ISO9660 0x40000
+#define ARCHIVE_FORMAT_ISO9660_ROCKRIDGE (ARCHIVE_FORMAT_ISO9660 | 1)
+#define ARCHIVE_FORMAT_ZIP 0x50000
+#define ARCHIVE_FORMAT_EMPTY 0x60000
+#define ARCHIVE_FORMAT_AR 0x70000
+#define ARCHIVE_FORMAT_AR_GNU (ARCHIVE_FORMAT_AR | 1)
+#define ARCHIVE_FORMAT_AR_BSD (ARCHIVE_FORMAT_AR | 2)
+#define ARCHIVE_FORMAT_MTREE 0x80000
+#define ARCHIVE_FORMAT_RAW 0x90000
+#define ARCHIVE_FORMAT_XAR 0xA0000
+#define ARCHIVE_FORMAT_LHA 0xB0000
+#define ARCHIVE_FORMAT_CAB 0xC0000
+#define ARCHIVE_FORMAT_RAR 0xD0000
+#define ARCHIVE_FORMAT_7ZIP 0xE0000
+#define ARCHIVE_FORMAT_WARC 0xF0000
+#define ARCHIVE_FORMAT_RAR_V5 0x100000
+
+/*
+ * Codes returned by archive_read_format_capabilities().
+ *
+ * This list can be extended with values between 0 and 0xffff.
+ * The original purpose of this list was to let different archive
+ * format readers expose their general capabilities in terms of
+ * encryption.
+ */
+#define ARCHIVE_READ_FORMAT_CAPS_NONE (0) /* no special capabilities */
+#define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA (1<<0) /* reader can detect encrypted data */
+#define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA (1<<1) /* reader can detect encryptable metadata (pathname, mtime, etc.) */
+
+/*
+ * Codes returned by archive_read_has_encrypted_entries().
+ *
+ * In case the archive does not support encryption detection at all
+ * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned. If the reader
+ * for some other reason (e.g. not enough bytes read) cannot say if
+ * there are encrypted entries, ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW
+ * is returned.
+ */
+#define ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED -2
+#define ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW -1
+
+/*-
+ * Basic outline for reading an archive:
+ * 1) Ask archive_read_new for an archive reader object.
+ * 2) Update any global properties as appropriate.
+ * In particular, you'll certainly want to call appropriate
+ * archive_read_support_XXX functions.
+ * 3) Call archive_read_open_XXX to open the archive
+ * 4) Repeatedly call archive_read_next_header to get information about
+ * successive archive entries. Call archive_read_data to extract
+ * data for entries of interest.
+ * 5) Call archive_read_free to end processing.
+ */
+__LA_DECL struct archive *archive_read_new(void);
+
+/*
+ * The archive_read_support_XXX calls enable auto-detect for this
+ * archive handle. They also link in the necessary support code.
+ * For example, if you don't want bzlib linked in, don't invoke
+ * support_compression_bzip2(). The "all" functions provide the
+ * obvious shorthand.
+ */
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+__LA_DECL int archive_read_support_compression_all(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_bzip2(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_compress(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_gzip(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_lzip(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_lzma(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_none(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_program(struct archive *,
+ const char *command) __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_program_signature
+ (struct archive *, const char *,
+ const void * /* match */, size_t) __LA_DEPRECATED;
+
+__LA_DECL int archive_read_support_compression_rpm(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_uu(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_xz(struct archive *)
+ __LA_DEPRECATED;
+#endif
+
+__LA_DECL int archive_read_support_filter_all(struct archive *);
+__LA_DECL int archive_read_support_filter_by_code(struct archive *, int);
+__LA_DECL int archive_read_support_filter_bzip2(struct archive *);
+__LA_DECL int archive_read_support_filter_compress(struct archive *);
+__LA_DECL int archive_read_support_filter_gzip(struct archive *);
+__LA_DECL int archive_read_support_filter_grzip(struct archive *);
+__LA_DECL int archive_read_support_filter_lrzip(struct archive *);
+__LA_DECL int archive_read_support_filter_lz4(struct archive *);
+__LA_DECL int archive_read_support_filter_lzip(struct archive *);
+__LA_DECL int archive_read_support_filter_lzma(struct archive *);
+__LA_DECL int archive_read_support_filter_lzop(struct archive *);
+__LA_DECL int archive_read_support_filter_none(struct archive *);
+__LA_DECL int archive_read_support_filter_program(struct archive *,
+ const char *command);
+__LA_DECL int archive_read_support_filter_program_signature
+ (struct archive *, const char * /* cmd */,
+ const void * /* match */, size_t);
+__LA_DECL int archive_read_support_filter_rpm(struct archive *);
+__LA_DECL int archive_read_support_filter_uu(struct archive *);
+__LA_DECL int archive_read_support_filter_xz(struct archive *);
+__LA_DECL int archive_read_support_filter_zstd(struct archive *);
+
+__LA_DECL int archive_read_support_format_7zip(struct archive *);
+__LA_DECL int archive_read_support_format_all(struct archive *);
+__LA_DECL int archive_read_support_format_ar(struct archive *);
+__LA_DECL int archive_read_support_format_by_code(struct archive *, int);
+__LA_DECL int archive_read_support_format_cab(struct archive *);
+__LA_DECL int archive_read_support_format_cpio(struct archive *);
+__LA_DECL int archive_read_support_format_empty(struct archive *);
+__LA_DECL int archive_read_support_format_gnutar(struct archive *);
+__LA_DECL int archive_read_support_format_iso9660(struct archive *);
+__LA_DECL int archive_read_support_format_lha(struct archive *);
+__LA_DECL int archive_read_support_format_mtree(struct archive *);
+__LA_DECL int archive_read_support_format_rar(struct archive *);
+__LA_DECL int archive_read_support_format_rar5(struct archive *);
+__LA_DECL int archive_read_support_format_raw(struct archive *);
+__LA_DECL int archive_read_support_format_tar(struct archive *);
+__LA_DECL int archive_read_support_format_warc(struct archive *);
+__LA_DECL int archive_read_support_format_xar(struct archive *);
+/* archive_read_support_format_zip() enables both streamable and seekable
+ * zip readers. */
+__LA_DECL int archive_read_support_format_zip(struct archive *);
+/* Reads Zip archives as stream from beginning to end. Doesn't
+ * correctly handle SFX ZIP files or ZIP archives that have been modified
+ * in-place. */
+__LA_DECL int archive_read_support_format_zip_streamable(struct archive *);
+/* Reads starting from central directory; requires seekable input. */
+__LA_DECL int archive_read_support_format_zip_seekable(struct archive *);
+
+/* Functions to manually set the format and filters to be used. This is
+ * useful to bypass the bidding process when the format and filters to use
+ * is known in advance.
+ */
+__LA_DECL int archive_read_set_format(struct archive *, int);
+__LA_DECL int archive_read_append_filter(struct archive *, int);
+__LA_DECL int archive_read_append_filter_program(struct archive *,
+ const char *);
+__LA_DECL int archive_read_append_filter_program_signature
+ (struct archive *, const char *, const void * /* match */, size_t);
+
+/* Set various callbacks. */
+__LA_DECL int archive_read_set_open_callback(struct archive *,
+ archive_open_callback *);
+__LA_DECL int archive_read_set_read_callback(struct archive *,
+ archive_read_callback *);
+__LA_DECL int archive_read_set_seek_callback(struct archive *,
+ archive_seek_callback *);
+__LA_DECL int archive_read_set_skip_callback(struct archive *,
+ archive_skip_callback *);
+__LA_DECL int archive_read_set_close_callback(struct archive *,
+ archive_close_callback *);
+/* Callback used to switch between one data object to the next */
+__LA_DECL int archive_read_set_switch_callback(struct archive *,
+ archive_switch_callback *);
+
+/* This sets the first data object. */
+__LA_DECL int archive_read_set_callback_data(struct archive *, void *);
+/* This sets data object at specified index */
+__LA_DECL int archive_read_set_callback_data2(struct archive *, void *,
+ unsigned int);
+/* This adds a data object at the specified index. */
+__LA_DECL int archive_read_add_callback_data(struct archive *, void *,
+ unsigned int);
+/* This appends a data object to the end of list */
+__LA_DECL int archive_read_append_callback_data(struct archive *, void *);
+/* This prepends a data object to the beginning of list */
+__LA_DECL int archive_read_prepend_callback_data(struct archive *, void *);
+
+/* Opening freezes the callbacks. */
+__LA_DECL int archive_read_open1(struct archive *);
+
+/* Convenience wrappers around the above. */
+__LA_DECL int archive_read_open(struct archive *, void *_client_data,
+ archive_open_callback *, archive_read_callback *,
+ archive_close_callback *);
+__LA_DECL int archive_read_open2(struct archive *, void *_client_data,
+ archive_open_callback *, archive_read_callback *,
+ archive_skip_callback *, archive_close_callback *);
+
+/*
+ * A variety of shortcuts that invoke archive_read_open() with
+ * canned callbacks suitable for common situations. The ones that
+ * accept a block size handle tape blocking correctly.
+ */
+/* Use this if you know the filename. Note: NULL indicates stdin. */
+__LA_DECL int archive_read_open_filename(struct archive *,
+ const char *_filename, size_t _block_size);
+/* Use this for reading multivolume files by filenames.
+ * NOTE: Must be NULL terminated. Sorting is NOT done. */
+__LA_DECL int archive_read_open_filenames(struct archive *,
+ const char **_filenames, size_t _block_size);
+__LA_DECL int archive_read_open_filename_w(struct archive *,
+ const wchar_t *_filename, size_t _block_size);
+/* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */
+__LA_DECL int archive_read_open_file(struct archive *,
+ const char *_filename, size_t _block_size) __LA_DEPRECATED;
+/* Read an archive that's stored in memory. */
+__LA_DECL int archive_read_open_memory(struct archive *,
+ const void * buff, size_t size);
+/* A more involved version that is only used for internal testing. */
+__LA_DECL int archive_read_open_memory2(struct archive *a, const void *buff,
+ size_t size, size_t read_size);
+/* Read an archive that's already open, using the file descriptor. */
+__LA_DECL int archive_read_open_fd(struct archive *, int _fd,
+ size_t _block_size);
+/* Read an archive that's already open, using a FILE *. */
+/* Note: DO NOT use this with tape drives. */
+__LA_DECL int archive_read_open_FILE(struct archive *, FILE *_file);
+
+/* Parses and returns next entry header. */
+__LA_DECL int archive_read_next_header(struct archive *,
+ struct archive_entry **);
+
+/* Parses and returns next entry header using the archive_entry passed in */
+__LA_DECL int archive_read_next_header2(struct archive *,
+ struct archive_entry *);
+
+/*
+ * Retrieve the byte offset in UNCOMPRESSED data where last-read
+ * header started.
+ */
+__LA_DECL la_int64_t archive_read_header_position(struct archive *);
+
+/*
+ * Returns 1 if the archive contains at least one encrypted entry.
+ * If the archive format not support encryption at all
+ * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned.
+ * If for any other reason (e.g. not enough data read so far)
+ * we cannot say whether there are encrypted entries, then
+ * ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW is returned.
+ * In general, this function will return values below zero when the
+ * reader is uncertain or totally incapable of encryption support.
+ * When this function returns 0 you can be sure that the reader
+ * supports encryption detection but no encrypted entries have
+ * been found yet.
+ *
+ * NOTE: If the metadata/header of an archive is also encrypted, you
+ * cannot rely on the number of encrypted entries. That is why this
+ * function does not return the number of encrypted entries but#
+ * just shows that there are some.
+ */
+__LA_DECL int archive_read_has_encrypted_entries(struct archive *);
+
+/*
+ * Returns a bitmask of capabilities that are supported by the archive format reader.
+ * If the reader has no special capabilities, ARCHIVE_READ_FORMAT_CAPS_NONE is returned.
+ */
+__LA_DECL int archive_read_format_capabilities(struct archive *);
+
+/* Read data from the body of an entry. Similar to read(2). */
+__LA_DECL la_ssize_t archive_read_data(struct archive *,
+ void *, size_t);
+
+/* Seek within the body of an entry. Similar to lseek(2). */
+__LA_DECL la_int64_t archive_seek_data(struct archive *, la_int64_t, int);
+
+/*
+ * A zero-copy version of archive_read_data that also exposes the file offset
+ * of each returned block. Note that the client has no way to specify
+ * the desired size of the block. The API does guarantee that offsets will
+ * be strictly increasing and that returned blocks will not overlap.
+ */
+__LA_DECL int archive_read_data_block(struct archive *a,
+ const void **buff, size_t *size, la_int64_t *offset);
+
+/*-
+ * Some convenience functions that are built on archive_read_data:
+ * 'skip': skips entire entry
+ * 'into_buffer': writes data into memory buffer that you provide
+ * 'into_fd': writes data to specified filedes
+ */
+__LA_DECL int archive_read_data_skip(struct archive *);
+__LA_DECL int archive_read_data_into_fd(struct archive *, int fd);
+
+/*
+ * Set read options.
+ */
+/* Apply option to the format only. */
+__LA_DECL int archive_read_set_format_option(struct archive *_a,
+ const char *m, const char *o,
+ const char *v);
+/* Apply option to the filter only. */
+__LA_DECL int archive_read_set_filter_option(struct archive *_a,
+ const char *m, const char *o,
+ const char *v);
+/* Apply option to both the format and the filter. */
+__LA_DECL int archive_read_set_option(struct archive *_a,
+ const char *m, const char *o,
+ const char *v);
+/* Apply option string to both the format and the filter. */
+__LA_DECL int archive_read_set_options(struct archive *_a,
+ const char *opts);
+
+/*
+ * Add a decryption passphrase.
+ */
+__LA_DECL int archive_read_add_passphrase(struct archive *, const char *);
+__LA_DECL int archive_read_set_passphrase_callback(struct archive *,
+ void *client_data, archive_passphrase_callback *);
+
+
+/*-
+ * Convenience function to recreate the current entry (whose header
+ * has just been read) on disk.
+ *
+ * This does quite a bit more than just copy data to disk. It also:
+ * - Creates intermediate directories as required.
+ * - Manages directory permissions: non-writable directories will
+ * be initially created with write permission enabled; when the
+ * archive is closed, dir permissions are edited to the values specified
+ * in the archive.
+ * - Checks hardlinks: hardlinks will not be extracted unless the
+ * linked-to file was also extracted within the same session. (TODO)
+ */
+
+/* The "flags" argument selects optional behavior, 'OR' the flags you want. */
+
+/* Default: Do not try to set owner/group. */
+#define ARCHIVE_EXTRACT_OWNER (0x0001)
+/* Default: Do obey umask, do not restore SUID/SGID/SVTX bits. */
+#define ARCHIVE_EXTRACT_PERM (0x0002)
+/* Default: Do not restore mtime/atime. */
+#define ARCHIVE_EXTRACT_TIME (0x0004)
+/* Default: Replace existing files. */
+#define ARCHIVE_EXTRACT_NO_OVERWRITE (0x0008)
+/* Default: Try create first, unlink only if create fails with EEXIST. */
+#define ARCHIVE_EXTRACT_UNLINK (0x0010)
+/* Default: Do not restore ACLs. */
+#define ARCHIVE_EXTRACT_ACL (0x0020)
+/* Default: Do not restore fflags. */
+#define ARCHIVE_EXTRACT_FFLAGS (0x0040)
+/* Default: Do not restore xattrs. */
+#define ARCHIVE_EXTRACT_XATTR (0x0080)
+/* Default: Do not try to guard against extracts redirected by symlinks. */
+/* Note: With ARCHIVE_EXTRACT_UNLINK, will remove any intermediate symlink. */
+#define ARCHIVE_EXTRACT_SECURE_SYMLINKS (0x0100)
+/* Default: Do not reject entries with '..' as path elements. */
+#define ARCHIVE_EXTRACT_SECURE_NODOTDOT (0x0200)
+/* Default: Create parent directories as needed. */
+#define ARCHIVE_EXTRACT_NO_AUTODIR (0x0400)
+/* Default: Overwrite files, even if one on disk is newer. */
+#define ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER (0x0800)
+/* Detect blocks of 0 and write holes instead. */
+#define ARCHIVE_EXTRACT_SPARSE (0x1000)
+/* Default: Do not restore Mac extended metadata. */
+/* This has no effect except on Mac OS. */
+#define ARCHIVE_EXTRACT_MAC_METADATA (0x2000)
+/* Default: Use HFS+ compression if it was compressed. */
+/* This has no effect except on Mac OS v10.6 or later. */
+#define ARCHIVE_EXTRACT_NO_HFS_COMPRESSION (0x4000)
+/* Default: Do not use HFS+ compression if it was not compressed. */
+/* This has no effect except on Mac OS v10.6 or later. */
+#define ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED (0x8000)
+/* Default: Do not reject entries with absolute paths */
+#define ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS (0x10000)
+/* Default: Do not clear no-change flags when unlinking object */
+#define ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS (0x20000)
+/* Default: Do not extract atomically (using rename) */
+#define ARCHIVE_EXTRACT_SAFE_WRITES (0x40000)
+
+__LA_DECL int archive_read_extract(struct archive *, struct archive_entry *,
+ int flags);
+__LA_DECL int archive_read_extract2(struct archive *, struct archive_entry *,
+ struct archive * /* dest */);
+__LA_DECL void archive_read_extract_set_progress_callback(struct archive *,
+ void (*_progress_func)(void *), void *_user_data);
+
+/* Record the dev/ino of a file that will not be written. This is
+ * generally set to the dev/ino of the archive being read. */
+__LA_DECL void archive_read_extract_set_skip_file(struct archive *,
+ la_int64_t, la_int64_t);
+
+/* Close the file and release most resources. */
+__LA_DECL int archive_read_close(struct archive *);
+/* Release all resources and destroy the object. */
+/* Note that archive_read_free will call archive_read_close for you. */
+__LA_DECL int archive_read_free(struct archive *);
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Synonym for archive_read_free() for backwards compatibility. */
+__LA_DECL int archive_read_finish(struct archive *) __LA_DEPRECATED;
+#endif
+
+/*-
+ * To create an archive:
+ * 1) Ask archive_write_new for an archive writer object.
+ * 2) Set any global properties. In particular, you should set
+ * the compression and format to use.
+ * 3) Call archive_write_open to open the file (most people
+ * will use archive_write_open_file or archive_write_open_fd,
+ * which provide convenient canned I/O callbacks for you).
+ * 4) For each entry:
+ * - construct an appropriate struct archive_entry structure
+ * - archive_write_header to write the header
+ * - archive_write_data to write the entry data
+ * 5) archive_write_close to close the output
+ * 6) archive_write_free to cleanup the writer and release resources
+ */
+__LA_DECL struct archive *archive_write_new(void);
+__LA_DECL int archive_write_set_bytes_per_block(struct archive *,
+ int bytes_per_block);
+__LA_DECL int archive_write_get_bytes_per_block(struct archive *);
+/* XXX This is badly misnamed; suggestions appreciated. XXX */
+__LA_DECL int archive_write_set_bytes_in_last_block(struct archive *,
+ int bytes_in_last_block);
+__LA_DECL int archive_write_get_bytes_in_last_block(struct archive *);
+
+/* The dev/ino of a file that won't be archived. This is used
+ * to avoid recursively adding an archive to itself. */
+__LA_DECL int archive_write_set_skip_file(struct archive *,
+ la_int64_t, la_int64_t);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+__LA_DECL int archive_write_set_compression_bzip2(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_compress(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_gzip(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_lzip(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_lzma(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_none(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_program(struct archive *,
+ const char *cmd) __LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_xz(struct archive *)
+ __LA_DEPRECATED;
+#endif
+
+/* A convenience function to set the filter based on the code. */
+__LA_DECL int archive_write_add_filter(struct archive *, int filter_code);
+__LA_DECL int archive_write_add_filter_by_name(struct archive *,
+ const char *name);
+__LA_DECL int archive_write_add_filter_b64encode(struct archive *);
+__LA_DECL int archive_write_add_filter_bzip2(struct archive *);
+__LA_DECL int archive_write_add_filter_compress(struct archive *);
+__LA_DECL int archive_write_add_filter_grzip(struct archive *);
+__LA_DECL int archive_write_add_filter_gzip(struct archive *);
+__LA_DECL int archive_write_add_filter_lrzip(struct archive *);
+__LA_DECL int archive_write_add_filter_lz4(struct archive *);
+__LA_DECL int archive_write_add_filter_lzip(struct archive *);
+__LA_DECL int archive_write_add_filter_lzma(struct archive *);
+__LA_DECL int archive_write_add_filter_lzop(struct archive *);
+__LA_DECL int archive_write_add_filter_none(struct archive *);
+__LA_DECL int archive_write_add_filter_program(struct archive *,
+ const char *cmd);
+__LA_DECL int archive_write_add_filter_uuencode(struct archive *);
+__LA_DECL int archive_write_add_filter_xz(struct archive *);
+__LA_DECL int archive_write_add_filter_zstd(struct archive *);
+
+
+/* A convenience function to set the format based on the code or name. */
+__LA_DECL int archive_write_set_format(struct archive *, int format_code);
+__LA_DECL int archive_write_set_format_by_name(struct archive *,
+ const char *name);
+/* To minimize link pollution, use one or more of the following. */
+__LA_DECL int archive_write_set_format_7zip(struct archive *);
+__LA_DECL int archive_write_set_format_ar_bsd(struct archive *);
+__LA_DECL int archive_write_set_format_ar_svr4(struct archive *);
+__LA_DECL int archive_write_set_format_cpio(struct archive *);
+__LA_DECL int archive_write_set_format_cpio_bin(struct archive *);
+__LA_DECL int archive_write_set_format_cpio_newc(struct archive *);
+__LA_DECL int archive_write_set_format_cpio_odc(struct archive *);
+__LA_DECL int archive_write_set_format_cpio_pwb(struct archive *);
+__LA_DECL int archive_write_set_format_gnutar(struct archive *);
+__LA_DECL int archive_write_set_format_iso9660(struct archive *);
+__LA_DECL int archive_write_set_format_mtree(struct archive *);
+__LA_DECL int archive_write_set_format_mtree_classic(struct archive *);
+/* TODO: int archive_write_set_format_old_tar(struct archive *); */
+__LA_DECL int archive_write_set_format_pax(struct archive *);
+__LA_DECL int archive_write_set_format_pax_restricted(struct archive *);
+__LA_DECL int archive_write_set_format_raw(struct archive *);
+__LA_DECL int archive_write_set_format_shar(struct archive *);
+__LA_DECL int archive_write_set_format_shar_dump(struct archive *);
+__LA_DECL int archive_write_set_format_ustar(struct archive *);
+__LA_DECL int archive_write_set_format_v7tar(struct archive *);
+__LA_DECL int archive_write_set_format_warc(struct archive *);
+__LA_DECL int archive_write_set_format_xar(struct archive *);
+__LA_DECL int archive_write_set_format_zip(struct archive *);
+__LA_DECL int archive_write_set_format_filter_by_ext(struct archive *a, const char *filename);
+__LA_DECL int archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext);
+__LA_DECL int archive_write_zip_set_compression_deflate(struct archive *);
+__LA_DECL int archive_write_zip_set_compression_store(struct archive *);
+/* Deprecated; use archive_write_open2 instead */
+__LA_DECL int archive_write_open(struct archive *, void *,
+ archive_open_callback *, archive_write_callback *,
+ archive_close_callback *);
+__LA_DECL int archive_write_open2(struct archive *, void *,
+ archive_open_callback *, archive_write_callback *,
+ archive_close_callback *, archive_free_callback *);
+__LA_DECL int archive_write_open_fd(struct archive *, int _fd);
+__LA_DECL int archive_write_open_filename(struct archive *, const char *_file);
+__LA_DECL int archive_write_open_filename_w(struct archive *,
+ const wchar_t *_file);
+/* A deprecated synonym for archive_write_open_filename() */
+__LA_DECL int archive_write_open_file(struct archive *, const char *_file)
+ __LA_DEPRECATED;
+__LA_DECL int archive_write_open_FILE(struct archive *, FILE *);
+/* _buffSize is the size of the buffer, _used refers to a variable that
+ * will be updated after each write into the buffer. */
+__LA_DECL int archive_write_open_memory(struct archive *,
+ void *_buffer, size_t _buffSize, size_t *_used);
+
+/*
+ * Note that the library will truncate writes beyond the size provided
+ * to archive_write_header or pad if the provided data is short.
+ */
+__LA_DECL int archive_write_header(struct archive *,
+ struct archive_entry *);
+__LA_DECL la_ssize_t archive_write_data(struct archive *,
+ const void *, size_t);
+
+/* This interface is currently only available for archive_write_disk handles. */
+__LA_DECL la_ssize_t archive_write_data_block(struct archive *,
+ const void *, size_t, la_int64_t);
+
+__LA_DECL int archive_write_finish_entry(struct archive *);
+__LA_DECL int archive_write_close(struct archive *);
+/* Marks the archive as FATAL so that a subsequent free() operation
+ * won't try to close() cleanly. Provides a fast abort capability
+ * when the client discovers that things have gone wrong. */
+__LA_DECL int archive_write_fail(struct archive *);
+/* This can fail if the archive wasn't already closed, in which case
+ * archive_write_free() will implicitly call archive_write_close(). */
+__LA_DECL int archive_write_free(struct archive *);
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Synonym for archive_write_free() for backwards compatibility. */
+__LA_DECL int archive_write_finish(struct archive *) __LA_DEPRECATED;
+#endif
+
+/*
+ * Set write options.
+ */
+/* Apply option to the format only. */
+__LA_DECL int archive_write_set_format_option(struct archive *_a,
+ const char *m, const char *o,
+ const char *v);
+/* Apply option to the filter only. */
+__LA_DECL int archive_write_set_filter_option(struct archive *_a,
+ const char *m, const char *o,
+ const char *v);
+/* Apply option to both the format and the filter. */
+__LA_DECL int archive_write_set_option(struct archive *_a,
+ const char *m, const char *o,
+ const char *v);
+/* Apply option string to both the format and the filter. */
+__LA_DECL int archive_write_set_options(struct archive *_a,
+ const char *opts);
+
+/*
+ * Set a encryption passphrase.
+ */
+__LA_DECL int archive_write_set_passphrase(struct archive *_a, const char *p);
+__LA_DECL int archive_write_set_passphrase_callback(struct archive *,
+ void *client_data, archive_passphrase_callback *);
+
+/*-
+ * ARCHIVE_WRITE_DISK API
+ *
+ * To create objects on disk:
+ * 1) Ask archive_write_disk_new for a new archive_write_disk object.
+ * 2) Set any global properties. In particular, you probably
+ * want to set the options.
+ * 3) For each entry:
+ * - construct an appropriate struct archive_entry structure
+ * - archive_write_header to create the file/dir/etc on disk
+ * - archive_write_data to write the entry data
+ * 4) archive_write_free to cleanup the writer and release resources
+ *
+ * In particular, you can use this in conjunction with archive_read()
+ * to pull entries out of an archive and create them on disk.
+ */
+__LA_DECL struct archive *archive_write_disk_new(void);
+/* This file will not be overwritten. */
+__LA_DECL int archive_write_disk_set_skip_file(struct archive *,
+ la_int64_t, la_int64_t);
+/* Set flags to control how the next item gets created.
+ * This accepts a bitmask of ARCHIVE_EXTRACT_XXX flags defined above. */
+__LA_DECL int archive_write_disk_set_options(struct archive *,
+ int flags);
+/*
+ * The lookup functions are given uname/uid (or gname/gid) pairs and
+ * return a uid (gid) suitable for this system. These are used for
+ * restoring ownership and for setting ACLs. The default functions
+ * are naive, they just return the uid/gid. These are small, so reasonable
+ * for applications that don't need to preserve ownership; they
+ * are probably also appropriate for applications that are doing
+ * same-system backup and restore.
+ */
+/*
+ * The "standard" lookup functions use common system calls to lookup
+ * the uname/gname, falling back to the uid/gid if the names can't be
+ * found. They cache lookups and are reasonably fast, but can be very
+ * large, so they are not used unless you ask for them. In
+ * particular, these match the specifications of POSIX "pax" and old
+ * POSIX "tar".
+ */
+__LA_DECL int archive_write_disk_set_standard_lookup(struct archive *);
+/*
+ * If neither the default (naive) nor the standard (big) functions suit
+ * your needs, you can write your own and register them. Be sure to
+ * include a cleanup function if you have allocated private data.
+ */
+__LA_DECL int archive_write_disk_set_group_lookup(struct archive *,
+ void * /* private_data */,
+ la_int64_t (*)(void *, const char *, la_int64_t),
+ void (* /* cleanup */)(void *));
+__LA_DECL int archive_write_disk_set_user_lookup(struct archive *,
+ void * /* private_data */,
+ la_int64_t (*)(void *, const char *, la_int64_t),
+ void (* /* cleanup */)(void *));
+__LA_DECL la_int64_t archive_write_disk_gid(struct archive *, const char *, la_int64_t);
+__LA_DECL la_int64_t archive_write_disk_uid(struct archive *, const char *, la_int64_t);
+
+/*
+ * ARCHIVE_READ_DISK API
+ *
+ * This is still evolving and somewhat experimental.
+ */
+__LA_DECL struct archive *archive_read_disk_new(void);
+/* The names for symlink modes here correspond to an old BSD
+ * command-line argument convention: -L, -P, -H */
+/* Follow all symlinks. */
+__LA_DECL int archive_read_disk_set_symlink_logical(struct archive *);
+/* Follow no symlinks. */
+__LA_DECL int archive_read_disk_set_symlink_physical(struct archive *);
+/* Follow symlink initially, then not. */
+__LA_DECL int archive_read_disk_set_symlink_hybrid(struct archive *);
+/* TODO: Handle Linux stat32/stat64 ugliness. <sigh> */
+__LA_DECL int archive_read_disk_entry_from_file(struct archive *,
+ struct archive_entry *, int /* fd */, const struct stat *);
+/* Look up gname for gid or uname for uid. */
+/* Default implementations are very, very stupid. */
+__LA_DECL const char *archive_read_disk_gname(struct archive *, la_int64_t);
+__LA_DECL const char *archive_read_disk_uname(struct archive *, la_int64_t);
+/* "Standard" implementation uses getpwuid_r, getgrgid_r and caches the
+ * results for performance. */
+__LA_DECL int archive_read_disk_set_standard_lookup(struct archive *);
+/* You can install your own lookups if you like. */
+__LA_DECL int archive_read_disk_set_gname_lookup(struct archive *,
+ void * /* private_data */,
+ const char *(* /* lookup_fn */)(void *, la_int64_t),
+ void (* /* cleanup_fn */)(void *));
+__LA_DECL int archive_read_disk_set_uname_lookup(struct archive *,
+ void * /* private_data */,
+ const char *(* /* lookup_fn */)(void *, la_int64_t),
+ void (* /* cleanup_fn */)(void *));
+/* Start traversal. */
+__LA_DECL int archive_read_disk_open(struct archive *, const char *);
+__LA_DECL int archive_read_disk_open_w(struct archive *, const wchar_t *);
+/*
+ * Request that current entry be visited. If you invoke it on every
+ * directory, you'll get a physical traversal. This is ignored if the
+ * current entry isn't a directory or a link to a directory. So, if
+ * you invoke this on every returned path, you'll get a full logical
+ * traversal.
+ */
+__LA_DECL int archive_read_disk_descend(struct archive *);
+__LA_DECL int archive_read_disk_can_descend(struct archive *);
+__LA_DECL int archive_read_disk_current_filesystem(struct archive *);
+__LA_DECL int archive_read_disk_current_filesystem_is_synthetic(struct archive *);
+__LA_DECL int archive_read_disk_current_filesystem_is_remote(struct archive *);
+/* Request that the access time of the entry visited by traversal be restored. */
+__LA_DECL int archive_read_disk_set_atime_restored(struct archive *);
+/*
+ * Set behavior. The "flags" argument selects optional behavior.
+ */
+/* Request that the access time of the entry visited by traversal be restored.
+ * This is the same as archive_read_disk_set_atime_restored. */
+#define ARCHIVE_READDISK_RESTORE_ATIME (0x0001)
+/* Default: Do not skip an entry which has nodump flags. */
+#define ARCHIVE_READDISK_HONOR_NODUMP (0x0002)
+/* Default: Skip a mac resource fork file whose prefix is "._" because of
+ * using copyfile. */
+#define ARCHIVE_READDISK_MAC_COPYFILE (0x0004)
+/* Default: Traverse mount points. */
+#define ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS (0x0008)
+/* Default: Xattrs are read from disk. */
+#define ARCHIVE_READDISK_NO_XATTR (0x0010)
+/* Default: ACLs are read from disk. */
+#define ARCHIVE_READDISK_NO_ACL (0x0020)
+/* Default: File flags are read from disk. */
+#define ARCHIVE_READDISK_NO_FFLAGS (0x0040)
+/* Default: Sparse file information is read from disk. */
+#define ARCHIVE_READDISK_NO_SPARSE (0x0080)
+
+__LA_DECL int archive_read_disk_set_behavior(struct archive *,
+ int flags);
+
+/*
+ * Set archive_match object that will be used in archive_read_disk to
+ * know whether an entry should be skipped. The callback function
+ * _excluded_func will be invoked when an entry is skipped by the result
+ * of archive_match.
+ */
+__LA_DECL int archive_read_disk_set_matching(struct archive *,
+ struct archive *_matching, void (*_excluded_func)
+ (struct archive *, void *, struct archive_entry *),
+ void *_client_data);
+__LA_DECL int archive_read_disk_set_metadata_filter_callback(struct archive *,
+ int (*_metadata_filter_func)(struct archive *, void *,
+ struct archive_entry *), void *_client_data);
+
+/* Simplified cleanup interface;
+ * This calls archive_read_free() or archive_write_free() as needed. */
+__LA_DECL int archive_free(struct archive *);
+
+/*
+ * Accessor functions to read/set various information in
+ * the struct archive object:
+ */
+
+/* Number of filters in the current filter pipeline. */
+/* Filter #0 is the one closest to the format, -1 is a synonym for the
+ * last filter, which is always the pseudo-filter that wraps the
+ * client callbacks. */
+__LA_DECL int archive_filter_count(struct archive *);
+__LA_DECL la_int64_t archive_filter_bytes(struct archive *, int);
+__LA_DECL int archive_filter_code(struct archive *, int);
+__LA_DECL const char * archive_filter_name(struct archive *, int);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* These don't properly handle multiple filters, so are deprecated and
+ * will eventually be removed. */
+/* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, -1); */
+__LA_DECL la_int64_t archive_position_compressed(struct archive *)
+ __LA_DEPRECATED;
+/* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, 0); */
+__LA_DECL la_int64_t archive_position_uncompressed(struct archive *)
+ __LA_DEPRECATED;
+/* As of libarchive 3.0, this is an alias for archive_filter_name(a, 0); */
+__LA_DECL const char *archive_compression_name(struct archive *)
+ __LA_DEPRECATED;
+/* As of libarchive 3.0, this is an alias for archive_filter_code(a, 0); */
+__LA_DECL int archive_compression(struct archive *)
+ __LA_DEPRECATED;
+#endif
+
+__LA_DECL int archive_errno(struct archive *);
+__LA_DECL const char *archive_error_string(struct archive *);
+__LA_DECL const char *archive_format_name(struct archive *);
+__LA_DECL int archive_format(struct archive *);
+__LA_DECL void archive_clear_error(struct archive *);
+__LA_DECL void archive_set_error(struct archive *, int _err,
+ const char *fmt, ...) __LA_PRINTF(3, 4);
+__LA_DECL void archive_copy_error(struct archive *dest,
+ struct archive *src);
+__LA_DECL int archive_file_count(struct archive *);
+
+/*
+ * ARCHIVE_MATCH API
+ */
+__LA_DECL struct archive *archive_match_new(void);
+__LA_DECL int archive_match_free(struct archive *);
+
+/*
+ * Test if archive_entry is excluded.
+ * This is a convenience function. This is the same as calling all
+ * archive_match_path_excluded, archive_match_time_excluded
+ * and archive_match_owner_excluded.
+ */
+__LA_DECL int archive_match_excluded(struct archive *,
+ struct archive_entry *);
+
+/*
+ * Test if pathname is excluded. The conditions are set by following functions.
+ */
+__LA_DECL int archive_match_path_excluded(struct archive *,
+ struct archive_entry *);
+/* Control recursive inclusion of directory content when directory is included. Default on. */
+__LA_DECL int archive_match_set_inclusion_recursion(struct archive *, int);
+/* Add exclusion pathname pattern. */
+__LA_DECL int archive_match_exclude_pattern(struct archive *, const char *);
+__LA_DECL int archive_match_exclude_pattern_w(struct archive *,
+ const wchar_t *);
+/* Add exclusion pathname pattern from file. */
+__LA_DECL int archive_match_exclude_pattern_from_file(struct archive *,
+ const char *, int _nullSeparator);
+__LA_DECL int archive_match_exclude_pattern_from_file_w(struct archive *,
+ const wchar_t *, int _nullSeparator);
+/* Add inclusion pathname pattern. */
+__LA_DECL int archive_match_include_pattern(struct archive *, const char *);
+__LA_DECL int archive_match_include_pattern_w(struct archive *,
+ const wchar_t *);
+/* Add inclusion pathname pattern from file. */
+__LA_DECL int archive_match_include_pattern_from_file(struct archive *,
+ const char *, int _nullSeparator);
+__LA_DECL int archive_match_include_pattern_from_file_w(struct archive *,
+ const wchar_t *, int _nullSeparator);
+/*
+ * How to get statistic information for inclusion patterns.
+ */
+/* Return the amount number of unmatched inclusion patterns. */
+__LA_DECL int archive_match_path_unmatched_inclusions(struct archive *);
+/* Return the pattern of unmatched inclusion with ARCHIVE_OK.
+ * Return ARCHIVE_EOF if there is no inclusion pattern. */
+__LA_DECL int archive_match_path_unmatched_inclusions_next(
+ struct archive *, const char **);
+__LA_DECL int archive_match_path_unmatched_inclusions_next_w(
+ struct archive *, const wchar_t **);
+
+/*
+ * Test if a file is excluded by its time stamp.
+ * The conditions are set by following functions.
+ */
+__LA_DECL int archive_match_time_excluded(struct archive *,
+ struct archive_entry *);
+
+/*
+ * Flags to tell a matching type of time stamps. These are used for
+ * following functions.
+ */
+/* Time flag: mtime to be tested. */
+#define ARCHIVE_MATCH_MTIME (0x0100)
+/* Time flag: ctime to be tested. */
+#define ARCHIVE_MATCH_CTIME (0x0200)
+/* Comparison flag: Match the time if it is newer than. */
+#define ARCHIVE_MATCH_NEWER (0x0001)
+/* Comparison flag: Match the time if it is older than. */
+#define ARCHIVE_MATCH_OLDER (0x0002)
+/* Comparison flag: Match the time if it is equal to. */
+#define ARCHIVE_MATCH_EQUAL (0x0010)
+/* Set inclusion time. */
+__LA_DECL int archive_match_include_time(struct archive *, int _flag,
+ time_t _sec, long _nsec);
+/* Set inclusion time by a date string. */
+__LA_DECL int archive_match_include_date(struct archive *, int _flag,
+ const char *_datestr);
+__LA_DECL int archive_match_include_date_w(struct archive *, int _flag,
+ const wchar_t *_datestr);
+/* Set inclusion time by a particular file. */
+__LA_DECL int archive_match_include_file_time(struct archive *,
+ int _flag, const char *_pathname);
+__LA_DECL int archive_match_include_file_time_w(struct archive *,
+ int _flag, const wchar_t *_pathname);
+/* Add exclusion entry. */
+__LA_DECL int archive_match_exclude_entry(struct archive *,
+ int _flag, struct archive_entry *);
+
+/*
+ * Test if a file is excluded by its uid ,gid, uname or gname.
+ * The conditions are set by following functions.
+ */
+__LA_DECL int archive_match_owner_excluded(struct archive *,
+ struct archive_entry *);
+/* Add inclusion uid, gid, uname and gname. */
+__LA_DECL int archive_match_include_uid(struct archive *, la_int64_t);
+__LA_DECL int archive_match_include_gid(struct archive *, la_int64_t);
+__LA_DECL int archive_match_include_uname(struct archive *, const char *);
+__LA_DECL int archive_match_include_uname_w(struct archive *,
+ const wchar_t *);
+__LA_DECL int archive_match_include_gname(struct archive *, const char *);
+__LA_DECL int archive_match_include_gname_w(struct archive *,
+ const wchar_t *);
+
+/* Utility functions */
+/* Convenience function to sort a NULL terminated list of strings */
+__LA_DECL int archive_utility_string_sort(char **);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* These are meaningless outside of this header. */
+#undef __LA_DECL
+
+#endif /* !ARCHIVE_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_acl.c b/contrib/libs/libarchive/libarchive/archive_acl.c
new file mode 100644
index 00000000000..ead7e36e49e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_acl.c
@@ -0,0 +1,2097 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+
+#include "archive_acl_private.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+
+#undef max
+#define max(a, b) ((a)>(b)?(a):(b))
+
+#ifndef HAVE_WMEMCMP
+/* Good enough for simple equality testing, but not for sorting. */
+#define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t))
+#endif
+
+static int acl_special(struct archive_acl *acl,
+ int type, int permset, int tag);
+static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,
+ int type, int permset, int tag, int id);
+static int archive_acl_add_entry_len_l(struct archive_acl *acl,
+ int type, int permset, int tag, int id, const char *name,
+ size_t len, struct archive_string_conv *sc);
+static int archive_acl_text_want_type(struct archive_acl *acl, int flags);
+static ssize_t archive_acl_text_len(struct archive_acl *acl, int want_type,
+ int flags, int wide, struct archive *a,
+ struct archive_string_conv *sc);
+static int isint_w(const wchar_t *start, const wchar_t *end, int *result);
+static int ismode_w(const wchar_t *start, const wchar_t *end, int *result);
+static int is_nfs4_flags_w(const wchar_t *start, const wchar_t *end,
+ int *result);
+static int is_nfs4_perms_w(const wchar_t *start, const wchar_t *end,
+ int *result);
+static void next_field_w(const wchar_t **wp, const wchar_t **start,
+ const wchar_t **end, wchar_t *sep);
+static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
+ int tag, int flags, const wchar_t *wname, int perm, int id);
+static void append_id_w(wchar_t **wp, int id);
+static int isint(const char *start, const char *end, int *result);
+static int ismode(const char *start, const char *end, int *result);
+static int is_nfs4_flags(const char *start, const char *end,
+ int *result);
+static int is_nfs4_perms(const char *start, const char *end,
+ int *result);
+static void next_field(const char **p, const char **start,
+ const char **end, char *sep);
+static void append_entry(char **p, const char *prefix, int type,
+ int tag, int flags, const char *name, int perm, int id);
+static void append_id(char **p, int id);
+
+static const struct {
+ const int perm;
+ const char c;
+ const wchar_t wc;
+} nfsv4_acl_perm_map[] = {
+ { ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, 'r',
+ L'r' },
+ { ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_ADD_FILE, 'w',
+ L'w' },
+ { ARCHIVE_ENTRY_ACL_EXECUTE, 'x', L'x' },
+ { ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,
+ 'p', L'p' },
+ { ARCHIVE_ENTRY_ACL_DELETE, 'd', L'd' },
+ { ARCHIVE_ENTRY_ACL_DELETE_CHILD, 'D', L'D' },
+ { ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, 'a', L'a' },
+ { ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, 'A', L'A' },
+ { ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, 'R', L'R' },
+ { ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, 'W', L'W' },
+ { ARCHIVE_ENTRY_ACL_READ_ACL, 'c', L'c' },
+ { ARCHIVE_ENTRY_ACL_WRITE_ACL, 'C', L'C' },
+ { ARCHIVE_ENTRY_ACL_WRITE_OWNER, 'o', L'o' },
+ { ARCHIVE_ENTRY_ACL_SYNCHRONIZE, 's', L's' }
+};
+
+static const int nfsv4_acl_perm_map_size = (int)(sizeof(nfsv4_acl_perm_map) /
+ sizeof(nfsv4_acl_perm_map[0]));
+
+static const struct {
+ const int perm;
+ const char c;
+ const wchar_t wc;
+} nfsv4_acl_flag_map[] = {
+ { ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, 'f', L'f' },
+ { ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, 'd', L'd' },
+ { ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, 'i', L'i' },
+ { ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, 'n', L'n' },
+ { ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, 'S', L'S' },
+ { ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, 'F', L'F' },
+ { ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, 'I', L'I' }
+};
+
+static const int nfsv4_acl_flag_map_size = (int)(sizeof(nfsv4_acl_flag_map) /
+ sizeof(nfsv4_acl_flag_map[0]));
+
+void
+archive_acl_clear(struct archive_acl *acl)
+{
+ struct archive_acl_entry *ap;
+
+ while (acl->acl_head != NULL) {
+ ap = acl->acl_head->next;
+ archive_mstring_clean(&acl->acl_head->name);
+ free(acl->acl_head);
+ acl->acl_head = ap;
+ }
+ free(acl->acl_text_w);
+ acl->acl_text_w = NULL;
+ free(acl->acl_text);
+ acl->acl_text = NULL;
+ acl->acl_p = NULL;
+ acl->acl_types = 0;
+ acl->acl_state = 0; /* Not counting. */
+}
+
+void
+archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
+{
+ struct archive_acl_entry *ap, *ap2;
+
+ archive_acl_clear(dest);
+
+ dest->mode = src->mode;
+ ap = src->acl_head;
+ while (ap != NULL) {
+ ap2 = acl_new_entry(dest,
+ ap->type, ap->permset, ap->tag, ap->id);
+ if (ap2 != NULL)
+ archive_mstring_copy(&ap2->name, &ap->name);
+ ap = ap->next;
+ }
+}
+
+int
+archive_acl_add_entry(struct archive_acl *acl,
+ int type, int permset, int tag, int id, const char *name)
+{
+ struct archive_acl_entry *ap;
+
+ if (acl_special(acl, type, permset, tag) == 0)
+ return ARCHIVE_OK;
+ ap = acl_new_entry(acl, type, permset, tag, id);
+ if (ap == NULL) {
+ /* XXX Error XXX */
+ return ARCHIVE_FAILED;
+ }
+ if (name != NULL && *name != '\0')
+ archive_mstring_copy_mbs(&ap->name, name);
+ else
+ archive_mstring_clean(&ap->name);
+ return ARCHIVE_OK;
+}
+
+int
+archive_acl_add_entry_w_len(struct archive_acl *acl,
+ int type, int permset, int tag, int id, const wchar_t *name, size_t len)
+{
+ struct archive_acl_entry *ap;
+
+ if (acl_special(acl, type, permset, tag) == 0)
+ return ARCHIVE_OK;
+ ap = acl_new_entry(acl, type, permset, tag, id);
+ if (ap == NULL) {
+ /* XXX Error XXX */
+ return ARCHIVE_FAILED;
+ }
+ if (name != NULL && *name != L'\0' && len > 0)
+ archive_mstring_copy_wcs_len(&ap->name, name, len);
+ else
+ archive_mstring_clean(&ap->name);
+ return ARCHIVE_OK;
+}
+
+static int
+archive_acl_add_entry_len_l(struct archive_acl *acl,
+ int type, int permset, int tag, int id, const char *name, size_t len,
+ struct archive_string_conv *sc)
+{
+ struct archive_acl_entry *ap;
+ int r;
+
+ if (acl_special(acl, type, permset, tag) == 0)
+ return ARCHIVE_OK;
+ ap = acl_new_entry(acl, type, permset, tag, id);
+ if (ap == NULL) {
+ /* XXX Error XXX */
+ return ARCHIVE_FAILED;
+ }
+ if (name != NULL && *name != '\0' && len > 0) {
+ r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
+ } else {
+ r = 0;
+ archive_mstring_clean(&ap->name);
+ }
+ if (r == 0)
+ return (ARCHIVE_OK);
+ else if (errno == ENOMEM)
+ return (ARCHIVE_FATAL);
+ else
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * If this ACL entry is part of the standard POSIX permissions set,
+ * store the permissions in the stat structure and return zero.
+ */
+static int
+acl_special(struct archive_acl *acl, int type, int permset, int tag)
+{
+ if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
+ && ((permset & ~007) == 0)) {
+ switch (tag) {
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ acl->mode &= ~0700;
+ acl->mode |= (permset & 7) << 6;
+ return (0);
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ acl->mode &= ~0070;
+ acl->mode |= (permset & 7) << 3;
+ return (0);
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ acl->mode &= ~0007;
+ acl->mode |= permset & 7;
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Allocate and populate a new ACL entry with everything but the
+ * name.
+ */
+static struct archive_acl_entry *
+acl_new_entry(struct archive_acl *acl,
+ int type, int permset, int tag, int id)
+{
+ struct archive_acl_entry *ap, *aq;
+
+ /* Type argument must be a valid NFS4 or POSIX.1e type.
+ * The type must agree with anything already set and
+ * the permset must be compatible. */
+ if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ return (NULL);
+ }
+ if (permset &
+ ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
+ | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
+ return (NULL);
+ }
+ } else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
+ if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
+ return (NULL);
+ }
+ if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
+ return (NULL);
+ }
+ } else {
+ return (NULL);
+ }
+
+ /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
+ switch (tag) {
+ case ARCHIVE_ENTRY_ACL_USER:
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ case ARCHIVE_ENTRY_ACL_GROUP:
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ /* Tags valid in both NFS4 and POSIX.1e */
+ break;
+ case ARCHIVE_ENTRY_ACL_MASK:
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ /* Tags valid only in POSIX.1e. */
+ if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
+ return (NULL);
+ }
+ break;
+ case ARCHIVE_ENTRY_ACL_EVERYONE:
+ /* Tags valid only in NFS4. */
+ if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ return (NULL);
+ }
+ break;
+ default:
+ /* No other values are valid. */
+ return (NULL);
+ }
+
+ free(acl->acl_text_w);
+ acl->acl_text_w = NULL;
+ free(acl->acl_text);
+ acl->acl_text = NULL;
+
+ /*
+ * If there's a matching entry already in the list, overwrite it.
+ * NFSv4 entries may be repeated and are not overwritten.
+ *
+ * TODO: compare names of no id is provided (needs more rework)
+ */
+ ap = acl->acl_head;
+ aq = NULL;
+ while (ap != NULL) {
+ if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&
+ ap->type == type && ap->tag == tag && ap->id == id) {
+ if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&
+ tag != ARCHIVE_ENTRY_ACL_GROUP)) {
+ ap->permset = permset;
+ return (ap);
+ }
+ }
+ aq = ap;
+ ap = ap->next;
+ }
+
+ /* Add a new entry to the end of the list. */
+ ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap));
+ if (ap == NULL)
+ return (NULL);
+ if (aq == NULL)
+ acl->acl_head = ap;
+ else
+ aq->next = ap;
+ ap->type = type;
+ ap->tag = tag;
+ ap->id = id;
+ ap->permset = permset;
+ acl->acl_types |= type;
+ return (ap);
+}
+
+/*
+ * Return a count of entries matching "want_type".
+ */
+int
+archive_acl_count(struct archive_acl *acl, int want_type)
+{
+ int count;
+ struct archive_acl_entry *ap;
+
+ count = 0;
+ ap = acl->acl_head;
+ while (ap != NULL) {
+ if ((ap->type & want_type) != 0)
+ count++;
+ ap = ap->next;
+ }
+
+ if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
+ count += 3;
+ return (count);
+}
+
+/*
+ * Return a bitmask of stored ACL types in an ACL list
+ */
+int
+archive_acl_types(struct archive_acl *acl)
+{
+ return (acl->acl_types);
+}
+
+/*
+ * Prepare for reading entries from the ACL data. Returns a count
+ * of entries matching "want_type", or zero if there are no
+ * non-extended ACL entries of that type.
+ */
+int
+archive_acl_reset(struct archive_acl *acl, int want_type)
+{
+ int count, cutoff;
+
+ count = archive_acl_count(acl, want_type);
+
+ /*
+ * If the only entries are the three standard ones,
+ * then don't return any ACL data. (In this case,
+ * client can just use chmod(2) to set permissions.)
+ */
+ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
+ cutoff = 3;
+ else
+ cutoff = 0;
+
+ if (count > cutoff)
+ acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
+ else
+ acl->acl_state = 0;
+ acl->acl_p = acl->acl_head;
+ return (count);
+}
+
+
+/*
+ * Return the next ACL entry in the list. Fake entries for the
+ * standard permissions and include them in the returned list.
+ */
+int
+archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,
+ int *type, int *permset, int *tag, int *id, const char **name)
+{
+ *name = NULL;
+ *id = -1;
+
+ /*
+ * The acl_state is either zero (no entries available), -1
+ * (reading from list), or an entry type (retrieve that type
+ * from ae_stat.aest_mode).
+ */
+ if (acl->acl_state == 0)
+ return (ARCHIVE_WARN);
+
+ /* The first three access entries are special. */
+ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+ switch (acl->acl_state) {
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ *permset = (acl->mode >> 6) & 7;
+ *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+ *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+ acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+ return (ARCHIVE_OK);
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ *permset = (acl->mode >> 3) & 7;
+ *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+ *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+ acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
+ return (ARCHIVE_OK);
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ *permset = acl->mode & 7;
+ *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+ *tag = ARCHIVE_ENTRY_ACL_OTHER;
+ acl->acl_state = -1;
+ acl->acl_p = acl->acl_head;
+ return (ARCHIVE_OK);
+ default:
+ break;
+ }
+ }
+
+ while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
+ acl->acl_p = acl->acl_p->next;
+ if (acl->acl_p == NULL) {
+ acl->acl_state = 0;
+ *type = 0;
+ *permset = 0;
+ *tag = 0;
+ *id = -1;
+ *name = NULL;
+ return (ARCHIVE_EOF); /* End of ACL entries. */
+ }
+ *type = acl->acl_p->type;
+ *permset = acl->acl_p->permset;
+ *tag = acl->acl_p->tag;
+ *id = acl->acl_p->id;
+ if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
+ if (errno == ENOMEM)
+ return (ARCHIVE_FATAL);
+ *name = NULL;
+ }
+ acl->acl_p = acl->acl_p->next;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Determine what type of ACL do we want
+ */
+static int
+archive_acl_text_want_type(struct archive_acl *acl, int flags)
+{
+ int want_type;
+
+ /* Check if ACL is NFSv4 */
+ if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+ /* NFSv4 should never mix with POSIX.1e */
+ if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
+ return (0);
+ else
+ return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+ }
+
+ /* Now deal with POSIX.1e ACLs */
+
+ want_type = 0;
+ if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
+ want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+ if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+ want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+
+ /* By default we want both access and default ACLs */
+ if (want_type == 0)
+ return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);
+
+ return (want_type);
+}
+
+/*
+ * Calculate ACL text string length
+ */
+static ssize_t
+archive_acl_text_len(struct archive_acl *acl, int want_type, int flags,
+ int wide, struct archive *a, struct archive_string_conv *sc) {
+ struct archive_acl_entry *ap;
+ const char *name;
+ const wchar_t *wname;
+ int count, idlen, tmp, r;
+ ssize_t length;
+ size_t len;
+
+ count = 0;
+ length = 0;
+ for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
+ if ((ap->type & want_type) == 0)
+ continue;
+ /*
+ * Filemode-mapping ACL entries are stored exclusively in
+ * ap->mode so they should not be in the list
+ */
+ if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
+ && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
+ || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
+ || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
+ continue;
+ count++;
+ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0
+ && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+ length += 8; /* "default:" */
+ switch (ap->tag) {
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ length += 6; /* "owner@" */
+ break;
+ }
+ /* FALLTHROUGH */
+ case ARCHIVE_ENTRY_ACL_USER:
+ case ARCHIVE_ENTRY_ACL_MASK:
+ length += 4; /* "user", "mask" */
+ break;
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ length += 6; /* "group@" */
+ break;
+ }
+ /* FALLTHROUGH */
+ case ARCHIVE_ENTRY_ACL_GROUP:
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ length += 5; /* "group", "other" */
+ break;
+ case ARCHIVE_ENTRY_ACL_EVERYONE:
+ length += 9; /* "everyone@" */
+ break;
+ }
+ length += 1; /* colon after tag */
+ if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||
+ ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {
+ if (wide) {
+ r = archive_mstring_get_wcs(a, &ap->name,
+ &wname);
+ if (r == 0 && wname != NULL)
+ length += wcslen(wname);
+ else if (r < 0 && errno == ENOMEM)
+ return (0);
+ else
+ length += sizeof(uid_t) * 3 + 1;
+ } else {
+ r = archive_mstring_get_mbs_l(a, &ap->name, &name,
+ &len, sc);
+ if (r != 0)
+ return (0);
+ if (len > 0 && name != NULL)
+ length += len;
+ else
+ length += sizeof(uid_t) * 3 + 1;
+ }
+ length += 1; /* colon after user or group name */
+ } else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)
+ length += 1; /* 2nd colon empty user,group or other */
+
+ if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)
+ && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
+ && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER
+ || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {
+ /* Solaris has no colon after other: and mask: */
+ length = length - 1;
+ }
+
+ if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ /* rwxpdDaARWcCos:fdinSFI:deny */
+ length += 27;
+ if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)
+ length += 1; /* allow, alarm, audit */
+ } else
+ length += 3; /* rwx */
+
+ if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||
+ ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&
+ (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {
+ length += 1; /* colon */
+ /* ID digit count */
+ idlen = 1;
+ tmp = ap->id;
+ while (tmp > 9) {
+ tmp = tmp / 10;
+ idlen++;
+ }
+ length += idlen;
+ }
+ length ++; /* entry separator */
+ }
+
+ /* Add filemode-mapping access entries to the length */
+ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+ if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {
+ /* "user::rwx\ngroup::rwx\nother:rwx\n" */
+ length += 31;
+ } else {
+ /* "user::rwx\ngroup::rwx\nother::rwx\n" */
+ length += 32;
+ }
+ } else if (count == 0)
+ return (0);
+
+ /* The terminating character is included in count */
+ return (length);
+}
+
+/*
+ * Generate a wide text version of the ACL. The flags parameter controls
+ * the type and style of the generated ACL.
+ */
+wchar_t *
+archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,
+ struct archive *a)
+{
+ int count;
+ ssize_t length;
+ size_t len;
+ const wchar_t *wname;
+ const wchar_t *prefix;
+ wchar_t separator;
+ struct archive_acl_entry *ap;
+ int id, r, want_type;
+ wchar_t *wp, *ws;
+
+ want_type = archive_acl_text_want_type(acl, flags);
+
+ /* Both NFSv4 and POSIX.1 types found */
+ if (want_type == 0)
+ return (NULL);
+
+ if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
+ flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
+
+ length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);
+
+ if (length == 0)
+ return (NULL);
+
+ if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
+ separator = L',';
+ else
+ separator = L'\n';
+
+ /* Now, allocate the string and actually populate it. */
+ wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t));
+ if (wp == NULL) {
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+ }
+ count = 0;
+
+ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+ append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+ ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
+ acl->mode & 0700, -1);
+ *wp++ = separator;
+ append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+ ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
+ acl->mode & 0070, -1);
+ *wp++ = separator;
+ append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+ ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
+ acl->mode & 0007, -1);
+ count += 3;
+ }
+
+ for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
+ if ((ap->type & want_type) == 0)
+ continue;
+ /*
+ * Filemode-mapping ACL entries are stored exclusively in
+ * ap->mode so they should not be in the list
+ */
+ if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
+ && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
+ || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
+ || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
+ continue;
+ if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
+ (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
+ prefix = L"default:";
+ else
+ prefix = NULL;
+ r = archive_mstring_get_wcs(a, &ap->name, &wname);
+ if (r == 0) {
+ if (count > 0)
+ *wp++ = separator;
+ if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
+ id = ap->id;
+ else
+ id = -1;
+ append_entry_w(&wp, prefix, ap->type, ap->tag, flags,
+ wname, ap->permset, id);
+ count++;
+ } else if (r < 0 && errno == ENOMEM) {
+ free(ws);
+ return (NULL);
+ }
+ }
+
+ /* Add terminating character */
+ *wp++ = L'\0';
+
+ len = wcslen(ws);
+
+ if ((ssize_t)len > (length - 1))
+ __archive_errx(1, "Buffer overrun");
+
+ if (text_len != NULL)
+ *text_len = len;
+
+ return (ws);
+}
+
+static void
+append_id_w(wchar_t **wp, int id)
+{
+ if (id < 0)
+ id = 0;
+ if (id > 9)
+ append_id_w(wp, id / 10);
+ *(*wp)++ = L"0123456789"[id % 10];
+}
+
+static void
+append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
+ int tag, int flags, const wchar_t *wname, int perm, int id)
+{
+ int i;
+
+ if (prefix != NULL) {
+ wcscpy(*wp, prefix);
+ *wp += wcslen(*wp);
+ }
+ switch (tag) {
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ wname = NULL;
+ id = -1;
+ if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+ wcscpy(*wp, L"owner@");
+ break;
+ }
+ /* FALLTHROUGH */
+ case ARCHIVE_ENTRY_ACL_USER:
+ wcscpy(*wp, L"user");
+ break;
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ wname = NULL;
+ id = -1;
+ if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+ wcscpy(*wp, L"group@");
+ break;
+ }
+ /* FALLTHROUGH */
+ case ARCHIVE_ENTRY_ACL_GROUP:
+ wcscpy(*wp, L"group");
+ break;
+ case ARCHIVE_ENTRY_ACL_MASK:
+ wcscpy(*wp, L"mask");
+ wname = NULL;
+ id = -1;
+ break;
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ wcscpy(*wp, L"other");
+ wname = NULL;
+ id = -1;
+ break;
+ case ARCHIVE_ENTRY_ACL_EVERYONE:
+ wcscpy(*wp, L"everyone@");
+ wname = NULL;
+ id = -1;
+ break;
+ }
+ *wp += wcslen(*wp);
+ *(*wp)++ = L':';
+ if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
+ tag == ARCHIVE_ENTRY_ACL_USER ||
+ tag == ARCHIVE_ENTRY_ACL_GROUP) {
+ if (wname != NULL) {
+ wcscpy(*wp, wname);
+ *wp += wcslen(*wp);
+ } else if (tag == ARCHIVE_ENTRY_ACL_USER
+ || tag == ARCHIVE_ENTRY_ACL_GROUP) {
+ append_id_w(wp, id);
+ if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
+ id = -1;
+ }
+ /* Solaris style has no second colon after other and mask */
+ if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
+ || (tag != ARCHIVE_ENTRY_ACL_OTHER
+ && tag != ARCHIVE_ENTRY_ACL_MASK))
+ *(*wp)++ = L':';
+ }
+ if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+ /* POSIX.1e ACL perms */
+ *(*wp)++ = (perm & 0444) ? L'r' : L'-';
+ *(*wp)++ = (perm & 0222) ? L'w' : L'-';
+ *(*wp)++ = (perm & 0111) ? L'x' : L'-';
+ } else {
+ /* NFSv4 ACL perms */
+ for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
+ if (perm & nfsv4_acl_perm_map[i].perm)
+ *(*wp)++ = nfsv4_acl_perm_map[i].wc;
+ else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
+ *(*wp)++ = L'-';
+ }
+ *(*wp)++ = L':';
+ for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
+ if (perm & nfsv4_acl_flag_map[i].perm)
+ *(*wp)++ = nfsv4_acl_flag_map[i].wc;
+ else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
+ *(*wp)++ = L'-';
+ }
+ *(*wp)++ = L':';
+ switch (type) {
+ case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+ wcscpy(*wp, L"allow");
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+ wcscpy(*wp, L"deny");
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+ wcscpy(*wp, L"audit");
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+ wcscpy(*wp, L"alarm");
+ break;
+ default:
+ break;
+ }
+ *wp += wcslen(*wp);
+ }
+ if (id != -1) {
+ *(*wp)++ = L':';
+ append_id_w(wp, id);
+ }
+}
+
+/*
+ * Generate a text version of the ACL. The flags parameter controls
+ * the type and style of the generated ACL.
+ */
+char *
+archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
+ struct archive_string_conv *sc)
+{
+ int count;
+ ssize_t length;
+ size_t len;
+ const char *name;
+ const char *prefix;
+ char separator;
+ struct archive_acl_entry *ap;
+ int id, r, want_type;
+ char *p, *s;
+
+ want_type = archive_acl_text_want_type(acl, flags);
+
+ /* Both NFSv4 and POSIX.1 types found */
+ if (want_type == 0)
+ return (NULL);
+
+ if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
+ flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
+
+ length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);
+
+ if (length == 0)
+ return (NULL);
+
+ if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
+ separator = ',';
+ else
+ separator = '\n';
+
+ /* Now, allocate the string and actually populate it. */
+ p = s = (char *)malloc(length * sizeof(char));
+ if (p == NULL) {
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+ }
+ count = 0;
+
+ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+ append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+ ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
+ acl->mode & 0700, -1);
+ *p++ = separator;
+ append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+ ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
+ acl->mode & 0070, -1);
+ *p++ = separator;
+ append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+ ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
+ acl->mode & 0007, -1);
+ count += 3;
+ }
+
+ for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
+ if ((ap->type & want_type) == 0)
+ continue;
+ /*
+ * Filemode-mapping ACL entries are stored exclusively in
+ * ap->mode so they should not be in the list
+ */
+ if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
+ && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
+ || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
+ || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
+ continue;
+ if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
+ (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
+ prefix = "default:";
+ else
+ prefix = NULL;
+ r = archive_mstring_get_mbs_l(
+ NULL, &ap->name, &name, &len, sc);
+ if (r != 0) {
+ free(s);
+ return (NULL);
+ }
+ if (count > 0)
+ *p++ = separator;
+ if (name == NULL ||
+ (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
+ id = ap->id;
+ } else {
+ id = -1;
+ }
+ append_entry(&p, prefix, ap->type, ap->tag, flags, name,
+ ap->permset, id);
+ count++;
+ }
+
+ /* Add terminating character */
+ *p++ = '\0';
+
+ len = strlen(s);
+
+ if ((ssize_t)len > (length - 1))
+ __archive_errx(1, "Buffer overrun");
+
+ if (text_len != NULL)
+ *text_len = len;
+
+ return (s);
+}
+
+static void
+append_id(char **p, int id)
+{
+ if (id < 0)
+ id = 0;
+ if (id > 9)
+ append_id(p, id / 10);
+ *(*p)++ = "0123456789"[id % 10];
+}
+
+static void
+append_entry(char **p, const char *prefix, int type,
+ int tag, int flags, const char *name, int perm, int id)
+{
+ int i;
+
+ if (prefix != NULL) {
+ strcpy(*p, prefix);
+ *p += strlen(*p);
+ }
+ switch (tag) {
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ name = NULL;
+ id = -1;
+ if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+ strcpy(*p, "owner@");
+ break;
+ }
+ /* FALLTHROUGH */
+ case ARCHIVE_ENTRY_ACL_USER:
+ strcpy(*p, "user");
+ break;
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ name = NULL;
+ id = -1;
+ if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+ strcpy(*p, "group@");
+ break;
+ }
+ /* FALLTHROUGH */
+ case ARCHIVE_ENTRY_ACL_GROUP:
+ strcpy(*p, "group");
+ break;
+ case ARCHIVE_ENTRY_ACL_MASK:
+ strcpy(*p, "mask");
+ name = NULL;
+ id = -1;
+ break;
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ strcpy(*p, "other");
+ name = NULL;
+ id = -1;
+ break;
+ case ARCHIVE_ENTRY_ACL_EVERYONE:
+ strcpy(*p, "everyone@");
+ name = NULL;
+ id = -1;
+ break;
+ }
+ *p += strlen(*p);
+ *(*p)++ = ':';
+ if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
+ tag == ARCHIVE_ENTRY_ACL_USER ||
+ tag == ARCHIVE_ENTRY_ACL_GROUP) {
+ if (name != NULL) {
+ strcpy(*p, name);
+ *p += strlen(*p);
+ } else if (tag == ARCHIVE_ENTRY_ACL_USER
+ || tag == ARCHIVE_ENTRY_ACL_GROUP) {
+ append_id(p, id);
+ if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
+ id = -1;
+ }
+ /* Solaris style has no second colon after other and mask */
+ if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
+ || (tag != ARCHIVE_ENTRY_ACL_OTHER
+ && tag != ARCHIVE_ENTRY_ACL_MASK))
+ *(*p)++ = ':';
+ }
+ if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+ /* POSIX.1e ACL perms */
+ *(*p)++ = (perm & 0444) ? 'r' : '-';
+ *(*p)++ = (perm & 0222) ? 'w' : '-';
+ *(*p)++ = (perm & 0111) ? 'x' : '-';
+ } else {
+ /* NFSv4 ACL perms */
+ for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
+ if (perm & nfsv4_acl_perm_map[i].perm)
+ *(*p)++ = nfsv4_acl_perm_map[i].c;
+ else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
+ *(*p)++ = '-';
+ }
+ *(*p)++ = ':';
+ for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
+ if (perm & nfsv4_acl_flag_map[i].perm)
+ *(*p)++ = nfsv4_acl_flag_map[i].c;
+ else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
+ *(*p)++ = '-';
+ }
+ *(*p)++ = ':';
+ switch (type) {
+ case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+ strcpy(*p, "allow");
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+ strcpy(*p, "deny");
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+ strcpy(*p, "audit");
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+ strcpy(*p, "alarm");
+ break;
+ }
+ *p += strlen(*p);
+ }
+ if (id != -1) {
+ *(*p)++ = ':';
+ append_id(p, id);
+ }
+}
+
+/*
+ * Parse a wide ACL text string.
+ *
+ * The want_type argument may be one of the following:
+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
+ * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
+ *
+ * POSIX.1e ACL entries prefixed with "default:" are treated as
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
+ */
+int
+archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
+ int want_type)
+{
+ struct {
+ const wchar_t *start;
+ const wchar_t *end;
+ } field[6], name;
+
+ const wchar_t *s, *st;
+
+ int numfields, fields, n, r, sol, ret;
+ int type, types, tag, permset, id;
+ size_t len;
+ wchar_t sep;
+
+ ret = ARCHIVE_OK;
+ types = 0;
+
+ switch (want_type) {
+ case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
+ want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+ __LA_FALLTHROUGH;
+ case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+ case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+ numfields = 5;
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+ numfields = 6;
+ break;
+ default:
+ return (ARCHIVE_FATAL);
+ }
+
+ while (text != NULL && *text != L'\0') {
+ /*
+ * Parse the fields out of the next entry,
+ * advance 'text' to start of next entry.
+ */
+ fields = 0;
+ do {
+ const wchar_t *start, *end;
+ next_field_w(&text, &start, &end, &sep);
+ if (fields < numfields) {
+ field[fields].start = start;
+ field[fields].end = end;
+ }
+ ++fields;
+ } while (sep == L':');
+
+ /* Set remaining fields to blank. */
+ for (n = fields; n < numfields; ++n)
+ field[n].start = field[n].end = NULL;
+
+ if (field[0].start != NULL && *(field[0].start) == L'#') {
+ /* Comment, skip entry */
+ continue;
+ }
+
+ n = 0;
+ sol = 0;
+ id = -1;
+ permset = 0;
+ name.start = name.end = NULL;
+
+ if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ /* POSIX.1e ACLs */
+ /*
+ * Default keyword "default:user::rwx"
+ * if found, we have one more field
+ *
+ * We also support old Solaris extension:
+ * "defaultuser::rwx" is the default ACL corresponding
+ * to "user::rwx", etc. valid only for first field
+ */
+ s = field[0].start;
+ len = field[0].end - field[0].start;
+ if (*s == L'd' && (len == 1 || (len >= 7
+ && wmemcmp((s + 1), L"efault", 6) == 0))) {
+ type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+ if (len > 7)
+ field[0].start += 7;
+ else
+ n = 1;
+ } else
+ type = want_type;
+
+ /* Check for a numeric ID in field n+1 or n+3. */
+ isint_w(field[n + 1].start, field[n + 1].end, &id);
+ /* Field n+3 is optional. */
+ if (id == -1 && fields > n+3)
+ isint_w(field[n + 3].start, field[n + 3].end,
+ &id);
+
+ tag = 0;
+ s = field[n].start;
+ st = field[n].start + 1;
+ len = field[n].end - field[n].start;
+
+ switch (*s) {
+ case L'u':
+ if (len == 1 || (len == 4
+ && wmemcmp(st, L"ser", 3) == 0))
+ tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+ break;
+ case L'g':
+ if (len == 1 || (len == 5
+ && wmemcmp(st, L"roup", 4) == 0))
+ tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+ break;
+ case L'o':
+ if (len == 1 || (len == 5
+ && wmemcmp(st, L"ther", 4) == 0))
+ tag = ARCHIVE_ENTRY_ACL_OTHER;
+ break;
+ case L'm':
+ if (len == 1 || (len == 4
+ && wmemcmp(st, L"ask", 3) == 0))
+ tag = ARCHIVE_ENTRY_ACL_MASK;
+ break;
+ default:
+ break;
+ }
+
+ switch (tag) {
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ case ARCHIVE_ENTRY_ACL_MASK:
+ if (fields == (n + 2)
+ && field[n + 1].start < field[n + 1].end
+ && ismode_w(field[n + 1].start,
+ field[n + 1].end, &permset)) {
+ /* This is Solaris-style "other:rwx" */
+ sol = 1;
+ } else if (fields == (n + 3) &&
+ field[n + 1].start < field[n + 1].end) {
+ /* Invalid mask or other field */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ break;
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ if (id != -1 ||
+ field[n + 1].start < field[n + 1].end) {
+ name = field[n + 1];
+ if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
+ tag = ARCHIVE_ENTRY_ACL_USER;
+ else
+ tag = ARCHIVE_ENTRY_ACL_GROUP;
+ }
+ break;
+ default:
+ /* Invalid tag, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+
+ /*
+ * Without "default:" we expect mode in field 2
+ * Exception: Solaris other and mask fields
+ */
+ if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
+ field[n + 2 - sol].end, &permset)) {
+ /* Invalid mode, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ } else {
+ /* NFS4 ACLs */
+ s = field[0].start;
+ len = field[0].end - field[0].start;
+ tag = 0;
+
+ switch (len) {
+ case 4:
+ if (wmemcmp(s, L"user", 4) == 0)
+ tag = ARCHIVE_ENTRY_ACL_USER;
+ break;
+ case 5:
+ if (wmemcmp(s, L"group", 5) == 0)
+ tag = ARCHIVE_ENTRY_ACL_GROUP;
+ break;
+ case 6:
+ if (wmemcmp(s, L"owner@", 6) == 0)
+ tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+ else if (wmemcmp(s, L"group@", len) == 0)
+ tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+ break;
+ case 9:
+ if (wmemcmp(s, L"everyone@", 9) == 0)
+ tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+ default:
+ break;
+ }
+
+ if (tag == 0) {
+ /* Invalid tag, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
+ tag == ARCHIVE_ENTRY_ACL_GROUP) {
+ n = 1;
+ name = field[1];
+ isint_w(name.start, name.end, &id);
+ } else
+ n = 0;
+
+ if (!is_nfs4_perms_w(field[1 + n].start,
+ field[1 + n].end, &permset)) {
+ /* Invalid NFSv4 perms, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ if (!is_nfs4_flags_w(field[2 + n].start,
+ field[2 + n].end, &permset)) {
+ /* Invalid NFSv4 flags, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ s = field[3 + n].start;
+ len = field[3 + n].end - field[3 + n].start;
+ type = 0;
+ if (len == 4) {
+ if (wmemcmp(s, L"deny", 4) == 0)
+ type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+ } else if (len == 5) {
+ if (wmemcmp(s, L"allow", 5) == 0)
+ type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+ else if (wmemcmp(s, L"audit", 5) == 0)
+ type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
+ else if (wmemcmp(s, L"alarm", 5) == 0)
+ type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+ }
+ if (type == 0) {
+ /* Invalid entry type, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ isint_w(field[4 + n].start, field[4 + n].end, &id);
+ }
+
+ /* Add entry to the internal list. */
+ r = archive_acl_add_entry_w_len(acl, type, permset,
+ tag, id, name.start, name.end - name.start);
+ if (r < ARCHIVE_WARN)
+ return (r);
+ if (r != ARCHIVE_OK)
+ ret = ARCHIVE_WARN;
+ types |= type;
+ }
+
+ /* Reset ACL */
+ archive_acl_reset(acl, types);
+
+ return (ret);
+}
+
+/*
+ * Parse a string to a positive decimal integer. Returns true if
+ * the string is non-empty and consists only of decimal digits,
+ * false otherwise.
+ */
+static int
+isint_w(const wchar_t *start, const wchar_t *end, int *result)
+{
+ int n = 0;
+ if (start >= end)
+ return (0);
+ while (start < end) {
+ if (*start < L'0' || *start > L'9')
+ return (0);
+ if (n > (INT_MAX / 10) ||
+ (n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) {
+ n = INT_MAX;
+ } else {
+ n *= 10;
+ n += *start - L'0';
+ }
+ start++;
+ }
+ *result = n;
+ return (1);
+}
+
+/*
+ * Parse a string as a mode field. Returns true if
+ * the string is non-empty and consists only of mode characters,
+ * false otherwise.
+ */
+static int
+ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
+{
+ const wchar_t *p;
+
+ if (start >= end)
+ return (0);
+ p = start;
+ *permset = 0;
+ while (p < end) {
+ switch (*p++) {
+ case L'r': case L'R':
+ *permset |= ARCHIVE_ENTRY_ACL_READ;
+ break;
+ case L'w': case L'W':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE;
+ break;
+ case L'x': case L'X':
+ *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+ break;
+ case L'-':
+ break;
+ default:
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL permission field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * permission characters, false otherwise
+ */
+static int
+is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
+{
+ const wchar_t *p = start;
+
+ while (p < end) {
+ switch (*p++) {
+ case L'r':
+ *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
+ break;
+ case L'w':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
+ break;
+ case L'x':
+ *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+ break;
+ case L'p':
+ *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
+ break;
+ case L'D':
+ *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
+ break;
+ case L'd':
+ *permset |= ARCHIVE_ENTRY_ACL_DELETE;
+ break;
+ case L'a':
+ *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
+ break;
+ case L'A':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
+ break;
+ case L'R':
+ *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
+ break;
+ case L'W':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
+ break;
+ case L'c':
+ *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
+ break;
+ case L'C':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
+ break;
+ case L'o':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
+ break;
+ case L's':
+ *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+ break;
+ case L'-':
+ break;
+ default:
+ return(0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL flags field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * flag characters, false otherwise
+ */
+static int
+is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
+{
+ const wchar_t *p = start;
+
+ while (p < end) {
+ switch(*p++) {
+ case L'f':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
+ break;
+ case L'd':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
+ break;
+ case L'i':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
+ break;
+ case L'n':
+ *permset |=
+ ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
+ break;
+ case L'S':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
+ break;
+ case L'F':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
+ break;
+ case L'I':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
+ break;
+ case L'-':
+ break;
+ default:
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
+ * to point to just after the separator. *start points to the first
+ * character of the matched text and *end just after the last
+ * character of the matched identifier. In particular *end - *start
+ * is the length of the field body, not including leading or trailing
+ * whitespace.
+ */
+static void
+next_field_w(const wchar_t **wp, const wchar_t **start,
+ const wchar_t **end, wchar_t *sep)
+{
+ /* Skip leading whitespace to find start of field. */
+ while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
+ (*wp)++;
+ }
+ *start = *wp;
+
+ /* Scan for the separator. */
+ while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
+ **wp != L'\n' && **wp != L'#') {
+ (*wp)++;
+ }
+ *sep = **wp;
+
+ /* Locate end of field, trim trailing whitespace if necessary */
+ if (*wp == *start) {
+ *end = *wp;
+ } else {
+ *end = *wp - 1;
+ while (**end == L' ' || **end == L'\t' || **end == L'\n') {
+ (*end)--;
+ }
+ (*end)++;
+ }
+
+ /* Handle in-field comments */
+ if (*sep == L'#') {
+ while (**wp != L'\0' && **wp != L',' && **wp != L'\n') {
+ (*wp)++;
+ }
+ *sep = **wp;
+ }
+
+ /* Adjust scanner location. */
+ if (**wp != L'\0')
+ (*wp)++;
+}
+
+/*
+ * Parse an ACL text string.
+ *
+ * The want_type argument may be one of the following:
+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
+ * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
+ *
+ * POSIX.1e ACL entries prefixed with "default:" are treated as
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
+ */
+int
+archive_acl_from_text_l(struct archive_acl *acl, const char *text,
+ int want_type, struct archive_string_conv *sc)
+{
+ struct {
+ const char *start;
+ const char *end;
+ } field[6], name;
+
+ const char *s, *st;
+ int numfields, fields, n, r, sol, ret;
+ int type, types, tag, permset, id;
+ size_t len;
+ char sep;
+
+ switch (want_type) {
+ case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
+ want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+ __LA_FALLTHROUGH;
+ case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+ case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+ numfields = 5;
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+ numfields = 6;
+ break;
+ default:
+ return (ARCHIVE_FATAL);
+ }
+
+ ret = ARCHIVE_OK;
+ types = 0;
+
+ while (text != NULL && *text != '\0') {
+ /*
+ * Parse the fields out of the next entry,
+ * advance 'text' to start of next entry.
+ */
+ fields = 0;
+ do {
+ const char *start, *end;
+ next_field(&text, &start, &end, &sep);
+ if (fields < numfields) {
+ field[fields].start = start;
+ field[fields].end = end;
+ }
+ ++fields;
+ } while (sep == ':');
+
+ /* Set remaining fields to blank. */
+ for (n = fields; n < numfields; ++n)
+ field[n].start = field[n].end = NULL;
+
+ if (field[0].start != NULL && *(field[0].start) == '#') {
+ /* Comment, skip entry */
+ continue;
+ }
+
+ n = 0;
+ sol = 0;
+ id = -1;
+ permset = 0;
+ name.start = name.end = NULL;
+
+ if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ /* POSIX.1e ACLs */
+ /*
+ * Default keyword "default:user::rwx"
+ * if found, we have one more field
+ *
+ * We also support old Solaris extension:
+ * "defaultuser::rwx" is the default ACL corresponding
+ * to "user::rwx", etc. valid only for first field
+ */
+ s = field[0].start;
+ len = field[0].end - field[0].start;
+ if (*s == 'd' && (len == 1 || (len >= 7
+ && memcmp((s + 1), "efault", 6) == 0))) {
+ type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+ if (len > 7)
+ field[0].start += 7;
+ else
+ n = 1;
+ } else
+ type = want_type;
+
+ /* Check for a numeric ID in field n+1 or n+3. */
+ isint(field[n + 1].start, field[n + 1].end, &id);
+ /* Field n+3 is optional. */
+ if (id == -1 && fields > (n + 3))
+ isint(field[n + 3].start, field[n + 3].end,
+ &id);
+
+ tag = 0;
+ s = field[n].start;
+ st = field[n].start + 1;
+ len = field[n].end - field[n].start;
+
+ if (len == 0) {
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+
+ switch (*s) {
+ case 'u':
+ if (len == 1 || (len == 4
+ && memcmp(st, "ser", 3) == 0))
+ tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+ break;
+ case 'g':
+ if (len == 1 || (len == 5
+ && memcmp(st, "roup", 4) == 0))
+ tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+ break;
+ case 'o':
+ if (len == 1 || (len == 5
+ && memcmp(st, "ther", 4) == 0))
+ tag = ARCHIVE_ENTRY_ACL_OTHER;
+ break;
+ case 'm':
+ if (len == 1 || (len == 4
+ && memcmp(st, "ask", 3) == 0))
+ tag = ARCHIVE_ENTRY_ACL_MASK;
+ break;
+ default:
+ break;
+ }
+
+ switch (tag) {
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ case ARCHIVE_ENTRY_ACL_MASK:
+ if (fields == (n + 2)
+ && field[n + 1].start < field[n + 1].end
+ && ismode(field[n + 1].start,
+ field[n + 1].end, &permset)) {
+ /* This is Solaris-style "other:rwx" */
+ sol = 1;
+ } else if (fields == (n + 3) &&
+ field[n + 1].start < field[n + 1].end) {
+ /* Invalid mask or other field */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ break;
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ if (id != -1 ||
+ field[n + 1].start < field[n + 1].end) {
+ name = field[n + 1];
+ if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
+ tag = ARCHIVE_ENTRY_ACL_USER;
+ else
+ tag = ARCHIVE_ENTRY_ACL_GROUP;
+ }
+ break;
+ default:
+ /* Invalid tag, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+
+ /*
+ * Without "default:" we expect mode in field 3
+ * Exception: Solaris other and mask fields
+ */
+ if (permset == 0 && !ismode(field[n + 2 - sol].start,
+ field[n + 2 - sol].end, &permset)) {
+ /* Invalid mode, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ } else {
+ /* NFS4 ACLs */
+ s = field[0].start;
+ len = field[0].end - field[0].start;
+ tag = 0;
+
+ switch (len) {
+ case 4:
+ if (memcmp(s, "user", 4) == 0)
+ tag = ARCHIVE_ENTRY_ACL_USER;
+ break;
+ case 5:
+ if (memcmp(s, "group", 5) == 0)
+ tag = ARCHIVE_ENTRY_ACL_GROUP;
+ break;
+ case 6:
+ if (memcmp(s, "owner@", 6) == 0)
+ tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+ else if (memcmp(s, "group@", 6) == 0)
+ tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+ break;
+ case 9:
+ if (memcmp(s, "everyone@", 9) == 0)
+ tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+ break;
+ default:
+ break;
+ }
+
+ if (tag == 0) {
+ /* Invalid tag, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
+ tag == ARCHIVE_ENTRY_ACL_GROUP) {
+ n = 1;
+ name = field[1];
+ isint(name.start, name.end, &id);
+ } else
+ n = 0;
+
+ if (!is_nfs4_perms(field[1 + n].start,
+ field[1 + n].end, &permset)) {
+ /* Invalid NFSv4 perms, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ if (!is_nfs4_flags(field[2 + n].start,
+ field[2 + n].end, &permset)) {
+ /* Invalid NFSv4 flags, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ s = field[3 + n].start;
+ len = field[3 + n].end - field[3 + n].start;
+ type = 0;
+ if (len == 4) {
+ if (memcmp(s, "deny", 4) == 0)
+ type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+ } else if (len == 5) {
+ if (memcmp(s, "allow", 5) == 0)
+ type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+ else if (memcmp(s, "audit", 5) == 0)
+ type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
+ else if (memcmp(s, "alarm", 5) == 0)
+ type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+ }
+ if (type == 0) {
+ /* Invalid entry type, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ isint(field[4 + n].start, field[4 + n].end,
+ &id);
+ }
+
+ /* Add entry to the internal list. */
+ r = archive_acl_add_entry_len_l(acl, type, permset,
+ tag, id, name.start, name.end - name.start, sc);
+ if (r < ARCHIVE_WARN)
+ return (r);
+ if (r != ARCHIVE_OK)
+ ret = ARCHIVE_WARN;
+ types |= type;
+ }
+
+ /* Reset ACL */
+ archive_acl_reset(acl, types);
+
+ return (ret);
+}
+
+/*
+ * Parse a string to a positive decimal integer. Returns true if
+ * the string is non-empty and consists only of decimal digits,
+ * false otherwise.
+ */
+static int
+isint(const char *start, const char *end, int *result)
+{
+ int n = 0;
+ if (start >= end)
+ return (0);
+ while (start < end) {
+ if (*start < '0' || *start > '9')
+ return (0);
+ if (n > (INT_MAX / 10) ||
+ (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
+ n = INT_MAX;
+ } else {
+ n *= 10;
+ n += *start - '0';
+ }
+ start++;
+ }
+ *result = n;
+ return (1);
+}
+
+/*
+ * Parse a string as a mode field. Returns true if
+ * the string is non-empty and consists only of mode characters,
+ * false otherwise.
+ */
+static int
+ismode(const char *start, const char *end, int *permset)
+{
+ const char *p;
+
+ if (start >= end)
+ return (0);
+ p = start;
+ *permset = 0;
+ while (p < end) {
+ switch (*p++) {
+ case 'r': case 'R':
+ *permset |= ARCHIVE_ENTRY_ACL_READ;
+ break;
+ case 'w': case 'W':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE;
+ break;
+ case 'x': case 'X':
+ *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+ break;
+ case '-':
+ break;
+ default:
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL permission field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * permission characters, false otherwise
+ */
+static int
+is_nfs4_perms(const char *start, const char *end, int *permset)
+{
+ const char *p = start;
+
+ while (p < end) {
+ switch (*p++) {
+ case 'r':
+ *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
+ break;
+ case 'w':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
+ break;
+ case 'x':
+ *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+ break;
+ case 'p':
+ *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
+ break;
+ case 'D':
+ *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
+ break;
+ case 'd':
+ *permset |= ARCHIVE_ENTRY_ACL_DELETE;
+ break;
+ case 'a':
+ *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
+ break;
+ case 'A':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
+ break;
+ case 'R':
+ *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
+ break;
+ case 'W':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
+ break;
+ case 'c':
+ *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
+ break;
+ case 'C':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
+ break;
+ case 'o':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
+ break;
+ case 's':
+ *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+ break;
+ case '-':
+ break;
+ default:
+ return(0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL flags field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * flag characters, false otherwise
+ */
+static int
+is_nfs4_flags(const char *start, const char *end, int *permset)
+{
+ const char *p = start;
+
+ while (p < end) {
+ switch(*p++) {
+ case 'f':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
+ break;
+ case 'd':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
+ break;
+ case 'i':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
+ break;
+ case 'n':
+ *permset |=
+ ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
+ break;
+ case 'S':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
+ break;
+ case 'F':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
+ break;
+ case 'I':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
+ break;
+ case '-':
+ break;
+ default:
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
+ * to point to just after the separator. *start points to the first
+ * character of the matched text and *end just after the last
+ * character of the matched identifier. In particular *end - *start
+ * is the length of the field body, not including leading or trailing
+ * whitespace.
+ */
+static void
+next_field(const char **p, const char **start,
+ const char **end, char *sep)
+{
+ /* Skip leading whitespace to find start of field. */
+ while (**p == ' ' || **p == '\t' || **p == '\n') {
+ (*p)++;
+ }
+ *start = *p;
+
+ /* Scan for the separator. */
+ while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n' &&
+ **p != '#') {
+ (*p)++;
+ }
+ *sep = **p;
+
+ /* Locate end of field, trim trailing whitespace if necessary */
+ if (*p == *start) {
+ *end = *p;
+ } else {
+ *end = *p - 1;
+ while (**end == ' ' || **end == '\t' || **end == '\n') {
+ (*end)--;
+ }
+ (*end)++;
+ }
+
+ /* Handle in-field comments */
+ if (*sep == '#') {
+ while (**p != '\0' && **p != ',' && **p != '\n') {
+ (*p)++;
+ }
+ *sep = **p;
+ }
+
+ /* Adjust scanner location. */
+ if (**p != '\0')
+ (*p)++;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_acl_private.h b/contrib/libs/libarchive/libarchive/archive_acl_private.h
new file mode 100644
index 00000000000..af108162c66
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_acl_private.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_ACL_PRIVATE_H_INCLUDED
+#define ARCHIVE_ACL_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include "archive_string.h"
+
+struct archive_acl_entry {
+ struct archive_acl_entry *next;
+ int type; /* E.g., access or default */
+ int tag; /* E.g., user/group/other/mask */
+ int permset; /* r/w/x bits */
+ int id; /* uid/gid for user/group */
+ struct archive_mstring name; /* uname/gname */
+};
+
+struct archive_acl {
+ mode_t mode;
+ struct archive_acl_entry *acl_head;
+ struct archive_acl_entry *acl_p;
+ int acl_state; /* See acl_next for details. */
+ wchar_t *acl_text_w;
+ char *acl_text;
+ int acl_types;
+};
+
+void archive_acl_clear(struct archive_acl *);
+void archive_acl_copy(struct archive_acl *, struct archive_acl *);
+int archive_acl_count(struct archive_acl *, int);
+int archive_acl_types(struct archive_acl *);
+int archive_acl_reset(struct archive_acl *, int);
+int archive_acl_next(struct archive *, struct archive_acl *, int,
+ int *, int *, int *, int *, const char **);
+
+int archive_acl_add_entry(struct archive_acl *, int, int, int, int, const char *);
+int archive_acl_add_entry_w_len(struct archive_acl *,
+ int, int, int, int, const wchar_t *, size_t);
+int archive_acl_add_entry_len(struct archive_acl *,
+ int, int, int, int, const char *, size_t);
+
+wchar_t *archive_acl_to_text_w(struct archive_acl *, ssize_t *, int,
+ struct archive *);
+char *archive_acl_to_text_l(struct archive_acl *, ssize_t *, int,
+ struct archive_string_conv *);
+
+/*
+ * ACL text parser.
+ */
+int archive_acl_from_text_w(struct archive_acl *, const wchar_t * /* wtext */,
+ int /* type */);
+int archive_acl_from_text_l(struct archive_acl *, const char * /* text */,
+ int /* type */, struct archive_string_conv *);
+
+#endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_check_magic.c b/contrib/libs/libarchive/libarchive/archive_check_magic.c
new file mode 100644
index 00000000000..1f40072f81d
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_check_magic.c
@@ -0,0 +1,175 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_check_magic.c 201089 2009-12-28 02:20:23Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include <windows.h>
+#include <winbase.h>
+#endif
+
+#include "archive_private.h"
+
+static void
+errmsg(const char *m)
+{
+ size_t s = strlen(m);
+ ssize_t written;
+
+ while (s > 0) {
+ written = write(2, m, s);
+ if (written <= 0)
+ return;
+ m += written;
+ s -= written;
+ }
+}
+
+static __LA_DEAD void
+diediedie(void)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
+ /* Cause a breakpoint exception */
+ DebugBreak();
+#endif
+ abort(); /* Terminate the program abnormally. */
+}
+
+static const char *
+state_name(unsigned s)
+{
+ switch (s) {
+ case ARCHIVE_STATE_NEW: return ("new");
+ case ARCHIVE_STATE_HEADER: return ("header");
+ case ARCHIVE_STATE_DATA: return ("data");
+ case ARCHIVE_STATE_EOF: return ("eof");
+ case ARCHIVE_STATE_CLOSED: return ("closed");
+ case ARCHIVE_STATE_FATAL: return ("fatal");
+ default: return ("??");
+ }
+}
+
+static const char *
+archive_handle_type_name(unsigned m)
+{
+ switch (m) {
+ case ARCHIVE_WRITE_MAGIC: return ("archive_write");
+ case ARCHIVE_READ_MAGIC: return ("archive_read");
+ case ARCHIVE_WRITE_DISK_MAGIC: return ("archive_write_disk");
+ case ARCHIVE_READ_DISK_MAGIC: return ("archive_read_disk");
+ case ARCHIVE_MATCH_MAGIC: return ("archive_match");
+ default: return NULL;
+ }
+}
+
+
+static char *
+write_all_states(char *buff, unsigned int states)
+{
+ unsigned int lowbit;
+
+ buff[0] = '\0';
+
+ /* A trick for computing the lowest set bit. */
+ while ((lowbit = states & (1 + ~states)) != 0) {
+ states &= ~lowbit; /* Clear the low bit. */
+ strcat(buff, state_name(lowbit));
+ if (states != 0)
+ strcat(buff, "/");
+ }
+ return buff;
+}
+
+/*
+ * Check magic value and current state.
+ * Magic value mismatches are fatal and result in calls to abort().
+ * State mismatches return ARCHIVE_FATAL.
+ * Otherwise, returns ARCHIVE_OK.
+ *
+ * This is designed to catch serious programming errors that violate
+ * the libarchive API.
+ */
+int
+__archive_check_magic(struct archive *a, unsigned int magic,
+ unsigned int state, const char *function)
+{
+ char states1[64];
+ char states2[64];
+ const char *handle_type;
+
+ /*
+ * If this isn't some form of archive handle,
+ * then the library user has screwed up so bad that
+ * we don't even have a reliable way to report an error.
+ */
+ handle_type = archive_handle_type_name(a->magic);
+
+ if (!handle_type) {
+ errmsg("PROGRAMMER ERROR: Function ");
+ errmsg(function);
+ errmsg(" invoked with invalid archive handle.\n");
+ diediedie();
+ }
+
+ if (a->magic != magic) {
+ archive_set_error(a, -1,
+ "PROGRAMMER ERROR: Function '%s' invoked"
+ " on '%s' archive object, which is not supported.",
+ function,
+ handle_type);
+ a->state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+
+ if ((a->state & state) == 0) {
+ /* If we're already FATAL, don't overwrite the error. */
+ if (a->state != ARCHIVE_STATE_FATAL)
+ archive_set_error(a, -1,
+ "INTERNAL ERROR: Function '%s' invoked with"
+ " archive structure in state '%s',"
+ " should be in state '%s'",
+ function,
+ write_all_states(states1, a->state),
+ write_all_states(states2, state));
+ a->state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ return ARCHIVE_OK;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_cmdline.c b/contrib/libs/libarchive/libarchive/archive_cmdline.c
new file mode 100644
index 00000000000..5c519cd17f1
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_cmdline.c
@@ -0,0 +1,227 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_cmdline_private.h"
+#include "archive_string.h"
+
+static int cmdline_set_path(struct archive_cmdline *, const char *);
+static int cmdline_add_arg(struct archive_cmdline *, const char *);
+
+static ssize_t
+extract_quotation(struct archive_string *as, const char *p)
+{
+ const char *s;
+
+ for (s = p + 1; *s;) {
+ if (*s == '\\') {
+ if (s[1] != '\0') {
+ archive_strappend_char(as, s[1]);
+ s += 2;
+ } else
+ s++;
+ } else if (*s == '"')
+ break;
+ else {
+ archive_strappend_char(as, s[0]);
+ s++;
+ }
+ }
+ if (*s != '"')
+ return (ARCHIVE_FAILED);/* Invalid sequence. */
+ return ((ssize_t)(s + 1 - p));
+}
+
+static ssize_t
+get_argument(struct archive_string *as, const char *p)
+{
+ const char *s = p;
+
+ archive_string_empty(as);
+
+ /* Skip beginning space characters. */
+ while (*s != '\0' && *s == ' ')
+ s++;
+ /* Copy non-space characters. */
+ while (*s != '\0' && *s != ' ') {
+ if (*s == '\\') {
+ if (s[1] != '\0') {
+ archive_strappend_char(as, s[1]);
+ s += 2;
+ } else {
+ s++;/* Ignore this character.*/
+ break;
+ }
+ } else if (*s == '"') {
+ ssize_t q = extract_quotation(as, s);
+ if (q < 0)
+ return (ARCHIVE_FAILED);/* Invalid sequence. */
+ s += q;
+ } else {
+ archive_strappend_char(as, s[0]);
+ s++;
+ }
+ }
+ return ((ssize_t)(s - p));
+}
+
+/*
+ * Set up command line arguments.
+ * Returns ARCHIVE_OK if everything okey.
+ * Returns ARCHIVE_FAILED if there is a lack of the `"' terminator or an
+ * empty command line.
+ * Returns ARCHIVE_FATAL if no memory.
+ */
+int
+__archive_cmdline_parse(struct archive_cmdline *data, const char *cmd)
+{
+ struct archive_string as;
+ const char *p;
+ ssize_t al;
+ int r;
+
+ archive_string_init(&as);
+
+ /* Get first argument as a command path. */
+ al = get_argument(&as, cmd);
+ if (al < 0) {
+ r = ARCHIVE_FAILED;/* Invalid sequence. */
+ goto exit_function;
+ }
+ if (archive_strlen(&as) == 0) {
+ r = ARCHIVE_FAILED;/* An empty command path. */
+ goto exit_function;
+ }
+ r = cmdline_set_path(data, as.s);
+ if (r != ARCHIVE_OK)
+ goto exit_function;
+ p = strrchr(as.s, '/');
+ if (p == NULL)
+ p = as.s;
+ else
+ p++;
+ r = cmdline_add_arg(data, p);
+ if (r != ARCHIVE_OK)
+ goto exit_function;
+ cmd += al;
+
+ for (;;) {
+ al = get_argument(&as, cmd);
+ if (al < 0) {
+ r = ARCHIVE_FAILED;/* Invalid sequence. */
+ goto exit_function;
+ }
+ if (al == 0)
+ break;
+ cmd += al;
+ if (archive_strlen(&as) == 0 && *cmd == '\0')
+ break;
+ r = cmdline_add_arg(data, as.s);
+ if (r != ARCHIVE_OK)
+ goto exit_function;
+ }
+ r = ARCHIVE_OK;
+exit_function:
+ archive_string_free(&as);
+ return (r);
+}
+
+/*
+ * Set the program path.
+ */
+static int
+cmdline_set_path(struct archive_cmdline *data, const char *path)
+{
+ char *newptr;
+
+ newptr = realloc(data->path, strlen(path) + 1);
+ if (newptr == NULL)
+ return (ARCHIVE_FATAL);
+ data->path = newptr;
+ strcpy(data->path, path);
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Add a argument for the program.
+ */
+static int
+cmdline_add_arg(struct archive_cmdline *data, const char *arg)
+{
+ char **newargv;
+
+ if (data->path == NULL)
+ return (ARCHIVE_FAILED);
+
+ newargv = realloc(data->argv, (data->argc + 2) * sizeof(char *));
+ if (newargv == NULL)
+ return (ARCHIVE_FATAL);
+ data->argv = newargv;
+ data->argv[data->argc] = strdup(arg);
+ if (data->argv[data->argc] == NULL)
+ return (ARCHIVE_FATAL);
+ /* Set the terminator of argv. */
+ data->argv[++data->argc] = NULL;
+ return (ARCHIVE_OK);
+}
+
+struct archive_cmdline *
+__archive_cmdline_allocate(void)
+{
+ return (struct archive_cmdline *)
+ calloc(1, sizeof(struct archive_cmdline));
+}
+
+/*
+ * Release the resources.
+ */
+int
+__archive_cmdline_free(struct archive_cmdline *data)
+{
+
+ if (data) {
+ free(data->path);
+ if (data->argv != NULL) {
+ int i;
+ for (i = 0; data->argv[i] != NULL; i++)
+ free(data->argv[i]);
+ free(data->argv);
+ }
+ free(data);
+ }
+ return (ARCHIVE_OK);
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_cmdline_private.h b/contrib/libs/libarchive/libarchive/archive_cmdline_private.h
new file mode 100644
index 00000000000..57a19494fd7
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_cmdline_private.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_CMDLINE_PRIVATE_H
+#define ARCHIVE_CMDLINE_PRIVATE_H
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+struct archive_cmdline {
+ char *path;
+ char **argv;
+ int argc;
+};
+
+struct archive_cmdline *__archive_cmdline_allocate(void);
+int __archive_cmdline_parse(struct archive_cmdline *, const char *);
+int __archive_cmdline_free(struct archive_cmdline *);
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_cryptor.c b/contrib/libs/libarchive/libarchive/archive_cryptor.c
new file mode 100644
index 00000000000..112baf16134
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_cryptor.c
@@ -0,0 +1,534 @@
+/*-
+* Copyright (c) 2014 Michihiro NAKAJIMA
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "archive_platform.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include "archive.h"
+#include "archive_cryptor_private.h"
+
+/*
+ * On systems that do not support any recognized crypto libraries,
+ * this file will normally define no usable symbols.
+ *
+ * But some compilers and linkers choke on empty object files, so
+ * define a public symbol that will always exist. This could
+ * be removed someday if this file gains another always-present
+ * symbol definition.
+ */
+int __libarchive_cryptor_build_hack(void) {
+ return 0;
+}
+
+#ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto
+
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+ size_t salt_len, unsigned rounds, uint8_t *derived_key,
+ size_t derived_key_len)
+{
+ CCKeyDerivationPBKDF(kCCPBKDF2, (const char *)pw,
+ pw_len, salt, salt_len, kCCPRFHmacAlgSHA1, rounds,
+ derived_key, derived_key_len);
+ return 0;
+}
+
+#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)
+#ifdef _MSC_VER
+#pragma comment(lib, "Bcrypt.lib")
+#endif
+
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+ size_t salt_len, unsigned rounds, uint8_t *derived_key,
+ size_t derived_key_len)
+{
+ NTSTATUS status;
+ BCRYPT_ALG_HANDLE hAlg;
+
+ status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM,
+ MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG);
+ if (!BCRYPT_SUCCESS(status))
+ return -1;
+
+ status = BCryptDeriveKeyPBKDF2(hAlg,
+ (PUCHAR)(uintptr_t)pw, (ULONG)pw_len,
+ (PUCHAR)(uintptr_t)salt, (ULONG)salt_len, rounds,
+ (PUCHAR)derived_key, (ULONG)derived_key_len, 0);
+
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+
+ return (BCRYPT_SUCCESS(status)) ? 0: -1;
+}
+
+#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_PKCS5_H)
+
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+ size_t salt_len, unsigned rounds, uint8_t *derived_key,
+ size_t derived_key_len)
+{
+ mbedtls_md_context_t ctx;
+ const mbedtls_md_info_t *info;
+ int ret;
+
+ mbedtls_md_init(&ctx);
+ info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+ if (info == NULL) {
+ mbedtls_md_free(&ctx);
+ return (-1);
+ }
+ ret = mbedtls_md_setup(&ctx, info, 1);
+ if (ret != 0) {
+ mbedtls_md_free(&ctx);
+ return (-1);
+ }
+ ret = mbedtls_pkcs5_pbkdf2_hmac(&ctx, (const unsigned char *)pw,
+ pw_len, salt, salt_len, rounds, derived_key_len, derived_key);
+
+ mbedtls_md_free(&ctx);
+ return (ret);
+}
+
+#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_PBKDF2_H)
+
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+ size_t salt_len, unsigned rounds, uint8_t *derived_key,
+ size_t derived_key_len) {
+ pbkdf2_hmac_sha1((unsigned)pw_len, (const uint8_t *)pw, rounds,
+ salt_len, salt, derived_key_len, derived_key);
+ return 0;
+}
+
+#elif defined(HAVE_LIBCRYPTO) && defined(HAVE_PKCS5_PBKDF2_HMAC_SHA1)
+
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+ size_t salt_len, unsigned rounds, uint8_t *derived_key,
+ size_t derived_key_len) {
+
+ PKCS5_PBKDF2_HMAC_SHA1(pw, pw_len, salt, salt_len, rounds,
+ derived_key_len, derived_key);
+ return 0;
+}
+
+#else
+
+/* Stub */
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+ size_t salt_len, unsigned rounds, uint8_t *derived_key,
+ size_t derived_key_len) {
+ (void)pw; /* UNUSED */
+ (void)pw_len; /* UNUSED */
+ (void)salt; /* UNUSED */
+ (void)salt_len; /* UNUSED */
+ (void)rounds; /* UNUSED */
+ (void)derived_key; /* UNUSED */
+ (void)derived_key_len; /* UNUSED */
+ return -1; /* UNSUPPORTED */
+}
+
+#endif
+
+#ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto
+# if MAC_OS_X_VERSION_MAX_ALLOWED < 1090
+# define kCCAlgorithmAES kCCAlgorithmAES128
+# endif
+
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ CCCryptorStatus r;
+
+ ctx->key_len = key_len;
+ memcpy(ctx->key, key, key_len);
+ memset(ctx->nonce, 0, sizeof(ctx->nonce));
+ ctx->encr_pos = AES_BLOCK_SIZE;
+ r = CCCryptorCreateWithMode(kCCEncrypt, kCCModeECB, kCCAlgorithmAES,
+ ccNoPadding, NULL, key, key_len, NULL, 0, 0, 0, &ctx->ctx);
+ return (r == kCCSuccess)? 0: -1;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+ CCCryptorRef ref = ctx->ctx;
+ CCCryptorStatus r;
+
+ r = CCCryptorReset(ref, NULL);
+ if (r != kCCSuccess && r != kCCUnimplemented)
+ return -1;
+ r = CCCryptorUpdate(ref, ctx->nonce, AES_BLOCK_SIZE, ctx->encr_buf,
+ AES_BLOCK_SIZE, NULL);
+ return (r == kCCSuccess)? 0: -1;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+ memset(ctx->key, 0, ctx->key_len);
+ memset(ctx->nonce, 0, sizeof(ctx->nonce));
+ return 0;
+}
+
+#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)
+
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ BCRYPT_ALG_HANDLE hAlg;
+ BCRYPT_KEY_HANDLE hKey;
+ DWORD keyObj_len, aes_key_len;
+ PBYTE keyObj;
+ ULONG result;
+ NTSTATUS status;
+ BCRYPT_KEY_LENGTHS_STRUCT key_lengths;
+
+ ctx->hAlg = NULL;
+ ctx->hKey = NULL;
+ ctx->keyObj = NULL;
+ switch (key_len) {
+ case 16: aes_key_len = 128; break;
+ case 24: aes_key_len = 192; break;
+ case 32: aes_key_len = 256; break;
+ default: return -1;
+ }
+ status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM,
+ MS_PRIMITIVE_PROVIDER, 0);
+ if (!BCRYPT_SUCCESS(status))
+ return -1;
+ status = BCryptGetProperty(hAlg, BCRYPT_KEY_LENGTHS, (PUCHAR)&key_lengths,
+ sizeof(key_lengths), &result, 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ return -1;
+ }
+ if (key_lengths.dwMinLength > aes_key_len
+ || key_lengths.dwMaxLength < aes_key_len) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ return -1;
+ }
+ status = BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PUCHAR)&keyObj_len,
+ sizeof(keyObj_len), &result, 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ return -1;
+ }
+ keyObj = (PBYTE)HeapAlloc(GetProcessHeap(), 0, keyObj_len);
+ if (keyObj == NULL) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ return -1;
+ }
+ status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE,
+ (PUCHAR)BCRYPT_CHAIN_MODE_ECB, sizeof(BCRYPT_CHAIN_MODE_ECB), 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ HeapFree(GetProcessHeap(), 0, keyObj);
+ return -1;
+ }
+ status = BCryptGenerateSymmetricKey(hAlg, &hKey,
+ keyObj, keyObj_len,
+ (PUCHAR)(uintptr_t)key, (ULONG)key_len, 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ HeapFree(GetProcessHeap(), 0, keyObj);
+ return -1;
+ }
+
+ ctx->hAlg = hAlg;
+ ctx->hKey = hKey;
+ ctx->keyObj = keyObj;
+ ctx->keyObj_len = keyObj_len;
+ ctx->encr_pos = AES_BLOCK_SIZE;
+
+ return 0;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+ NTSTATUS status;
+ ULONG result;
+
+ status = BCryptEncrypt(ctx->hKey, (PUCHAR)ctx->nonce, AES_BLOCK_SIZE,
+ NULL, NULL, 0, (PUCHAR)ctx->encr_buf, AES_BLOCK_SIZE,
+ &result, 0);
+ return BCRYPT_SUCCESS(status) ? 0 : -1;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+
+ if (ctx->hAlg != NULL) {
+ BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
+ ctx->hAlg = NULL;
+ BCryptDestroyKey(ctx->hKey);
+ ctx->hKey = NULL;
+ HeapFree(GetProcessHeap(), 0, ctx->keyObj);
+ ctx->keyObj = NULL;
+ }
+ memset(ctx, 0, sizeof(*ctx));
+ return 0;
+}
+
+#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_AES_H)
+
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ mbedtls_aes_init(&ctx->ctx);
+ ctx->key_len = key_len;
+ memcpy(ctx->key, key, key_len);
+ memset(ctx->nonce, 0, sizeof(ctx->nonce));
+ ctx->encr_pos = AES_BLOCK_SIZE;
+ return 0;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+ if (mbedtls_aes_setkey_enc(&ctx->ctx, ctx->key,
+ ctx->key_len * 8) != 0)
+ return (-1);
+ if (mbedtls_aes_crypt_ecb(&ctx->ctx, MBEDTLS_AES_ENCRYPT, ctx->nonce,
+ ctx->encr_buf) != 0)
+ return (-1);
+ return 0;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+ mbedtls_aes_free(&ctx->ctx);
+ memset(ctx, 0, sizeof(*ctx));
+ return 0;
+}
+
+#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_AES_H)
+
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ ctx->key_len = key_len;
+ memcpy(ctx->key, key, key_len);
+ memset(ctx->nonce, 0, sizeof(ctx->nonce));
+ ctx->encr_pos = AES_BLOCK_SIZE;
+ memset(&ctx->ctx, 0, sizeof(ctx->ctx));
+ return 0;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+#if NETTLE_VERSION_MAJOR < 3
+ aes_set_encrypt_key(&ctx->ctx, ctx->key_len, ctx->key);
+ aes_encrypt(&ctx->ctx, AES_BLOCK_SIZE, ctx->encr_buf, ctx->nonce);
+#else
+ switch(ctx->key_len) {
+ case AES128_KEY_SIZE:
+ aes128_set_encrypt_key(&ctx->ctx.c128, ctx->key);
+ aes128_encrypt(&ctx->ctx.c128, AES_BLOCK_SIZE, ctx->encr_buf,
+ ctx->nonce);
+ break;
+ case AES192_KEY_SIZE:
+ aes192_set_encrypt_key(&ctx->ctx.c192, ctx->key);
+ aes192_encrypt(&ctx->ctx.c192, AES_BLOCK_SIZE, ctx->encr_buf,
+ ctx->nonce);
+ break;
+ case AES256_KEY_SIZE:
+ aes256_set_encrypt_key(&ctx->ctx.c256, ctx->key);
+ aes256_encrypt(&ctx->ctx.c256, AES_BLOCK_SIZE, ctx->encr_buf,
+ ctx->nonce);
+ break;
+ default:
+ return -1;
+ break;
+ }
+#endif
+ return 0;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ return 0;
+}
+
+#elif defined(HAVE_LIBCRYPTO)
+
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ if ((ctx->ctx = EVP_CIPHER_CTX_new()) == NULL)
+ return -1;
+
+ switch (key_len) {
+ case 16: ctx->type = EVP_aes_128_ecb(); break;
+ case 24: ctx->type = EVP_aes_192_ecb(); break;
+ case 32: ctx->type = EVP_aes_256_ecb(); break;
+ default: ctx->type = NULL; return -1;
+ }
+
+ ctx->key_len = key_len;
+ memcpy(ctx->key, key, key_len);
+ memset(ctx->nonce, 0, sizeof(ctx->nonce));
+ ctx->encr_pos = AES_BLOCK_SIZE;
+ return 0;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+ int outl = 0;
+ int r;
+
+ r = EVP_EncryptInit_ex(ctx->ctx, ctx->type, NULL, ctx->key, NULL);
+ if (r == 0)
+ return -1;
+ r = EVP_EncryptUpdate(ctx->ctx, ctx->encr_buf, &outl, ctx->nonce,
+ AES_BLOCK_SIZE);
+ if (r == 0 || outl != AES_BLOCK_SIZE)
+ return -1;
+ return 0;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+ EVP_CIPHER_CTX_free(ctx->ctx);
+ memset(ctx->key, 0, ctx->key_len);
+ memset(ctx->nonce, 0, sizeof(ctx->nonce));
+ return 0;
+}
+
+#else
+
+#define ARCHIVE_CRYPTOR_STUB
+/* Stub */
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ (void)ctx; /* UNUSED */
+ (void)key; /* UNUSED */
+ (void)key_len; /* UNUSED */
+ return -1;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+ (void)ctx; /* UNUSED */
+ return -1;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+ (void)ctx; /* UNUSED */
+ return 0;
+}
+
+#endif
+
+#ifdef ARCHIVE_CRYPTOR_STUB
+static int
+aes_ctr_update(archive_crypto_ctx *ctx, const uint8_t * const in,
+ size_t in_len, uint8_t * const out, size_t *out_len)
+{
+ (void)ctx; /* UNUSED */
+ (void)in; /* UNUSED */
+ (void)in_len; /* UNUSED */
+ (void)out; /* UNUSED */
+ (void)out_len; /* UNUSED */
+ aes_ctr_encrypt_counter(ctx); /* UNUSED */ /* Fix unused function warning */
+ return -1;
+}
+
+#else
+static void
+aes_ctr_increase_counter(archive_crypto_ctx *ctx)
+{
+ uint8_t *const nonce = ctx->nonce;
+ int j;
+
+ for (j = 0; j < 8; j++) {
+ if (++nonce[j])
+ break;
+ }
+}
+
+static int
+aes_ctr_update(archive_crypto_ctx *ctx, const uint8_t * const in,
+ size_t in_len, uint8_t * const out, size_t *out_len)
+{
+ uint8_t *const ebuf = ctx->encr_buf;
+ unsigned pos = ctx->encr_pos;
+ unsigned max = (unsigned)((in_len < *out_len)? in_len: *out_len);
+ unsigned i;
+
+ for (i = 0; i < max; ) {
+ if (pos == AES_BLOCK_SIZE) {
+ aes_ctr_increase_counter(ctx);
+ if (aes_ctr_encrypt_counter(ctx) != 0)
+ return -1;
+ while (max -i >= AES_BLOCK_SIZE) {
+ for (pos = 0; pos < AES_BLOCK_SIZE; pos++)
+ out[i+pos] = in[i+pos] ^ ebuf[pos];
+ i += AES_BLOCK_SIZE;
+ aes_ctr_increase_counter(ctx);
+ if (aes_ctr_encrypt_counter(ctx) != 0)
+ return -1;
+ }
+ pos = 0;
+ if (i >= max)
+ break;
+ }
+ out[i] = in[i] ^ ebuf[pos++];
+ i++;
+ }
+ ctx->encr_pos = pos;
+ *out_len = i;
+
+ return 0;
+}
+#endif /* ARCHIVE_CRYPTOR_STUB */
+
+
+const struct archive_cryptor __archive_cryptor =
+{
+ &pbkdf2_sha1,
+ &aes_ctr_init,
+ &aes_ctr_update,
+ &aes_ctr_release,
+ &aes_ctr_init,
+ &aes_ctr_update,
+ &aes_ctr_release,
+};
diff --git a/contrib/libs/libarchive/libarchive/archive_cryptor_private.h b/contrib/libs/libarchive/libarchive/archive_cryptor_private.h
new file mode 100644
index 00000000000..82591e61283
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_cryptor_private.h
@@ -0,0 +1,188 @@
+/*-
+* Copyright (c) 2014 Michihiro NAKAJIMA
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef ARCHIVE_CRYPTOR_PRIVATE_H_INCLUDED
+#define ARCHIVE_CRYPTOR_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+/*
+ * On systems that do not support any recognized crypto libraries,
+ * the archive_cryptor.c file will normally define no usable symbols.
+ *
+ * But some compilers and linkers choke on empty object files, so
+ * define a public symbol that will always exist. This could
+ * be removed someday if this file gains another always-present
+ * symbol definition.
+ */
+int __libarchive_cryptor_build_hack(void);
+
+#ifdef __APPLE__
+# include <AvailabilityMacros.h>
+# if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
+# define ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto
+# endif
+#endif
+
+#ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto
+#include <CommonCrypto/CommonCryptor.h>
+#include <CommonCrypto/CommonKeyDerivation.h>
+#define AES_BLOCK_SIZE 16
+#define AES_MAX_KEY_SIZE kCCKeySizeAES256
+
+typedef struct {
+ CCCryptorRef ctx;
+ uint8_t key[AES_MAX_KEY_SIZE];
+ unsigned key_len;
+ uint8_t nonce[AES_BLOCK_SIZE];
+ uint8_t encr_buf[AES_BLOCK_SIZE];
+ unsigned encr_pos;
+} archive_crypto_ctx;
+
+#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)
+#include <bcrypt.h>
+
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#endif
+
+#define AES_MAX_KEY_SIZE 32
+#define AES_BLOCK_SIZE 16
+typedef struct {
+ BCRYPT_ALG_HANDLE hAlg;
+ BCRYPT_KEY_HANDLE hKey;
+ PBYTE keyObj;
+ DWORD keyObj_len;
+ uint8_t nonce[AES_BLOCK_SIZE];
+ uint8_t encr_buf[AES_BLOCK_SIZE];
+ unsigned encr_pos;
+} archive_crypto_ctx;
+
+#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_AES_H)
+#error #include <mbedtls/aes.h>
+#error #include <mbedtls/md.h>
+#error #include <mbedtls/pkcs5.h>
+
+#define AES_MAX_KEY_SIZE 32
+#define AES_BLOCK_SIZE 16
+
+typedef struct {
+ mbedtls_aes_context ctx;
+ uint8_t key[AES_MAX_KEY_SIZE];
+ unsigned key_len;
+ uint8_t nonce[AES_BLOCK_SIZE];
+ uint8_t encr_buf[AES_BLOCK_SIZE];
+ unsigned encr_pos;
+} archive_crypto_ctx;
+
+#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_AES_H)
+#if defined(HAVE_NETTLE_PBKDF2_H)
+#error #include <nettle/pbkdf2.h>
+#endif
+#error #include <nettle/aes.h>
+#error #include <nettle/version.h>
+
+typedef struct {
+#if NETTLE_VERSION_MAJOR < 3
+ struct aes_ctx ctx;
+#else
+ union {
+ struct aes128_ctx c128;
+ struct aes192_ctx c192;
+ struct aes256_ctx c256;
+ } ctx;
+#endif
+ uint8_t key[AES_MAX_KEY_SIZE];
+ unsigned key_len;
+ uint8_t nonce[AES_BLOCK_SIZE];
+ uint8_t encr_buf[AES_BLOCK_SIZE];
+ unsigned encr_pos;
+} archive_crypto_ctx;
+
+#elif defined(HAVE_LIBCRYPTO)
+#include "archive_openssl_evp_private.h"
+#define AES_BLOCK_SIZE 16
+#define AES_MAX_KEY_SIZE 32
+
+typedef struct {
+ EVP_CIPHER_CTX *ctx;
+ const EVP_CIPHER *type;
+ uint8_t key[AES_MAX_KEY_SIZE];
+ unsigned key_len;
+ uint8_t nonce[AES_BLOCK_SIZE];
+ uint8_t encr_buf[AES_BLOCK_SIZE];
+ unsigned encr_pos;
+} archive_crypto_ctx;
+
+#else
+
+#define AES_BLOCK_SIZE 16
+#define AES_MAX_KEY_SIZE 32
+typedef int archive_crypto_ctx;
+
+#endif
+
+/* defines */
+#define archive_pbkdf2_sha1(pw, pw_len, salt, salt_len, rounds, dk, dk_len)\
+ __archive_cryptor.pbkdf2sha1(pw, pw_len, salt, salt_len, rounds, dk, dk_len)
+
+#define archive_decrypto_aes_ctr_init(ctx, key, key_len) \
+ __archive_cryptor.decrypto_aes_ctr_init(ctx, key, key_len)
+#define archive_decrypto_aes_ctr_update(ctx, in, in_len, out, out_len) \
+ __archive_cryptor.decrypto_aes_ctr_update(ctx, in, in_len, out, out_len)
+#define archive_decrypto_aes_ctr_release(ctx) \
+ __archive_cryptor.decrypto_aes_ctr_release(ctx)
+
+#define archive_encrypto_aes_ctr_init(ctx, key, key_len) \
+ __archive_cryptor.encrypto_aes_ctr_init(ctx, key, key_len)
+#define archive_encrypto_aes_ctr_update(ctx, in, in_len, out, out_len) \
+ __archive_cryptor.encrypto_aes_ctr_update(ctx, in, in_len, out, out_len)
+#define archive_encrypto_aes_ctr_release(ctx) \
+ __archive_cryptor.encrypto_aes_ctr_release(ctx)
+
+/* Minimal interface to cryptographic functionality for internal use in
+ * libarchive */
+struct archive_cryptor
+{
+ /* PKCS5 PBKDF2 HMAC-SHA1 */
+ int (*pbkdf2sha1)(const char *pw, size_t pw_len, const uint8_t *salt,
+ size_t salt_len, unsigned rounds, uint8_t *derived_key,
+ size_t derived_key_len);
+ /* AES CTR mode(little endian version) */
+ int (*decrypto_aes_ctr_init)(archive_crypto_ctx *, const uint8_t *, size_t);
+ int (*decrypto_aes_ctr_update)(archive_crypto_ctx *, const uint8_t *,
+ size_t, uint8_t *, size_t *);
+ int (*decrypto_aes_ctr_release)(archive_crypto_ctx *);
+ int (*encrypto_aes_ctr_init)(archive_crypto_ctx *, const uint8_t *, size_t);
+ int (*encrypto_aes_ctr_update)(archive_crypto_ctx *, const uint8_t *,
+ size_t, uint8_t *, size_t *);
+ int (*encrypto_aes_ctr_release)(archive_crypto_ctx *);
+};
+
+extern const struct archive_cryptor __archive_cryptor;
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_digest.c b/contrib/libs/libarchive/libarchive/archive_digest.c
new file mode 100644
index 00000000000..08a9aeb0232
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_digest.c
@@ -0,0 +1,1565 @@
+/*-
+* Copyright (c) 2003-2007 Tim Kientzle
+* Copyright (c) 2011 Andres Mejia
+* Copyright (c) 2011 Michihiro NAKAJIMA
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "archive_platform.h"
+
+#include "archive.h"
+#include "archive_digest_private.h"
+
+/* In particular, force the configure probe to break if it tries
+ * to test a combination of OpenSSL and libmd. */
+#if defined(ARCHIVE_CRYPTO_OPENSSL) && defined(ARCHIVE_CRYPTO_LIBMD)
+#error Cannot use both OpenSSL and libmd.
+#endif
+
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#endif
+
+/*
+ * Message digest functions for Windows platform.
+ */
+#if defined(ARCHIVE_CRYPTO_MD5_WIN) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_WIN) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_WIN)
+
+/*
+ * Initialize a Message digest.
+ */
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+static int
+win_crypto_init(Digest_CTX *ctx, const WCHAR *algo)
+{
+ NTSTATUS status;
+ ctx->valid = 0;
+
+ status = BCryptOpenAlgorithmProvider(&ctx->hAlg, algo, NULL, 0);
+ if (!BCRYPT_SUCCESS(status))
+ return (ARCHIVE_FAILED);
+ status = BCryptCreateHash(ctx->hAlg, &ctx->hHash, NULL, 0, NULL, 0, 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
+ return (ARCHIVE_FAILED);
+ }
+
+ ctx->valid = 1;
+ return (ARCHIVE_OK);
+}
+#else
+static int
+win_crypto_init(Digest_CTX *ctx, DWORD prov, ALG_ID algId)
+{
+
+ ctx->valid = 0;
+ if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL,
+ prov, CRYPT_VERIFYCONTEXT)) {
+ if (GetLastError() != (DWORD)NTE_BAD_KEYSET)
+ return (ARCHIVE_FAILED);
+ if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL,
+ prov, CRYPT_NEWKEYSET))
+ return (ARCHIVE_FAILED);
+ }
+
+ if (!CryptCreateHash(ctx->cryptProv, algId, 0, 0, &ctx->hash)) {
+ CryptReleaseContext(ctx->cryptProv, 0);
+ return (ARCHIVE_FAILED);
+ }
+
+ ctx->valid = 1;
+ return (ARCHIVE_OK);
+}
+#endif
+
+/*
+ * Update a Message digest.
+ */
+static int
+win_crypto_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len)
+{
+
+ if (!ctx->valid)
+ return (ARCHIVE_FAILED);
+
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ BCryptHashData(ctx->hHash,
+ (PUCHAR)(uintptr_t)buf,
+ len, 0);
+#else
+ CryptHashData(ctx->hash,
+ (unsigned char *)(uintptr_t)buf,
+ (DWORD)len, 0);
+#endif
+ return (ARCHIVE_OK);
+}
+
+static int
+win_crypto_Final(unsigned char *buf, size_t bufsize, Digest_CTX *ctx)
+{
+#if !(defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA)
+ DWORD siglen = (DWORD)bufsize;
+#endif
+
+ if (!ctx->valid)
+ return (ARCHIVE_FAILED);
+
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ BCryptFinishHash(ctx->hHash, buf, (ULONG)bufsize, 0);
+ BCryptDestroyHash(ctx->hHash);
+ BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
+#else
+ CryptGetHashParam(ctx->hash, HP_HASHVAL, buf, &siglen, 0);
+ CryptDestroyHash(ctx->hash);
+ CryptReleaseContext(ctx->cryptProv, 0);
+#endif
+ ctx->valid = 0;
+ return (ARCHIVE_OK);
+}
+
+#endif /* defined(ARCHIVE_CRYPTO_*_WIN) */
+
+
+/* MD5 implementations */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBC)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+ MD5Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ MD5Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+ MD5Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_LIBMD)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+ MD5Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ MD5Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+ MD5Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+ CC_MD5_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ CC_MD5_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+ CC_MD5_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+ mbedtls_md5_init(ctx);
+ if (mbedtls_md5_starts_ret(ctx) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ if (mbedtls_md5_update_ret(ctx, indata, insize) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+ if (mbedtls_md5_finish_ret(ctx, md) == 0) {
+ mbedtls_md5_free(ctx);
+ return (ARCHIVE_OK);
+ } else {
+ mbedtls_md5_free(ctx);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_NETTLE)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+ md5_init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ md5_update(ctx, insize, indata);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+ md5_digest(ctx, MD5_DIGEST_SIZE, md);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_OPENSSL)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+ if ((*ctx = EVP_MD_CTX_new()) == NULL)
+ return (ARCHIVE_FAILED);
+ if (!EVP_DigestInit(*ctx, EVP_md5()))
+ return (ARCHIVE_FAILED);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ EVP_DigestUpdate(*ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+ /* HACK: archive_write_set_format_xar.c is finalizing empty contexts, so
+ * this is meant to cope with that. Real fix is probably to fix
+ * archive_write_set_format_xar.c
+ */
+ if (*ctx) {
+ EVP_DigestFinal(*ctx, md, NULL);
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+ }
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_WIN)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ return (win_crypto_init(ctx, BCRYPT_MD5_ALGORITHM));
+#else
+ return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_MD5));
+#endif
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ return (win_crypto_Update(ctx, indata, insize));
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+ return (win_crypto_Final(md, 16, ctx));
+}
+
+#else
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+ (void)ctx; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ (void)ctx; /* UNUSED */
+ (void)indata; /* UNUSED */
+ (void)insize; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+ (void)ctx; /* UNUSED */
+ (void)md; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* RIPEMD160 implementations */
+#if defined(ARCHIVE_CRYPTO_RMD160_LIBC)
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+ RMD160Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ RMD160Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+ RMD160Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_RMD160_LIBMD)
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+ RIPEMD160_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ RIPEMD160_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+ RIPEMD160_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+ mbedtls_ripemd160_init(ctx);
+ if (mbedtls_ripemd160_starts_ret(ctx) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ if (mbedtls_ripemd160_update_ret(ctx, indata, insize) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+ if (mbedtls_ripemd160_finish_ret(ctx, md) == 0) {
+ mbedtls_ripemd160_free(ctx);
+ return (ARCHIVE_OK);
+ } else {
+ mbedtls_ripemd160_free(ctx);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+#elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE)
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+ ripemd160_init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ ripemd160_update(ctx, insize, indata);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+ ripemd160_digest(ctx, RIPEMD160_DIGEST_SIZE, md);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_RMD160_OPENSSL)
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+ if ((*ctx = EVP_MD_CTX_new()) == NULL)
+ return (ARCHIVE_FAILED);
+ if (!EVP_DigestInit(*ctx, EVP_ripemd160()))
+ return (ARCHIVE_FAILED);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ EVP_DigestUpdate(*ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+ if (*ctx) {
+ EVP_DigestFinal(*ctx, md, NULL);
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+ }
+ return (ARCHIVE_OK);
+}
+
+#else
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+ (void)ctx; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ (void)ctx; /* UNUSED */
+ (void)indata; /* UNUSED */
+ (void)insize; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+ (void)ctx; /* UNUSED */
+ (void)md; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* SHA1 implementations */
+#if defined(ARCHIVE_CRYPTO_SHA1_LIBC)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+ SHA1Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA1Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+ SHA1Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_LIBMD)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+ SHA1_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA1_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+ SHA1_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+ CC_SHA1_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ CC_SHA1_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+ CC_SHA1_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+ mbedtls_sha1_init(ctx);
+ if (mbedtls_sha1_starts_ret(ctx) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ if (mbedtls_sha1_update_ret(ctx, indata, insize) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+ if (mbedtls_sha1_finish_ret(ctx, md) == 0) {
+ mbedtls_sha1_free(ctx);
+ return (ARCHIVE_OK);
+ } else {
+ mbedtls_sha1_free(ctx);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+ sha1_init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ sha1_update(ctx, insize, indata);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+ sha1_digest(ctx, SHA1_DIGEST_SIZE, md);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_OPENSSL)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+ if ((*ctx = EVP_MD_CTX_new()) == NULL)
+ return (ARCHIVE_FAILED);
+ if (!EVP_DigestInit(*ctx, EVP_sha1()))
+ return (ARCHIVE_FAILED);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ EVP_DigestUpdate(*ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+ /* HACK: archive_write_set_format_xar.c is finalizing empty contexts, so
+ * this is meant to cope with that. Real fix is probably to fix
+ * archive_write_set_format_xar.c
+ */
+ if (*ctx) {
+ EVP_DigestFinal(*ctx, md, NULL);
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+ }
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_WIN)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ return (win_crypto_init(ctx, BCRYPT_SHA1_ALGORITHM));
+#else
+ return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_SHA1));
+#endif
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ return (win_crypto_Update(ctx, indata, insize));
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+ return (win_crypto_Final(md, 20, ctx));
+}
+
+#else
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+ (void)ctx; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ (void)ctx; /* UNUSED */
+ (void)indata; /* UNUSED */
+ (void)insize; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+ (void)ctx; /* UNUSED */
+ (void)md; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* SHA256 implementations */
+#if defined(ARCHIVE_CRYPTO_SHA256_LIBC)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ SHA256_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA256_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ SHA256_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC2)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ SHA256Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA256Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ SHA256Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC3)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ SHA256Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA256Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ SHA256Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBMD)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ SHA256_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA256_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ SHA256_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ CC_SHA256_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ CC_SHA256_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ CC_SHA256_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ mbedtls_sha256_init(ctx);
+ if (mbedtls_sha256_starts_ret(ctx, 0) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ if (mbedtls_sha256_update_ret(ctx, indata, insize) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ if (mbedtls_sha256_finish_ret(ctx, md) == 0) {
+ mbedtls_sha256_free(ctx);
+ return (ARCHIVE_OK);
+ } else {
+ mbedtls_sha256_free(ctx);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ sha256_init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ sha256_update(ctx, insize, indata);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ sha256_digest(ctx, SHA256_DIGEST_SIZE, md);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_OPENSSL)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ if ((*ctx = EVP_MD_CTX_new()) == NULL)
+ return (ARCHIVE_FAILED);
+ if (!EVP_DigestInit(*ctx, EVP_sha256()))
+ return (ARCHIVE_FAILED);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ EVP_DigestUpdate(*ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ if (*ctx) {
+ EVP_DigestFinal(*ctx, md, NULL);
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+ }
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_WIN)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ return (win_crypto_init(ctx, BCRYPT_SHA256_ALGORITHM));
+#else
+ return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_256));
+#endif
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ return (win_crypto_Update(ctx, indata, insize));
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ return (win_crypto_Final(md, 32, ctx));
+}
+
+#else
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ (void)ctx; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ (void)ctx; /* UNUSED */
+ (void)indata; /* UNUSED */
+ (void)insize; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ (void)ctx; /* UNUSED */
+ (void)md; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* SHA384 implementations */
+#if defined(ARCHIVE_CRYPTO_SHA384_LIBC)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+ SHA384_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA384_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ SHA384_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC2)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+ SHA384Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA384Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ SHA384Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC3)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+ SHA384Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA384Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ SHA384Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+ CC_SHA384_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ CC_SHA384_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ CC_SHA384_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+ mbedtls_sha512_init(ctx);
+ if (mbedtls_sha512_starts_ret(ctx, 1) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ if (mbedtls_sha512_finish_ret(ctx, md) == 0) {
+ mbedtls_sha512_free(ctx);
+ return (ARCHIVE_OK);
+ } else {
+ mbedtls_sha512_free(ctx);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+ sha384_init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ sha384_update(ctx, insize, indata);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ sha384_digest(ctx, SHA384_DIGEST_SIZE, md);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_OPENSSL)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+ if ((*ctx = EVP_MD_CTX_new()) == NULL)
+ return (ARCHIVE_FAILED);
+ if (!EVP_DigestInit(*ctx, EVP_sha384()))
+ return (ARCHIVE_FAILED);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ EVP_DigestUpdate(*ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ if (*ctx) {
+ EVP_DigestFinal(*ctx, md, NULL);
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+ }
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_WIN)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ return (win_crypto_init(ctx, BCRYPT_SHA384_ALGORITHM));
+#else
+ return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_384));
+#endif
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ return (win_crypto_Update(ctx, indata, insize));
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ return (win_crypto_Final(md, 48, ctx));
+}
+
+#else
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+ (void)ctx; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ (void)ctx; /* UNUSED */
+ (void)indata; /* UNUSED */
+ (void)insize; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ (void)ctx; /* UNUSED */
+ (void)md; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* SHA512 implementations */
+#if defined(ARCHIVE_CRYPTO_SHA512_LIBC)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ SHA512_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA512_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ SHA512_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC2)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ SHA512Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA512Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ SHA512Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC3)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ SHA512Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA512Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ SHA512Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBMD)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ SHA512_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA512_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ SHA512_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ CC_SHA512_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ CC_SHA512_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ CC_SHA512_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ mbedtls_sha512_init(ctx);
+ if (mbedtls_sha512_starts_ret(ctx, 0) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ if (mbedtls_sha512_finish_ret(ctx, md) == 0) {
+ mbedtls_sha512_free(ctx);
+ return (ARCHIVE_OK);
+ } else {
+ mbedtls_sha512_free(ctx);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ sha512_init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ sha512_update(ctx, insize, indata);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ sha512_digest(ctx, SHA512_DIGEST_SIZE, md);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_OPENSSL)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ if ((*ctx = EVP_MD_CTX_new()) == NULL)
+ return (ARCHIVE_FAILED);
+ if (!EVP_DigestInit(*ctx, EVP_sha512()))
+ return (ARCHIVE_FAILED);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ EVP_DigestUpdate(*ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ if (*ctx) {
+ EVP_DigestFinal(*ctx, md, NULL);
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+ }
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_WIN)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ return (win_crypto_init(ctx, BCRYPT_SHA512_ALGORITHM));
+#else
+ return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_512));
+#endif
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ return (win_crypto_Update(ctx, indata, insize));
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ return (win_crypto_Final(md, 64, ctx));
+}
+
+#else
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ (void)ctx; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ (void)ctx; /* UNUSED */
+ (void)indata; /* UNUSED */
+ (void)insize; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ (void)ctx; /* UNUSED */
+ (void)md; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* NOTE: Message Digest functions are set based on availability and by the
+ * following order of preference.
+ * 1. libc
+ * 2. libc2
+ * 3. libc3
+ * 4. libSystem
+ * 5. Nettle
+ * 6. OpenSSL
+ * 7. libmd
+ * 8. Windows API
+ */
+const struct archive_digest __archive_digest =
+{
+/* MD5 */
+ &__archive_md5init,
+ &__archive_md5update,
+ &__archive_md5final,
+
+/* RIPEMD160 */
+ &__archive_ripemd160init,
+ &__archive_ripemd160update,
+ &__archive_ripemd160final,
+
+/* SHA1 */
+ &__archive_sha1init,
+ &__archive_sha1update,
+ &__archive_sha1final,
+
+/* SHA256 */
+ &__archive_sha256init,
+ &__archive_sha256update,
+ &__archive_sha256final,
+
+/* SHA384 */
+ &__archive_sha384init,
+ &__archive_sha384update,
+ &__archive_sha384final,
+
+/* SHA512 */
+ &__archive_sha512init,
+ &__archive_sha512update,
+ &__archive_sha512final
+};
diff --git a/contrib/libs/libarchive/libarchive/archive_digest_private.h b/contrib/libs/libarchive/libarchive/archive_digest_private.h
new file mode 100644
index 00000000000..05367c22f45
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_digest_private.h
@@ -0,0 +1,426 @@
+/*-
+* Copyright (c) 2003-2007 Tim Kientzle
+* Copyright (c) 2011 Andres Mejia
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef ARCHIVE_DIGEST_PRIVATE_H_INCLUDED
+#define ARCHIVE_DIGEST_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+#ifndef __LIBARCHIVE_CONFIG_H_INCLUDED
+#error "Should have include config.h first!"
+#endif
+
+/*
+ * Crypto support in various Operating Systems:
+ *
+ * NetBSD:
+ * - MD5 and SHA1 in libc: without _ after algorithm name
+ * - SHA2 in libc: with _ after algorithm name
+ *
+ * OpenBSD:
+ * - MD5, SHA1 and SHA2 in libc: without _ after algorithm name
+ * - OpenBSD 4.4 and earlier have SHA2 in libc with _ after algorithm name
+ *
+ * DragonFly and FreeBSD:
+ * - MD5 libmd: without _ after algorithm name
+ * - SHA1, SHA256 and SHA512 in libmd: with _ after algorithm name
+ *
+ * Mac OS X (10.4 and later):
+ * - MD5, SHA1 and SHA2 in libSystem: with CC_ prefix and _ after algorithm name
+ *
+ * OpenSSL:
+ * - MD5, SHA1 and SHA2 in libcrypto: with _ after algorithm name
+ *
+ * Windows:
+ * - MD5, SHA1 and SHA2 in archive_crypto.c using Windows crypto API
+ */
+
+/* libc crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBC)
+#error #include <md5.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_RMD160_LIBC)
+#error #include <rmd160.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA1_LIBC)
+#error #include <sha1.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA256_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_LIBC2) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_LIBC3) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_LIBC2) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_LIBC3) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBC2) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBC3)
+#error #include <sha2.h>
+#endif
+
+/* libmd crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBMD) ||\
+ defined(ARCHIVE_CRYPTO_RMD160_LIBMD) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_LIBMD) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_LIBMD) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBMD)
+#define ARCHIVE_CRYPTO_LIBMD 1
+#endif
+
+#if defined(ARCHIVE_CRYPTO_MD5_LIBMD)
+#error #include <md5.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_RMD160_LIBMD)
+#include <ripemd.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA1_LIBMD)
+#error #include <sha.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA256_LIBMD)
+#error #include <sha256.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA512_LIBMD)
+#error #include <sha512.h>
+#endif
+
+/* libSystem crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM)
+#include <CommonCrypto/CommonDigest.h>
+#endif
+
+/* mbed TLS crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)
+#error #include <mbedtls/md5.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)
+#error #include <mbedtls/ripemd160.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
+#error #include <mbedtls/sha1.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
+#error #include <mbedtls/sha256.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
+#error #include <mbedtls/sha512.h>
+#endif
+
+/* Nettle crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_NETTLE)
+#error #include <nettle/md5.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_RMD160_NETTLE)
+#error #include <nettle/ripemd160.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA1_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_NETTLE)
+#error #include <nettle/sha.h>
+#endif
+
+/* OpenSSL crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_RMD160_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_OPENSSL)
+#define ARCHIVE_CRYPTO_OPENSSL 1
+#include "archive_openssl_evp_private.h"
+#endif
+
+/* Windows crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_WIN) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_WIN) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_WIN)
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+/* don't use bcrypt when XP needs to be supported */
+#include <bcrypt.h>
+typedef struct {
+ int valid;
+ BCRYPT_ALG_HANDLE hAlg;
+ BCRYPT_HASH_HANDLE hHash;
+} Digest_CTX;
+#else
+#include <windows.h>
+#include <wincrypt.h>
+typedef struct {
+ int valid;
+ HCRYPTPROV cryptProv;
+ HCRYPTHASH hash;
+} Digest_CTX;
+#endif
+#endif
+
+/* typedefs */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBC)
+typedef MD5_CTX archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_LIBMD)
+typedef MD5_CTX archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM)
+typedef CC_MD5_CTX archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)
+typedef mbedtls_md5_context archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_NETTLE)
+typedef struct md5_ctx archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_OPENSSL)
+typedef EVP_MD_CTX *archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_WIN)
+typedef Digest_CTX archive_md5_ctx;
+#else
+typedef unsigned char archive_md5_ctx;
+#endif
+
+#if defined(ARCHIVE_CRYPTO_RMD160_LIBC)
+typedef RMD160_CTX archive_rmd160_ctx;
+#elif defined(ARCHIVE_CRYPTO_RMD160_LIBMD)
+typedef RIPEMD160_CTX archive_rmd160_ctx;
+#elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)
+typedef mbedtls_ripemd160_context archive_rmd160_ctx;
+#elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE)
+typedef struct ripemd160_ctx archive_rmd160_ctx;
+#elif defined(ARCHIVE_CRYPTO_RMD160_OPENSSL)
+typedef EVP_MD_CTX *archive_rmd160_ctx;
+#else
+typedef unsigned char archive_rmd160_ctx;
+#endif
+
+#if defined(ARCHIVE_CRYPTO_SHA1_LIBC)
+typedef SHA1_CTX archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_LIBMD)
+typedef SHA1_CTX archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM)
+typedef CC_SHA1_CTX archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
+typedef mbedtls_sha1_context archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE)
+typedef struct sha1_ctx archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_OPENSSL)
+typedef EVP_MD_CTX *archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_WIN)
+typedef Digest_CTX archive_sha1_ctx;
+#else
+typedef unsigned char archive_sha1_ctx;
+#endif
+
+#if defined(ARCHIVE_CRYPTO_SHA256_LIBC)
+typedef SHA256_CTX archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC2)
+typedef SHA256_CTX archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC3)
+typedef SHA2_CTX archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBMD)
+typedef SHA256_CTX archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM)
+typedef CC_SHA256_CTX archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
+typedef mbedtls_sha256_context archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE)
+typedef struct sha256_ctx archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_OPENSSL)
+typedef EVP_MD_CTX *archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_WIN)
+typedef Digest_CTX archive_sha256_ctx;
+#else
+typedef unsigned char archive_sha256_ctx;
+#endif
+
+#if defined(ARCHIVE_CRYPTO_SHA384_LIBC)
+typedef SHA384_CTX archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC2)
+typedef SHA384_CTX archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC3)
+typedef SHA2_CTX archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM)
+typedef CC_SHA512_CTX archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS)
+typedef mbedtls_sha512_context archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE)
+typedef struct sha384_ctx archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_OPENSSL)
+typedef EVP_MD_CTX *archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_WIN)
+typedef Digest_CTX archive_sha384_ctx;
+#else
+typedef unsigned char archive_sha384_ctx;
+#endif
+
+#if defined(ARCHIVE_CRYPTO_SHA512_LIBC)
+typedef SHA512_CTX archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC2)
+typedef SHA512_CTX archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC3)
+typedef SHA2_CTX archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBMD)
+typedef SHA512_CTX archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM)
+typedef CC_SHA512_CTX archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
+typedef mbedtls_sha512_context archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE)
+typedef struct sha512_ctx archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_OPENSSL)
+typedef EVP_MD_CTX *archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_WIN)
+typedef Digest_CTX archive_sha512_ctx;
+#else
+typedef unsigned char archive_sha512_ctx;
+#endif
+
+/* defines */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_MD5_LIBMD) || \
+ defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_MD5_MBEDTLS) ||\
+ defined(ARCHIVE_CRYPTO_MD5_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_MD5_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_MD5_WIN)
+#define ARCHIVE_HAS_MD5
+#endif
+#define archive_md5_init(ctx)\
+ __archive_digest.md5init(ctx)
+#define archive_md5_final(ctx, md)\
+ __archive_digest.md5final(ctx, md)
+#define archive_md5_update(ctx, buf, n)\
+ __archive_digest.md5update(ctx, buf, n)
+
+#if defined(ARCHIVE_CRYPTO_RMD160_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS) ||\
+ defined(ARCHIVE_CRYPTO_RMD160_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_RMD160_OPENSSL)
+#define ARCHIVE_HAS_RMD160
+#endif
+#define archive_rmd160_init(ctx)\
+ __archive_digest.rmd160init(ctx)
+#define archive_rmd160_final(ctx, md)\
+ __archive_digest.rmd160final(ctx, md)
+#define archive_rmd160_update(ctx, buf, n)\
+ __archive_digest.rmd160update(ctx, buf, n)
+
+#if defined(ARCHIVE_CRYPTO_SHA1_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_LIBMD) || \
+ defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_WIN)
+#define ARCHIVE_HAS_SHA1
+#endif
+#define archive_sha1_init(ctx)\
+ __archive_digest.sha1init(ctx)
+#define archive_sha1_final(ctx, md)\
+ __archive_digest.sha1final(ctx, md)
+#define archive_sha1_update(ctx, buf, n)\
+ __archive_digest.sha1update(ctx, buf, n)
+
+#if defined(ARCHIVE_CRYPTO_SHA256_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_LIBC2) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_LIBC3) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_LIBMD) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_WIN)
+#define ARCHIVE_HAS_SHA256
+#endif
+#define archive_sha256_init(ctx)\
+ __archive_digest.sha256init(ctx)
+#define archive_sha256_final(ctx, md)\
+ __archive_digest.sha256final(ctx, md)
+#define archive_sha256_update(ctx, buf, n)\
+ __archive_digest.sha256update(ctx, buf, n)
+
+#if defined(ARCHIVE_CRYPTO_SHA384_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_LIBC2) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_LIBC3) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_WIN)
+#define ARCHIVE_HAS_SHA384
+#endif
+#define archive_sha384_init(ctx)\
+ __archive_digest.sha384init(ctx)
+#define archive_sha384_final(ctx, md)\
+ __archive_digest.sha384final(ctx, md)
+#define archive_sha384_update(ctx, buf, n)\
+ __archive_digest.sha384update(ctx, buf, n)
+
+#if defined(ARCHIVE_CRYPTO_SHA512_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBC2) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBC3) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBMD) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_WIN)
+#define ARCHIVE_HAS_SHA512
+#endif
+#define archive_sha512_init(ctx)\
+ __archive_digest.sha512init(ctx)
+#define archive_sha512_final(ctx, md)\
+ __archive_digest.sha512final(ctx, md)
+#define archive_sha512_update(ctx, buf, n)\
+ __archive_digest.sha512update(ctx, buf, n)
+
+/* Minimal interface to digest functionality for internal use in libarchive */
+struct archive_digest
+{
+ /* Message Digest */
+ int (*md5init)(archive_md5_ctx *ctx);
+ int (*md5update)(archive_md5_ctx *, const void *, size_t);
+ int (*md5final)(archive_md5_ctx *, void *);
+ int (*rmd160init)(archive_rmd160_ctx *);
+ int (*rmd160update)(archive_rmd160_ctx *, const void *, size_t);
+ int (*rmd160final)(archive_rmd160_ctx *, void *);
+ int (*sha1init)(archive_sha1_ctx *);
+ int (*sha1update)(archive_sha1_ctx *, const void *, size_t);
+ int (*sha1final)(archive_sha1_ctx *, void *);
+ int (*sha256init)(archive_sha256_ctx *);
+ int (*sha256update)(archive_sha256_ctx *, const void *, size_t);
+ int (*sha256final)(archive_sha256_ctx *, void *);
+ int (*sha384init)(archive_sha384_ctx *);
+ int (*sha384update)(archive_sha384_ctx *, const void *, size_t);
+ int (*sha384final)(archive_sha384_ctx *, void *);
+ int (*sha512init)(archive_sha512_ctx *);
+ int (*sha512update)(archive_sha512_ctx *, const void *, size_t);
+ int (*sha512final)(archive_sha512_ctx *, void *);
+};
+
+extern const struct archive_digest __archive_digest;
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_endian.h b/contrib/libs/libarchive/libarchive/archive_endian.h
new file mode 100644
index 00000000000..e6d3f2ce5e7
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_endian.h
@@ -0,0 +1,195 @@
+/*-
+ * Copyright (c) 2002 Thomas Moestl <tmm@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_endian.h 201085 2009-12-28 02:17:15Z kientzle $
+ *
+ * Borrowed from FreeBSD's <sys/endian.h>
+ */
+
+#ifndef ARCHIVE_ENDIAN_H_INCLUDED
+#define ARCHIVE_ENDIAN_H_INCLUDED
+
+/* Note: This is a purely internal header! */
+/* Do not use this outside of libarchive internal code! */
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+/*
+ * Disabling inline keyword for compilers known to choke on it:
+ * - Watcom C++ in C code. (For any version?)
+ * - SGI MIPSpro
+ * - Microsoft Visual C++ 6.0 (supposedly newer versions too)
+ * - IBM VisualAge 6 (XL v6)
+ * - Sun WorkShop C (SunPro) before 5.9
+ */
+#if defined(__WATCOMC__) || defined(__sgi) || defined(__hpux) || defined(__BORLANDC__)
+#define inline
+#elif defined(__IBMC__) && __IBMC__ < 700
+#define inline
+#elif defined(__SUNPRO_C) && __SUNPRO_C < 0x590
+#define inline
+#elif defined(_MSC_VER) || defined(__osf__)
+#define inline __inline
+#endif
+
+/* Alignment-agnostic encode/decode bytestream to/from little/big endian. */
+
+static inline uint16_t
+archive_be16dec(const void *pp)
+{
+ unsigned char const *p = (unsigned char const *)pp;
+
+ /* Store into unsigned temporaries before left shifting, to avoid
+ promotion to signed int and then left shifting into the sign bit,
+ which is undefined behaviour. */
+ unsigned int p1 = p[1];
+ unsigned int p0 = p[0];
+
+ return ((p0 << 8) | p1);
+}
+
+static inline uint32_t
+archive_be32dec(const void *pp)
+{
+ unsigned char const *p = (unsigned char const *)pp;
+
+ /* Store into unsigned temporaries before left shifting, to avoid
+ promotion to signed int and then left shifting into the sign bit,
+ which is undefined behaviour. */
+ unsigned int p3 = p[3];
+ unsigned int p2 = p[2];
+ unsigned int p1 = p[1];
+ unsigned int p0 = p[0];
+
+ return ((p0 << 24) | (p1 << 16) | (p2 << 8) | p3);
+}
+
+static inline uint64_t
+archive_be64dec(const void *pp)
+{
+ unsigned char const *p = (unsigned char const *)pp;
+
+ return (((uint64_t)archive_be32dec(p) << 32) | archive_be32dec(p + 4));
+}
+
+static inline uint16_t
+archive_le16dec(const void *pp)
+{
+ unsigned char const *p = (unsigned char const *)pp;
+
+ /* Store into unsigned temporaries before left shifting, to avoid
+ promotion to signed int and then left shifting into the sign bit,
+ which is undefined behaviour. */
+ unsigned int p1 = p[1];
+ unsigned int p0 = p[0];
+
+ return ((p1 << 8) | p0);
+}
+
+static inline uint32_t
+archive_le32dec(const void *pp)
+{
+ unsigned char const *p = (unsigned char const *)pp;
+
+ /* Store into unsigned temporaries before left shifting, to avoid
+ promotion to signed int and then left shifting into the sign bit,
+ which is undefined behaviour. */
+ unsigned int p3 = p[3];
+ unsigned int p2 = p[2];
+ unsigned int p1 = p[1];
+ unsigned int p0 = p[0];
+
+ return ((p3 << 24) | (p2 << 16) | (p1 << 8) | p0);
+}
+
+static inline uint64_t
+archive_le64dec(const void *pp)
+{
+ unsigned char const *p = (unsigned char const *)pp;
+
+ return (((uint64_t)archive_le32dec(p + 4) << 32) | archive_le32dec(p));
+}
+
+static inline void
+archive_be16enc(void *pp, uint16_t u)
+{
+ unsigned char *p = (unsigned char *)pp;
+
+ p[0] = (u >> 8) & 0xff;
+ p[1] = u & 0xff;
+}
+
+static inline void
+archive_be32enc(void *pp, uint32_t u)
+{
+ unsigned char *p = (unsigned char *)pp;
+
+ p[0] = (u >> 24) & 0xff;
+ p[1] = (u >> 16) & 0xff;
+ p[2] = (u >> 8) & 0xff;
+ p[3] = u & 0xff;
+}
+
+static inline void
+archive_be64enc(void *pp, uint64_t u)
+{
+ unsigned char *p = (unsigned char *)pp;
+
+ archive_be32enc(p, (uint32_t)(u >> 32));
+ archive_be32enc(p + 4, (uint32_t)(u & 0xffffffff));
+}
+
+static inline void
+archive_le16enc(void *pp, uint16_t u)
+{
+ unsigned char *p = (unsigned char *)pp;
+
+ p[0] = u & 0xff;
+ p[1] = (u >> 8) & 0xff;
+}
+
+static inline void
+archive_le32enc(void *pp, uint32_t u)
+{
+ unsigned char *p = (unsigned char *)pp;
+
+ p[0] = u & 0xff;
+ p[1] = (u >> 8) & 0xff;
+ p[2] = (u >> 16) & 0xff;
+ p[3] = (u >> 24) & 0xff;
+}
+
+static inline void
+archive_le64enc(void *pp, uint64_t u)
+{
+ unsigned char *p = (unsigned char *)pp;
+
+ archive_le32enc(p, (uint32_t)(u & 0xffffffff));
+ archive_le32enc(p + 4, (uint32_t)(u >> 32));
+}
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_entry.c b/contrib/libs/libarchive/libarchive/archive_entry.c
new file mode 100644
index 00000000000..a938fd7176c
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry.c
@@ -0,0 +1,2149 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry.c 201096 2009-12-28 02:41:27Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if MAJOR_IN_MKDEV
+#include <sys/mkdev.h>
+#define HAVE_MAJOR
+#elif MAJOR_IN_SYSMACROS
+#include <sys/sysmacros.h>
+#define HAVE_MAJOR
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h> /* for Linux file flags */
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#error #include <linux/ext2_fs.h> /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#error #include <ext2fs/ext2_fs.h> /* for Linux file flags */
+#endif
+#include <stddef.h>
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+
+#include "archive.h"
+#include "archive_acl_private.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_entry_private.h"
+
+#if !defined(HAVE_MAJOR) && !defined(major)
+/* Replacement for major/minor/makedev. */
+#define major(x) ((int)(0x00ff & ((x) >> 8)))
+#define minor(x) ((int)(0xffff00ff & (x)))
+#define makedev(maj,min) ((0xff00 & ((maj)<<8)) | (0xffff00ff & (min)))
+#endif
+
+/* Play games to come up with a suitable makedev() definition. */
+#ifdef __QNXNTO__
+/* QNX. <sigh> */
+#error #include <sys/netmgr.h>
+#define ae_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min))
+#elif defined makedev
+/* There's a "makedev" macro. */
+#define ae_makedev(maj, min) makedev((maj), (min))
+#elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__))
+/* Windows. <sigh> */
+#define ae_makedev(maj, min) mkdev((maj), (min))
+#else
+/* There's a "makedev" function. */
+#define ae_makedev(maj, min) makedev((maj), (min))
+#endif
+
+/*
+ * This adjustment is needed to support the following idiom for adding
+ * 1000ns to the stored time:
+ * archive_entry_set_atime(archive_entry_atime(),
+ * archive_entry_atime_nsec() + 1000)
+ * The additional if() here compensates for ambiguity in the C standard,
+ * which permits two possible interpretations of a % b when a is negative.
+ */
+#define FIX_NS(t,ns) \
+ do { \
+ t += ns / 1000000000; \
+ ns %= 1000000000; \
+ if (ns < 0) { --t; ns += 1000000000; } \
+ } while (0)
+
+static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear);
+static const wchar_t *ae_wcstofflags(const wchar_t *stringp,
+ unsigned long *setp, unsigned long *clrp);
+static const char *ae_strtofflags(const char *stringp,
+ unsigned long *setp, unsigned long *clrp);
+
+#ifndef HAVE_WCSCPY
+static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2)
+{
+ wchar_t *dest = s1;
+ while ((*s1 = *s2) != L'\0')
+ ++s1, ++s2;
+ return dest;
+}
+#endif
+#ifndef HAVE_WCSLEN
+static size_t wcslen(const wchar_t *s)
+{
+ const wchar_t *p = s;
+ while (*p != L'\0')
+ ++p;
+ return p - s;
+}
+#endif
+#ifndef HAVE_WMEMCMP
+/* Good enough for simple equality testing, but not for sorting. */
+#define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t))
+#endif
+
+/****************************************************************************
+ *
+ * Public Interface
+ *
+ ****************************************************************************/
+
+struct archive_entry *
+archive_entry_clear(struct archive_entry *entry)
+{
+ if (entry == NULL)
+ return (NULL);
+ archive_mstring_clean(&entry->ae_fflags_text);
+ archive_mstring_clean(&entry->ae_gname);
+ archive_mstring_clean(&entry->ae_hardlink);
+ archive_mstring_clean(&entry->ae_pathname);
+ archive_mstring_clean(&entry->ae_sourcepath);
+ archive_mstring_clean(&entry->ae_symlink);
+ archive_mstring_clean(&entry->ae_uname);
+ archive_entry_copy_mac_metadata(entry, NULL, 0);
+ archive_acl_clear(&entry->acl);
+ archive_entry_xattr_clear(entry);
+ archive_entry_sparse_clear(entry);
+ free(entry->stat);
+ entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED;
+ memset(entry, 0, sizeof(*entry));
+ return entry;
+}
+
+struct archive_entry *
+archive_entry_clone(struct archive_entry *entry)
+{
+ struct archive_entry *entry2;
+ struct ae_xattr *xp;
+ struct ae_sparse *sp;
+ size_t s;
+ const void *p;
+
+ /* Allocate new structure and copy over all of the fields. */
+ /* TODO: Should we copy the archive over? Or require a new archive
+ * as an argument? */
+ entry2 = archive_entry_new2(entry->archive);
+ if (entry2 == NULL)
+ return (NULL);
+ entry2->ae_stat = entry->ae_stat;
+ entry2->ae_fflags_set = entry->ae_fflags_set;
+ entry2->ae_fflags_clear = entry->ae_fflags_clear;
+
+ /* TODO: XXX If clone can have a different archive, what do we do here if
+ * character sets are different? XXX */
+ archive_mstring_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text);
+ archive_mstring_copy(&entry2->ae_gname, &entry->ae_gname);
+ archive_mstring_copy(&entry2->ae_hardlink, &entry->ae_hardlink);
+ archive_mstring_copy(&entry2->ae_pathname, &entry->ae_pathname);
+ archive_mstring_copy(&entry2->ae_sourcepath, &entry->ae_sourcepath);
+ archive_mstring_copy(&entry2->ae_symlink, &entry->ae_symlink);
+ entry2->ae_set = entry->ae_set;
+ archive_mstring_copy(&entry2->ae_uname, &entry->ae_uname);
+
+ /* Copy symlink type */
+ entry2->ae_symlink_type = entry->ae_symlink_type;
+
+ /* Copy encryption status */
+ entry2->encryption = entry->encryption;
+
+ /* Copy digests */
+#define copy_digest(_e2, _e, _t) \
+ memcpy(_e2->digest._t, _e->digest._t, sizeof(_e2->digest._t))
+
+ copy_digest(entry2, entry, md5);
+ copy_digest(entry2, entry, rmd160);
+ copy_digest(entry2, entry, sha1);
+ copy_digest(entry2, entry, sha256);
+ copy_digest(entry2, entry, sha384);
+ copy_digest(entry2, entry, sha512);
+
+#undef copy_digest
+
+ /* Copy ACL data over. */
+ archive_acl_copy(&entry2->acl, &entry->acl);
+
+ /* Copy Mac OS metadata. */
+ p = archive_entry_mac_metadata(entry, &s);
+ archive_entry_copy_mac_metadata(entry2, p, s);
+
+ /* Copy xattr data over. */
+ xp = entry->xattr_head;
+ while (xp != NULL) {
+ archive_entry_xattr_add_entry(entry2,
+ xp->name, xp->value, xp->size);
+ xp = xp->next;
+ }
+
+ /* Copy sparse data over. */
+ sp = entry->sparse_head;
+ while (sp != NULL) {
+ archive_entry_sparse_add_entry(entry2,
+ sp->offset, sp->length);
+ sp = sp->next;
+ }
+
+ return (entry2);
+}
+
+void
+archive_entry_free(struct archive_entry *entry)
+{
+ archive_entry_clear(entry);
+ free(entry);
+}
+
+struct archive_entry *
+archive_entry_new(void)
+{
+ return archive_entry_new2(NULL);
+}
+
+struct archive_entry *
+archive_entry_new2(struct archive *a)
+{
+ struct archive_entry *entry;
+
+ entry = (struct archive_entry *)calloc(1, sizeof(*entry));
+ if (entry == NULL)
+ return (NULL);
+ entry->archive = a;
+ entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED;
+ return (entry);
+}
+
+/*
+ * Functions for reading fields from an archive_entry.
+ */
+
+time_t
+archive_entry_atime(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_atime);
+}
+
+long
+archive_entry_atime_nsec(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_atime_nsec);
+}
+
+int
+archive_entry_atime_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_ATIME);
+}
+
+time_t
+archive_entry_birthtime(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_birthtime);
+}
+
+long
+archive_entry_birthtime_nsec(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_birthtime_nsec);
+}
+
+int
+archive_entry_birthtime_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_BIRTHTIME);
+}
+
+time_t
+archive_entry_ctime(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_ctime);
+}
+
+int
+archive_entry_ctime_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_CTIME);
+}
+
+long
+archive_entry_ctime_nsec(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_ctime_nsec);
+}
+
+dev_t
+archive_entry_dev(struct archive_entry *entry)
+{
+ if (entry->ae_stat.aest_dev_is_broken_down)
+ return ae_makedev(entry->ae_stat.aest_devmajor,
+ entry->ae_stat.aest_devminor);
+ else
+ return (entry->ae_stat.aest_dev);
+}
+
+int
+archive_entry_dev_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_DEV);
+}
+
+dev_t
+archive_entry_devmajor(struct archive_entry *entry)
+{
+ if (entry->ae_stat.aest_dev_is_broken_down)
+ return (entry->ae_stat.aest_devmajor);
+ else
+ return major(entry->ae_stat.aest_dev);
+}
+
+dev_t
+archive_entry_devminor(struct archive_entry *entry)
+{
+ if (entry->ae_stat.aest_dev_is_broken_down)
+ return (entry->ae_stat.aest_devminor);
+ else
+ return minor(entry->ae_stat.aest_dev);
+}
+
+__LA_MODE_T
+archive_entry_filetype(struct archive_entry *entry)
+{
+ return (AE_IFMT & entry->acl.mode);
+}
+
+void
+archive_entry_fflags(struct archive_entry *entry,
+ unsigned long *set, unsigned long *clear)
+{
+ *set = entry->ae_fflags_set;
+ *clear = entry->ae_fflags_clear;
+}
+
+/*
+ * Note: if text was provided, this just returns that text. If you
+ * really need the text to be rebuilt in a canonical form, set the
+ * text, ask for the bitmaps, then set the bitmaps. (Setting the
+ * bitmaps clears any stored text.) This design is deliberate: if
+ * we're editing archives, we don't want to discard flags just because
+ * they aren't supported on the current system. The bitmap<->text
+ * conversions are platform-specific (see below).
+ */
+const char *
+archive_entry_fflags_text(struct archive_entry *entry)
+{
+ const char *f;
+ char *p;
+
+ if (archive_mstring_get_mbs(entry->archive,
+ &entry->ae_fflags_text, &f) == 0) {
+ if (f != NULL)
+ return (f);
+ } else if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+
+ if (entry->ae_fflags_set == 0 && entry->ae_fflags_clear == 0)
+ return (NULL);
+
+ p = ae_fflagstostr(entry->ae_fflags_set, entry->ae_fflags_clear);
+ if (p == NULL)
+ return (NULL);
+
+ archive_mstring_copy_mbs(&entry->ae_fflags_text, p);
+ free(p);
+ if (archive_mstring_get_mbs(entry->archive,
+ &entry->ae_fflags_text, &f) == 0)
+ return (f);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+la_int64_t
+archive_entry_gid(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_gid);
+}
+
+const char *
+archive_entry_gname(struct archive_entry *entry)
+{
+ const char *p;
+ if (archive_mstring_get_mbs(entry->archive, &entry->ae_gname, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const char *
+archive_entry_gname_utf8(struct archive_entry *entry)
+{
+ const char *p;
+ if (archive_mstring_get_utf8(entry->archive, &entry->ae_gname, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+
+const wchar_t *
+archive_entry_gname_w(struct archive_entry *entry)
+{
+ const wchar_t *p;
+ if (archive_mstring_get_wcs(entry->archive, &entry->ae_gname, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+int
+_archive_entry_gname_l(struct archive_entry *entry,
+ const char **p, size_t *len, struct archive_string_conv *sc)
+{
+ return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_gname, p, len, sc));
+}
+
+const char *
+archive_entry_hardlink(struct archive_entry *entry)
+{
+ const char *p;
+ if ((entry->ae_set & AE_SET_HARDLINK) == 0)
+ return (NULL);
+ if (archive_mstring_get_mbs(
+ entry->archive, &entry->ae_hardlink, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const char *
+archive_entry_hardlink_utf8(struct archive_entry *entry)
+{
+ const char *p;
+ if ((entry->ae_set & AE_SET_HARDLINK) == 0)
+ return (NULL);
+ if (archive_mstring_get_utf8(
+ entry->archive, &entry->ae_hardlink, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const wchar_t *
+archive_entry_hardlink_w(struct archive_entry *entry)
+{
+ const wchar_t *p;
+ if ((entry->ae_set & AE_SET_HARDLINK) == 0)
+ return (NULL);
+ if (archive_mstring_get_wcs(
+ entry->archive, &entry->ae_hardlink, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+int
+_archive_entry_hardlink_l(struct archive_entry *entry,
+ const char **p, size_t *len, struct archive_string_conv *sc)
+{
+ if ((entry->ae_set & AE_SET_HARDLINK) == 0) {
+ *p = NULL;
+ *len = 0;
+ return (0);
+ }
+ return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_hardlink, p, len, sc));
+}
+
+la_int64_t
+archive_entry_ino(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_ino);
+}
+
+int
+archive_entry_ino_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_INO);
+}
+
+la_int64_t
+archive_entry_ino64(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_ino);
+}
+
+__LA_MODE_T
+archive_entry_mode(struct archive_entry *entry)
+{
+ return (entry->acl.mode);
+}
+
+time_t
+archive_entry_mtime(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_mtime);
+}
+
+long
+archive_entry_mtime_nsec(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_mtime_nsec);
+}
+
+int
+archive_entry_mtime_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_MTIME);
+}
+
+unsigned int
+archive_entry_nlink(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_nlink);
+}
+
+/* Instead, our caller could have chosen a specific encoding
+ * (archive_mstring_get_mbs, archive_mstring_get_utf8,
+ * archive_mstring_get_wcs). So we should try multiple
+ * encodings. Try mbs first because of history, even though
+ * utf8 might be better for pathname portability.
+ * Also omit wcs because of type mismatch (char * versus wchar *)
+ */
+const char *
+archive_entry_pathname(struct archive_entry *entry)
+{
+ const char *p;
+ if (archive_mstring_get_mbs(
+ entry->archive, &entry->ae_pathname, &p) == 0)
+ return (p);
+#if HAVE_EILSEQ /*{*/
+ if (errno == EILSEQ) {
+ if (archive_mstring_get_utf8(
+ entry->archive, &entry->ae_pathname, &p) == 0)
+ return (p);
+ }
+#endif /*}*/
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const char *
+archive_entry_pathname_utf8(struct archive_entry *entry)
+{
+ const char *p;
+ if (archive_mstring_get_utf8(
+ entry->archive, &entry->ae_pathname, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const wchar_t *
+archive_entry_pathname_w(struct archive_entry *entry)
+{
+ const wchar_t *p;
+ if (archive_mstring_get_wcs(
+ entry->archive, &entry->ae_pathname, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+int
+_archive_entry_pathname_l(struct archive_entry *entry,
+ const char **p, size_t *len, struct archive_string_conv *sc)
+{
+ return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_pathname, p, len, sc));
+}
+
+__LA_MODE_T
+archive_entry_perm(struct archive_entry *entry)
+{
+ return (~AE_IFMT & entry->acl.mode);
+}
+
+dev_t
+archive_entry_rdev(struct archive_entry *entry)
+{
+ if (entry->ae_stat.aest_rdev_is_broken_down)
+ return ae_makedev(entry->ae_stat.aest_rdevmajor,
+ entry->ae_stat.aest_rdevminor);
+ else
+ return (entry->ae_stat.aest_rdev);
+}
+
+dev_t
+archive_entry_rdevmajor(struct archive_entry *entry)
+{
+ if (entry->ae_stat.aest_rdev_is_broken_down)
+ return (entry->ae_stat.aest_rdevmajor);
+ else
+ return major(entry->ae_stat.aest_rdev);
+}
+
+dev_t
+archive_entry_rdevminor(struct archive_entry *entry)
+{
+ if (entry->ae_stat.aest_rdev_is_broken_down)
+ return (entry->ae_stat.aest_rdevminor);
+ else
+ return minor(entry->ae_stat.aest_rdev);
+}
+
+la_int64_t
+archive_entry_size(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_size);
+}
+
+int
+archive_entry_size_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_SIZE);
+}
+
+const char *
+archive_entry_sourcepath(struct archive_entry *entry)
+{
+ const char *p;
+ if (archive_mstring_get_mbs(
+ entry->archive, &entry->ae_sourcepath, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const wchar_t *
+archive_entry_sourcepath_w(struct archive_entry *entry)
+{
+ const wchar_t *p;
+ if (archive_mstring_get_wcs(
+ entry->archive, &entry->ae_sourcepath, &p) == 0)
+ return (p);
+ return (NULL);
+}
+
+const char *
+archive_entry_symlink(struct archive_entry *entry)
+{
+ const char *p;
+ if ((entry->ae_set & AE_SET_SYMLINK) == 0)
+ return (NULL);
+ if (archive_mstring_get_mbs(
+ entry->archive, &entry->ae_symlink, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+int
+archive_entry_symlink_type(struct archive_entry *entry)
+{
+ return (entry->ae_symlink_type);
+}
+
+const char *
+archive_entry_symlink_utf8(struct archive_entry *entry)
+{
+ const char *p;
+ if ((entry->ae_set & AE_SET_SYMLINK) == 0)
+ return (NULL);
+ if (archive_mstring_get_utf8(
+ entry->archive, &entry->ae_symlink, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const wchar_t *
+archive_entry_symlink_w(struct archive_entry *entry)
+{
+ const wchar_t *p;
+ if ((entry->ae_set & AE_SET_SYMLINK) == 0)
+ return (NULL);
+ if (archive_mstring_get_wcs(
+ entry->archive, &entry->ae_symlink, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+int
+_archive_entry_symlink_l(struct archive_entry *entry,
+ const char **p, size_t *len, struct archive_string_conv *sc)
+{
+ if ((entry->ae_set & AE_SET_SYMLINK) == 0) {
+ *p = NULL;
+ *len = 0;
+ return (0);
+ }
+ return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_symlink, p, len, sc));
+}
+
+la_int64_t
+archive_entry_uid(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_uid);
+}
+
+const char *
+archive_entry_uname(struct archive_entry *entry)
+{
+ const char *p;
+ if (archive_mstring_get_mbs(entry->archive, &entry->ae_uname, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const char *
+archive_entry_uname_utf8(struct archive_entry *entry)
+{
+ const char *p;
+ if (archive_mstring_get_utf8(entry->archive, &entry->ae_uname, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const wchar_t *
+archive_entry_uname_w(struct archive_entry *entry)
+{
+ const wchar_t *p;
+ if (archive_mstring_get_wcs(entry->archive, &entry->ae_uname, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+int
+_archive_entry_uname_l(struct archive_entry *entry,
+ const char **p, size_t *len, struct archive_string_conv *sc)
+{
+ return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_uname, p, len, sc));
+}
+
+int
+archive_entry_is_data_encrypted(struct archive_entry *entry)
+{
+ return ((entry->encryption & AE_ENCRYPTION_DATA) == AE_ENCRYPTION_DATA);
+}
+
+int
+archive_entry_is_metadata_encrypted(struct archive_entry *entry)
+{
+ return ((entry->encryption & AE_ENCRYPTION_METADATA) == AE_ENCRYPTION_METADATA);
+}
+
+int
+archive_entry_is_encrypted(struct archive_entry *entry)
+{
+ return (entry->encryption & (AE_ENCRYPTION_DATA|AE_ENCRYPTION_METADATA));
+}
+
+/*
+ * Functions to set archive_entry properties.
+ */
+
+void
+archive_entry_set_filetype(struct archive_entry *entry, unsigned int type)
+{
+ entry->stat_valid = 0;
+ entry->acl.mode &= ~AE_IFMT;
+ entry->acl.mode |= AE_IFMT & type;
+}
+
+void
+archive_entry_set_fflags(struct archive_entry *entry,
+ unsigned long set, unsigned long clear)
+{
+ archive_mstring_clean(&entry->ae_fflags_text);
+ entry->ae_fflags_set = set;
+ entry->ae_fflags_clear = clear;
+}
+
+const char *
+archive_entry_copy_fflags_text(struct archive_entry *entry,
+ const char *flags)
+{
+ archive_mstring_copy_mbs(&entry->ae_fflags_text, flags);
+ return (ae_strtofflags(flags,
+ &entry->ae_fflags_set, &entry->ae_fflags_clear));
+}
+
+const wchar_t *
+archive_entry_copy_fflags_text_w(struct archive_entry *entry,
+ const wchar_t *flags)
+{
+ archive_mstring_copy_wcs(&entry->ae_fflags_text, flags);
+ return (ae_wcstofflags(flags,
+ &entry->ae_fflags_set, &entry->ae_fflags_clear));
+}
+
+void
+archive_entry_set_gid(struct archive_entry *entry, la_int64_t g)
+{
+ entry->stat_valid = 0;
+ entry->ae_stat.aest_gid = g;
+}
+
+void
+archive_entry_set_gname(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_mbs(&entry->ae_gname, name);
+}
+
+void
+archive_entry_set_gname_utf8(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_utf8(&entry->ae_gname, name);
+}
+
+void
+archive_entry_copy_gname(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_mbs(&entry->ae_gname, name);
+}
+
+void
+archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name)
+{
+ archive_mstring_copy_wcs(&entry->ae_gname, name);
+}
+
+int
+archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name)
+{
+ if (archive_mstring_update_utf8(entry->archive,
+ &entry->ae_gname, name) == 0)
+ return (1);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (0);
+}
+
+int
+_archive_entry_copy_gname_l(struct archive_entry *entry,
+ const char *name, size_t len, struct archive_string_conv *sc)
+{
+ return (archive_mstring_copy_mbs_len_l(&entry->ae_gname, name, len, sc));
+}
+
+void
+archive_entry_set_ino(struct archive_entry *entry, la_int64_t ino)
+{
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_INO;
+ entry->ae_stat.aest_ino = ino;
+}
+
+void
+archive_entry_set_ino64(struct archive_entry *entry, la_int64_t ino)
+{
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_INO;
+ entry->ae_stat.aest_ino = ino;
+}
+
+void
+archive_entry_set_hardlink(struct archive_entry *entry, const char *target)
+{
+ archive_mstring_copy_mbs(&entry->ae_hardlink, target);
+ if (target != NULL)
+ entry->ae_set |= AE_SET_HARDLINK;
+ else
+ entry->ae_set &= ~AE_SET_HARDLINK;
+}
+
+void
+archive_entry_set_hardlink_utf8(struct archive_entry *entry, const char *target)
+{
+ archive_mstring_copy_utf8(&entry->ae_hardlink, target);
+ if (target != NULL)
+ entry->ae_set |= AE_SET_HARDLINK;
+ else
+ entry->ae_set &= ~AE_SET_HARDLINK;
+}
+
+void
+archive_entry_copy_hardlink(struct archive_entry *entry, const char *target)
+{
+ archive_mstring_copy_mbs(&entry->ae_hardlink, target);
+ if (target != NULL)
+ entry->ae_set |= AE_SET_HARDLINK;
+ else
+ entry->ae_set &= ~AE_SET_HARDLINK;
+}
+
+void
+archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target)
+{
+ archive_mstring_copy_wcs(&entry->ae_hardlink, target);
+ if (target != NULL)
+ entry->ae_set |= AE_SET_HARDLINK;
+ else
+ entry->ae_set &= ~AE_SET_HARDLINK;
+}
+
+int
+archive_entry_update_hardlink_utf8(struct archive_entry *entry, const char *target)
+{
+ if (target != NULL)
+ entry->ae_set |= AE_SET_HARDLINK;
+ else
+ entry->ae_set &= ~AE_SET_HARDLINK;
+ if (archive_mstring_update_utf8(entry->archive,
+ &entry->ae_hardlink, target) == 0)
+ return (1);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (0);
+}
+
+int
+_archive_entry_copy_hardlink_l(struct archive_entry *entry,
+ const char *target, size_t len, struct archive_string_conv *sc)
+{
+ int r;
+
+ r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink,
+ target, len, sc);
+ if (target != NULL && r == 0)
+ entry->ae_set |= AE_SET_HARDLINK;
+ else
+ entry->ae_set &= ~AE_SET_HARDLINK;
+ return (r);
+}
+
+void
+archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns)
+{
+ FIX_NS(t, ns);
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_ATIME;
+ entry->ae_stat.aest_atime = t;
+ entry->ae_stat.aest_atime_nsec = ns;
+}
+
+void
+archive_entry_unset_atime(struct archive_entry *entry)
+{
+ archive_entry_set_atime(entry, 0, 0);
+ entry->ae_set &= ~AE_SET_ATIME;
+}
+
+void
+archive_entry_set_birthtime(struct archive_entry *entry, time_t t, long ns)
+{
+ FIX_NS(t, ns);
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_BIRTHTIME;
+ entry->ae_stat.aest_birthtime = t;
+ entry->ae_stat.aest_birthtime_nsec = ns;
+}
+
+void
+archive_entry_unset_birthtime(struct archive_entry *entry)
+{
+ archive_entry_set_birthtime(entry, 0, 0);
+ entry->ae_set &= ~AE_SET_BIRTHTIME;
+}
+
+void
+archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns)
+{
+ FIX_NS(t, ns);
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_CTIME;
+ entry->ae_stat.aest_ctime = t;
+ entry->ae_stat.aest_ctime_nsec = ns;
+}
+
+void
+archive_entry_unset_ctime(struct archive_entry *entry)
+{
+ archive_entry_set_ctime(entry, 0, 0);
+ entry->ae_set &= ~AE_SET_CTIME;
+}
+
+void
+archive_entry_set_dev(struct archive_entry *entry, dev_t d)
+{
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_DEV;
+ entry->ae_stat.aest_dev_is_broken_down = 0;
+ entry->ae_stat.aest_dev = d;
+}
+
+void
+archive_entry_set_devmajor(struct archive_entry *entry, dev_t m)
+{
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_DEV;
+ entry->ae_stat.aest_dev_is_broken_down = 1;
+ entry->ae_stat.aest_devmajor = m;
+}
+
+void
+archive_entry_set_devminor(struct archive_entry *entry, dev_t m)
+{
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_DEV;
+ entry->ae_stat.aest_dev_is_broken_down = 1;
+ entry->ae_stat.aest_devminor = m;
+}
+
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_set_link(struct archive_entry *entry, const char *target)
+{
+ if (entry->ae_set & AE_SET_SYMLINK)
+ archive_mstring_copy_mbs(&entry->ae_symlink, target);
+ else
+ archive_mstring_copy_mbs(&entry->ae_hardlink, target);
+}
+
+void
+archive_entry_set_link_utf8(struct archive_entry *entry, const char *target)
+{
+ if (entry->ae_set & AE_SET_SYMLINK)
+ archive_mstring_copy_utf8(&entry->ae_symlink, target);
+ else
+ archive_mstring_copy_utf8(&entry->ae_hardlink, target);
+}
+
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_copy_link(struct archive_entry *entry, const char *target)
+{
+ if (entry->ae_set & AE_SET_SYMLINK)
+ archive_mstring_copy_mbs(&entry->ae_symlink, target);
+ else
+ archive_mstring_copy_mbs(&entry->ae_hardlink, target);
+}
+
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target)
+{
+ if (entry->ae_set & AE_SET_SYMLINK)
+ archive_mstring_copy_wcs(&entry->ae_symlink, target);
+ else
+ archive_mstring_copy_wcs(&entry->ae_hardlink, target);
+}
+
+int
+archive_entry_update_link_utf8(struct archive_entry *entry, const char *target)
+{
+ int r;
+ if (entry->ae_set & AE_SET_SYMLINK)
+ r = archive_mstring_update_utf8(entry->archive,
+ &entry->ae_symlink, target);
+ else
+ r = archive_mstring_update_utf8(entry->archive,
+ &entry->ae_hardlink, target);
+ if (r == 0)
+ return (1);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (0);
+}
+
+int
+_archive_entry_copy_link_l(struct archive_entry *entry,
+ const char *target, size_t len, struct archive_string_conv *sc)
+{
+ int r;
+
+ if (entry->ae_set & AE_SET_SYMLINK)
+ r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink,
+ target, len, sc);
+ else
+ r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink,
+ target, len, sc);
+ return (r);
+}
+
+void
+archive_entry_set_mode(struct archive_entry *entry, mode_t m)
+{
+ entry->stat_valid = 0;
+ entry->acl.mode = m;
+}
+
+void
+archive_entry_set_mtime(struct archive_entry *entry, time_t t, long ns)
+{
+ FIX_NS(t, ns);
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_MTIME;
+ entry->ae_stat.aest_mtime = t;
+ entry->ae_stat.aest_mtime_nsec = ns;
+}
+
+void
+archive_entry_unset_mtime(struct archive_entry *entry)
+{
+ archive_entry_set_mtime(entry, 0, 0);
+ entry->ae_set &= ~AE_SET_MTIME;
+}
+
+void
+archive_entry_set_nlink(struct archive_entry *entry, unsigned int nlink)
+{
+ entry->stat_valid = 0;
+ entry->ae_stat.aest_nlink = nlink;
+}
+
+void
+archive_entry_set_pathname(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_mbs(&entry->ae_pathname, name);
+}
+
+void
+archive_entry_set_pathname_utf8(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_utf8(&entry->ae_pathname, name);
+}
+
+void
+archive_entry_copy_pathname(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_mbs(&entry->ae_pathname, name);
+}
+
+void
+archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name)
+{
+ archive_mstring_copy_wcs(&entry->ae_pathname, name);
+}
+
+int
+archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name)
+{
+ if (archive_mstring_update_utf8(entry->archive,
+ &entry->ae_pathname, name) == 0)
+ return (1);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (0);
+}
+
+int
+_archive_entry_copy_pathname_l(struct archive_entry *entry,
+ const char *name, size_t len, struct archive_string_conv *sc)
+{
+ return (archive_mstring_copy_mbs_len_l(&entry->ae_pathname,
+ name, len, sc));
+}
+
+void
+archive_entry_set_perm(struct archive_entry *entry, mode_t p)
+{
+ entry->stat_valid = 0;
+ entry->acl.mode &= AE_IFMT;
+ entry->acl.mode |= ~AE_IFMT & p;
+}
+
+void
+archive_entry_set_rdev(struct archive_entry *entry, dev_t m)
+{
+ entry->stat_valid = 0;
+ entry->ae_stat.aest_rdev = m;
+ entry->ae_stat.aest_rdev_is_broken_down = 0;
+}
+
+void
+archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m)
+{
+ entry->stat_valid = 0;
+ entry->ae_stat.aest_rdev_is_broken_down = 1;
+ entry->ae_stat.aest_rdevmajor = m;
+}
+
+void
+archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m)
+{
+ entry->stat_valid = 0;
+ entry->ae_stat.aest_rdev_is_broken_down = 1;
+ entry->ae_stat.aest_rdevminor = m;
+}
+
+void
+archive_entry_set_size(struct archive_entry *entry, la_int64_t s)
+{
+ entry->stat_valid = 0;
+ entry->ae_stat.aest_size = s;
+ entry->ae_set |= AE_SET_SIZE;
+}
+
+void
+archive_entry_unset_size(struct archive_entry *entry)
+{
+ archive_entry_set_size(entry, 0);
+ entry->ae_set &= ~AE_SET_SIZE;
+}
+
+void
+archive_entry_copy_sourcepath(struct archive_entry *entry, const char *path)
+{
+ archive_mstring_copy_mbs(&entry->ae_sourcepath, path);
+}
+
+void
+archive_entry_copy_sourcepath_w(struct archive_entry *entry, const wchar_t *path)
+{
+ archive_mstring_copy_wcs(&entry->ae_sourcepath, path);
+}
+
+void
+archive_entry_set_symlink(struct archive_entry *entry, const char *linkname)
+{
+ archive_mstring_copy_mbs(&entry->ae_symlink, linkname);
+ if (linkname != NULL)
+ entry->ae_set |= AE_SET_SYMLINK;
+ else
+ entry->ae_set &= ~AE_SET_SYMLINK;
+}
+
+void
+archive_entry_set_symlink_type(struct archive_entry *entry, int type)
+{
+ entry->ae_symlink_type = type;
+}
+
+void
+archive_entry_set_symlink_utf8(struct archive_entry *entry, const char *linkname)
+{
+ archive_mstring_copy_utf8(&entry->ae_symlink, linkname);
+ if (linkname != NULL)
+ entry->ae_set |= AE_SET_SYMLINK;
+ else
+ entry->ae_set &= ~AE_SET_SYMLINK;
+}
+
+void
+archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname)
+{
+ archive_mstring_copy_mbs(&entry->ae_symlink, linkname);
+ if (linkname != NULL)
+ entry->ae_set |= AE_SET_SYMLINK;
+ else
+ entry->ae_set &= ~AE_SET_SYMLINK;
+}
+
+void
+archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname)
+{
+ archive_mstring_copy_wcs(&entry->ae_symlink, linkname);
+ if (linkname != NULL)
+ entry->ae_set |= AE_SET_SYMLINK;
+ else
+ entry->ae_set &= ~AE_SET_SYMLINK;
+}
+
+int
+archive_entry_update_symlink_utf8(struct archive_entry *entry, const char *linkname)
+{
+ if (linkname != NULL)
+ entry->ae_set |= AE_SET_SYMLINK;
+ else
+ entry->ae_set &= ~AE_SET_SYMLINK;
+ if (archive_mstring_update_utf8(entry->archive,
+ &entry->ae_symlink, linkname) == 0)
+ return (1);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (0);
+}
+
+int
+_archive_entry_copy_symlink_l(struct archive_entry *entry,
+ const char *linkname, size_t len, struct archive_string_conv *sc)
+{
+ int r;
+
+ r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink,
+ linkname, len, sc);
+ if (linkname != NULL && r == 0)
+ entry->ae_set |= AE_SET_SYMLINK;
+ else
+ entry->ae_set &= ~AE_SET_SYMLINK;
+ return (r);
+}
+
+void
+archive_entry_set_uid(struct archive_entry *entry, la_int64_t u)
+{
+ entry->stat_valid = 0;
+ entry->ae_stat.aest_uid = u;
+}
+
+void
+archive_entry_set_uname(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_mbs(&entry->ae_uname, name);
+}
+
+void
+archive_entry_set_uname_utf8(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_utf8(&entry->ae_uname, name);
+}
+
+void
+archive_entry_copy_uname(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_mbs(&entry->ae_uname, name);
+}
+
+void
+archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name)
+{
+ archive_mstring_copy_wcs(&entry->ae_uname, name);
+}
+
+int
+archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name)
+{
+ if (archive_mstring_update_utf8(entry->archive,
+ &entry->ae_uname, name) == 0)
+ return (1);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (0);
+}
+
+void
+archive_entry_set_is_data_encrypted(struct archive_entry *entry, char is_encrypted)
+{
+ if (is_encrypted) {
+ entry->encryption |= AE_ENCRYPTION_DATA;
+ } else {
+ entry->encryption &= ~AE_ENCRYPTION_DATA;
+ }
+}
+
+void
+archive_entry_set_is_metadata_encrypted(struct archive_entry *entry, char is_encrypted)
+{
+ if (is_encrypted) {
+ entry->encryption |= AE_ENCRYPTION_METADATA;
+ } else {
+ entry->encryption &= ~AE_ENCRYPTION_METADATA;
+ }
+}
+
+int
+_archive_entry_copy_uname_l(struct archive_entry *entry,
+ const char *name, size_t len, struct archive_string_conv *sc)
+{
+ return (archive_mstring_copy_mbs_len_l(&entry->ae_uname,
+ name, len, sc));
+}
+
+const void *
+archive_entry_mac_metadata(struct archive_entry *entry, size_t *s)
+{
+ *s = entry->mac_metadata_size;
+ return entry->mac_metadata;
+}
+
+void
+archive_entry_copy_mac_metadata(struct archive_entry *entry,
+ const void *p, size_t s)
+{
+ free(entry->mac_metadata);
+ if (p == NULL || s == 0) {
+ entry->mac_metadata = NULL;
+ entry->mac_metadata_size = 0;
+ } else {
+ entry->mac_metadata_size = s;
+ entry->mac_metadata = malloc(s);
+ if (entry->mac_metadata == NULL)
+ abort();
+ memcpy(entry->mac_metadata, p, s);
+ }
+}
+
+/* Digest handling */
+const unsigned char *
+archive_entry_digest(struct archive_entry *entry, int type)
+{
+ switch (type) {
+ case ARCHIVE_ENTRY_DIGEST_MD5:
+ return entry->digest.md5;
+ case ARCHIVE_ENTRY_DIGEST_RMD160:
+ return entry->digest.rmd160;
+ case ARCHIVE_ENTRY_DIGEST_SHA1:
+ return entry->digest.sha1;
+ case ARCHIVE_ENTRY_DIGEST_SHA256:
+ return entry->digest.sha256;
+ case ARCHIVE_ENTRY_DIGEST_SHA384:
+ return entry->digest.sha384;
+ case ARCHIVE_ENTRY_DIGEST_SHA512:
+ return entry->digest.sha512;
+ default:
+ return NULL;
+ }
+}
+
+int
+archive_entry_set_digest(struct archive_entry *entry, int type,
+ const unsigned char *digest)
+{
+#define copy_digest(_e, _t, _d)\
+ memcpy(_e->digest._t, _d, sizeof(_e->digest._t))
+
+ switch (type) {
+ case ARCHIVE_ENTRY_DIGEST_MD5:
+ copy_digest(entry, md5, digest);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_RMD160:
+ copy_digest(entry, rmd160, digest);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_SHA1:
+ copy_digest(entry, sha1, digest);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_SHA256:
+ copy_digest(entry, sha256, digest);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_SHA384:
+ copy_digest(entry, sha384, digest);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_SHA512:
+ copy_digest(entry, sha512, digest);
+ break;
+ default:
+ return ARCHIVE_WARN;
+ }
+
+ return ARCHIVE_OK;
+#undef copy_digest
+}
+
+/*
+ * ACL management. The following would, of course, be a lot simpler
+ * if: 1) the last draft of POSIX.1e were a really thorough and
+ * complete standard that addressed the needs of ACL archiving and 2)
+ * everyone followed it faithfully. Alas, neither is true, so the
+ * following is a lot more complex than might seem necessary to the
+ * uninitiated.
+ */
+
+struct archive_acl *
+archive_entry_acl(struct archive_entry *entry)
+{
+ return &entry->acl;
+}
+
+void
+archive_entry_acl_clear(struct archive_entry *entry)
+{
+ archive_acl_clear(&entry->acl);
+}
+
+/*
+ * Add a single ACL entry to the internal list of ACL data.
+ */
+int
+archive_entry_acl_add_entry(struct archive_entry *entry,
+ int type, int permset, int tag, int id, const char *name)
+{
+ return archive_acl_add_entry(&entry->acl, type, permset, tag, id, name);
+}
+
+/*
+ * As above, but with a wide-character name.
+ */
+int
+archive_entry_acl_add_entry_w(struct archive_entry *entry,
+ int type, int permset, int tag, int id, const wchar_t *name)
+{
+ return archive_acl_add_entry_w_len(&entry->acl,
+ type, permset, tag, id, name, wcslen(name));
+}
+
+/*
+ * Return a bitmask of ACL types in an archive entry ACL list
+ */
+int
+archive_entry_acl_types(struct archive_entry *entry)
+{
+ return (archive_acl_types(&entry->acl));
+}
+
+/*
+ * Return a count of entries matching "want_type".
+ */
+int
+archive_entry_acl_count(struct archive_entry *entry, int want_type)
+{
+ return archive_acl_count(&entry->acl, want_type);
+}
+
+/*
+ * Prepare for reading entries from the ACL data. Returns a count
+ * of entries matching "want_type", or zero if there are no
+ * non-extended ACL entries of that type.
+ */
+int
+archive_entry_acl_reset(struct archive_entry *entry, int want_type)
+{
+ return archive_acl_reset(&entry->acl, want_type);
+}
+
+/*
+ * Return the next ACL entry in the list. Fake entries for the
+ * standard permissions and include them in the returned list.
+ */
+int
+archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type,
+ int *permset, int *tag, int *id, const char **name)
+{
+ int r;
+ r = archive_acl_next(entry->archive, &entry->acl, want_type, type,
+ permset, tag, id, name);
+ if (r == ARCHIVE_FATAL && errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (r);
+}
+
+/*
+ * Generate a text version of the ACL. The flags parameter controls
+ * the style of the generated ACL.
+ */
+wchar_t *
+archive_entry_acl_to_text_w(struct archive_entry *entry, la_ssize_t *len,
+ int flags)
+{
+ return (archive_acl_to_text_w(&entry->acl, len, flags,
+ entry->archive));
+}
+
+char *
+archive_entry_acl_to_text(struct archive_entry *entry, la_ssize_t *len,
+ int flags)
+{
+ return (archive_acl_to_text_l(&entry->acl, len, flags, NULL));
+}
+
+char *
+_archive_entry_acl_to_text_l(struct archive_entry *entry, ssize_t *len,
+ int flags, struct archive_string_conv *sc)
+{
+ return (archive_acl_to_text_l(&entry->acl, len, flags, sc));
+}
+
+/*
+ * ACL text parser.
+ */
+int
+archive_entry_acl_from_text_w(struct archive_entry *entry,
+ const wchar_t *wtext, int type)
+{
+ return (archive_acl_from_text_w(&entry->acl, wtext, type));
+}
+
+int
+archive_entry_acl_from_text(struct archive_entry *entry,
+ const char *text, int type)
+{
+ return (archive_acl_from_text_l(&entry->acl, text, type, NULL));
+}
+
+int
+_archive_entry_acl_from_text_l(struct archive_entry *entry, const char *text,
+ int type, struct archive_string_conv *sc)
+{
+ return (archive_acl_from_text_l(&entry->acl, text, type, sc));
+}
+
+/* Deprecated */
+static int
+archive_entry_acl_text_compat(int *flags)
+{
+ if ((*flags & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) == 0)
+ return (1);
+
+ /* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID */
+ if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0)
+ *flags |= ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID;
+
+ /* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT */
+ if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
+ *flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
+
+ *flags |= ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA;
+
+ return (0);
+}
+
+/* Deprecated */
+const wchar_t *
+archive_entry_acl_text_w(struct archive_entry *entry, int flags)
+{
+ free(entry->acl.acl_text_w);
+ entry->acl.acl_text_w = NULL;
+ if (archive_entry_acl_text_compat(&flags) == 0)
+ entry->acl.acl_text_w = archive_acl_to_text_w(&entry->acl,
+ NULL, flags, entry->archive);
+ return (entry->acl.acl_text_w);
+}
+
+/* Deprecated */
+const char *
+archive_entry_acl_text(struct archive_entry *entry, int flags)
+{
+ free(entry->acl.acl_text);
+ entry->acl.acl_text = NULL;
+ if (archive_entry_acl_text_compat(&flags) == 0)
+ entry->acl.acl_text = archive_acl_to_text_l(&entry->acl, NULL,
+ flags, NULL);
+
+ return (entry->acl.acl_text);
+}
+
+/* Deprecated */
+int
+_archive_entry_acl_text_l(struct archive_entry *entry, int flags,
+ const char **acl_text, size_t *len, struct archive_string_conv *sc)
+{
+ free(entry->acl.acl_text);
+ entry->acl.acl_text = NULL;
+
+ if (archive_entry_acl_text_compat(&flags) == 0)
+ entry->acl.acl_text = archive_acl_to_text_l(&entry->acl,
+ (ssize_t *)len, flags, sc);
+
+ *acl_text = entry->acl.acl_text;
+
+ return (0);
+}
+
+/*
+ * Following code is modified from UC Berkeley sources, and
+ * is subject to the following copyright notice.
+ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Supported file flags on FreeBSD and Mac OS:
+ * sappnd,sappend SF_APPEND
+ * arch,archived SF_ARCHIVED
+ * schg,schange,simmutable SF_IMMUTABLE
+ * sunlnk,sunlink SF_NOUNLINK (FreeBSD only)
+ * uappnd,uappend UF_APPEND
+ * compressed UF_COMPRESSED (Mac OS only)
+ * hidden,uhidden UF_HIDDEN
+ * uchg,uchange,uimmutable UF_IMMUTABLE
+ * nodump UF_NODUMP
+ * uunlnk,uunlink UF_NOUNLINK (FreeBSD only)
+ * offline,uoffline UF_OFFLINE (FreeBSD only)
+ * opaque UF_OPAQUE
+ * rdonly,urdonly,readonly UF_READONLY (FreeBSD only)
+ * reparse,ureparse UF_REPARSE (FreeBSD only)
+ * sparse,usparse UF_SPARSE (FreeBSD only)
+ * system,usystem UF_SYSTEM (FreeBSD only)
+ *
+ * See chflags(2) for more information
+ *
+ * Supported file attributes on Linux:
+ * a append only FS_APPEND_FL sappnd
+ * A no atime updates FS_NOATIME_FL atime
+ * c compress FS_COMPR_FL compress
+ * C no copy on write FS_NOCOW_FL cow
+ * d no dump FS_NODUMP_FL dump
+ * D synchronous directory updates FS_DIRSYNC_FL dirsync
+ * i immutable FS_IMMUTABLE_FL schg
+ * j data journalling FS_JOURNAL_DATA_FL journal
+ * P project hierarchy FS_PROJINHERIT_FL projinherit
+ * s secure deletion FS_SECRM_FL securedeletion
+ * S synchronous updates FS_SYNC_FL sync
+ * t no tail-merging FS_NOTAIL_FL tail
+ * T top of directory hierarchy FS_TOPDIR_FL topdir
+ * u undeletable FS_UNRM_FL undel
+ *
+ * See ioctl_iflags(2) for more information
+ *
+ * Equivalent file flags supported on FreeBSD / Mac OS and Linux:
+ * SF_APPEND FS_APPEND_FL sappnd
+ * SF_IMMUTABLE FS_IMMUTABLE_FL schg
+ * UF_NODUMP FS_NODUMP_FL nodump
+ */
+
+static const struct flag {
+ const char *name;
+ const wchar_t *wname;
+ unsigned long set;
+ unsigned long clear;
+} fileflags[] = {
+ /* Preferred (shorter) names per flag first, all prefixed by "no" */
+#ifdef SF_APPEND
+ { "nosappnd", L"nosappnd", SF_APPEND, 0},
+ { "nosappend", L"nosappend", SF_APPEND, 0},
+#endif
+#if defined(FS_APPEND_FL) /* 'a' */
+ { "nosappnd", L"nosappnd", FS_APPEND_FL, 0},
+ { "nosappend", L"nosappend", FS_APPEND_FL, 0},
+#elif defined(EXT2_APPEND_FL) /* 'a' */
+ { "nosappnd", L"nosappnd", EXT2_APPEND_FL, 0},
+ { "nosappend", L"nosappend", EXT2_APPEND_FL, 0},
+#endif
+#ifdef SF_ARCHIVED
+ { "noarch", L"noarch", SF_ARCHIVED, 0},
+ { "noarchived", L"noarchived", SF_ARCHIVED, 0},
+#endif
+#ifdef SF_IMMUTABLE
+ { "noschg", L"noschg", SF_IMMUTABLE, 0},
+ { "noschange", L"noschange", SF_IMMUTABLE, 0},
+ { "nosimmutable", L"nosimmutable", SF_IMMUTABLE, 0},
+#endif
+#if defined(FS_IMMUTABLE_FL) /* 'i' */
+ { "noschg", L"noschg", FS_IMMUTABLE_FL, 0},
+ { "noschange", L"noschange", FS_IMMUTABLE_FL, 0},
+ { "nosimmutable", L"nosimmutable", FS_IMMUTABLE_FL, 0},
+#elif defined(EXT2_IMMUTABLE_FL) /* 'i' */
+ { "noschg", L"noschg", EXT2_IMMUTABLE_FL, 0},
+ { "noschange", L"noschange", EXT2_IMMUTABLE_FL, 0},
+ { "nosimmutable", L"nosimmutable", EXT2_IMMUTABLE_FL, 0},
+#endif
+#ifdef SF_NOUNLINK
+ { "nosunlnk", L"nosunlnk", SF_NOUNLINK, 0},
+ { "nosunlink", L"nosunlink", SF_NOUNLINK, 0},
+#endif
+#ifdef UF_APPEND
+ { "nouappnd", L"nouappnd", UF_APPEND, 0},
+ { "nouappend", L"nouappend", UF_APPEND, 0},
+#endif
+#ifdef UF_IMMUTABLE
+ { "nouchg", L"nouchg", UF_IMMUTABLE, 0},
+ { "nouchange", L"nouchange", UF_IMMUTABLE, 0},
+ { "nouimmutable", L"nouimmutable", UF_IMMUTABLE, 0},
+#endif
+#ifdef UF_NODUMP
+ { "nodump", L"nodump", 0, UF_NODUMP},
+#endif
+#if defined(FS_NODUMP_FL) /* 'd' */
+ { "nodump", L"nodump", 0, FS_NODUMP_FL},
+#elif defined(EXT2_NODUMP_FL)
+ { "nodump", L"nodump", 0, EXT2_NODUMP_FL},
+#endif
+#ifdef UF_OPAQUE
+ { "noopaque", L"noopaque", UF_OPAQUE, 0},
+#endif
+#ifdef UF_NOUNLINK
+ { "nouunlnk", L"nouunlnk", UF_NOUNLINK, 0},
+ { "nouunlink", L"nouunlink", UF_NOUNLINK, 0},
+#endif
+#ifdef UF_COMPRESSED
+ /* Mac OS */
+ { "nocompressed", L"nocompressed", UF_COMPRESSED, 0},
+#endif
+#ifdef UF_HIDDEN
+ { "nohidden", L"nohidden", UF_HIDDEN, 0},
+ { "nouhidden", L"nouhidden", UF_HIDDEN, 0},
+#endif
+#ifdef FILE_ATTRIBUTE_HIDDEN
+ { "nohidden", L"nohidden", FILE_ATTRIBUTE_HIDDEN, 0},
+ { "nouhidden", L"nouhidden", FILE_ATTRIBUTE_HIDDEN, 0},
+#endif
+#ifdef UF_OFFLINE
+ { "nooffline", L"nooffline", UF_OFFLINE, 0},
+ { "nouoffline", L"nouoffline", UF_OFFLINE, 0},
+#endif
+#ifdef UF_READONLY
+ { "nordonly", L"nordonly", UF_READONLY, 0},
+ { "nourdonly", L"nourdonly", UF_READONLY, 0},
+ { "noreadonly", L"noreadonly", UF_READONLY, 0},
+#endif
+#ifdef FILE_ATTRIBUTE_READONLY
+ { "nordonly", L"nordonly", FILE_ATTRIBUTE_READONLY, 0},
+ { "nourdonly", L"nourdonly", FILE_ATTRIBUTE_READONLY, 0},
+ { "noreadonly", L"noreadonly", FILE_ATTRIBUTE_READONLY, 0},
+#endif
+#ifdef UF_SPARSE
+ { "nosparse", L"nosparse", UF_SPARSE, 0},
+ { "nousparse", L"nousparse", UF_SPARSE, 0},
+#endif
+#ifdef UF_REPARSE
+ { "noreparse", L"noreparse", UF_REPARSE, 0},
+ { "noureparse", L"noureparse", UF_REPARSE, 0},
+#endif
+#ifdef UF_SYSTEM
+ { "nosystem", L"nosystem", UF_SYSTEM, 0},
+ { "nousystem", L"nousystem", UF_SYSTEM, 0},
+#endif
+#ifdef FILE_ATTRIBUTE_SYSTEM
+ { "nosystem", L"nosystem", FILE_ATTRIBUTE_SYSTEM, 0},
+ { "nousystem", L"nousystem", FILE_ATTRIBUTE_SYSTEM, 0},
+#endif
+#if defined(FS_UNRM_FL) /* 'u' */
+ { "noundel", L"noundel", FS_UNRM_FL, 0},
+#elif defined(EXT2_UNRM_FL)
+ { "noundel", L"noundel", EXT2_UNRM_FL, 0},
+#endif
+
+#if defined(FS_COMPR_FL) /* 'c' */
+ { "nocompress", L"nocompress", FS_COMPR_FL, 0},
+#elif defined(EXT2_COMPR_FL)
+ { "nocompress", L"nocompress", EXT2_COMPR_FL, 0},
+#endif
+
+#if defined(FS_NOATIME_FL) /* 'A' */
+ { "noatime", L"noatime", 0, FS_NOATIME_FL},
+#elif defined(EXT2_NOATIME_FL)
+ { "noatime", L"noatime", 0, EXT2_NOATIME_FL},
+#endif
+#if defined(FS_DIRSYNC_FL) /* 'D' */
+ { "nodirsync", L"nodirsync", FS_DIRSYNC_FL, 0},
+#elif defined(EXT2_DIRSYNC_FL)
+ { "nodirsync", L"nodirsync", EXT2_DIRSYNC_FL, 0},
+#endif
+#if defined(FS_JOURNAL_DATA_FL) /* 'j' */
+ { "nojournal-data",L"nojournal-data", FS_JOURNAL_DATA_FL, 0},
+ { "nojournal", L"nojournal", FS_JOURNAL_DATA_FL, 0},
+#elif defined(EXT3_JOURNAL_DATA_FL)
+ { "nojournal-data",L"nojournal-data", EXT3_JOURNAL_DATA_FL, 0},
+ { "nojournal", L"nojournal", EXT3_JOURNAL_DATA_FL, 0},
+#endif
+#if defined(FS_SECRM_FL) /* 's' */
+ { "nosecdel", L"nosecdel", FS_SECRM_FL, 0},
+ { "nosecuredeletion",L"nosecuredeletion",FS_SECRM_FL, 0},
+#elif defined(EXT2_SECRM_FL)
+ { "nosecdel", L"nosecdel", EXT2_SECRM_FL, 0},
+ { "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL, 0},
+#endif
+#if defined(FS_SYNC_FL) /* 'S' */
+ { "nosync", L"nosync", FS_SYNC_FL, 0},
+#elif defined(EXT2_SYNC_FL)
+ { "nosync", L"nosync", EXT2_SYNC_FL, 0},
+#endif
+#if defined(FS_NOTAIL_FL) /* 't' */
+ { "notail", L"notail", 0, FS_NOTAIL_FL},
+#elif defined(EXT2_NOTAIL_FL)
+ { "notail", L"notail", 0, EXT2_NOTAIL_FL},
+#endif
+#if defined(FS_TOPDIR_FL) /* 'T' */
+ { "notopdir", L"notopdir", FS_TOPDIR_FL, 0},
+#elif defined(EXT2_TOPDIR_FL)
+ { "notopdir", L"notopdir", EXT2_TOPDIR_FL, 0},
+#endif
+#ifdef FS_NOCOW_FL /* 'C' */
+ { "nocow", L"nocow", 0, FS_NOCOW_FL},
+#endif
+#ifdef FS_PROJINHERIT_FL /* 'P' */
+ { "noprojinherit",L"noprojinherit", FS_PROJINHERIT_FL, 0},
+#endif
+ { NULL, NULL, 0, 0}
+};
+
+/*
+ * fflagstostr --
+ * Convert file flags to a comma-separated string. If no flags
+ * are set, return the empty string.
+ */
+static char *
+ae_fflagstostr(unsigned long bitset, unsigned long bitclear)
+{
+ char *string, *dp;
+ const char *sp;
+ unsigned long bits;
+ const struct flag *flag;
+ size_t length;
+
+ bits = bitset | bitclear;
+ length = 0;
+ for (flag = fileflags; flag->name != NULL; flag++)
+ if (bits & (flag->set | flag->clear)) {
+ length += strlen(flag->name) + 1;
+ bits &= ~(flag->set | flag->clear);
+ }
+
+ if (length == 0)
+ return (NULL);
+ string = (char *)malloc(length);
+ if (string == NULL)
+ return (NULL);
+
+ dp = string;
+ for (flag = fileflags; flag->name != NULL; flag++) {
+ if (bitset & flag->set || bitclear & flag->clear) {
+ sp = flag->name + 2;
+ } else if (bitset & flag->clear || bitclear & flag->set) {
+ sp = flag->name;
+ } else
+ continue;
+ bitset &= ~(flag->set | flag->clear);
+ bitclear &= ~(flag->set | flag->clear);
+ if (dp > string)
+ *dp++ = ',';
+ while ((*dp++ = *sp++) != '\0')
+ ;
+ dp--;
+ }
+
+ *dp = '\0';
+ return (string);
+}
+
+/*
+ * strtofflags --
+ * Take string of arguments and return file flags. This
+ * version works a little differently than strtofflags(3).
+ * In particular, it always tests every token, skipping any
+ * unrecognized tokens. It returns a pointer to the first
+ * unrecognized token, or NULL if every token was recognized.
+ * This version is also const-correct and does not modify the
+ * provided string.
+ */
+static const char *
+ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp)
+{
+ const char *start, *end;
+ const struct flag *flag;
+ unsigned long set, clear;
+ const char *failed;
+
+ set = clear = 0;
+ start = s;
+ failed = NULL;
+ /* Find start of first token. */
+ while (*start == '\t' || *start == ' ' || *start == ',')
+ start++;
+ while (*start != '\0') {
+ size_t length;
+ /* Locate end of token. */
+ end = start;
+ while (*end != '\0' && *end != '\t' &&
+ *end != ' ' && *end != ',')
+ end++;
+ length = end - start;
+ for (flag = fileflags; flag->name != NULL; flag++) {
+ size_t flag_length = strlen(flag->name);
+ if (length == flag_length
+ && memcmp(start, flag->name, length) == 0) {
+ /* Matched "noXXXX", so reverse the sense. */
+ clear |= flag->set;
+ set |= flag->clear;
+ break;
+ } else if (length == flag_length - 2
+ && memcmp(start, flag->name + 2, length) == 0) {
+ /* Matched "XXXX", so don't reverse. */
+ set |= flag->set;
+ clear |= flag->clear;
+ break;
+ }
+ }
+ /* Ignore unknown flag names. */
+ if (flag->name == NULL && failed == NULL)
+ failed = start;
+
+ /* Find start of next token. */
+ start = end;
+ while (*start == '\t' || *start == ' ' || *start == ',')
+ start++;
+
+ }
+
+ if (setp)
+ *setp = set;
+ if (clrp)
+ *clrp = clear;
+
+ /* Return location of first failure. */
+ return (failed);
+}
+
+/*
+ * wcstofflags --
+ * Take string of arguments and return file flags. This
+ * version works a little differently than strtofflags(3).
+ * In particular, it always tests every token, skipping any
+ * unrecognized tokens. It returns a pointer to the first
+ * unrecognized token, or NULL if every token was recognized.
+ * This version is also const-correct and does not modify the
+ * provided string.
+ */
+static const wchar_t *
+ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp)
+{
+ const wchar_t *start, *end;
+ const struct flag *flag;
+ unsigned long set, clear;
+ const wchar_t *failed;
+
+ set = clear = 0;
+ start = s;
+ failed = NULL;
+ /* Find start of first token. */
+ while (*start == L'\t' || *start == L' ' || *start == L',')
+ start++;
+ while (*start != L'\0') {
+ size_t length;
+ /* Locate end of token. */
+ end = start;
+ while (*end != L'\0' && *end != L'\t' &&
+ *end != L' ' && *end != L',')
+ end++;
+ length = end - start;
+ for (flag = fileflags; flag->wname != NULL; flag++) {
+ size_t flag_length = wcslen(flag->wname);
+ if (length == flag_length
+ && wmemcmp(start, flag->wname, length) == 0) {
+ /* Matched "noXXXX", so reverse the sense. */
+ clear |= flag->set;
+ set |= flag->clear;
+ break;
+ } else if (length == flag_length - 2
+ && wmemcmp(start, flag->wname + 2, length) == 0) {
+ /* Matched "XXXX", so don't reverse. */
+ set |= flag->set;
+ clear |= flag->clear;
+ break;
+ }
+ }
+ /* Ignore unknown flag names. */
+ if (flag->wname == NULL && failed == NULL)
+ failed = start;
+
+ /* Find start of next token. */
+ start = end;
+ while (*start == L'\t' || *start == L' ' || *start == L',')
+ start++;
+
+ }
+
+ if (setp)
+ *setp = set;
+ if (clrp)
+ *clrp = clear;
+
+ /* Return location of first failure. */
+ return (failed);
+}
+
+
+#ifdef TEST
+#include <stdio.h>
+int
+main(int argc, char **argv)
+{
+ struct archive_entry *entry = archive_entry_new();
+ unsigned long set, clear;
+ const wchar_t *remainder;
+
+ remainder = archive_entry_copy_fflags_text_w(entry, L"nosappnd dump archive,,,,,,,");
+ archive_entry_fflags(entry, &set, &clear);
+
+ wprintf(L"set=0x%lX clear=0x%lX remainder='%ls'\n", set, clear, remainder);
+
+ wprintf(L"new flags='%s'\n", archive_entry_fflags_text(entry));
+ return (0);
+}
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_entry.h b/contrib/libs/libarchive/libarchive/archive_entry.h
new file mode 100644
index 00000000000..890cf2f3f1d
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry.h
@@ -0,0 +1,723 @@
+/*-
+ * Copyright (c) 2003-2008 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_entry.h 201096 2009-12-28 02:41:27Z kientzle $
+ */
+
+#ifndef ARCHIVE_ENTRY_H_INCLUDED
+#define ARCHIVE_ENTRY_H_INCLUDED
+
+/* Note: Compiler will complain if this does not match archive.h! */
+#define ARCHIVE_VERSION_NUMBER 3007001
+
+/*
+ * Note: archive_entry.h is for use outside of libarchive; the
+ * configuration headers (config.h, archive_platform.h, etc.) are
+ * purely internal. Do NOT use HAVE_XXX configuration macros to
+ * control the behavior of this header! If you must conditionalize,
+ * use predefined compiler and/or platform macros.
+ */
+
+#include <sys/types.h>
+#include <stddef.h> /* for wchar_t */
+#include <stdint.h>
+#include <time.h>
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include <windows.h>
+#endif
+
+/* Get a suitable 64-bit integer type. */
+#if !defined(__LA_INT64_T_DEFINED)
+# if ARCHIVE_VERSION_NUMBER < 4000000
+#define __LA_INT64_T la_int64_t
+# endif
+#define __LA_INT64_T_DEFINED
+# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
+typedef __int64 la_int64_t;
+# else
+#include <unistd.h>
+# if defined(_SCO_DS) || defined(__osf__)
+typedef long long la_int64_t;
+# else
+typedef int64_t la_int64_t;
+# endif
+# endif
+#endif
+
+/* The la_ssize_t should match the type used in 'struct stat' */
+#if !defined(__LA_SSIZE_T_DEFINED)
+/* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */
+# if ARCHIVE_VERSION_NUMBER < 4000000
+#define __LA_SSIZE_T la_ssize_t
+# endif
+#define __LA_SSIZE_T_DEFINED
+# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
+# if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)
+typedef ssize_t la_ssize_t;
+# elif defined(_WIN64)
+typedef __int64 la_ssize_t;
+# else
+typedef long la_ssize_t;
+# endif
+# else
+# include <unistd.h> /* ssize_t */
+typedef ssize_t la_ssize_t;
+# endif
+#endif
+
+/* Get a suitable definition for mode_t */
+#if ARCHIVE_VERSION_NUMBER >= 3999000
+/* Switch to plain 'int' for libarchive 4.0. It's less broken than 'mode_t' */
+# define __LA_MODE_T int
+#elif defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__) && !defined(__WATCOMC__)
+# define __LA_MODE_T unsigned short
+#else
+# define __LA_MODE_T mode_t
+#endif
+
+/* Large file support for Android */
+#if defined(__LIBARCHIVE_BUILD) && defined(__ANDROID__)
+#error #include "android_lf.h"
+#endif
+
+/*
+ * On Windows, define LIBARCHIVE_STATIC if you're building or using a
+ * .lib. The default here assumes you're building a DLL. Only
+ * libarchive source should ever define __LIBARCHIVE_BUILD.
+ */
+#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC)
+# ifdef __LIBARCHIVE_BUILD
+# ifdef __GNUC__
+# define __LA_DECL __attribute__((dllexport)) extern
+# else
+# define __LA_DECL __declspec(dllexport)
+# endif
+# else
+# ifdef __GNUC__
+# define __LA_DECL
+# else
+# define __LA_DECL __declspec(dllimport)
+# endif
+# endif
+#elif defined __LIBARCHIVE_ENABLE_VISIBILITY
+# define __LA_DECL __attribute__((visibility("default")))
+#else
+/* Static libraries on all platforms and shared libraries on non-Windows. */
+# define __LA_DECL
+#endif
+
+#if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1
+# define __LA_DEPRECATED __attribute__((deprecated))
+#else
+# define __LA_DEPRECATED
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Description of an archive entry.
+ *
+ * You can think of this as "struct stat" with some text fields added in.
+ *
+ * TODO: Add "comment", "charset", and possibly other entries that are
+ * supported by "pax interchange" format. However, GNU, ustar, cpio,
+ * and other variants don't support these features, so they're not an
+ * excruciatingly high priority right now.
+ *
+ * TODO: "pax interchange" format allows essentially arbitrary
+ * key/value attributes to be attached to any entry. Supporting
+ * such extensions may make this library useful for special
+ * applications (e.g., a package manager could attach special
+ * package-management attributes to each entry).
+ */
+struct archive;
+struct archive_entry;
+
+/*
+ * File-type constants. These are returned from archive_entry_filetype()
+ * and passed to archive_entry_set_filetype().
+ *
+ * These values match S_XXX defines on every platform I've checked,
+ * including Windows, AIX, Linux, Solaris, and BSD. They're
+ * (re)defined here because platforms generally don't define the ones
+ * they don't support. For example, Windows doesn't define S_IFLNK or
+ * S_IFBLK. Instead of having a mass of conditional logic and system
+ * checks to define any S_XXX values that aren't supported locally,
+ * I've just defined a new set of such constants so that
+ * libarchive-based applications can manipulate and identify archive
+ * entries properly even if the hosting platform can't store them on
+ * disk.
+ *
+ * These values are also used directly within some portable formats,
+ * such as cpio. If you find a platform that varies from these, the
+ * correct solution is to leave these alone and translate from these
+ * portable values to platform-native values when entries are read from
+ * or written to disk.
+ */
+/*
+ * In libarchive 4.0, we can drop the casts here.
+ * They're needed to work around Borland C's broken mode_t.
+ */
+#define AE_IFMT ((__LA_MODE_T)0170000)
+#define AE_IFREG ((__LA_MODE_T)0100000)
+#define AE_IFLNK ((__LA_MODE_T)0120000)
+#define AE_IFSOCK ((__LA_MODE_T)0140000)
+#define AE_IFCHR ((__LA_MODE_T)0020000)
+#define AE_IFBLK ((__LA_MODE_T)0060000)
+#define AE_IFDIR ((__LA_MODE_T)0040000)
+#define AE_IFIFO ((__LA_MODE_T)0010000)
+
+/*
+ * Symlink types
+ */
+#define AE_SYMLINK_TYPE_UNDEFINED 0
+#define AE_SYMLINK_TYPE_FILE 1
+#define AE_SYMLINK_TYPE_DIRECTORY 2
+
+/*
+ * Basic object manipulation
+ */
+
+__LA_DECL struct archive_entry *archive_entry_clear(struct archive_entry *);
+/* The 'clone' function does a deep copy; all of the strings are copied too. */
+__LA_DECL struct archive_entry *archive_entry_clone(struct archive_entry *);
+__LA_DECL void archive_entry_free(struct archive_entry *);
+__LA_DECL struct archive_entry *archive_entry_new(void);
+
+/*
+ * This form of archive_entry_new2() will pull character-set
+ * conversion information from the specified archive handle. The
+ * older archive_entry_new(void) form is equivalent to calling
+ * archive_entry_new2(NULL) and will result in the use of an internal
+ * default character-set conversion.
+ */
+__LA_DECL struct archive_entry *archive_entry_new2(struct archive *);
+
+/*
+ * Retrieve fields from an archive_entry.
+ *
+ * There are a number of implicit conversions among these fields. For
+ * example, if a regular string field is set and you read the _w wide
+ * character field, the entry will implicitly convert narrow-to-wide
+ * using the current locale. Similarly, dev values are automatically
+ * updated when you write devmajor or devminor and vice versa.
+ *
+ * In addition, fields can be "set" or "unset." Unset string fields
+ * return NULL, non-string fields have _is_set() functions to test
+ * whether they've been set. You can "unset" a string field by
+ * assigning NULL; non-string fields have _unset() functions to
+ * unset them.
+ *
+ * Note: There is one ambiguity in the above; string fields will
+ * also return NULL when implicit character set conversions fail.
+ * This is usually what you want.
+ */
+__LA_DECL time_t archive_entry_atime(struct archive_entry *);
+__LA_DECL long archive_entry_atime_nsec(struct archive_entry *);
+__LA_DECL int archive_entry_atime_is_set(struct archive_entry *);
+__LA_DECL time_t archive_entry_birthtime(struct archive_entry *);
+__LA_DECL long archive_entry_birthtime_nsec(struct archive_entry *);
+__LA_DECL int archive_entry_birthtime_is_set(struct archive_entry *);
+__LA_DECL time_t archive_entry_ctime(struct archive_entry *);
+__LA_DECL long archive_entry_ctime_nsec(struct archive_entry *);
+__LA_DECL int archive_entry_ctime_is_set(struct archive_entry *);
+__LA_DECL dev_t archive_entry_dev(struct archive_entry *);
+__LA_DECL int archive_entry_dev_is_set(struct archive_entry *);
+__LA_DECL dev_t archive_entry_devmajor(struct archive_entry *);
+__LA_DECL dev_t archive_entry_devminor(struct archive_entry *);
+__LA_DECL __LA_MODE_T archive_entry_filetype(struct archive_entry *);
+__LA_DECL void archive_entry_fflags(struct archive_entry *,
+ unsigned long * /* set */,
+ unsigned long * /* clear */);
+__LA_DECL const char *archive_entry_fflags_text(struct archive_entry *);
+__LA_DECL la_int64_t archive_entry_gid(struct archive_entry *);
+__LA_DECL const char *archive_entry_gname(struct archive_entry *);
+__LA_DECL const char *archive_entry_gname_utf8(struct archive_entry *);
+__LA_DECL const wchar_t *archive_entry_gname_w(struct archive_entry *);
+__LA_DECL const char *archive_entry_hardlink(struct archive_entry *);
+__LA_DECL const char *archive_entry_hardlink_utf8(struct archive_entry *);
+__LA_DECL const wchar_t *archive_entry_hardlink_w(struct archive_entry *);
+__LA_DECL la_int64_t archive_entry_ino(struct archive_entry *);
+__LA_DECL la_int64_t archive_entry_ino64(struct archive_entry *);
+__LA_DECL int archive_entry_ino_is_set(struct archive_entry *);
+__LA_DECL __LA_MODE_T archive_entry_mode(struct archive_entry *);
+__LA_DECL time_t archive_entry_mtime(struct archive_entry *);
+__LA_DECL long archive_entry_mtime_nsec(struct archive_entry *);
+__LA_DECL int archive_entry_mtime_is_set(struct archive_entry *);
+__LA_DECL unsigned int archive_entry_nlink(struct archive_entry *);
+__LA_DECL const char *archive_entry_pathname(struct archive_entry *);
+__LA_DECL const char *archive_entry_pathname_utf8(struct archive_entry *);
+__LA_DECL const wchar_t *archive_entry_pathname_w(struct archive_entry *);
+__LA_DECL __LA_MODE_T archive_entry_perm(struct archive_entry *);
+__LA_DECL dev_t archive_entry_rdev(struct archive_entry *);
+__LA_DECL dev_t archive_entry_rdevmajor(struct archive_entry *);
+__LA_DECL dev_t archive_entry_rdevminor(struct archive_entry *);
+__LA_DECL const char *archive_entry_sourcepath(struct archive_entry *);
+__LA_DECL const wchar_t *archive_entry_sourcepath_w(struct archive_entry *);
+__LA_DECL la_int64_t archive_entry_size(struct archive_entry *);
+__LA_DECL int archive_entry_size_is_set(struct archive_entry *);
+__LA_DECL const char *archive_entry_strmode(struct archive_entry *);
+__LA_DECL const char *archive_entry_symlink(struct archive_entry *);
+__LA_DECL const char *archive_entry_symlink_utf8(struct archive_entry *);
+__LA_DECL int archive_entry_symlink_type(struct archive_entry *);
+__LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *);
+__LA_DECL la_int64_t archive_entry_uid(struct archive_entry *);
+__LA_DECL const char *archive_entry_uname(struct archive_entry *);
+__LA_DECL const char *archive_entry_uname_utf8(struct archive_entry *);
+__LA_DECL const wchar_t *archive_entry_uname_w(struct archive_entry *);
+__LA_DECL int archive_entry_is_data_encrypted(struct archive_entry *);
+__LA_DECL int archive_entry_is_metadata_encrypted(struct archive_entry *);
+__LA_DECL int archive_entry_is_encrypted(struct archive_entry *);
+
+/*
+ * Set fields in an archive_entry.
+ *
+ * Note: Before libarchive 2.4, there were 'set' and 'copy' versions
+ * of the string setters. 'copy' copied the actual string, 'set' just
+ * stored the pointer. In libarchive 2.4 and later, strings are
+ * always copied.
+ */
+
+__LA_DECL void archive_entry_set_atime(struct archive_entry *, time_t, long);
+__LA_DECL void archive_entry_unset_atime(struct archive_entry *);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+__LA_DECL void archive_entry_copy_bhfi(struct archive_entry *, BY_HANDLE_FILE_INFORMATION *);
+#endif
+__LA_DECL void archive_entry_set_birthtime(struct archive_entry *, time_t, long);
+__LA_DECL void archive_entry_unset_birthtime(struct archive_entry *);
+__LA_DECL void archive_entry_set_ctime(struct archive_entry *, time_t, long);
+__LA_DECL void archive_entry_unset_ctime(struct archive_entry *);
+__LA_DECL void archive_entry_set_dev(struct archive_entry *, dev_t);
+__LA_DECL void archive_entry_set_devmajor(struct archive_entry *, dev_t);
+__LA_DECL void archive_entry_set_devminor(struct archive_entry *, dev_t);
+__LA_DECL void archive_entry_set_filetype(struct archive_entry *, unsigned int);
+__LA_DECL void archive_entry_set_fflags(struct archive_entry *,
+ unsigned long /* set */, unsigned long /* clear */);
+/* Returns pointer to start of first invalid token, or NULL if none. */
+/* Note that all recognized tokens are processed, regardless. */
+__LA_DECL const char *archive_entry_copy_fflags_text(struct archive_entry *,
+ const char *);
+__LA_DECL const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,
+ const wchar_t *);
+__LA_DECL void archive_entry_set_gid(struct archive_entry *, la_int64_t);
+__LA_DECL void archive_entry_set_gname(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_gname_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_gname(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int archive_entry_update_gname_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_hardlink(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_hardlink_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_hardlink(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int archive_entry_update_hardlink_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_ino(struct archive_entry *, la_int64_t);
+__LA_DECL void archive_entry_set_ino64(struct archive_entry *, la_int64_t);
+__LA_DECL void archive_entry_set_link(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_link_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_link(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_link_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int archive_entry_update_link_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_mode(struct archive_entry *, __LA_MODE_T);
+__LA_DECL void archive_entry_set_mtime(struct archive_entry *, time_t, long);
+__LA_DECL void archive_entry_unset_mtime(struct archive_entry *);
+__LA_DECL void archive_entry_set_nlink(struct archive_entry *, unsigned int);
+__LA_DECL void archive_entry_set_pathname(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_pathname_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_pathname(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int archive_entry_update_pathname_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_perm(struct archive_entry *, __LA_MODE_T);
+__LA_DECL void archive_entry_set_rdev(struct archive_entry *, dev_t);
+__LA_DECL void archive_entry_set_rdevmajor(struct archive_entry *, dev_t);
+__LA_DECL void archive_entry_set_rdevminor(struct archive_entry *, dev_t);
+__LA_DECL void archive_entry_set_size(struct archive_entry *, la_int64_t);
+__LA_DECL void archive_entry_unset_size(struct archive_entry *);
+__LA_DECL void archive_entry_copy_sourcepath(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_sourcepath_w(struct archive_entry *, const wchar_t *);
+__LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_symlink_type(struct archive_entry *, int);
+__LA_DECL void archive_entry_set_symlink_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_symlink(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int archive_entry_update_symlink_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_uid(struct archive_entry *, la_int64_t);
+__LA_DECL void archive_entry_set_uname(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_uname_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_uname(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int archive_entry_update_uname_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_is_data_encrypted(struct archive_entry *, char is_encrypted);
+__LA_DECL void archive_entry_set_is_metadata_encrypted(struct archive_entry *, char is_encrypted);
+/*
+ * Routines to bulk copy fields to/from a platform-native "struct
+ * stat." Libarchive used to just store a struct stat inside of each
+ * archive_entry object, but this created issues when trying to
+ * manipulate archives on systems different than the ones they were
+ * created on.
+ *
+ * TODO: On Linux and other LFS systems, provide both stat32 and
+ * stat64 versions of these functions and all of the macro glue so
+ * that archive_entry_stat is magically defined to
+ * archive_entry_stat32 or archive_entry_stat64 as appropriate.
+ */
+__LA_DECL const struct stat *archive_entry_stat(struct archive_entry *);
+__LA_DECL void archive_entry_copy_stat(struct archive_entry *, const struct stat *);
+
+/*
+ * Storage for Mac OS-specific AppleDouble metadata information.
+ * Apple-format tar files store a separate binary blob containing
+ * encoded metadata with ACL, extended attributes, etc.
+ * This provides a place to store that blob.
+ */
+
+__LA_DECL const void * archive_entry_mac_metadata(struct archive_entry *, size_t *);
+__LA_DECL void archive_entry_copy_mac_metadata(struct archive_entry *, const void *, size_t);
+
+/*
+ * Digest routine. This is used to query the raw hex digest for the
+ * given entry. The type of digest is provided as an argument.
+ */
+#define ARCHIVE_ENTRY_DIGEST_MD5 0x00000001
+#define ARCHIVE_ENTRY_DIGEST_RMD160 0x00000002
+#define ARCHIVE_ENTRY_DIGEST_SHA1 0x00000003
+#define ARCHIVE_ENTRY_DIGEST_SHA256 0x00000004
+#define ARCHIVE_ENTRY_DIGEST_SHA384 0x00000005
+#define ARCHIVE_ENTRY_DIGEST_SHA512 0x00000006
+
+__LA_DECL const unsigned char * archive_entry_digest(struct archive_entry *, int /* type */);
+
+/*
+ * ACL routines. This used to simply store and return text-format ACL
+ * strings, but that proved insufficient for a number of reasons:
+ * = clients need control over uname/uid and gname/gid mappings
+ * = there are many different ACL text formats
+ * = would like to be able to read/convert archives containing ACLs
+ * on platforms that lack ACL libraries
+ *
+ * This last point, in particular, forces me to implement a reasonably
+ * complete set of ACL support routines.
+ */
+
+/*
+ * Permission bits.
+ */
+#define ARCHIVE_ENTRY_ACL_EXECUTE 0x00000001
+#define ARCHIVE_ENTRY_ACL_WRITE 0x00000002
+#define ARCHIVE_ENTRY_ACL_READ 0x00000004
+#define ARCHIVE_ENTRY_ACL_READ_DATA 0x00000008
+#define ARCHIVE_ENTRY_ACL_LIST_DIRECTORY 0x00000008
+#define ARCHIVE_ENTRY_ACL_WRITE_DATA 0x00000010
+#define ARCHIVE_ENTRY_ACL_ADD_FILE 0x00000010
+#define ARCHIVE_ENTRY_ACL_APPEND_DATA 0x00000020
+#define ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY 0x00000020
+#define ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS 0x00000040
+#define ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS 0x00000080
+#define ARCHIVE_ENTRY_ACL_DELETE_CHILD 0x00000100
+#define ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES 0x00000200
+#define ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES 0x00000400
+#define ARCHIVE_ENTRY_ACL_DELETE 0x00000800
+#define ARCHIVE_ENTRY_ACL_READ_ACL 0x00001000
+#define ARCHIVE_ENTRY_ACL_WRITE_ACL 0x00002000
+#define ARCHIVE_ENTRY_ACL_WRITE_OWNER 0x00004000
+#define ARCHIVE_ENTRY_ACL_SYNCHRONIZE 0x00008000
+
+#define ARCHIVE_ENTRY_ACL_PERMS_POSIX1E \
+ (ARCHIVE_ENTRY_ACL_EXECUTE \
+ | ARCHIVE_ENTRY_ACL_WRITE \
+ | ARCHIVE_ENTRY_ACL_READ)
+
+#define ARCHIVE_ENTRY_ACL_PERMS_NFS4 \
+ (ARCHIVE_ENTRY_ACL_EXECUTE \
+ | ARCHIVE_ENTRY_ACL_READ_DATA \
+ | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY \
+ | ARCHIVE_ENTRY_ACL_WRITE_DATA \
+ | ARCHIVE_ENTRY_ACL_ADD_FILE \
+ | ARCHIVE_ENTRY_ACL_APPEND_DATA \
+ | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY \
+ | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS \
+ | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS \
+ | ARCHIVE_ENTRY_ACL_DELETE_CHILD \
+ | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES \
+ | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES \
+ | ARCHIVE_ENTRY_ACL_DELETE \
+ | ARCHIVE_ENTRY_ACL_READ_ACL \
+ | ARCHIVE_ENTRY_ACL_WRITE_ACL \
+ | ARCHIVE_ENTRY_ACL_WRITE_OWNER \
+ | ARCHIVE_ENTRY_ACL_SYNCHRONIZE)
+
+/*
+ * Inheritance values (NFS4 ACLs only); included in permset.
+ */
+#define ARCHIVE_ENTRY_ACL_ENTRY_INHERITED 0x01000000
+#define ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT 0x02000000
+#define ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT 0x04000000
+#define ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT 0x08000000
+#define ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY 0x10000000
+#define ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS 0x20000000
+#define ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS 0x40000000
+
+#define ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4 \
+ (ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT \
+ | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT \
+ | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT \
+ | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY \
+ | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS \
+ | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS \
+ | ARCHIVE_ENTRY_ACL_ENTRY_INHERITED)
+
+/* We need to be able to specify combinations of these. */
+#define ARCHIVE_ENTRY_ACL_TYPE_ACCESS 0x00000100 /* POSIX.1e only */
+#define ARCHIVE_ENTRY_ACL_TYPE_DEFAULT 0x00000200 /* POSIX.1e only */
+#define ARCHIVE_ENTRY_ACL_TYPE_ALLOW 0x00000400 /* NFS4 only */
+#define ARCHIVE_ENTRY_ACL_TYPE_DENY 0x00000800 /* NFS4 only */
+#define ARCHIVE_ENTRY_ACL_TYPE_AUDIT 0x00001000 /* NFS4 only */
+#define ARCHIVE_ENTRY_ACL_TYPE_ALARM 0x00002000 /* NFS4 only */
+#define ARCHIVE_ENTRY_ACL_TYPE_POSIX1E (ARCHIVE_ENTRY_ACL_TYPE_ACCESS \
+ | ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)
+#define ARCHIVE_ENTRY_ACL_TYPE_NFS4 (ARCHIVE_ENTRY_ACL_TYPE_ALLOW \
+ | ARCHIVE_ENTRY_ACL_TYPE_DENY \
+ | ARCHIVE_ENTRY_ACL_TYPE_AUDIT \
+ | ARCHIVE_ENTRY_ACL_TYPE_ALARM)
+
+/* Tag values mimic POSIX.1e */
+#define ARCHIVE_ENTRY_ACL_USER 10001 /* Specified user. */
+#define ARCHIVE_ENTRY_ACL_USER_OBJ 10002 /* User who owns the file. */
+#define ARCHIVE_ENTRY_ACL_GROUP 10003 /* Specified group. */
+#define ARCHIVE_ENTRY_ACL_GROUP_OBJ 10004 /* Group who owns the file. */
+#define ARCHIVE_ENTRY_ACL_MASK 10005 /* Modify group access (POSIX.1e only) */
+#define ARCHIVE_ENTRY_ACL_OTHER 10006 /* Public (POSIX.1e only) */
+#define ARCHIVE_ENTRY_ACL_EVERYONE 10107 /* Everyone (NFS4 only) */
+
+/*
+ * Set the ACL by clearing it and adding entries one at a time.
+ * Unlike the POSIX.1e ACL routines, you must specify the type
+ * (access/default) for each entry. Internally, the ACL data is just
+ * a soup of entries. API calls here allow you to retrieve just the
+ * entries of interest. This design (which goes against the spirit of
+ * POSIX.1e) is useful for handling archive formats that combine
+ * default and access information in a single ACL list.
+ */
+__LA_DECL void archive_entry_acl_clear(struct archive_entry *);
+__LA_DECL int archive_entry_acl_add_entry(struct archive_entry *,
+ int /* type */, int /* permset */, int /* tag */,
+ int /* qual */, const char * /* name */);
+__LA_DECL int archive_entry_acl_add_entry_w(struct archive_entry *,
+ int /* type */, int /* permset */, int /* tag */,
+ int /* qual */, const wchar_t * /* name */);
+
+/*
+ * To retrieve the ACL, first "reset", then repeatedly ask for the
+ * "next" entry. The want_type parameter allows you to request only
+ * certain types of entries.
+ */
+__LA_DECL int archive_entry_acl_reset(struct archive_entry *, int /* want_type */);
+__LA_DECL int archive_entry_acl_next(struct archive_entry *, int /* want_type */,
+ int * /* type */, int * /* permset */, int * /* tag */,
+ int * /* qual */, const char ** /* name */);
+
+/*
+ * Construct a text-format ACL. The flags argument is a bitmask that
+ * can include any of the following:
+ *
+ * Flags only for archive entries with POSIX.1e ACL:
+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include POSIX.1e "access" entries.
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include POSIX.1e "default" entries.
+ * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each
+ * default ACL entry.
+ * ARCHIVE_ENTRY_ACL_STYLE_SOLARIS - Output only one colon after "other" and
+ * "mask" entries.
+ *
+ * Flags only for archive entries with NFSv4 ACL:
+ * ARCHIVE_ENTRY_ACL_STYLE_COMPACT - Do not output the minus character for
+ * unset permissions and flags in NFSv4 ACL permission and flag fields
+ *
+ * Flags for for archive entries with POSIX.1e ACL or NFSv4 ACL:
+ * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in
+ * each ACL entry.
+ * ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA - Separate entries with comma
+ * instead of newline.
+ */
+#define ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID 0x00000001
+#define ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT 0x00000002
+#define ARCHIVE_ENTRY_ACL_STYLE_SOLARIS 0x00000004
+#define ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA 0x00000008
+#define ARCHIVE_ENTRY_ACL_STYLE_COMPACT 0x00000010
+
+__LA_DECL wchar_t *archive_entry_acl_to_text_w(struct archive_entry *,
+ la_ssize_t * /* len */, int /* flags */);
+__LA_DECL char *archive_entry_acl_to_text(struct archive_entry *,
+ la_ssize_t * /* len */, int /* flags */);
+__LA_DECL int archive_entry_acl_from_text_w(struct archive_entry *,
+ const wchar_t * /* wtext */, int /* type */);
+__LA_DECL int archive_entry_acl_from_text(struct archive_entry *,
+ const char * /* text */, int /* type */);
+
+/* Deprecated constants */
+#define OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID 1024
+#define OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT 2048
+
+/* Deprecated functions */
+__LA_DECL const wchar_t *archive_entry_acl_text_w(struct archive_entry *,
+ int /* flags */) __LA_DEPRECATED;
+__LA_DECL const char *archive_entry_acl_text(struct archive_entry *,
+ int /* flags */) __LA_DEPRECATED;
+
+/* Return bitmask of ACL types in an archive entry */
+__LA_DECL int archive_entry_acl_types(struct archive_entry *);
+
+/* Return a count of entries matching 'want_type' */
+__LA_DECL int archive_entry_acl_count(struct archive_entry *, int /* want_type */);
+
+/* Return an opaque ACL object. */
+/* There's not yet anything clients can actually do with this... */
+struct archive_acl;
+__LA_DECL struct archive_acl *archive_entry_acl(struct archive_entry *);
+
+/*
+ * extended attributes
+ */
+
+__LA_DECL void archive_entry_xattr_clear(struct archive_entry *);
+__LA_DECL void archive_entry_xattr_add_entry(struct archive_entry *,
+ const char * /* name */, const void * /* value */,
+ size_t /* size */);
+
+/*
+ * To retrieve the xattr list, first "reset", then repeatedly ask for the
+ * "next" entry.
+ */
+
+__LA_DECL int archive_entry_xattr_count(struct archive_entry *);
+__LA_DECL int archive_entry_xattr_reset(struct archive_entry *);
+__LA_DECL int archive_entry_xattr_next(struct archive_entry *,
+ const char ** /* name */, const void ** /* value */, size_t *);
+
+/*
+ * sparse
+ */
+
+__LA_DECL void archive_entry_sparse_clear(struct archive_entry *);
+__LA_DECL void archive_entry_sparse_add_entry(struct archive_entry *,
+ la_int64_t /* offset */, la_int64_t /* length */);
+
+/*
+ * To retrieve the xattr list, first "reset", then repeatedly ask for the
+ * "next" entry.
+ */
+
+__LA_DECL int archive_entry_sparse_count(struct archive_entry *);
+__LA_DECL int archive_entry_sparse_reset(struct archive_entry *);
+__LA_DECL int archive_entry_sparse_next(struct archive_entry *,
+ la_int64_t * /* offset */, la_int64_t * /* length */);
+
+/*
+ * Utility to match up hardlinks.
+ *
+ * The 'struct archive_entry_linkresolver' is a cache of archive entries
+ * for files with multiple links. Here's how to use it:
+ * 1. Create a lookup object with archive_entry_linkresolver_new()
+ * 2. Tell it the archive format you're using.
+ * 3. Hand each archive_entry to archive_entry_linkify().
+ * That function will return 0, 1, or 2 entries that should
+ * be written.
+ * 4. Call archive_entry_linkify(resolver, NULL) until
+ * no more entries are returned.
+ * 5. Call archive_entry_linkresolver_free(resolver) to free resources.
+ *
+ * The entries returned have their hardlink and size fields updated
+ * appropriately. If an entry is passed in that does not refer to
+ * a file with multiple links, it is returned unchanged. The intention
+ * is that you should be able to simply filter all entries through
+ * this machine.
+ *
+ * To make things more efficient, be sure that each entry has a valid
+ * nlinks value. The hardlink cache uses this to track when all links
+ * have been found. If the nlinks value is zero, it will keep every
+ * name in the cache indefinitely, which can use a lot of memory.
+ *
+ * Note that archive_entry_size() is reset to zero if the file
+ * body should not be written to the archive. Pay attention!
+ */
+struct archive_entry_linkresolver;
+
+/*
+ * There are three different strategies for marking hardlinks.
+ * The descriptions below name them after the best-known
+ * formats that rely on each strategy:
+ *
+ * "Old cpio" is the simplest, it always returns any entry unmodified.
+ * As far as I know, only cpio formats use this. Old cpio archives
+ * store every link with the full body; the onus is on the dearchiver
+ * to detect and properly link the files as they are restored.
+ * "tar" is also pretty simple; it caches a copy the first time it sees
+ * any link. Subsequent appearances are modified to be hardlink
+ * references to the first one without any body. Used by all tar
+ * formats, although the newest tar formats permit the "old cpio" strategy
+ * as well. This strategy is very simple for the dearchiver,
+ * and reasonably straightforward for the archiver.
+ * "new cpio" is trickier. It stores the body only with the last
+ * occurrence. The complication is that we might not
+ * see every link to a particular file in a single session, so
+ * there's no easy way to know when we've seen the last occurrence.
+ * The solution here is to queue one link until we see the next.
+ * At the end of the session, you can enumerate any remaining
+ * entries by calling archive_entry_linkify(NULL) and store those
+ * bodies. If you have a file with three links l1, l2, and l3,
+ * you'll get the following behavior if you see all three links:
+ * linkify(l1) => NULL (the resolver stores l1 internally)
+ * linkify(l2) => l1 (resolver stores l2, you write l1)
+ * linkify(l3) => l2, l3 (all links seen, you can write both).
+ * If you only see l1 and l2, you'll get this behavior:
+ * linkify(l1) => NULL
+ * linkify(l2) => l1
+ * linkify(NULL) => l2 (at end, you retrieve remaining links)
+ * As the name suggests, this strategy is used by newer cpio variants.
+ * It's noticeably more complex for the archiver, slightly more complex
+ * for the dearchiver than the tar strategy, but makes it straightforward
+ * to restore a file using any link by simply continuing to scan until
+ * you see a link that is stored with a body. In contrast, the tar
+ * strategy requires you to rescan the archive from the beginning to
+ * correctly extract an arbitrary link.
+ */
+
+__LA_DECL struct archive_entry_linkresolver *archive_entry_linkresolver_new(void);
+__LA_DECL void archive_entry_linkresolver_set_strategy(
+ struct archive_entry_linkresolver *, int /* format_code */);
+__LA_DECL void archive_entry_linkresolver_free(struct archive_entry_linkresolver *);
+__LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *,
+ struct archive_entry **, struct archive_entry **);
+__LA_DECL struct archive_entry *archive_entry_partial_links(
+ struct archive_entry_linkresolver *res, unsigned int *links);
+#ifdef __cplusplus
+}
+#endif
+
+/* This is meaningless outside of this header. */
+#undef __LA_DECL
+
+#endif /* !ARCHIVE_ENTRY_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_copy_bhfi.c b/contrib/libs/libarchive/libarchive/archive_entry_copy_bhfi.c
new file mode 100644
index 00000000000..77bf38e450f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_copy_bhfi.c
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive_private.h"
+#include "archive_entry.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+
+__inline static void
+fileTimeToUtc(const FILETIME *filetime, time_t *t, long *ns)
+{
+ ULARGE_INTEGER utc;
+
+ utc.HighPart = filetime->dwHighDateTime;
+ utc.LowPart = filetime->dwLowDateTime;
+ if (utc.QuadPart >= EPOC_TIME) {
+ utc.QuadPart -= EPOC_TIME;
+ *t = (time_t)(utc.QuadPart / 10000000); /* milli seconds base */
+ *ns = (long)(utc.QuadPart % 10000000) * 100;/* nano seconds base */
+ } else {
+ *t = 0;
+ *ns = 0;
+ }
+}
+
+void
+archive_entry_copy_bhfi(struct archive_entry *entry,
+ BY_HANDLE_FILE_INFORMATION *bhfi)
+{
+ time_t secs;
+ long nsecs;
+
+ fileTimeToUtc(&bhfi->ftLastAccessTime, &secs, &nsecs);
+ archive_entry_set_atime(entry, secs, nsecs);
+ fileTimeToUtc(&bhfi->ftLastWriteTime, &secs, &nsecs);
+ archive_entry_set_mtime(entry, secs, nsecs);
+ fileTimeToUtc(&bhfi->ftCreationTime, &secs, &nsecs);
+ archive_entry_set_birthtime(entry, secs, nsecs);
+ archive_entry_set_ctime(entry, secs, nsecs);
+ archive_entry_set_dev(entry, bhfi->dwVolumeSerialNumber);
+ archive_entry_set_ino64(entry, (((int64_t)bhfi->nFileIndexHigh) << 32)
+ + bhfi->nFileIndexLow);
+ archive_entry_set_nlink(entry, bhfi->nNumberOfLinks);
+ archive_entry_set_size(entry, (((int64_t)bhfi->nFileSizeHigh) << 32)
+ + bhfi->nFileSizeLow);
+ /* archive_entry_set_mode(entry, st->st_mode); */
+}
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_copy_stat.c b/contrib/libs/libarchive/libarchive/archive_entry_copy_stat.c
new file mode 100644
index 00000000000..ac83868e8f8
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_copy_stat.c
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_copy_stat.c 189466 2009-03-07 00:52:02Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+
+void
+archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st)
+{
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+ archive_entry_set_atime(entry, st->st_atime, st->st_atimespec.tv_nsec);
+ archive_entry_set_ctime(entry, st->st_ctime, st->st_ctimespec.tv_nsec);
+ archive_entry_set_mtime(entry, st->st_mtime, st->st_mtimespec.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+ archive_entry_set_atime(entry, st->st_atime, st->st_atim.tv_nsec);
+ archive_entry_set_ctime(entry, st->st_ctime, st->st_ctim.tv_nsec);
+ archive_entry_set_mtime(entry, st->st_mtime, st->st_mtim.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_MTIME_NSEC
+ archive_entry_set_atime(entry, st->st_atime, st->st_atime_nsec);
+ archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_nsec);
+ archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_nsec);
+#elif HAVE_STRUCT_STAT_ST_MTIME_N
+ archive_entry_set_atime(entry, st->st_atime, st->st_atime_n);
+ archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_n);
+ archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_n);
+#elif HAVE_STRUCT_STAT_ST_UMTIME
+ archive_entry_set_atime(entry, st->st_atime, st->st_uatime * 1000);
+ archive_entry_set_ctime(entry, st->st_ctime, st->st_uctime * 1000);
+ archive_entry_set_mtime(entry, st->st_mtime, st->st_umtime * 1000);
+#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
+ archive_entry_set_atime(entry, st->st_atime, st->st_atime_usec * 1000);
+ archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_usec * 1000);
+ archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_usec * 1000);
+#else
+ archive_entry_set_atime(entry, st->st_atime, 0);
+ archive_entry_set_ctime(entry, st->st_ctime, 0);
+ archive_entry_set_mtime(entry, st->st_mtime, 0);
+#endif
+#if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC
+ archive_entry_set_birthtime(entry, st->st_birthtime, st->st_birthtimespec.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_BIRTHTIME
+ archive_entry_set_birthtime(entry, st->st_birthtime, 0);
+#else
+ archive_entry_unset_birthtime(entry);
+#endif
+ archive_entry_set_dev(entry, st->st_dev);
+ archive_entry_set_gid(entry, st->st_gid);
+ archive_entry_set_uid(entry, st->st_uid);
+ archive_entry_set_ino(entry, st->st_ino);
+ archive_entry_set_nlink(entry, st->st_nlink);
+ archive_entry_set_rdev(entry, st->st_rdev);
+ archive_entry_set_size(entry, st->st_size);
+ archive_entry_set_mode(entry, st->st_mode);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_link_resolver.c b/contrib/libs/libarchive/libarchive/archive_entry_link_resolver.c
new file mode 100644
index 00000000000..c7d59497a7c
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_link_resolver.c
@@ -0,0 +1,447 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_link_resolver.c 201100 2009-12-28 03:05:31Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+
+/*
+ * This is mostly a pretty straightforward hash table implementation.
+ * The only interesting bit is the different strategies used to
+ * match up links. These strategies match those used by various
+ * archiving formats:
+ * tar - content stored with first link, remainder refer back to it.
+ * This requires us to match each subsequent link up with the
+ * first appearance.
+ * cpio - Old cpio just stored body with each link, match-ups were
+ * implicit. This is trivial.
+ * new cpio - New cpio only stores body with last link, match-ups
+ * are implicit. This is actually quite tricky; see the notes
+ * below.
+ */
+
+/* Users pass us a format code, we translate that into a strategy here. */
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_TAR 0
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE 1
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO 2
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO 3
+
+/* Initial size of link cache. */
+#define links_cache_initial_size 1024
+
+struct links_entry {
+ struct links_entry *next;
+ struct links_entry *previous;
+ struct archive_entry *canonical;
+ struct archive_entry *entry;
+ size_t hash;
+ unsigned int links; /* # links not yet seen */
+};
+
+struct archive_entry_linkresolver {
+ struct links_entry **buckets;
+ struct links_entry *spare;
+ unsigned long number_entries;
+ size_t number_buckets;
+ int strategy;
+};
+
+#define NEXT_ENTRY_DEFERRED 1
+#define NEXT_ENTRY_PARTIAL 2
+#define NEXT_ENTRY_ALL (NEXT_ENTRY_DEFERRED | NEXT_ENTRY_PARTIAL)
+
+static struct links_entry *find_entry(struct archive_entry_linkresolver *,
+ struct archive_entry *);
+static void grow_hash(struct archive_entry_linkresolver *);
+static struct links_entry *insert_entry(struct archive_entry_linkresolver *,
+ struct archive_entry *);
+static struct links_entry *next_entry(struct archive_entry_linkresolver *,
+ int);
+
+struct archive_entry_linkresolver *
+archive_entry_linkresolver_new(void)
+{
+ struct archive_entry_linkresolver *res;
+
+ /* Check for positive power-of-two */
+ if (links_cache_initial_size == 0 ||
+ (links_cache_initial_size & (links_cache_initial_size - 1)) != 0)
+ return (NULL);
+
+ res = calloc(1, sizeof(struct archive_entry_linkresolver));
+ if (res == NULL)
+ return (NULL);
+ res->number_buckets = links_cache_initial_size;
+ res->buckets = calloc(res->number_buckets, sizeof(res->buckets[0]));
+ if (res->buckets == NULL) {
+ free(res);
+ return (NULL);
+ }
+ return (res);
+}
+
+void
+archive_entry_linkresolver_set_strategy(struct archive_entry_linkresolver *res,
+ int fmt)
+{
+ int fmtbase = fmt & ARCHIVE_FORMAT_BASE_MASK;
+
+ switch (fmtbase) {
+ case ARCHIVE_FORMAT_7ZIP:
+ case ARCHIVE_FORMAT_AR:
+ case ARCHIVE_FORMAT_ZIP:
+ res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
+ break;
+ case ARCHIVE_FORMAT_CPIO:
+ switch (fmt) {
+ case ARCHIVE_FORMAT_CPIO_SVR4_NOCRC:
+ case ARCHIVE_FORMAT_CPIO_SVR4_CRC:
+ res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO;
+ break;
+ default:
+ res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
+ break;
+ }
+ break;
+ case ARCHIVE_FORMAT_MTREE:
+ res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE;
+ break;
+ case ARCHIVE_FORMAT_ISO9660:
+ case ARCHIVE_FORMAT_SHAR:
+ case ARCHIVE_FORMAT_TAR:
+ case ARCHIVE_FORMAT_XAR:
+ res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;
+ break;
+ default:
+ res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
+ break;
+ }
+}
+
+void
+archive_entry_linkresolver_free(struct archive_entry_linkresolver *res)
+{
+ struct links_entry *le;
+
+ if (res == NULL)
+ return;
+
+ while ((le = next_entry(res, NEXT_ENTRY_ALL)) != NULL)
+ archive_entry_free(le->entry);
+ free(res->buckets);
+ free(res);
+}
+
+void
+archive_entry_linkify(struct archive_entry_linkresolver *res,
+ struct archive_entry **e, struct archive_entry **f)
+{
+ struct links_entry *le;
+ struct archive_entry *t;
+
+ *f = NULL; /* Default: Don't return a second entry. */
+
+ if (*e == NULL) {
+ le = next_entry(res, NEXT_ENTRY_DEFERRED);
+ if (le != NULL) {
+ *e = le->entry;
+ le->entry = NULL;
+ }
+ return;
+ }
+
+ /* If it has only one link, then we're done. */
+ if (archive_entry_nlink(*e) == 1)
+ return;
+ /* Directories, devices never have hardlinks. */
+ if (archive_entry_filetype(*e) == AE_IFDIR
+ || archive_entry_filetype(*e) == AE_IFBLK
+ || archive_entry_filetype(*e) == AE_IFCHR)
+ return;
+
+ switch (res->strategy) {
+ case ARCHIVE_ENTRY_LINKIFY_LIKE_TAR:
+ le = find_entry(res, *e);
+ if (le != NULL) {
+ archive_entry_unset_size(*e);
+ archive_entry_copy_hardlink(*e,
+ archive_entry_pathname(le->canonical));
+ } else
+ insert_entry(res, *e);
+ return;
+ case ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE:
+ le = find_entry(res, *e);
+ if (le != NULL) {
+ archive_entry_copy_hardlink(*e,
+ archive_entry_pathname(le->canonical));
+ } else
+ insert_entry(res, *e);
+ return;
+ case ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO:
+ /* This one is trivial. */
+ return;
+ case ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO:
+ le = find_entry(res, *e);
+ if (le != NULL) {
+ /*
+ * Put the new entry in le, return the
+ * old entry from le.
+ */
+ t = *e;
+ *e = le->entry;
+ le->entry = t;
+ /* Make the old entry into a hardlink. */
+ archive_entry_unset_size(*e);
+ archive_entry_copy_hardlink(*e,
+ archive_entry_pathname(le->canonical));
+ /* If we ran out of links, return the
+ * final entry as well. */
+ if (le->links == 0) {
+ *f = le->entry;
+ le->entry = NULL;
+ }
+ } else {
+ /*
+ * If we haven't seen it, tuck it away
+ * for future use.
+ */
+ le = insert_entry(res, *e);
+ if (le == NULL)
+ /* XXX We should return an error code XXX */
+ return;
+ le->entry = *e;
+ *e = NULL;
+ }
+ return;
+ default:
+ break;
+ }
+ return;
+}
+
+static struct links_entry *
+find_entry(struct archive_entry_linkresolver *res,
+ struct archive_entry *entry)
+{
+ struct links_entry *le;
+ size_t hash, bucket;
+ dev_t dev;
+ int64_t ino;
+
+ /* Free a held entry. */
+ if (res->spare != NULL) {
+ archive_entry_free(res->spare->canonical);
+ archive_entry_free(res->spare->entry);
+ free(res->spare);
+ res->spare = NULL;
+ }
+
+ dev = archive_entry_dev(entry);
+ ino = archive_entry_ino64(entry);
+ hash = (size_t)(dev ^ ino);
+
+ /* Try to locate this entry in the links cache. */
+ bucket = hash & (res->number_buckets - 1);
+ for (le = res->buckets[bucket]; le != NULL; le = le->next) {
+ if (le->hash == hash
+ && dev == archive_entry_dev(le->canonical)
+ && ino == archive_entry_ino64(le->canonical)) {
+ /*
+ * Decrement link count each time and release
+ * the entry if it hits zero. This saves
+ * memory and is necessary for detecting
+ * missed links.
+ */
+ --le->links;
+ if (le->links > 0)
+ return (le);
+ /* Remove it from this hash bucket. */
+ if (le->previous != NULL)
+ le->previous->next = le->next;
+ if (le->next != NULL)
+ le->next->previous = le->previous;
+ if (res->buckets[bucket] == le)
+ res->buckets[bucket] = le->next;
+ res->number_entries--;
+ /* Defer freeing this entry. */
+ res->spare = le;
+ return (le);
+ }
+ }
+ return (NULL);
+}
+
+static struct links_entry *
+next_entry(struct archive_entry_linkresolver *res, int mode)
+{
+ struct links_entry *le;
+ size_t bucket;
+
+ /* Free a held entry. */
+ if (res->spare != NULL) {
+ archive_entry_free(res->spare->canonical);
+ archive_entry_free(res->spare->entry);
+ free(res->spare);
+ res->spare = NULL;
+ }
+
+ /* Look for next non-empty bucket in the links cache. */
+ for (bucket = 0; bucket < res->number_buckets; bucket++) {
+ for (le = res->buckets[bucket]; le != NULL; le = le->next) {
+ if (le->entry != NULL &&
+ (mode & NEXT_ENTRY_DEFERRED) == 0)
+ continue;
+ if (le->entry == NULL &&
+ (mode & NEXT_ENTRY_PARTIAL) == 0)
+ continue;
+ /* Remove it from this hash bucket. */
+ if (le->next != NULL)
+ le->next->previous = le->previous;
+ if (le->previous != NULL)
+ le->previous->next = le->next;
+ else
+ res->buckets[bucket] = le->next;
+ res->number_entries--;
+ /* Defer freeing this entry. */
+ res->spare = le;
+ return (le);
+ }
+ }
+ return (NULL);
+}
+
+static struct links_entry *
+insert_entry(struct archive_entry_linkresolver *res,
+ struct archive_entry *entry)
+{
+ struct links_entry *le;
+ size_t hash, bucket;
+
+ /* Add this entry to the links cache. */
+ le = calloc(1, sizeof(struct links_entry));
+ if (le == NULL)
+ return (NULL);
+ le->canonical = archive_entry_clone(entry);
+
+ /* If the links cache is getting too full, enlarge the hash table. */
+ if (res->number_entries > res->number_buckets * 2)
+ grow_hash(res);
+
+ hash = (size_t)(archive_entry_dev(entry) ^ archive_entry_ino64(entry));
+ bucket = hash & (res->number_buckets - 1);
+
+ /* If we could allocate the entry, record it. */
+ if (res->buckets[bucket] != NULL)
+ res->buckets[bucket]->previous = le;
+ res->number_entries++;
+ le->next = res->buckets[bucket];
+ le->previous = NULL;
+ res->buckets[bucket] = le;
+ le->hash = hash;
+ le->links = archive_entry_nlink(entry) - 1;
+ return (le);
+}
+
+static void
+grow_hash(struct archive_entry_linkresolver *res)
+{
+ struct links_entry *le, **new_buckets;
+ size_t new_size;
+ size_t i, bucket;
+
+ /* Try to enlarge the bucket list. */
+ new_size = res->number_buckets * 2;
+ if (new_size < res->number_buckets)
+ return;
+ new_buckets = calloc(new_size, sizeof(struct links_entry *));
+
+ if (new_buckets == NULL)
+ return;
+
+ for (i = 0; i < res->number_buckets; i++) {
+ while (res->buckets[i] != NULL) {
+ /* Remove entry from old bucket. */
+ le = res->buckets[i];
+ res->buckets[i] = le->next;
+
+ /* Add entry to new bucket. */
+ bucket = le->hash & (new_size - 1);
+
+ if (new_buckets[bucket] != NULL)
+ new_buckets[bucket]->previous = le;
+ le->next = new_buckets[bucket];
+ le->previous = NULL;
+ new_buckets[bucket] = le;
+ }
+ }
+ free(res->buckets);
+ res->buckets = new_buckets;
+ res->number_buckets = new_size;
+}
+
+struct archive_entry *
+archive_entry_partial_links(struct archive_entry_linkresolver *res,
+ unsigned int *links)
+{
+ struct archive_entry *e;
+ struct links_entry *le;
+
+ /* Free a held entry. */
+ if (res->spare != NULL) {
+ archive_entry_free(res->spare->canonical);
+ archive_entry_free(res->spare->entry);
+ free(res->spare);
+ res->spare = NULL;
+ }
+
+ le = next_entry(res, NEXT_ENTRY_PARTIAL);
+ if (le != NULL) {
+ e = le->canonical;
+ if (links != NULL)
+ *links = le->links;
+ le->canonical = NULL;
+ } else {
+ e = NULL;
+ if (links != NULL)
+ *links = 0;
+ }
+ return (e);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_locale.h b/contrib/libs/libarchive/libarchive/archive_entry_locale.h
new file mode 100644
index 00000000000..803c0368bb6
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_locale.h
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 2011 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_ENTRY_LOCALE_H_INCLUDED
+#define ARCHIVE_ENTRY_LOCALE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+struct archive_entry;
+struct archive_string_conv;
+
+/*
+ * Utility functions to set and get entry attributes by translating
+ * character-set. These are designed for use in format readers and writers.
+ *
+ * The return code and interface of these are quite different from other
+ * functions for archive_entry defined in archive_entry.h.
+ * Common return code are:
+ * Return 0 if the string conversion succeeded.
+ * Return -1 if the string conversion failed.
+ */
+
+#define archive_entry_gname_l _archive_entry_gname_l
+int _archive_entry_gname_l(struct archive_entry *,
+ const char **, size_t *, struct archive_string_conv *);
+#define archive_entry_hardlink_l _archive_entry_hardlink_l
+int _archive_entry_hardlink_l(struct archive_entry *,
+ const char **, size_t *, struct archive_string_conv *);
+#define archive_entry_pathname_l _archive_entry_pathname_l
+int _archive_entry_pathname_l(struct archive_entry *,
+ const char **, size_t *, struct archive_string_conv *);
+#define archive_entry_symlink_l _archive_entry_symlink_l
+int _archive_entry_symlink_l(struct archive_entry *,
+ const char **, size_t *, struct archive_string_conv *);
+#define archive_entry_uname_l _archive_entry_uname_l
+int _archive_entry_uname_l(struct archive_entry *,
+ const char **, size_t *, struct archive_string_conv *);
+#define archive_entry_acl_text_l _archive_entry_acl_text_l
+int _archive_entry_acl_text_l(struct archive_entry *, int,
+const char **, size_t *, struct archive_string_conv *) __LA_DEPRECATED;
+#define archive_entry_acl_to_text_l _archive_entry_acl_to_text_l
+char *_archive_entry_acl_to_text_l(struct archive_entry *, ssize_t *, int,
+ struct archive_string_conv *);
+#define archive_entry_acl_from_text_l _archive_entry_acl_from_text_l
+int _archive_entry_acl_from_text_l(struct archive_entry *, const char* text,
+ int type, struct archive_string_conv *);
+#define archive_entry_copy_gname_l _archive_entry_copy_gname_l
+int _archive_entry_copy_gname_l(struct archive_entry *,
+ const char *, size_t, struct archive_string_conv *);
+#define archive_entry_copy_hardlink_l _archive_entry_copy_hardlink_l
+int _archive_entry_copy_hardlink_l(struct archive_entry *,
+ const char *, size_t, struct archive_string_conv *);
+#define archive_entry_copy_link_l _archive_entry_copy_link_l
+int _archive_entry_copy_link_l(struct archive_entry *,
+ const char *, size_t, struct archive_string_conv *);
+#define archive_entry_copy_pathname_l _archive_entry_copy_pathname_l
+int _archive_entry_copy_pathname_l(struct archive_entry *,
+ const char *, size_t, struct archive_string_conv *);
+#define archive_entry_copy_symlink_l _archive_entry_copy_symlink_l
+int _archive_entry_copy_symlink_l(struct archive_entry *,
+ const char *, size_t, struct archive_string_conv *);
+#define archive_entry_copy_uname_l _archive_entry_copy_uname_l
+int _archive_entry_copy_uname_l(struct archive_entry *,
+ const char *, size_t, struct archive_string_conv *);
+
+#endif /* ARCHIVE_ENTRY_LOCALE_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_private.h b/contrib/libs/libarchive/libarchive/archive_entry_private.h
new file mode 100644
index 00000000000..cf4deb24ec8
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_private.h
@@ -0,0 +1,200 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_entry_private.h 201096 2009-12-28 02:41:27Z kientzle $
+ */
+
+#ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
+#define ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include "archive_acl_private.h"
+#include "archive_string.h"
+
+struct ae_xattr {
+ struct ae_xattr *next;
+
+ char *name;
+ void *value;
+ size_t size;
+};
+
+struct ae_sparse {
+ struct ae_sparse *next;
+
+ int64_t offset;
+ int64_t length;
+};
+
+struct ae_digest {
+ unsigned char md5[16];
+ unsigned char rmd160[20];
+ unsigned char sha1[20];
+ unsigned char sha256[32];
+ unsigned char sha384[48];
+ unsigned char sha512[64];
+};
+
+/*
+ * Description of an archive entry.
+ *
+ * Basically, this is a "struct stat" with a few text fields added in.
+ *
+ * TODO: Add "comment", "charset", and possibly other entries
+ * that are supported by "pax interchange" format. However, GNU, ustar,
+ * cpio, and other variants don't support these features, so they're not an
+ * excruciatingly high priority right now.
+ *
+ * TODO: "pax interchange" format allows essentially arbitrary
+ * key/value attributes to be attached to any entry. Supporting
+ * such extensions may make this library useful for special
+ * applications (e.g., a package manager could attach special
+ * package-management attributes to each entry). There are tricky
+ * API issues involved, so this is not going to happen until
+ * there's a real demand for it.
+ *
+ * TODO: Design a good API for handling sparse files.
+ */
+struct archive_entry {
+ struct archive *archive;
+
+ /*
+ * Note that ae_stat.st_mode & AE_IFMT can be 0!
+ *
+ * This occurs when the actual file type of the object is not
+ * in the archive. For example, 'tar' archives store
+ * hardlinks without marking the type of the underlying
+ * object.
+ */
+
+ /*
+ * We have a "struct aest" for holding file metadata rather than just
+ * a "struct stat" because on some platforms the "struct stat" has
+ * fields which are too narrow to hold the range of possible values;
+ * we don't want to lose information if we read an archive and write
+ * out another (e.g., in "tar -cf new.tar @old.tar").
+ *
+ * The "stat" pointer points to some form of platform-specific struct
+ * stat; it is declared as a void * rather than a struct stat * as
+ * some platforms have multiple varieties of stat structures.
+ */
+ void *stat;
+ int stat_valid; /* Set to 0 whenever a field in aest changes. */
+
+ struct aest {
+ int64_t aest_atime;
+ uint32_t aest_atime_nsec;
+ int64_t aest_ctime;
+ uint32_t aest_ctime_nsec;
+ int64_t aest_mtime;
+ uint32_t aest_mtime_nsec;
+ int64_t aest_birthtime;
+ uint32_t aest_birthtime_nsec;
+ int64_t aest_gid;
+ int64_t aest_ino;
+ uint32_t aest_nlink;
+ uint64_t aest_size;
+ int64_t aest_uid;
+ /*
+ * Because converting between device codes and
+ * major/minor values is platform-specific and
+ * inherently a bit risky, we only do that conversion
+ * lazily. That way, we will do a better job of
+ * preserving information in those cases where no
+ * conversion is actually required.
+ */
+ int aest_dev_is_broken_down;
+ dev_t aest_dev;
+ dev_t aest_devmajor;
+ dev_t aest_devminor;
+ int aest_rdev_is_broken_down;
+ dev_t aest_rdev;
+ dev_t aest_rdevmajor;
+ dev_t aest_rdevminor;
+ } ae_stat;
+
+ int ae_set; /* bitmap of fields that are currently set */
+#define AE_SET_HARDLINK 1
+#define AE_SET_SYMLINK 2
+#define AE_SET_ATIME 4
+#define AE_SET_CTIME 8
+#define AE_SET_MTIME 16
+#define AE_SET_BIRTHTIME 32
+#define AE_SET_SIZE 64
+#define AE_SET_INO 128
+#define AE_SET_DEV 256
+
+ /*
+ * Use aes here so that we get transparent mbs<->wcs conversions.
+ */
+ struct archive_mstring ae_fflags_text; /* Text fflags per fflagstostr(3) */
+ unsigned long ae_fflags_set; /* Bitmap fflags */
+ unsigned long ae_fflags_clear;
+ struct archive_mstring ae_gname; /* Name of owning group */
+ struct archive_mstring ae_hardlink; /* Name of target for hardlink */
+ struct archive_mstring ae_pathname; /* Name of entry */
+ struct archive_mstring ae_symlink; /* symlink contents */
+ struct archive_mstring ae_uname; /* Name of owner */
+
+ /* Not used within libarchive; useful for some clients. */
+ struct archive_mstring ae_sourcepath; /* Path this entry is sourced from. */
+
+#define AE_ENCRYPTION_NONE 0
+#define AE_ENCRYPTION_DATA 1
+#define AE_ENCRYPTION_METADATA 2
+ char encryption;
+
+ void *mac_metadata;
+ size_t mac_metadata_size;
+
+ /* Digest support. */
+ struct ae_digest digest;
+
+ /* ACL support. */
+ struct archive_acl acl;
+
+ /* extattr support. */
+ struct ae_xattr *xattr_head;
+ struct ae_xattr *xattr_p;
+
+ /* sparse support. */
+ struct ae_sparse *sparse_head;
+ struct ae_sparse *sparse_tail;
+ struct ae_sparse *sparse_p;
+
+ /* Miscellaneous. */
+ char strmode[12];
+
+ /* Symlink type support */
+ int ae_symlink_type;
+};
+
+int
+archive_entry_set_digest(struct archive_entry *entry, int type,
+ const unsigned char *digest);
+
+#endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_sparse.c b/contrib/libs/libarchive/libarchive/archive_entry_sparse.c
new file mode 100644
index 00000000000..74917b37b80
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_sparse.c
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2010-2011 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_entry_private.h"
+
+/*
+ * sparse handling
+ */
+
+void
+archive_entry_sparse_clear(struct archive_entry *entry)
+{
+ struct ae_sparse *sp;
+
+ while (entry->sparse_head != NULL) {
+ sp = entry->sparse_head->next;
+ free(entry->sparse_head);
+ entry->sparse_head = sp;
+ }
+ entry->sparse_tail = NULL;
+}
+
+void
+archive_entry_sparse_add_entry(struct archive_entry *entry,
+ la_int64_t offset, la_int64_t length)
+{
+ struct ae_sparse *sp;
+
+ if (offset < 0 || length < 0)
+ /* Invalid value */
+ return;
+ if (offset > INT64_MAX - length ||
+ offset + length > archive_entry_size(entry))
+ /* A value of "length" parameter is too large. */
+ return;
+ if ((sp = entry->sparse_tail) != NULL) {
+ if (sp->offset + sp->length > offset)
+ /* Invalid value. */
+ return;
+ if (sp->offset + sp->length == offset) {
+ if (sp->offset + sp->length + length < 0)
+ /* A value of "length" parameter is
+ * too large. */
+ return;
+ /* Expand existing sparse block size. */
+ sp->length += length;
+ return;
+ }
+ }
+
+ if ((sp = (struct ae_sparse *)malloc(sizeof(*sp))) == NULL)
+ /* XXX Error XXX */
+ return;
+
+ sp->offset = offset;
+ sp->length = length;
+ sp->next = NULL;
+
+ if (entry->sparse_head == NULL)
+ entry->sparse_head = entry->sparse_tail = sp;
+ else {
+ /* Add a new sparse block to the tail of list. */
+ if (entry->sparse_tail != NULL)
+ entry->sparse_tail->next = sp;
+ entry->sparse_tail = sp;
+ }
+}
+
+
+/*
+ * returns number of the sparse entries
+ */
+int
+archive_entry_sparse_count(struct archive_entry *entry)
+{
+ struct ae_sparse *sp;
+ int count = 0;
+
+ for (sp = entry->sparse_head; sp != NULL; sp = sp->next)
+ count++;
+
+ /*
+ * Sanity check if this entry is exactly sparse.
+ * If amount of sparse blocks is just one and it indicates the whole
+ * file data, we should remove it and return zero.
+ */
+ if (count == 1) {
+ sp = entry->sparse_head;
+ if (sp->offset == 0 &&
+ sp->length >= archive_entry_size(entry)) {
+ count = 0;
+ archive_entry_sparse_clear(entry);
+ }
+ }
+
+ return (count);
+}
+
+int
+archive_entry_sparse_reset(struct archive_entry * entry)
+{
+ entry->sparse_p = entry->sparse_head;
+
+ return archive_entry_sparse_count(entry);
+}
+
+int
+archive_entry_sparse_next(struct archive_entry * entry,
+ la_int64_t *offset, la_int64_t *length)
+{
+ if (entry->sparse_p) {
+ *offset = entry->sparse_p->offset;
+ *length = entry->sparse_p->length;
+
+ entry->sparse_p = entry->sparse_p->next;
+
+ return (ARCHIVE_OK);
+ } else {
+ *offset = 0;
+ *length = 0;
+ return (ARCHIVE_WARN);
+ }
+}
+
+/*
+ * end of sparse handling
+ */
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_stat.c b/contrib/libs/libarchive/libarchive/archive_entry_stat.c
new file mode 100644
index 00000000000..71a407b1f8b
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_stat.c
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_stat.c 201100 2009-12-28 03:05:31Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_entry_private.h"
+
+const struct stat *
+archive_entry_stat(struct archive_entry *entry)
+{
+ struct stat *st;
+ if (entry->stat == NULL) {
+ entry->stat = calloc(1, sizeof(*st));
+ if (entry->stat == NULL)
+ return (NULL);
+ entry->stat_valid = 0;
+ }
+
+ /*
+ * If none of the underlying fields have been changed, we
+ * don't need to regenerate. In theory, we could use a bitmap
+ * here to flag only those items that have changed, but the
+ * extra complexity probably isn't worth it. It will be very
+ * rare for anyone to change just one field then request a new
+ * stat structure.
+ */
+ if (entry->stat_valid)
+ return (entry->stat);
+
+ st = entry->stat;
+ /*
+ * Use the public interfaces to extract items, so that
+ * the appropriate conversions get invoked.
+ */
+ st->st_atime = archive_entry_atime(entry);
+#if HAVE_STRUCT_STAT_ST_BIRTHTIME
+ st->st_birthtime = archive_entry_birthtime(entry);
+#endif
+ st->st_ctime = archive_entry_ctime(entry);
+ st->st_mtime = archive_entry_mtime(entry);
+ st->st_dev = archive_entry_dev(entry);
+ st->st_gid = (gid_t)archive_entry_gid(entry);
+ st->st_uid = (uid_t)archive_entry_uid(entry);
+ st->st_ino = (ino_t)archive_entry_ino64(entry);
+ st->st_nlink = archive_entry_nlink(entry);
+ st->st_rdev = archive_entry_rdev(entry);
+ st->st_size = (off_t)archive_entry_size(entry);
+ st->st_mode = archive_entry_mode(entry);
+
+ /*
+ * On systems that support high-res timestamps, copy that
+ * information into struct stat.
+ */
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+ st->st_atimespec.tv_nsec = archive_entry_atime_nsec(entry);
+ st->st_ctimespec.tv_nsec = archive_entry_ctime_nsec(entry);
+ st->st_mtimespec.tv_nsec = archive_entry_mtime_nsec(entry);
+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+ st->st_atim.tv_nsec = archive_entry_atime_nsec(entry);
+ st->st_ctim.tv_nsec = archive_entry_ctime_nsec(entry);
+ st->st_mtim.tv_nsec = archive_entry_mtime_nsec(entry);
+#elif HAVE_STRUCT_STAT_ST_MTIME_N
+ st->st_atime_n = archive_entry_atime_nsec(entry);
+ st->st_ctime_n = archive_entry_ctime_nsec(entry);
+ st->st_mtime_n = archive_entry_mtime_nsec(entry);
+#elif HAVE_STRUCT_STAT_ST_UMTIME
+ st->st_uatime = archive_entry_atime_nsec(entry) / 1000;
+ st->st_uctime = archive_entry_ctime_nsec(entry) / 1000;
+ st->st_umtime = archive_entry_mtime_nsec(entry) / 1000;
+#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
+ st->st_atime_usec = archive_entry_atime_nsec(entry) / 1000;
+ st->st_ctime_usec = archive_entry_ctime_nsec(entry) / 1000;
+ st->st_mtime_usec = archive_entry_mtime_nsec(entry) / 1000;
+#endif
+#if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC
+ st->st_birthtimespec.tv_nsec = archive_entry_birthtime_nsec(entry);
+#endif
+
+ /*
+ * TODO: On Linux, store 32 or 64 here depending on whether
+ * the cached stat structure is a stat32 or a stat64. This
+ * will allow us to support both variants interchangeably.
+ */
+ entry->stat_valid = 1;
+
+ return (st);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_strmode.c b/contrib/libs/libarchive/libarchive/archive_entry_strmode.c
new file mode 100644
index 00000000000..af2517a3219
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_strmode.c
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_strmode.c,v 1.4 2008/06/15 05:14:01 kientzle Exp $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_entry_private.h"
+
+const char *
+archive_entry_strmode(struct archive_entry *entry)
+{
+ static const mode_t permbits[] =
+ { 0400, 0200, 0100, 0040, 0020, 0010, 0004, 0002, 0001 };
+ char *bp = entry->strmode;
+ mode_t mode;
+ int i;
+
+ /* Fill in a default string, then selectively override. */
+ strcpy(bp, "?rwxrwxrwx ");
+
+ mode = archive_entry_mode(entry);
+ switch (archive_entry_filetype(entry)) {
+ case AE_IFREG: bp[0] = '-'; break;
+ case AE_IFBLK: bp[0] = 'b'; break;
+ case AE_IFCHR: bp[0] = 'c'; break;
+ case AE_IFDIR: bp[0] = 'd'; break;
+ case AE_IFLNK: bp[0] = 'l'; break;
+ case AE_IFSOCK: bp[0] = 's'; break;
+ case AE_IFIFO: bp[0] = 'p'; break;
+ default:
+ if (archive_entry_hardlink(entry) != NULL) {
+ bp[0] = 'h';
+ break;
+ }
+ }
+
+ for (i = 0; i < 9; i++)
+ if (!(mode & permbits[i]))
+ bp[i+1] = '-';
+
+ if (mode & S_ISUID) {
+ if (mode & 0100) bp[3] = 's';
+ else bp[3] = 'S';
+ }
+ if (mode & S_ISGID) {
+ if (mode & 0010) bp[6] = 's';
+ else bp[6] = 'S';
+ }
+ if (mode & S_ISVTX) {
+ if (mode & 0001) bp[9] = 't';
+ else bp[9] = 'T';
+ }
+ if (archive_entry_acl_types(entry) != 0)
+ bp[10] = '+';
+
+ return (bp);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_xattr.c b/contrib/libs/libarchive/libarchive/archive_entry_xattr.c
new file mode 100644
index 00000000000..e28e515aa87
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_xattr.c
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_xattr.c 201096 2009-12-28 02:41:27Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h> /* for Linux file flags */
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#error #include <linux/ext2_fs.h> /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#error #include <ext2fs/ext2_fs.h> /* for Linux file flags */
+#endif
+#include <stddef.h>
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_entry_private.h"
+
+/*
+ * extended attribute handling
+ */
+
+void
+archive_entry_xattr_clear(struct archive_entry *entry)
+{
+ struct ae_xattr *xp;
+
+ while (entry->xattr_head != NULL) {
+ xp = entry->xattr_head->next;
+ free(entry->xattr_head->name);
+ free(entry->xattr_head->value);
+ free(entry->xattr_head);
+ entry->xattr_head = xp;
+ }
+
+ entry->xattr_head = NULL;
+}
+
+void
+archive_entry_xattr_add_entry(struct archive_entry *entry,
+ const char *name, const void *value, size_t size)
+{
+ struct ae_xattr *xp;
+
+ if ((xp = (struct ae_xattr *)malloc(sizeof(struct ae_xattr))) == NULL)
+ __archive_errx(1, "Out of memory");
+
+ if ((xp->name = strdup(name)) == NULL)
+ __archive_errx(1, "Out of memory");
+
+ if ((xp->value = malloc(size)) != NULL) {
+ memcpy(xp->value, value, size);
+ xp->size = size;
+ } else
+ xp->size = 0;
+
+ xp->next = entry->xattr_head;
+ entry->xattr_head = xp;
+}
+
+
+/*
+ * returns number of the extended attribute entries
+ */
+int
+archive_entry_xattr_count(struct archive_entry *entry)
+{
+ struct ae_xattr *xp;
+ int count = 0;
+
+ for (xp = entry->xattr_head; xp != NULL; xp = xp->next)
+ count++;
+
+ return count;
+}
+
+int
+archive_entry_xattr_reset(struct archive_entry * entry)
+{
+ entry->xattr_p = entry->xattr_head;
+
+ return archive_entry_xattr_count(entry);
+}
+
+int
+archive_entry_xattr_next(struct archive_entry * entry,
+ const char **name, const void **value, size_t *size)
+{
+ if (entry->xattr_p) {
+ *name = entry->xattr_p->name;
+ *value = entry->xattr_p->value;
+ *size = entry->xattr_p->size;
+
+ entry->xattr_p = entry->xattr_p->next;
+
+ return (ARCHIVE_OK);
+ } else {
+ *name = NULL;
+ *value = NULL;
+ *size = (size_t)0;
+ return (ARCHIVE_WARN);
+ }
+}
+
+/*
+ * end of xattr handling
+ */
diff --git a/contrib/libs/libarchive/libarchive/archive_getdate.c b/contrib/libs/libarchive/libarchive/archive_getdate.c
new file mode 100644
index 00000000000..20ab1b1588f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_getdate.c
@@ -0,0 +1,1104 @@
+/*
+ * This code is in the public domain and has no copyright.
+ *
+ * This is a plain C recursive-descent translation of an old
+ * public-domain YACC grammar that has been used for parsing dates in
+ * very many open-source projects.
+ *
+ * Since the original authors were generous enough to donate their
+ * work to the public domain, I feel compelled to match their
+ * generosity.
+ *
+ * Tim Kientzle, February 2009.
+ */
+
+/*
+ * Header comment from original getdate.y:
+ */
+
+/*
+** Originally written by Steven M. Bellovin <smb@research.att.com> while
+** at the University of North Carolina at Chapel Hill. Later tweaked by
+** a couple of people on Usenet. Completely overhauled by Rich $alz
+** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
+**
+** This grammar has 10 shift/reduce conflicts.
+**
+** This code is in the public domain and has no copyright.
+*/
+
+#include "archive_platform.h"
+#ifdef __FreeBSD__
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#define __LIBARCHIVE_BUILD 1
+#include "archive_getdate.h"
+
+/* Basic time units. */
+#define EPOCH 1970
+#define MINUTE (60L)
+#define HOUR (60L * MINUTE)
+#define DAY (24L * HOUR)
+
+/* Daylight-savings mode: on, off, or not yet known. */
+enum DSTMODE { DSTon, DSToff, DSTmaybe };
+/* Meridian: am or pm. */
+enum { tAM, tPM };
+/* Token types returned by nexttoken() */
+enum { tAGO = 260, tDAY, tDAYZONE, tAMPM, tMONTH, tMONTH_UNIT, tSEC_UNIT,
+ tUNUMBER, tZONE, tDST };
+struct token { int token; time_t value; };
+
+/*
+ * Parser state.
+ */
+struct gdstate {
+ struct token *tokenp; /* Pointer to next token. */
+ /* HaveXxxx counts how many of this kind of phrase we've seen;
+ * it's a fatal error to have more than one time, zone, day,
+ * or date phrase. */
+ int HaveYear;
+ int HaveMonth;
+ int HaveDay;
+ int HaveWeekDay; /* Day of week */
+ int HaveTime; /* Hour/minute/second */
+ int HaveZone; /* timezone and/or DST info */
+ int HaveRel; /* time offset; we can have more than one */
+ /* Absolute time values. */
+ time_t Timezone; /* Seconds offset from GMT */
+ time_t Day;
+ time_t Hour;
+ time_t Minutes;
+ time_t Month;
+ time_t Seconds;
+ time_t Year;
+ /* DST selection */
+ enum DSTMODE DSTmode;
+ /* Day of week accounting, e.g., "3rd Tuesday" */
+ time_t DayOrdinal; /* "3" in "3rd Tuesday" */
+ time_t DayNumber; /* "Tuesday" in "3rd Tuesday" */
+ /* Relative time values: hour/day/week offsets are measured in
+ * seconds, month/year are counted in months. */
+ time_t RelMonth;
+ time_t RelSeconds;
+};
+
+/*
+ * A series of functions that recognize certain common time phrases.
+ * Each function returns 1 if it managed to make sense of some of the
+ * tokens, zero otherwise.
+ */
+
+/*
+ * hour:minute or hour:minute:second with optional AM, PM, or numeric
+ * timezone offset
+ */
+static int
+timephrase(struct gdstate *gds)
+{
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == ':'
+ && gds->tokenp[2].token == tUNUMBER
+ && gds->tokenp[3].token == ':'
+ && gds->tokenp[4].token == tUNUMBER) {
+ /* "12:14:18" or "22:08:07" */
+ ++gds->HaveTime;
+ gds->Hour = gds->tokenp[0].value;
+ gds->Minutes = gds->tokenp[2].value;
+ gds->Seconds = gds->tokenp[4].value;
+ gds->tokenp += 5;
+ }
+ else if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == ':'
+ && gds->tokenp[2].token == tUNUMBER) {
+ /* "12:14" or "22:08" */
+ ++gds->HaveTime;
+ gds->Hour = gds->tokenp[0].value;
+ gds->Minutes = gds->tokenp[2].value;
+ gds->Seconds = 0;
+ gds->tokenp += 3;
+ }
+ else if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == tAMPM) {
+ /* "7" is a time if it's followed by "am" or "pm" */
+ ++gds->HaveTime;
+ gds->Hour = gds->tokenp[0].value;
+ gds->Minutes = gds->Seconds = 0;
+ /* We'll handle the AM/PM below. */
+ gds->tokenp += 1;
+ } else {
+ /* We can't handle this. */
+ return 0;
+ }
+
+ if (gds->tokenp[0].token == tAMPM) {
+ /* "7:12pm", "12:20:13am" */
+ if (gds->Hour == 12)
+ gds->Hour = 0;
+ if (gds->tokenp[0].value == tPM)
+ gds->Hour += 12;
+ gds->tokenp += 1;
+ }
+ if (gds->tokenp[0].token == '+'
+ && gds->tokenp[1].token == tUNUMBER) {
+ /* "7:14+0700" */
+ gds->HaveZone++;
+ gds->DSTmode = DSToff;
+ gds->Timezone = - ((gds->tokenp[1].value / 100) * HOUR
+ + (gds->tokenp[1].value % 100) * MINUTE);
+ gds->tokenp += 2;
+ }
+ if (gds->tokenp[0].token == '-'
+ && gds->tokenp[1].token == tUNUMBER) {
+ /* "19:14:12-0530" */
+ gds->HaveZone++;
+ gds->DSTmode = DSToff;
+ gds->Timezone = + ((gds->tokenp[1].value / 100) * HOUR
+ + (gds->tokenp[1].value % 100) * MINUTE);
+ gds->tokenp += 2;
+ }
+ return 1;
+}
+
+/*
+ * Timezone name, possibly including DST.
+ */
+static int
+zonephrase(struct gdstate *gds)
+{
+ if (gds->tokenp[0].token == tZONE
+ && gds->tokenp[1].token == tDST) {
+ gds->HaveZone++;
+ gds->Timezone = gds->tokenp[0].value;
+ gds->DSTmode = DSTon;
+ gds->tokenp += 1;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tZONE) {
+ gds->HaveZone++;
+ gds->Timezone = gds->tokenp[0].value;
+ gds->DSTmode = DSToff;
+ gds->tokenp += 1;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tDAYZONE) {
+ gds->HaveZone++;
+ gds->Timezone = gds->tokenp[0].value;
+ gds->DSTmode = DSTon;
+ gds->tokenp += 1;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Year/month/day in various combinations.
+ */
+static int
+datephrase(struct gdstate *gds)
+{
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == '/'
+ && gds->tokenp[2].token == tUNUMBER
+ && gds->tokenp[3].token == '/'
+ && gds->tokenp[4].token == tUNUMBER) {
+ gds->HaveYear++;
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ if (gds->tokenp[0].value >= 13) {
+ /* First number is big: 2004/01/29, 99/02/17 */
+ gds->Year = gds->tokenp[0].value;
+ gds->Month = gds->tokenp[2].value;
+ gds->Day = gds->tokenp[4].value;
+ } else if ((gds->tokenp[4].value >= 13)
+ || (gds->tokenp[2].value >= 13)) {
+ /* Last number is big: 01/07/98 */
+ /* Middle number is big: 01/29/04 */
+ gds->Month = gds->tokenp[0].value;
+ gds->Day = gds->tokenp[2].value;
+ gds->Year = gds->tokenp[4].value;
+ } else {
+ /* No significant clues: 02/03/04 */
+ gds->Month = gds->tokenp[0].value;
+ gds->Day = gds->tokenp[2].value;
+ gds->Year = gds->tokenp[4].value;
+ }
+ gds->tokenp += 5;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == '/'
+ && gds->tokenp[2].token == tUNUMBER) {
+ /* "1/15" */
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ gds->Month = gds->tokenp[0].value;
+ gds->Day = gds->tokenp[2].value;
+ gds->tokenp += 3;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == '-'
+ && gds->tokenp[2].token == tUNUMBER
+ && gds->tokenp[3].token == '-'
+ && gds->tokenp[4].token == tUNUMBER) {
+ /* ISO 8601 format. yyyy-mm-dd. */
+ gds->HaveYear++;
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ gds->Year = gds->tokenp[0].value;
+ gds->Month = gds->tokenp[2].value;
+ gds->Day = gds->tokenp[4].value;
+ gds->tokenp += 5;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == '-'
+ && gds->tokenp[2].token == tMONTH
+ && gds->tokenp[3].token == '-'
+ && gds->tokenp[4].token == tUNUMBER) {
+ gds->HaveYear++;
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ if (gds->tokenp[0].value > 31) {
+ /* e.g. 1992-Jun-17 */
+ gds->Year = gds->tokenp[0].value;
+ gds->Month = gds->tokenp[2].value;
+ gds->Day = gds->tokenp[4].value;
+ } else {
+ /* e.g. 17-JUN-1992. */
+ gds->Day = gds->tokenp[0].value;
+ gds->Month = gds->tokenp[2].value;
+ gds->Year = gds->tokenp[4].value;
+ }
+ gds->tokenp += 5;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tMONTH
+ && gds->tokenp[1].token == tUNUMBER
+ && gds->tokenp[2].token == ','
+ && gds->tokenp[3].token == tUNUMBER) {
+ /* "June 17, 2001" */
+ gds->HaveYear++;
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ gds->Month = gds->tokenp[0].value;
+ gds->Day = gds->tokenp[1].value;
+ gds->Year = gds->tokenp[3].value;
+ gds->tokenp += 4;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tMONTH
+ && gds->tokenp[1].token == tUNUMBER) {
+ /* "May 3" */
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ gds->Month = gds->tokenp[0].value;
+ gds->Day = gds->tokenp[1].value;
+ gds->tokenp += 2;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == tMONTH
+ && gds->tokenp[2].token == tUNUMBER) {
+ /* "12 Sept 1997" */
+ gds->HaveYear++;
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ gds->Day = gds->tokenp[0].value;
+ gds->Month = gds->tokenp[1].value;
+ gds->Year = gds->tokenp[2].value;
+ gds->tokenp += 3;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == tMONTH) {
+ /* "12 Sept" */
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ gds->Day = gds->tokenp[0].value;
+ gds->Month = gds->tokenp[1].value;
+ gds->tokenp += 2;
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Relative time phrase: "tomorrow", "yesterday", "+1 hour", etc.
+ */
+static int
+relunitphrase(struct gdstate *gds)
+{
+ if (gds->tokenp[0].token == '-'
+ && gds->tokenp[1].token == tUNUMBER
+ && gds->tokenp[2].token == tSEC_UNIT) {
+ /* "-3 hours" */
+ gds->HaveRel++;
+ gds->RelSeconds -= gds->tokenp[1].value * gds->tokenp[2].value;
+ gds->tokenp += 3;
+ return 1;
+ }
+ if (gds->tokenp[0].token == '+'
+ && gds->tokenp[1].token == tUNUMBER
+ && gds->tokenp[2].token == tSEC_UNIT) {
+ /* "+1 minute" */
+ gds->HaveRel++;
+ gds->RelSeconds += gds->tokenp[1].value * gds->tokenp[2].value;
+ gds->tokenp += 3;
+ return 1;
+ }
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == tSEC_UNIT) {
+ /* "1 day" */
+ gds->HaveRel++;
+ gds->RelSeconds += gds->tokenp[0].value * gds->tokenp[1].value;
+ gds->tokenp += 2;
+ return 1;
+ }
+ if (gds->tokenp[0].token == '-'
+ && gds->tokenp[1].token == tUNUMBER
+ && gds->tokenp[2].token == tMONTH_UNIT) {
+ /* "-3 months" */
+ gds->HaveRel++;
+ gds->RelMonth -= gds->tokenp[1].value * gds->tokenp[2].value;
+ gds->tokenp += 3;
+ return 1;
+ }
+ if (gds->tokenp[0].token == '+'
+ && gds->tokenp[1].token == tUNUMBER
+ && gds->tokenp[2].token == tMONTH_UNIT) {
+ /* "+5 years" */
+ gds->HaveRel++;
+ gds->RelMonth += gds->tokenp[1].value * gds->tokenp[2].value;
+ gds->tokenp += 3;
+ return 1;
+ }
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == tMONTH_UNIT) {
+ /* "2 years" */
+ gds->HaveRel++;
+ gds->RelMonth += gds->tokenp[0].value * gds->tokenp[1].value;
+ gds->tokenp += 2;
+ return 1;
+ }
+ if (gds->tokenp[0].token == tSEC_UNIT) {
+ /* "now", "tomorrow" */
+ gds->HaveRel++;
+ gds->RelSeconds += gds->tokenp[0].value;
+ gds->tokenp += 1;
+ return 1;
+ }
+ if (gds->tokenp[0].token == tMONTH_UNIT) {
+ /* "month" */
+ gds->HaveRel++;
+ gds->RelMonth += gds->tokenp[0].value;
+ gds->tokenp += 1;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Day of the week specification.
+ */
+static int
+dayphrase(struct gdstate *gds)
+{
+ if (gds->tokenp[0].token == tDAY) {
+ /* "tues", "wednesday," */
+ gds->HaveWeekDay++;
+ gds->DayOrdinal = 1;
+ gds->DayNumber = gds->tokenp[0].value;
+ gds->tokenp += 1;
+ if (gds->tokenp[0].token == ',')
+ gds->tokenp += 1;
+ return 1;
+ }
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == tDAY) {
+ /* "second tues" "3 wed" */
+ gds->HaveWeekDay++;
+ gds->DayOrdinal = gds->tokenp[0].value;
+ gds->DayNumber = gds->tokenp[1].value;
+ gds->tokenp += 2;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Try to match a phrase using one of the above functions.
+ * This layer also deals with a couple of generic issues.
+ */
+static int
+phrase(struct gdstate *gds)
+{
+ if (timephrase(gds))
+ return 1;
+ if (zonephrase(gds))
+ return 1;
+ if (datephrase(gds))
+ return 1;
+ if (dayphrase(gds))
+ return 1;
+ if (relunitphrase(gds)) {
+ if (gds->tokenp[0].token == tAGO) {
+ gds->RelSeconds = -gds->RelSeconds;
+ gds->RelMonth = -gds->RelMonth;
+ gds->tokenp += 1;
+ }
+ return 1;
+ }
+
+ /* Bare numbers sometimes have meaning. */
+ if (gds->tokenp[0].token == tUNUMBER) {
+ if (gds->HaveTime && !gds->HaveYear && !gds->HaveRel) {
+ gds->HaveYear++;
+ gds->Year = gds->tokenp[0].value;
+ gds->tokenp += 1;
+ return 1;
+ }
+
+ if(gds->tokenp[0].value > 10000) {
+ /* "20040301" */
+ gds->HaveYear++;
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ gds->Day= (gds->tokenp[0].value)%100;
+ gds->Month= (gds->tokenp[0].value/100)%100;
+ gds->Year = gds->tokenp[0].value/10000;
+ gds->tokenp += 1;
+ return 1;
+ }
+
+ if (gds->tokenp[0].value < 24) {
+ gds->HaveTime++;
+ gds->Hour = gds->tokenp[0].value;
+ gds->Minutes = 0;
+ gds->Seconds = 0;
+ gds->tokenp += 1;
+ return 1;
+ }
+
+ if ((gds->tokenp[0].value / 100 < 24)
+ && (gds->tokenp[0].value % 100 < 60)) {
+ /* "513" is same as "5:13" */
+ gds->Hour = gds->tokenp[0].value / 100;
+ gds->Minutes = gds->tokenp[0].value % 100;
+ gds->Seconds = 0;
+ gds->tokenp += 1;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * A dictionary of time words.
+ */
+static struct LEXICON {
+ size_t abbrev;
+ const char *name;
+ int type;
+ time_t value;
+} const TimeWords[] = {
+ /* am/pm */
+ { 0, "am", tAMPM, tAM },
+ { 0, "pm", tAMPM, tPM },
+
+ /* Month names. */
+ { 3, "january", tMONTH, 1 },
+ { 3, "february", tMONTH, 2 },
+ { 3, "march", tMONTH, 3 },
+ { 3, "april", tMONTH, 4 },
+ { 3, "may", tMONTH, 5 },
+ { 3, "june", tMONTH, 6 },
+ { 3, "july", tMONTH, 7 },
+ { 3, "august", tMONTH, 8 },
+ { 3, "september", tMONTH, 9 },
+ { 3, "october", tMONTH, 10 },
+ { 3, "november", tMONTH, 11 },
+ { 3, "december", tMONTH, 12 },
+
+ /* Days of the week. */
+ { 2, "sunday", tDAY, 0 },
+ { 3, "monday", tDAY, 1 },
+ { 2, "tuesday", tDAY, 2 },
+ { 3, "wednesday", tDAY, 3 },
+ { 2, "thursday", tDAY, 4 },
+ { 2, "friday", tDAY, 5 },
+ { 2, "saturday", tDAY, 6 },
+
+ /* Timezones: Offsets are in seconds. */
+ { 0, "gmt", tZONE, 0*HOUR }, /* Greenwich Mean */
+ { 0, "ut", tZONE, 0*HOUR }, /* Universal (Coordinated) */
+ { 0, "utc", tZONE, 0*HOUR },
+ { 0, "wet", tZONE, 0*HOUR }, /* Western European */
+ { 0, "bst", tDAYZONE, 0*HOUR }, /* British Summer */
+ { 0, "wat", tZONE, 1*HOUR }, /* West Africa */
+ { 0, "at", tZONE, 2*HOUR }, /* Azores */
+ /* { 0, "bst", tZONE, 3*HOUR }, */ /* Brazil Standard: Conflict */
+ /* { 0, "gst", tZONE, 3*HOUR }, */ /* Greenland Standard: Conflict*/
+ { 0, "nft", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland */
+ { 0, "nst", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Standard */
+ { 0, "ndt", tDAYZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Daylight */
+ { 0, "ast", tZONE, 4*HOUR }, /* Atlantic Standard */
+ { 0, "adt", tDAYZONE, 4*HOUR }, /* Atlantic Daylight */
+ { 0, "est", tZONE, 5*HOUR }, /* Eastern Standard */
+ { 0, "edt", tDAYZONE, 5*HOUR }, /* Eastern Daylight */
+ { 0, "cst", tZONE, 6*HOUR }, /* Central Standard */
+ { 0, "cdt", tDAYZONE, 6*HOUR }, /* Central Daylight */
+ { 0, "mst", tZONE, 7*HOUR }, /* Mountain Standard */
+ { 0, "mdt", tDAYZONE, 7*HOUR }, /* Mountain Daylight */
+ { 0, "pst", tZONE, 8*HOUR }, /* Pacific Standard */
+ { 0, "pdt", tDAYZONE, 8*HOUR }, /* Pacific Daylight */
+ { 0, "yst", tZONE, 9*HOUR }, /* Yukon Standard */
+ { 0, "ydt", tDAYZONE, 9*HOUR }, /* Yukon Daylight */
+ { 0, "hst", tZONE, 10*HOUR }, /* Hawaii Standard */
+ { 0, "hdt", tDAYZONE, 10*HOUR }, /* Hawaii Daylight */
+ { 0, "cat", tZONE, 10*HOUR }, /* Central Alaska */
+ { 0, "ahst", tZONE, 10*HOUR }, /* Alaska-Hawaii Standard */
+ { 0, "nt", tZONE, 11*HOUR }, /* Nome */
+ { 0, "idlw", tZONE, 12*HOUR }, /* Intl Date Line West */
+ { 0, "cet", tZONE, -1*HOUR }, /* Central European */
+ { 0, "met", tZONE, -1*HOUR }, /* Middle European */
+ { 0, "mewt", tZONE, -1*HOUR }, /* Middle European Winter */
+ { 0, "mest", tDAYZONE, -1*HOUR }, /* Middle European Summer */
+ { 0, "swt", tZONE, -1*HOUR }, /* Swedish Winter */
+ { 0, "sst", tDAYZONE, -1*HOUR }, /* Swedish Summer */
+ { 0, "fwt", tZONE, -1*HOUR }, /* French Winter */
+ { 0, "fst", tDAYZONE, -1*HOUR }, /* French Summer */
+ { 0, "eet", tZONE, -2*HOUR }, /* Eastern Eur, USSR Zone 1 */
+ { 0, "bt", tZONE, -3*HOUR }, /* Baghdad, USSR Zone 2 */
+ { 0, "it", tZONE, -3*HOUR-30*MINUTE },/* Iran */
+ { 0, "zp4", tZONE, -4*HOUR }, /* USSR Zone 3 */
+ { 0, "zp5", tZONE, -5*HOUR }, /* USSR Zone 4 */
+ { 0, "ist", tZONE, -5*HOUR-30*MINUTE },/* Indian Standard */
+ { 0, "zp6", tZONE, -6*HOUR }, /* USSR Zone 5 */
+ /* { 0, "nst", tZONE, -6.5*HOUR }, */ /* North Sumatra: Conflict */
+ /* { 0, "sst", tZONE, -7*HOUR }, */ /* So Sumatra, USSR 6: Conflict */
+ { 0, "wast", tZONE, -7*HOUR }, /* West Australian Standard */
+ { 0, "wadt", tDAYZONE, -7*HOUR }, /* West Australian Daylight */
+ { 0, "jt", tZONE, -7*HOUR-30*MINUTE },/* Java (3pm in Cronusland!)*/
+ { 0, "cct", tZONE, -8*HOUR }, /* China Coast, USSR Zone 7 */
+ { 0, "jst", tZONE, -9*HOUR }, /* Japan Std, USSR Zone 8 */
+ { 0, "cast", tZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Std */
+ { 0, "cadt", tDAYZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Daylt */
+ { 0, "east", tZONE, -10*HOUR }, /* Eastern Australian Std */
+ { 0, "eadt", tDAYZONE, -10*HOUR }, /* Eastern Australian Daylt */
+ { 0, "gst", tZONE, -10*HOUR }, /* Guam Std, USSR Zone 9 */
+ { 0, "nzt", tZONE, -12*HOUR }, /* New Zealand */
+ { 0, "nzst", tZONE, -12*HOUR }, /* New Zealand Standard */
+ { 0, "nzdt", tDAYZONE, -12*HOUR }, /* New Zealand Daylight */
+ { 0, "idle", tZONE, -12*HOUR }, /* Intl Date Line East */
+
+ { 0, "dst", tDST, 0 },
+
+ /* Time units. */
+ { 4, "years", tMONTH_UNIT, 12 },
+ { 5, "months", tMONTH_UNIT, 1 },
+ { 9, "fortnights", tSEC_UNIT, 14 * DAY },
+ { 4, "weeks", tSEC_UNIT, 7 * DAY },
+ { 3, "days", tSEC_UNIT, DAY },
+ { 4, "hours", tSEC_UNIT, HOUR },
+ { 3, "minutes", tSEC_UNIT, MINUTE },
+ { 3, "seconds", tSEC_UNIT, 1 },
+
+ /* Relative-time words. */
+ { 0, "tomorrow", tSEC_UNIT, DAY },
+ { 0, "yesterday", tSEC_UNIT, -DAY },
+ { 0, "today", tSEC_UNIT, 0 },
+ { 0, "now", tSEC_UNIT, 0 },
+ { 0, "last", tUNUMBER, -1 },
+ { 0, "this", tSEC_UNIT, 0 },
+ { 0, "next", tUNUMBER, 2 },
+ { 0, "first", tUNUMBER, 1 },
+ { 0, "1st", tUNUMBER, 1 },
+/* { 0, "second", tUNUMBER, 2 }, */
+ { 0, "2nd", tUNUMBER, 2 },
+ { 0, "third", tUNUMBER, 3 },
+ { 0, "3rd", tUNUMBER, 3 },
+ { 0, "fourth", tUNUMBER, 4 },
+ { 0, "4th", tUNUMBER, 4 },
+ { 0, "fifth", tUNUMBER, 5 },
+ { 0, "5th", tUNUMBER, 5 },
+ { 0, "sixth", tUNUMBER, 6 },
+ { 0, "seventh", tUNUMBER, 7 },
+ { 0, "eighth", tUNUMBER, 8 },
+ { 0, "ninth", tUNUMBER, 9 },
+ { 0, "tenth", tUNUMBER, 10 },
+ { 0, "eleventh", tUNUMBER, 11 },
+ { 0, "twelfth", tUNUMBER, 12 },
+ { 0, "ago", tAGO, 1 },
+
+ /* Military timezones. */
+ { 0, "a", tZONE, 1*HOUR },
+ { 0, "b", tZONE, 2*HOUR },
+ { 0, "c", tZONE, 3*HOUR },
+ { 0, "d", tZONE, 4*HOUR },
+ { 0, "e", tZONE, 5*HOUR },
+ { 0, "f", tZONE, 6*HOUR },
+ { 0, "g", tZONE, 7*HOUR },
+ { 0, "h", tZONE, 8*HOUR },
+ { 0, "i", tZONE, 9*HOUR },
+ { 0, "k", tZONE, 10*HOUR },
+ { 0, "l", tZONE, 11*HOUR },
+ { 0, "m", tZONE, 12*HOUR },
+ { 0, "n", tZONE, -1*HOUR },
+ { 0, "o", tZONE, -2*HOUR },
+ { 0, "p", tZONE, -3*HOUR },
+ { 0, "q", tZONE, -4*HOUR },
+ { 0, "r", tZONE, -5*HOUR },
+ { 0, "s", tZONE, -6*HOUR },
+ { 0, "t", tZONE, -7*HOUR },
+ { 0, "u", tZONE, -8*HOUR },
+ { 0, "v", tZONE, -9*HOUR },
+ { 0, "w", tZONE, -10*HOUR },
+ { 0, "x", tZONE, -11*HOUR },
+ { 0, "y", tZONE, -12*HOUR },
+ { 0, "z", tZONE, 0*HOUR },
+
+ /* End of table. */
+ { 0, NULL, 0, 0 }
+};
+
+/*
+ * Year is either:
+ * = A number from 0 to 99, which means a year from 1970 to 2069, or
+ * = The actual year (>=100).
+ */
+static time_t
+Convert(time_t Month, time_t Day, time_t Year,
+ time_t Hours, time_t Minutes, time_t Seconds,
+ time_t Timezone, enum DSTMODE DSTmode)
+{
+ signed char DaysInMonth[12] = {
+ 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ time_t Julian;
+ int i;
+ struct tm *ltime;
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
+ struct tm tmbuf;
+#endif
+
+ if (Year < 69)
+ Year += 2000;
+ else if (Year < 100)
+ Year += 1900;
+ DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
+ ? 29 : 28;
+ /* Checking for 2038 bogusly assumes that time_t is 32 bits. But
+ I'm too lazy to try to check for time_t overflow in another way. */
+ if (Year < EPOCH || Year >= 2038
+ || Month < 1 || Month > 12
+ /* Lint fluff: "conversion from long may lose accuracy" */
+ || Day < 1 || Day > DaysInMonth[(int)--Month]
+ || Hours < 0 || Hours > 23
+ || Minutes < 0 || Minutes > 59
+ || Seconds < 0 || Seconds > 59)
+ return -1;
+
+ Julian = Day - 1;
+ for (i = 0; i < Month; i++)
+ Julian += DaysInMonth[i];
+ for (i = EPOCH; i < Year; i++)
+ Julian += 365 + (i % 4 == 0);
+ Julian *= DAY;
+ Julian += Timezone;
+ Julian += Hours * HOUR + Minutes * MINUTE + Seconds;
+#if defined(HAVE_LOCALTIME_S)
+ ltime = localtime_s(&tmbuf, &Julian) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+ ltime = localtime_r(&Julian, &tmbuf);
+#else
+ ltime = localtime(&Julian);
+#endif
+ if (DSTmode == DSTon
+ || (DSTmode == DSTmaybe && ltime->tm_isdst))
+ Julian -= HOUR;
+ return Julian;
+}
+
+static time_t
+DSTcorrect(time_t Start, time_t Future)
+{
+ time_t StartDay;
+ time_t FutureDay;
+ struct tm *ltime;
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
+ struct tm tmbuf;
+#endif
+#if defined(HAVE_LOCALTIME_S)
+ ltime = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+ ltime = localtime_r(&Start, &tmbuf);
+#else
+ ltime = localtime(&Start);
+#endif
+ StartDay = (ltime->tm_hour + 1) % 24;
+#if defined(HAVE_LOCALTIME_S)
+ ltime = localtime_s(&tmbuf, &Future) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+ ltime = localtime_r(&Future, &tmbuf);
+#else
+ ltime = localtime(&Future);
+#endif
+ FutureDay = (ltime->tm_hour + 1) % 24;
+ return (Future - Start) + (StartDay - FutureDay) * HOUR;
+}
+
+
+static time_t
+RelativeDate(time_t Start, time_t zone, int dstmode,
+ time_t DayOrdinal, time_t DayNumber)
+{
+ struct tm *tm;
+ time_t t, now;
+#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
+ struct tm tmbuf;
+#endif
+
+ t = Start - zone;
+#if defined(HAVE_GMTIME_S)
+ tm = gmtime_s(&tmbuf, &t) ? NULL : &tmbuf;
+#elif defined(HAVE_GMTIME_R)
+ tm = gmtime_r(&t, &tmbuf);
+#else
+ tm = gmtime(&t);
+#endif
+ now = Start;
+ now += DAY * ((DayNumber - tm->tm_wday + 7) % 7);
+ now += 7 * DAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
+ if (dstmode == DSTmaybe)
+ return DSTcorrect(Start, now);
+ return now - Start;
+}
+
+
+static time_t
+RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth)
+{
+ struct tm *tm;
+ time_t Month;
+ time_t Year;
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
+ struct tm tmbuf;
+#endif
+
+ if (RelMonth == 0)
+ return 0;
+#if defined(HAVE_LOCALTIME_S)
+ tm = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+ tm = localtime_r(&Start, &tmbuf);
+#else
+ tm = localtime(&Start);
+#endif
+ Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
+ Year = Month / 12;
+ Month = Month % 12 + 1;
+ return DSTcorrect(Start,
+ Convert(Month, (time_t)tm->tm_mday, Year,
+ (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
+ Timezone, DSTmaybe));
+}
+
+/*
+ * Tokenizer.
+ */
+static int
+nexttoken(const char **in, time_t *value)
+{
+ char c;
+ char buff[64];
+
+ for ( ; ; ) {
+ while (isspace((unsigned char)**in))
+ ++*in;
+
+ /* Skip parenthesized comments. */
+ if (**in == '(') {
+ int Count = 0;
+ do {
+ c = *(*in)++;
+ if (c == '\0')
+ return c;
+ if (c == '(')
+ Count++;
+ else if (c == ')')
+ Count--;
+ } while (Count > 0);
+ continue;
+ }
+
+ /* Try the next token in the word table first. */
+ /* This allows us to match "2nd", for example. */
+ {
+ const char *src = *in;
+ const struct LEXICON *tp;
+ unsigned i = 0;
+
+ /* Force to lowercase and strip '.' characters. */
+ while (*src != '\0'
+ && (isalnum((unsigned char)*src) || *src == '.')
+ && i < sizeof(buff)-1) {
+ if (*src != '.') {
+ if (isupper((unsigned char)*src))
+ buff[i++] = tolower((unsigned char)*src);
+ else
+ buff[i++] = *src;
+ }
+ src++;
+ }
+ buff[i] = '\0';
+
+ /*
+ * Find the first match. If the word can be
+ * abbreviated, make sure we match at least
+ * the minimum abbreviation.
+ */
+ for (tp = TimeWords; tp->name; tp++) {
+ size_t abbrev = tp->abbrev;
+ if (abbrev == 0)
+ abbrev = strlen(tp->name);
+ if (strlen(buff) >= abbrev
+ && strncmp(tp->name, buff, strlen(buff))
+ == 0) {
+ /* Skip over token. */
+ *in = src;
+ /* Return the match. */
+ *value = tp->value;
+ return tp->type;
+ }
+ }
+ }
+
+ /*
+ * Not in the word table, maybe it's a number. Note:
+ * Because '-' and '+' have other special meanings, I
+ * don't deal with signed numbers here.
+ */
+ if (isdigit((unsigned char)(c = **in))) {
+ for (*value = 0; isdigit((unsigned char)(c = *(*in)++)); )
+ *value = 10 * *value + c - '0';
+ (*in)--;
+ return (tUNUMBER);
+ }
+
+ return *(*in)++;
+ }
+}
+
+#define TM_YEAR_ORIGIN 1900
+
+/* Yield A - B, measured in seconds. */
+static long
+difftm (struct tm *a, struct tm *b)
+{
+ int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
+ int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
+ int days = (
+ /* difference in day of year */
+ a->tm_yday - b->tm_yday
+ /* + intervening leap days */
+ + ((ay >> 2) - (by >> 2))
+ - (ay/100 - by/100)
+ + ((ay/100 >> 2) - (by/100 >> 2))
+ /* + difference in years * 365 */
+ + (long)(ay-by) * 365
+ );
+ return (days * DAY + (a->tm_hour - b->tm_hour) * HOUR
+ + (a->tm_min - b->tm_min) * MINUTE
+ + (a->tm_sec - b->tm_sec));
+}
+
+/*
+ *
+ * The public function.
+ *
+ * TODO: tokens[] array should be dynamically sized.
+ */
+time_t
+__archive_get_date(time_t now, const char *p)
+{
+ struct token tokens[256];
+ struct gdstate _gds;
+ struct token *lasttoken;
+ struct gdstate *gds;
+ struct tm local, *tm;
+ struct tm gmt, *gmt_ptr;
+ time_t Start;
+ time_t tod;
+ long tzone;
+
+ /* Clear out the parsed token array. */
+ memset(tokens, 0, sizeof(tokens));
+ /* Initialize the parser state. */
+ memset(&_gds, 0, sizeof(_gds));
+ gds = &_gds;
+
+ /* Look up the current time. */
+#if defined(HAVE_LOCALTIME_S)
+ tm = localtime_s(&local, &now) ? NULL : &local;
+#elif defined(HAVE_LOCALTIME_R)
+ tm = localtime_r(&now, &local);
+#else
+ memset(&local, 0, sizeof(local));
+ tm = localtime(&now);
+#endif
+ if (tm == NULL)
+ return -1;
+#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S)
+ local = *tm;
+#endif
+
+ /* Look up UTC if we can and use that to determine the current
+ * timezone offset. */
+#if defined(HAVE_GMTIME_S)
+ gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt;
+#elif defined(HAVE_GMTIME_R)
+ gmt_ptr = gmtime_r(&now, &gmt);
+#else
+ memset(&gmt, 0, sizeof(gmt));
+ gmt_ptr = gmtime(&now);
+ if (gmt_ptr != NULL) {
+ /* Copy, in case localtime and gmtime use the same buffer. */
+ gmt = *gmt_ptr;
+ }
+#endif
+ if (gmt_ptr != NULL)
+ tzone = difftm (&gmt, &local);
+ else
+ /* This system doesn't understand timezones; fake it. */
+ tzone = 0;
+ if(local.tm_isdst)
+ tzone += HOUR;
+
+ /* Tokenize the input string. */
+ lasttoken = tokens;
+ while ((lasttoken->token = nexttoken(&p, &lasttoken->value)) != 0) {
+ ++lasttoken;
+ if (lasttoken > tokens + 255)
+ return -1;
+ }
+ gds->tokenp = tokens;
+
+ /* Match phrases until we run out of input tokens. */
+ while (gds->tokenp < lasttoken) {
+ if (!phrase(gds))
+ return -1;
+ }
+
+ /* Use current local timezone if none was specified. */
+ if (!gds->HaveZone) {
+ gds->Timezone = tzone;
+ gds->DSTmode = DSTmaybe;
+ }
+
+ /* If a timezone was specified, use that for generating the default
+ * time components instead of the local timezone. */
+ if (gds->HaveZone && gmt_ptr != NULL) {
+ now -= gds->Timezone;
+#if defined(HAVE_GMTIME_S)
+ gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt;
+#elif defined(HAVE_GMTIME_R)
+ gmt_ptr = gmtime_r(&now, &gmt);
+#else
+ gmt_ptr = gmtime(&now);
+#endif
+ if (gmt_ptr != NULL)
+ local = *gmt_ptr;
+ now += gds->Timezone;
+ }
+
+ if (!gds->HaveYear)
+ gds->Year = local.tm_year + 1900;
+ if (!gds->HaveMonth)
+ gds->Month = local.tm_mon + 1;
+ if (!gds->HaveDay)
+ gds->Day = local.tm_mday;
+ /* Note: No default for hour/min/sec; a specifier that just
+ * gives date always refers to 00:00 on that date. */
+
+ /* If we saw more than one time, timezone, weekday, year, month,
+ * or day, then give up. */
+ if (gds->HaveTime > 1 || gds->HaveZone > 1 || gds->HaveWeekDay > 1
+ || gds->HaveYear > 1 || gds->HaveMonth > 1 || gds->HaveDay > 1)
+ return -1;
+
+ /* Compute an absolute time based on whatever absolute information
+ * we collected. */
+ if (gds->HaveYear || gds->HaveMonth || gds->HaveDay
+ || gds->HaveTime || gds->HaveWeekDay) {
+ Start = Convert(gds->Month, gds->Day, gds->Year,
+ gds->Hour, gds->Minutes, gds->Seconds,
+ gds->Timezone, gds->DSTmode);
+ if (Start < 0)
+ return -1;
+ } else {
+ Start = now;
+ if (!gds->HaveRel)
+ Start -= local.tm_hour * HOUR + local.tm_min * MINUTE
+ + local.tm_sec;
+ }
+
+ /* Add the relative offset. */
+ Start += gds->RelSeconds;
+ Start += RelativeMonth(Start, gds->Timezone, gds->RelMonth);
+
+ /* Adjust for day-of-week offsets. */
+ if (gds->HaveWeekDay
+ && !(gds->HaveYear || gds->HaveMonth || gds->HaveDay)) {
+ tod = RelativeDate(Start, gds->Timezone,
+ gds->DSTmode, gds->DayOrdinal, gds->DayNumber);
+ Start += tod;
+ }
+
+ /* -1 is an error indicator, so return 0 instead of -1 if
+ * that's the actual time. */
+ return Start == -1 ? 0 : Start;
+}
+
+
+#if defined(TEST)
+
+/* ARGSUSED */
+int
+main(int argc, char **argv)
+{
+ time_t d;
+ time_t now = time(NULL);
+
+ while (*++argv != NULL) {
+ (void)printf("Input: %s\n", *argv);
+ d = get_date(now, *argv);
+ if (d == -1)
+ (void)printf("Bad format - couldn't convert.\n");
+ else
+ (void)printf("Output: %s\n", ctime(&d));
+ }
+ exit(0);
+ /* NOTREACHED */
+}
+#endif /* defined(TEST) */
diff --git a/contrib/libs/libarchive/libarchive/archive_getdate.h b/contrib/libs/libarchive/libarchive/archive_getdate.h
new file mode 100644
index 00000000000..900a8f692e9
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_getdate.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2003-2015 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_GETDATE_H_INCLUDED
+#define ARCHIVE_GETDATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include <time.h>
+
+time_t __archive_get_date(time_t now, const char *);
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_hmac.c b/contrib/libs/libarchive/libarchive/archive_hmac.c
new file mode 100644
index 00000000000..edb3bf5abd4
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_hmac.c
@@ -0,0 +1,339 @@
+/*-
+* Copyright (c) 2014 Michihiro NAKAJIMA
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "archive_platform.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include "archive.h"
+#include "archive_hmac_private.h"
+
+/*
+ * On systems that do not support any recognized crypto libraries,
+ * the archive_hmac.c file is expected to define no usable symbols.
+ *
+ * But some compilers and linkers choke on empty object files, so
+ * define a public symbol that will always exist. This could
+ * be removed someday if this file gains another always-present
+ * symbol definition.
+ */
+int __libarchive_hmac_build_hack(void) {
+ return 0;
+}
+
+
+#ifdef ARCHIVE_HMAC_USE_Apple_CommonCrypto
+
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ CCHmacInit(ctx, kCCHmacAlgSHA1, key, key_len);
+ return 0;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+ size_t data_len)
+{
+ CCHmacUpdate(ctx, data, data_len);
+}
+
+static void
+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+ CCHmacFinal(ctx, out);
+ *out_len = 20;
+}
+
+static void
+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)
+
+#ifndef BCRYPT_HASH_REUSABLE_FLAG
+# define BCRYPT_HASH_REUSABLE_FLAG 0x00000020
+#endif
+
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+ BCRYPT_ALG_HANDLE hAlg;
+ BCRYPT_HASH_HANDLE hHash;
+ DWORD hash_len;
+ PBYTE hash;
+ ULONG result;
+ NTSTATUS status;
+
+ ctx->hAlg = NULL;
+ status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM,
+ MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG);
+ if (!BCRYPT_SUCCESS(status))
+ return -1;
+ status = BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH, (PUCHAR)&hash_len,
+ sizeof(hash_len), &result, 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ return -1;
+ }
+ hash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, hash_len);
+ if (hash == NULL) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ return -1;
+ }
+ status = BCryptCreateHash(hAlg, &hHash, NULL, 0,
+ (PUCHAR)key, (ULONG)key_len, BCRYPT_HASH_REUSABLE_FLAG);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ HeapFree(GetProcessHeap(), 0, hash);
+ return -1;
+ }
+
+ ctx->hAlg = hAlg;
+ ctx->hHash = hHash;
+ ctx->hash_len = hash_len;
+ ctx->hash = hash;
+
+ return 0;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+ size_t data_len)
+{
+ BCryptHashData(ctx->hHash, (PUCHAR)(uintptr_t)data, (ULONG)data_len, 0);
+}
+
+static void
+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+ BCryptFinishHash(ctx->hHash, ctx->hash, ctx->hash_len, 0);
+ if (ctx->hash_len == *out_len)
+ memcpy(out, ctx->hash, *out_len);
+}
+
+static void
+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+ if (ctx->hAlg != NULL) {
+ BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
+ HeapFree(GetProcessHeap(), 0, ctx->hash);
+ ctx->hAlg = NULL;
+ }
+}
+
+#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_MD_H)
+
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ const mbedtls_md_info_t *info;
+ int ret;
+
+ mbedtls_md_init(ctx);
+ info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+ if (info == NULL) {
+ mbedtls_md_free(ctx);
+ return (-1);
+ }
+ ret = mbedtls_md_setup(ctx, info, 1);
+ if (ret != 0) {
+ mbedtls_md_free(ctx);
+ return (-1);
+ }
+ ret = mbedtls_md_hmac_starts(ctx, key, key_len);
+ if (ret != 0) {
+ mbedtls_md_free(ctx);
+ return (-1);
+ }
+ return 0;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+ size_t data_len)
+{
+ mbedtls_md_hmac_update(ctx, data, data_len);
+}
+
+static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+ (void)out_len; /* UNUSED */
+
+ mbedtls_md_hmac_finish(ctx, out);
+}
+
+static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+ mbedtls_md_free(ctx);
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H)
+
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ hmac_sha1_set_key(ctx, key_len, key);
+ return 0;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+ size_t data_len)
+{
+ hmac_sha1_update(ctx, data_len, data);
+}
+
+static void
+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+ hmac_sha1_digest(ctx, (unsigned)*out_len, out);
+}
+
+static void
+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+#elif defined(HAVE_LIBCRYPTO)
+
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC *mac;
+
+ char sha1[] = "SHA1";
+ OSSL_PARAM params[] = {
+ OSSL_PARAM_utf8_string("digest", sha1, sizeof(sha1) - 1),
+ OSSL_PARAM_END
+ };
+
+ mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+ *ctx = EVP_MAC_CTX_new(mac);
+ EVP_MAC_free(mac);
+ if (*ctx == NULL)
+ return -1;
+
+ EVP_MAC_init(*ctx, key, key_len, params);
+#else
+ *ctx = HMAC_CTX_new();
+ if (*ctx == NULL)
+ return -1;
+ HMAC_Init_ex(*ctx, key, key_len, EVP_sha1(), NULL);
+#endif
+ return 0;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+ size_t data_len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC_update(*ctx, data, data_len);
+#else
+ HMAC_Update(*ctx, data, data_len);
+#endif
+}
+
+static void
+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ size_t len = *out_len;
+#else
+ unsigned int len = (unsigned int)*out_len;
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC_final(*ctx, out, &len, *out_len);
+#else
+ HMAC_Final(*ctx, out, &len);
+#endif
+ *out_len = len;
+}
+
+static void
+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC_CTX_free(*ctx);
+#else
+ HMAC_CTX_free(*ctx);
+#endif
+ *ctx = NULL;
+}
+
+#else
+
+/* Stub */
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ (void)ctx;/* UNUSED */
+ (void)key;/* UNUSED */
+ (void)key_len;/* UNUSED */
+ return -1;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+ size_t data_len)
+{
+ (void)ctx;/* UNUSED */
+ (void)data;/* UNUSED */
+ (void)data_len;/* UNUSED */
+}
+
+static void
+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+ (void)ctx;/* UNUSED */
+ (void)out;/* UNUSED */
+ (void)out_len;/* UNUSED */
+}
+
+static void
+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+ (void)ctx;/* UNUSED */
+}
+
+#endif
+
+const struct archive_hmac __archive_hmac = {
+ &__hmac_sha1_init,
+ &__hmac_sha1_update,
+ &__hmac_sha1_final,
+ &__hmac_sha1_cleanup,
+};
diff --git a/contrib/libs/libarchive/libarchive/archive_hmac_private.h b/contrib/libs/libarchive/libarchive/archive_hmac_private.h
new file mode 100644
index 00000000000..8d13b0fd72e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_hmac_private.h
@@ -0,0 +1,119 @@
+/*-
+* Copyright (c) 2014 Michihiro NAKAJIMA
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef ARCHIVE_HMAC_PRIVATE_H_INCLUDED
+#define ARCHIVE_HMAC_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+/*
+ * On systems that do not support any recognized crypto libraries,
+ * the archive_hmac.c file is expected to define no usable symbols.
+ *
+ * But some compilers and linkers choke on empty object files, so
+ * define a public symbol that will always exist. This could
+ * be removed someday if this file gains another always-present
+ * symbol definition.
+ */
+int __libarchive_hmac_build_hack(void);
+
+#ifdef __APPLE__
+# include <AvailabilityMacros.h>
+# if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
+# define ARCHIVE_HMAC_USE_Apple_CommonCrypto
+# endif
+#endif
+
+#ifdef ARCHIVE_HMAC_USE_Apple_CommonCrypto
+#include <CommonCrypto/CommonHMAC.h>
+
+typedef CCHmacContext archive_hmac_sha1_ctx;
+
+#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)
+#include <bcrypt.h>
+
+typedef struct {
+ BCRYPT_ALG_HANDLE hAlg;
+ BCRYPT_HASH_HANDLE hHash;
+ DWORD hash_len;
+ PBYTE hash;
+
+} archive_hmac_sha1_ctx;
+
+#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_MD_H)
+#error #include <mbedtls/md.h>
+
+typedef mbedtls_md_context_t archive_hmac_sha1_ctx;
+
+#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H)
+#error #include <nettle/hmac.h>
+
+typedef struct hmac_sha1_ctx archive_hmac_sha1_ctx;
+
+#elif defined(HAVE_LIBCRYPTO)
+#include <openssl/opensslv.h>
+#include <openssl/hmac.h>
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#error #include <openssl/params.h>
+
+typedef EVP_MAC_CTX *archive_hmac_sha1_ctx;
+
+#else
+#include "archive_openssl_hmac_private.h"
+
+typedef HMAC_CTX* archive_hmac_sha1_ctx;
+#endif
+
+#else
+
+typedef int archive_hmac_sha1_ctx;
+
+#endif
+
+
+/* HMAC */
+#define archive_hmac_sha1_init(ctx, key, key_len)\
+ __archive_hmac.__hmac_sha1_init(ctx, key, key_len)
+#define archive_hmac_sha1_update(ctx, data, data_len)\
+ __archive_hmac.__hmac_sha1_update(ctx, data, data_len)
+#define archive_hmac_sha1_final(ctx, out, out_len)\
+ __archive_hmac.__hmac_sha1_final(ctx, out, out_len)
+#define archive_hmac_sha1_cleanup(ctx)\
+ __archive_hmac.__hmac_sha1_cleanup(ctx)
+
+
+struct archive_hmac {
+ /* HMAC */
+ int (*__hmac_sha1_init)(archive_hmac_sha1_ctx *, const uint8_t *,
+ size_t);
+ void (*__hmac_sha1_update)(archive_hmac_sha1_ctx *, const uint8_t *,
+ size_t);
+ void (*__hmac_sha1_final)(archive_hmac_sha1_ctx *, uint8_t *, size_t *);
+ void (*__hmac_sha1_cleanup)(archive_hmac_sha1_ctx *);
+};
+
+extern const struct archive_hmac __archive_hmac;
+#endif /* ARCHIVE_HMAC_PRIVATE_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_match.c b/contrib/libs/libarchive/libarchive/archive_match.c
new file mode 100644
index 00000000000..04747b1f666
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_match.c
@@ -0,0 +1,1875 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_entry.h"
+#include "archive_getdate.h"
+#include "archive_pathmatch.h"
+#include "archive_rb.h"
+#include "archive_string.h"
+
+struct match {
+ struct match *next;
+ int matches;
+ struct archive_mstring pattern;
+};
+
+struct match_list {
+ struct match *first;
+ struct match **last;
+ int count;
+ int unmatched_count;
+ struct match *unmatched_next;
+ int unmatched_eof;
+};
+
+struct match_file {
+ struct archive_rb_node node;
+ struct match_file *next;
+ struct archive_mstring pathname;
+ int flag;
+ time_t mtime_sec;
+ long mtime_nsec;
+ time_t ctime_sec;
+ long ctime_nsec;
+};
+
+struct entry_list {
+ struct match_file *first;
+ struct match_file **last;
+ int count;
+};
+
+struct id_array {
+ size_t size;/* Allocated size */
+ size_t count;
+ int64_t *ids;
+};
+
+#define PATTERN_IS_SET 1
+#define TIME_IS_SET 2
+#define ID_IS_SET 4
+
+struct archive_match {
+ struct archive archive;
+
+ /* exclusion/inclusion set flag. */
+ int setflag;
+
+ /* Recursively include directory content? */
+ int recursive_include;
+
+ /*
+ * Matching filename patterns.
+ */
+ struct match_list exclusions;
+ struct match_list inclusions;
+
+ /*
+ * Matching time stamps.
+ */
+ time_t now;
+ int newer_mtime_filter;
+ time_t newer_mtime_sec;
+ long newer_mtime_nsec;
+ int newer_ctime_filter;
+ time_t newer_ctime_sec;
+ long newer_ctime_nsec;
+ int older_mtime_filter;
+ time_t older_mtime_sec;
+ long older_mtime_nsec;
+ int older_ctime_filter;
+ time_t older_ctime_sec;
+ long older_ctime_nsec;
+ /*
+ * Matching time stamps with its filename.
+ */
+ struct archive_rb_tree exclusion_tree;
+ struct entry_list exclusion_entry_list;
+
+ /*
+ * Matching file owners.
+ */
+ struct id_array inclusion_uids;
+ struct id_array inclusion_gids;
+ struct match_list inclusion_unames;
+ struct match_list inclusion_gnames;
+};
+
+static int add_pattern_from_file(struct archive_match *,
+ struct match_list *, int, const void *, int);
+static int add_entry(struct archive_match *, int,
+ struct archive_entry *);
+static int add_owner_id(struct archive_match *, struct id_array *,
+ int64_t);
+static int add_owner_name(struct archive_match *, struct match_list *,
+ int, const void *);
+static int add_pattern_mbs(struct archive_match *, struct match_list *,
+ const char *);
+static int add_pattern_wcs(struct archive_match *, struct match_list *,
+ const wchar_t *);
+static int cmp_key_mbs(const struct archive_rb_node *, const void *);
+static int cmp_key_wcs(const struct archive_rb_node *, const void *);
+static int cmp_node_mbs(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+static int cmp_node_wcs(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+static void entry_list_add(struct entry_list *, struct match_file *);
+static void entry_list_free(struct entry_list *);
+static void entry_list_init(struct entry_list *);
+static int error_nomem(struct archive_match *);
+static void match_list_add(struct match_list *, struct match *);
+static void match_list_free(struct match_list *);
+static void match_list_init(struct match_list *);
+static int match_list_unmatched_inclusions_next(struct archive_match *,
+ struct match_list *, int, const void **);
+static int match_owner_id(struct id_array *, int64_t);
+#if !defined(_WIN32) || defined(__CYGWIN__)
+static int match_owner_name_mbs(struct archive_match *,
+ struct match_list *, const char *);
+#else
+static int match_owner_name_wcs(struct archive_match *,
+ struct match_list *, const wchar_t *);
+#endif
+static int match_path_exclusion(struct archive_match *,
+ struct match *, int, const void *);
+static int match_path_inclusion(struct archive_match *,
+ struct match *, int, const void *);
+static int owner_excluded(struct archive_match *,
+ struct archive_entry *);
+static int path_excluded(struct archive_match *, int, const void *);
+static int set_timefilter(struct archive_match *, int, time_t, long,
+ time_t, long);
+static int set_timefilter_pathname_mbs(struct archive_match *,
+ int, const char *);
+static int set_timefilter_pathname_wcs(struct archive_match *,
+ int, const wchar_t *);
+static int set_timefilter_date(struct archive_match *, int, const char *);
+static int set_timefilter_date_w(struct archive_match *, int,
+ const wchar_t *);
+static int time_excluded(struct archive_match *,
+ struct archive_entry *);
+static int validate_time_flag(struct archive *, int, const char *);
+
+#define get_date __archive_get_date
+
+static const struct archive_rb_tree_ops rb_ops_mbs = {
+ cmp_node_mbs, cmp_key_mbs
+};
+
+static const struct archive_rb_tree_ops rb_ops_wcs = {
+ cmp_node_wcs, cmp_key_wcs
+};
+
+/*
+ * The matching logic here needs to be re-thought. I started out to
+ * try to mimic gtar's matching logic, but it's not entirely
+ * consistent. In particular 'tar -t' and 'tar -x' interpret patterns
+ * on the command line as anchored, but --exclude doesn't.
+ */
+
+static int
+error_nomem(struct archive_match *a)
+{
+ archive_set_error(&(a->archive), ENOMEM, "No memory");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Create an ARCHIVE_MATCH object.
+ */
+struct archive *
+archive_match_new(void)
+{
+ struct archive_match *a;
+
+ a = (struct archive_match *)calloc(1, sizeof(*a));
+ if (a == NULL)
+ return (NULL);
+ a->archive.magic = ARCHIVE_MATCH_MAGIC;
+ a->archive.state = ARCHIVE_STATE_NEW;
+ a->recursive_include = 1;
+ match_list_init(&(a->inclusions));
+ match_list_init(&(a->exclusions));
+ __archive_rb_tree_init(&(a->exclusion_tree), &rb_ops_mbs);
+ entry_list_init(&(a->exclusion_entry_list));
+ match_list_init(&(a->inclusion_unames));
+ match_list_init(&(a->inclusion_gnames));
+ time(&a->now);
+ return (&(a->archive));
+}
+
+/*
+ * Free an ARCHIVE_MATCH object.
+ */
+int
+archive_match_free(struct archive *_a)
+{
+ struct archive_match *a;
+
+ if (_a == NULL)
+ return (ARCHIVE_OK);
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_match_free");
+ a = (struct archive_match *)_a;
+ match_list_free(&(a->inclusions));
+ match_list_free(&(a->exclusions));
+ entry_list_free(&(a->exclusion_entry_list));
+ free(a->inclusion_uids.ids);
+ free(a->inclusion_gids.ids);
+ match_list_free(&(a->inclusion_unames));
+ match_list_free(&(a->inclusion_gnames));
+ free(a);
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Convenience function to perform all exclusion tests.
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_match_excluded(struct archive *_a, struct archive_entry *entry)
+{
+ struct archive_match *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_excluded_ae");
+
+ a = (struct archive_match *)_a;
+ if (entry == NULL) {
+ archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+ return (ARCHIVE_FAILED);
+ }
+
+ r = 0;
+ if (a->setflag & PATTERN_IS_SET) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ r = path_excluded(a, 0, archive_entry_pathname_w(entry));
+#else
+ r = path_excluded(a, 1, archive_entry_pathname(entry));
+#endif
+ if (r != 0)
+ return (r);
+ }
+
+ if (a->setflag & TIME_IS_SET) {
+ r = time_excluded(a, entry);
+ if (r != 0)
+ return (r);
+ }
+
+ if (a->setflag & ID_IS_SET)
+ r = owner_excluded(a, entry);
+ return (r);
+}
+
+/*
+ * Utility functions to manage exclusion/inclusion patterns
+ */
+
+int
+archive_match_exclude_pattern(struct archive *_a, const char *pattern)
+{
+ struct archive_match *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_exclude_pattern");
+ a = (struct archive_match *)_a;
+
+ if (pattern == NULL || *pattern == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+ return (ARCHIVE_FAILED);
+ }
+ if ((r = add_pattern_mbs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)
+ return (r);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_match_exclude_pattern_w(struct archive *_a, const wchar_t *pattern)
+{
+ struct archive_match *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_w");
+ a = (struct archive_match *)_a;
+
+ if (pattern == NULL || *pattern == L'\0') {
+ archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+ return (ARCHIVE_FAILED);
+ }
+ if ((r = add_pattern_wcs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)
+ return (r);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_match_exclude_pattern_from_file(struct archive *_a,
+ const char *pathname, int nullSeparator)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file");
+ a = (struct archive_match *)_a;
+
+ return add_pattern_from_file(a, &(a->exclusions), 1, pathname,
+ nullSeparator);
+}
+
+int
+archive_match_exclude_pattern_from_file_w(struct archive *_a,
+ const wchar_t *pathname, int nullSeparator)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file_w");
+ a = (struct archive_match *)_a;
+
+ return add_pattern_from_file(a, &(a->exclusions), 0, pathname,
+ nullSeparator);
+}
+
+int
+archive_match_include_pattern(struct archive *_a, const char *pattern)
+{
+ struct archive_match *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_pattern");
+ a = (struct archive_match *)_a;
+
+ if (pattern == NULL || *pattern == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+ return (ARCHIVE_FAILED);
+ }
+ if ((r = add_pattern_mbs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)
+ return (r);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_match_include_pattern_w(struct archive *_a, const wchar_t *pattern)
+{
+ struct archive_match *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_pattern_w");
+ a = (struct archive_match *)_a;
+
+ if (pattern == NULL || *pattern == L'\0') {
+ archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+ return (ARCHIVE_FAILED);
+ }
+ if ((r = add_pattern_wcs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)
+ return (r);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_match_include_pattern_from_file(struct archive *_a,
+ const char *pathname, int nullSeparator)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file");
+ a = (struct archive_match *)_a;
+
+ return add_pattern_from_file(a, &(a->inclusions), 1, pathname,
+ nullSeparator);
+}
+
+int
+archive_match_include_pattern_from_file_w(struct archive *_a,
+ const wchar_t *pathname, int nullSeparator)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file_w");
+ a = (struct archive_match *)_a;
+
+ return add_pattern_from_file(a, &(a->inclusions), 0, pathname,
+ nullSeparator);
+}
+
+/*
+ * Test functions for pathname patterns.
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_match_path_excluded(struct archive *_a,
+ struct archive_entry *entry)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_path_excluded");
+
+ a = (struct archive_match *)_a;
+ if (entry == NULL) {
+ archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* If we don't have exclusion/inclusion pattern set at all,
+ * the entry is always not excluded. */
+ if ((a->setflag & PATTERN_IS_SET) == 0)
+ return (0);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ return (path_excluded(a, 0, archive_entry_pathname_w(entry)));
+#else
+ return (path_excluded(a, 1, archive_entry_pathname(entry)));
+#endif
+}
+
+/*
+ * When recursive inclusion of directory content is enabled,
+ * an inclusion pattern that matches a directory will also
+ * include everything beneath that directory. Enabled by default.
+ *
+ * For compatibility with GNU tar, exclusion patterns always
+ * match if a subset of the full patch matches (i.e., they are
+ * are not rooted at the beginning of the path) and thus there
+ * is no corresponding non-recursive exclusion mode.
+ */
+int
+archive_match_set_inclusion_recursion(struct archive *_a, int enabled)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_set_inclusion_recursion");
+ a = (struct archive_match *)_a;
+ a->recursive_include = enabled;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Utility functions to get statistic information for inclusion patterns.
+ */
+int
+archive_match_path_unmatched_inclusions(struct archive *_a)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions");
+ a = (struct archive_match *)_a;
+
+ return (a->inclusions.unmatched_count);
+}
+
+int
+archive_match_path_unmatched_inclusions_next(struct archive *_a,
+ const char **_p)
+{
+ struct archive_match *a;
+ const void *v;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next");
+ a = (struct archive_match *)_a;
+
+ r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 1, &v);
+ *_p = (const char *)v;
+ return (r);
+}
+
+int
+archive_match_path_unmatched_inclusions_next_w(struct archive *_a,
+ const wchar_t **_p)
+{
+ struct archive_match *a;
+ const void *v;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next_w");
+ a = (struct archive_match *)_a;
+
+ r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 0, &v);
+ *_p = (const wchar_t *)v;
+ return (r);
+}
+
+/*
+ * Add inclusion/exclusion patterns.
+ */
+static int
+add_pattern_mbs(struct archive_match *a, struct match_list *list,
+ const char *pattern)
+{
+ struct match *match;
+ size_t len;
+
+ match = calloc(1, sizeof(*match));
+ if (match == NULL)
+ return (error_nomem(a));
+ /* Both "foo/" and "foo" should match "foo/bar". */
+ len = strlen(pattern);
+ if (len && pattern[len - 1] == '/')
+ --len;
+ archive_mstring_copy_mbs_len(&(match->pattern), pattern, len);
+ match_list_add(list, match);
+ a->setflag |= PATTERN_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+static int
+add_pattern_wcs(struct archive_match *a, struct match_list *list,
+ const wchar_t *pattern)
+{
+ struct match *match;
+ size_t len;
+
+ match = calloc(1, sizeof(*match));
+ if (match == NULL)
+ return (error_nomem(a));
+ /* Both "foo/" and "foo" should match "foo/bar". */
+ len = wcslen(pattern);
+ if (len && pattern[len - 1] == L'/')
+ --len;
+ archive_mstring_copy_wcs_len(&(match->pattern), pattern, len);
+ match_list_add(list, match);
+ a->setflag |= PATTERN_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+static int
+add_pattern_from_file(struct archive_match *a, struct match_list *mlist,
+ int mbs, const void *pathname, int nullSeparator)
+{
+ struct archive *ar;
+ struct archive_entry *ae;
+ struct archive_string as;
+ const void *buff;
+ size_t size;
+ int64_t offset;
+ int r;
+
+ ar = archive_read_new();
+ if (ar == NULL) {
+ archive_set_error(&(a->archive), ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ r = archive_read_support_format_raw(ar);
+ r = archive_read_support_format_empty(ar);
+ if (r != ARCHIVE_OK) {
+ archive_copy_error(&(a->archive), ar);
+ archive_read_free(ar);
+ return (r);
+ }
+ if (mbs)
+ r = archive_read_open_filename(ar, pathname, 512*20);
+ else
+ r = archive_read_open_filename_w(ar, pathname, 512*20);
+ if (r != ARCHIVE_OK) {
+ archive_copy_error(&(a->archive), ar);
+ archive_read_free(ar);
+ return (r);
+ }
+ r = archive_read_next_header(ar, &ae);
+ if (r != ARCHIVE_OK) {
+ archive_read_free(ar);
+ if (r == ARCHIVE_EOF) {
+ return (ARCHIVE_OK);
+ } else {
+ archive_copy_error(&(a->archive), ar);
+ return (r);
+ }
+ }
+
+ archive_string_init(&as);
+
+ while ((r = archive_read_data_block(ar, &buff, &size, &offset))
+ == ARCHIVE_OK) {
+ const char *b = (const char *)buff;
+
+ while (size) {
+ const char *s = (const char *)b;
+ size_t length = 0;
+ int found_separator = 0;
+
+ while (length < size) {
+ if (nullSeparator) {
+ if (*b == '\0') {
+ found_separator = 1;
+ break;
+ }
+ } else {
+ if (*b == 0x0d || *b == 0x0a) {
+ found_separator = 1;
+ break;
+ }
+ }
+ b++;
+ length++;
+ }
+ if (!found_separator) {
+ archive_strncat(&as, s, length);
+ /* Read next data block. */
+ break;
+ }
+ b++;
+ size -= length + 1;
+ archive_strncat(&as, s, length);
+
+ /* If the line is not empty, add the pattern. */
+ if (archive_strlen(&as) > 0) {
+ /* Add pattern. */
+ r = add_pattern_mbs(a, mlist, as.s);
+ if (r != ARCHIVE_OK) {
+ archive_read_free(ar);
+ archive_string_free(&as);
+ return (r);
+ }
+ archive_string_empty(&as);
+ }
+ }
+ }
+
+ /* If an error occurred, report it immediately. */
+ if (r < ARCHIVE_OK) {
+ archive_copy_error(&(a->archive), ar);
+ archive_read_free(ar);
+ archive_string_free(&as);
+ return (r);
+ }
+
+ /* If the line is not empty, add the pattern. */
+ if (r == ARCHIVE_EOF && archive_strlen(&as) > 0) {
+ /* Add pattern. */
+ r = add_pattern_mbs(a, mlist, as.s);
+ if (r != ARCHIVE_OK) {
+ archive_read_free(ar);
+ archive_string_free(&as);
+ return (r);
+ }
+ }
+ archive_read_free(ar);
+ archive_string_free(&as);
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Test if pathname is excluded by inclusion/exclusion patterns.
+ */
+static int
+path_excluded(struct archive_match *a, int mbs, const void *pathname)
+{
+ struct match *match;
+ struct match *matched;
+ int r;
+
+ if (a == NULL)
+ return (0);
+
+ /* Mark off any unmatched inclusions. */
+ /* In particular, if a filename does appear in the archive and
+ * is explicitly included and excluded, then we don't report
+ * it as missing even though we don't extract it.
+ */
+ matched = NULL;
+ for (match = a->inclusions.first; match != NULL;
+ match = match->next){
+ if (match->matches == 0 &&
+ (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
+ if (r < 0)
+ return (r);
+ a->inclusions.unmatched_count--;
+ match->matches++;
+ matched = match;
+ }
+ }
+
+ /* Exclusions take priority */
+ for (match = a->exclusions.first; match != NULL;
+ match = match->next){
+ r = match_path_exclusion(a, match, mbs, pathname);
+ if (r)
+ return (r);
+ }
+
+ /* It's not excluded and we found an inclusion above, so it's
+ * included. */
+ if (matched != NULL)
+ return (0);
+
+
+ /* We didn't find an unmatched inclusion, check the remaining ones. */
+ for (match = a->inclusions.first; match != NULL;
+ match = match->next){
+ /* We looked at previously-unmatched inclusions already. */
+ if (match->matches > 0 &&
+ (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
+ if (r < 0)
+ return (r);
+ match->matches++;
+ return (0);
+ }
+ }
+
+ /* If there were inclusions, default is to exclude. */
+ if (a->inclusions.first != NULL)
+ return (1);
+
+ /* No explicit inclusions, default is to match. */
+ return (0);
+}
+
+/*
+ * This is a little odd, but it matches the default behavior of
+ * gtar. In particular, 'a*b' will match 'foo/a1111/222b/bar'
+ *
+ */
+static int
+match_path_exclusion(struct archive_match *a, struct match *m,
+ int mbs, const void *pn)
+{
+ int flag = PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END;
+ int r;
+
+ if (mbs) {
+ const char *p;
+ r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
+ if (r == 0)
+ return (archive_pathmatch(p, (const char *)pn, flag));
+ } else {
+ const wchar_t *p;
+ r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
+ if (r == 0)
+ return (archive_pathmatch_w(p, (const wchar_t *)pn,
+ flag));
+ }
+ if (errno == ENOMEM)
+ return (error_nomem(a));
+ return (0);
+}
+
+/*
+ * Again, mimic gtar: inclusions are always anchored (have to match
+ * the beginning of the path) even though exclusions are not anchored.
+ */
+static int
+match_path_inclusion(struct archive_match *a, struct match *m,
+ int mbs, const void *pn)
+{
+ /* Recursive operation requires only a prefix match. */
+ int flag = a->recursive_include ?
+ PATHMATCH_NO_ANCHOR_END :
+ 0;
+ int r;
+
+ if (mbs) {
+ const char *p;
+ r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
+ if (r == 0)
+ return (archive_pathmatch(p, (const char *)pn, flag));
+ } else {
+ const wchar_t *p;
+ r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
+ if (r == 0)
+ return (archive_pathmatch_w(p, (const wchar_t *)pn,
+ flag));
+ }
+ if (errno == ENOMEM)
+ return (error_nomem(a));
+ return (0);
+}
+
+static void
+match_list_init(struct match_list *list)
+{
+ list->first = NULL;
+ list->last = &(list->first);
+ list->count = 0;
+}
+
+static void
+match_list_free(struct match_list *list)
+{
+ struct match *p, *q;
+
+ for (p = list->first; p != NULL; ) {
+ q = p;
+ p = p->next;
+ archive_mstring_clean(&(q->pattern));
+ free(q);
+ }
+}
+
+static void
+match_list_add(struct match_list *list, struct match *m)
+{
+ *list->last = m;
+ list->last = &(m->next);
+ list->count++;
+ list->unmatched_count++;
+}
+
+static int
+match_list_unmatched_inclusions_next(struct archive_match *a,
+ struct match_list *list, int mbs, const void **vp)
+{
+ struct match *m;
+
+ *vp = NULL;
+ if (list->unmatched_eof) {
+ list->unmatched_eof = 0;
+ return (ARCHIVE_EOF);
+ }
+ if (list->unmatched_next == NULL) {
+ if (list->unmatched_count == 0)
+ return (ARCHIVE_EOF);
+ list->unmatched_next = list->first;
+ }
+
+ for (m = list->unmatched_next; m != NULL; m = m->next) {
+ int r;
+
+ if (m->matches)
+ continue;
+ if (mbs) {
+ const char *p;
+ r = archive_mstring_get_mbs(&(a->archive),
+ &(m->pattern), &p);
+ if (r < 0 && errno == ENOMEM)
+ return (error_nomem(a));
+ if (p == NULL)
+ p = "";
+ *vp = p;
+ } else {
+ const wchar_t *p;
+ r = archive_mstring_get_wcs(&(a->archive),
+ &(m->pattern), &p);
+ if (r < 0 && errno == ENOMEM)
+ return (error_nomem(a));
+ if (p == NULL)
+ p = L"";
+ *vp = p;
+ }
+ list->unmatched_next = m->next;
+ if (list->unmatched_next == NULL)
+ /* To return EOF next time. */
+ list->unmatched_eof = 1;
+ return (ARCHIVE_OK);
+ }
+ list->unmatched_next = NULL;
+ return (ARCHIVE_EOF);
+}
+
+/*
+ * Utility functions to manage inclusion timestamps.
+ */
+int
+archive_match_include_time(struct archive *_a, int flag, time_t sec,
+ long nsec)
+{
+ int r;
+
+ r = validate_time_flag(_a, flag, "archive_match_include_time");
+ if (r != ARCHIVE_OK)
+ return (r);
+ return set_timefilter((struct archive_match *)_a, flag,
+ sec, nsec, sec, nsec);
+}
+
+int
+archive_match_include_date(struct archive *_a, int flag,
+ const char *datestr)
+{
+ int r;
+
+ r = validate_time_flag(_a, flag, "archive_match_include_date");
+ if (r != ARCHIVE_OK)
+ return (r);
+ return set_timefilter_date((struct archive_match *)_a, flag, datestr);
+}
+
+int
+archive_match_include_date_w(struct archive *_a, int flag,
+ const wchar_t *datestr)
+{
+ int r;
+
+ r = validate_time_flag(_a, flag, "archive_match_include_date_w");
+ if (r != ARCHIVE_OK)
+ return (r);
+
+ return set_timefilter_date_w((struct archive_match *)_a, flag, datestr);
+}
+
+int
+archive_match_include_file_time(struct archive *_a, int flag,
+ const char *pathname)
+{
+ int r;
+
+ r = validate_time_flag(_a, flag, "archive_match_include_file_time");
+ if (r != ARCHIVE_OK)
+ return (r);
+ return set_timefilter_pathname_mbs((struct archive_match *)_a,
+ flag, pathname);
+}
+
+int
+archive_match_include_file_time_w(struct archive *_a, int flag,
+ const wchar_t *pathname)
+{
+ int r;
+
+ r = validate_time_flag(_a, flag, "archive_match_include_file_time_w");
+ if (r != ARCHIVE_OK)
+ return (r);
+ return set_timefilter_pathname_wcs((struct archive_match *)_a,
+ flag, pathname);
+}
+
+int
+archive_match_exclude_entry(struct archive *_a, int flag,
+ struct archive_entry *entry)
+{
+ struct archive_match *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_time_include_entry");
+ a = (struct archive_match *)_a;
+
+ if (entry == NULL) {
+ archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+ return (ARCHIVE_FAILED);
+ }
+ r = validate_time_flag(_a, flag, "archive_match_exclude_entry");
+ if (r != ARCHIVE_OK)
+ return (r);
+ return (add_entry(a, flag, entry));
+}
+
+/*
+ * Test function for time stamps.
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_match_time_excluded(struct archive *_a,
+ struct archive_entry *entry)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_time_excluded_ae");
+
+ a = (struct archive_match *)_a;
+ if (entry == NULL) {
+ archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* If we don't have inclusion time set at all, the entry is always
+ * not excluded. */
+ if ((a->setflag & TIME_IS_SET) == 0)
+ return (0);
+ return (time_excluded(a, entry));
+}
+
+static int
+validate_time_flag(struct archive *_a, int flag, const char *_fn)
+{
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, _fn);
+
+ /* Check a type of time. */
+ if (flag &
+ ((~(ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) & 0xff00)) {
+ archive_set_error(_a, EINVAL, "Invalid time flag");
+ return (ARCHIVE_FAILED);
+ }
+ if ((flag & (ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) == 0) {
+ archive_set_error(_a, EINVAL, "No time flag");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Check a type of comparison. */
+ if (flag &
+ ((~(ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER
+ | ARCHIVE_MATCH_EQUAL)) & 0x00ff)) {
+ archive_set_error(_a, EINVAL, "Invalid comparison flag");
+ return (ARCHIVE_FAILED);
+ }
+ if ((flag & (ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER
+ | ARCHIVE_MATCH_EQUAL)) == 0) {
+ archive_set_error(_a, EINVAL, "No comparison flag");
+ return (ARCHIVE_FAILED);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+#define JUST_EQUAL(t) (((t) & (ARCHIVE_MATCH_EQUAL |\
+ ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER)) == ARCHIVE_MATCH_EQUAL)
+static int
+set_timefilter(struct archive_match *a, int timetype,
+ time_t mtime_sec, long mtime_nsec, time_t ctime_sec, long ctime_nsec)
+{
+ if (timetype & ARCHIVE_MATCH_MTIME) {
+ if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {
+ a->newer_mtime_filter = timetype;
+ a->newer_mtime_sec = mtime_sec;
+ a->newer_mtime_nsec = mtime_nsec;
+ a->setflag |= TIME_IS_SET;
+ }
+ if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {
+ a->older_mtime_filter = timetype;
+ a->older_mtime_sec = mtime_sec;
+ a->older_mtime_nsec = mtime_nsec;
+ a->setflag |= TIME_IS_SET;
+ }
+ }
+ if (timetype & ARCHIVE_MATCH_CTIME) {
+ if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {
+ a->newer_ctime_filter = timetype;
+ a->newer_ctime_sec = ctime_sec;
+ a->newer_ctime_nsec = ctime_nsec;
+ a->setflag |= TIME_IS_SET;
+ }
+ if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {
+ a->older_ctime_filter = timetype;
+ a->older_ctime_sec = ctime_sec;
+ a->older_ctime_nsec = ctime_nsec;
+ a->setflag |= TIME_IS_SET;
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+set_timefilter_date(struct archive_match *a, int timetype, const char *datestr)
+{
+ time_t t;
+
+ if (datestr == NULL || *datestr == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "date is empty");
+ return (ARCHIVE_FAILED);
+ }
+ t = get_date(a->now, datestr);
+ if (t == (time_t)-1) {
+ archive_set_error(&(a->archive), EINVAL, "invalid date string");
+ return (ARCHIVE_FAILED);
+ }
+ return set_timefilter(a, timetype, t, 0, t, 0);
+}
+
+static int
+set_timefilter_date_w(struct archive_match *a, int timetype,
+ const wchar_t *datestr)
+{
+ struct archive_string as;
+ time_t t;
+
+ if (datestr == NULL || *datestr == L'\0') {
+ archive_set_error(&(a->archive), EINVAL, "date is empty");
+ return (ARCHIVE_FAILED);
+ }
+
+ archive_string_init(&as);
+ if (archive_string_append_from_wcs(&as, datestr, wcslen(datestr)) < 0) {
+ archive_string_free(&as);
+ if (errno == ENOMEM)
+ return (error_nomem(a));
+ archive_set_error(&(a->archive), -1,
+ "Failed to convert WCS to MBS");
+ return (ARCHIVE_FAILED);
+ }
+ t = get_date(a->now, as.s);
+ archive_string_free(&as);
+ if (t == (time_t)-1) {
+ archive_set_error(&(a->archive), EINVAL, "invalid date string");
+ return (ARCHIVE_FAILED);
+ }
+ return set_timefilter(a, timetype, t, 0, t, 0);
+}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+static int
+set_timefilter_find_data(struct archive_match *a, int timetype,
+ DWORD ftLastWriteTime_dwHighDateTime, DWORD ftLastWriteTime_dwLowDateTime,
+ DWORD ftCreationTime_dwHighDateTime, DWORD ftCreationTime_dwLowDateTime)
+{
+ ULARGE_INTEGER utc;
+ time_t ctime_sec, mtime_sec;
+ long ctime_ns, mtime_ns;
+
+ utc.HighPart = ftCreationTime_dwHighDateTime;
+ utc.LowPart = ftCreationTime_dwLowDateTime;
+ if (utc.QuadPart >= EPOC_TIME) {
+ utc.QuadPart -= EPOC_TIME;
+ ctime_sec = (time_t)(utc.QuadPart / 10000000);
+ ctime_ns = (long)(utc.QuadPart % 10000000) * 100;
+ } else {
+ ctime_sec = 0;
+ ctime_ns = 0;
+ }
+ utc.HighPart = ftLastWriteTime_dwHighDateTime;
+ utc.LowPart = ftLastWriteTime_dwLowDateTime;
+ if (utc.QuadPart >= EPOC_TIME) {
+ utc.QuadPart -= EPOC_TIME;
+ mtime_sec = (time_t)(utc.QuadPart / 10000000);
+ mtime_ns = (long)(utc.QuadPart % 10000000) * 100;
+ } else {
+ mtime_sec = 0;
+ mtime_ns = 0;
+ }
+ return set_timefilter(a, timetype,
+ mtime_sec, mtime_ns, ctime_sec, ctime_ns);
+}
+
+static int
+set_timefilter_pathname_mbs(struct archive_match *a, int timetype,
+ const char *path)
+{
+ /* NOTE: stat() on Windows cannot handle nano seconds. */
+ HANDLE h;
+ WIN32_FIND_DATAA d;
+
+ if (path == NULL || *path == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+ h = FindFirstFileA(path, &d);
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&(a->archive), errno,
+ "Failed to FindFirstFileA");
+ return (ARCHIVE_FAILED);
+ }
+ FindClose(h);
+ return set_timefilter_find_data(a, timetype,
+ d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,
+ d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);
+}
+
+static int
+set_timefilter_pathname_wcs(struct archive_match *a, int timetype,
+ const wchar_t *path)
+{
+ HANDLE h;
+ WIN32_FIND_DATAW d;
+
+ if (path == NULL || *path == L'\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+ h = FindFirstFileW(path, &d);
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&(a->archive), errno,
+ "Failed to FindFirstFile");
+ return (ARCHIVE_FAILED);
+ }
+ FindClose(h);
+ return set_timefilter_find_data(a, timetype,
+ d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,
+ d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);
+}
+
+#else /* _WIN32 && !__CYGWIN__ */
+
+static int
+set_timefilter_stat(struct archive_match *a, int timetype, struct stat *st)
+{
+ struct archive_entry *ae;
+ time_t ctime_sec, mtime_sec;
+ long ctime_ns, mtime_ns;
+
+ ae = archive_entry_new();
+ if (ae == NULL)
+ return (error_nomem(a));
+ archive_entry_copy_stat(ae, st);
+ ctime_sec = archive_entry_ctime(ae);
+ ctime_ns = archive_entry_ctime_nsec(ae);
+ mtime_sec = archive_entry_mtime(ae);
+ mtime_ns = archive_entry_mtime_nsec(ae);
+ archive_entry_free(ae);
+ return set_timefilter(a, timetype, mtime_sec, mtime_ns,
+ ctime_sec, ctime_ns);
+}
+
+static int
+set_timefilter_pathname_mbs(struct archive_match *a, int timetype,
+ const char *path)
+{
+ struct stat st;
+
+ if (path == NULL || *path == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+ if (la_stat(path, &st) != 0) {
+ archive_set_error(&(a->archive), errno, "Failed to stat()");
+ return (ARCHIVE_FAILED);
+ }
+ return (set_timefilter_stat(a, timetype, &st));
+}
+
+static int
+set_timefilter_pathname_wcs(struct archive_match *a, int timetype,
+ const wchar_t *path)
+{
+ struct archive_string as;
+ int r;
+
+ if (path == NULL || *path == L'\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Convert WCS filename to MBS filename. */
+ archive_string_init(&as);
+ if (archive_string_append_from_wcs(&as, path, wcslen(path)) < 0) {
+ archive_string_free(&as);
+ if (errno == ENOMEM)
+ return (error_nomem(a));
+ archive_set_error(&(a->archive), -1,
+ "Failed to convert WCS to MBS");
+ return (ARCHIVE_FAILED);
+ }
+
+ r = set_timefilter_pathname_mbs(a, timetype, as.s);
+ archive_string_free(&as);
+
+ return (r);
+}
+#endif /* _WIN32 && !__CYGWIN__ */
+
+/*
+ * Call back functions for archive_rb.
+ */
+static int
+cmp_node_mbs(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ struct match_file *f1 = (struct match_file *)(uintptr_t)n1;
+ struct match_file *f2 = (struct match_file *)(uintptr_t)n2;
+ const char *p1, *p2;
+
+ archive_mstring_get_mbs(NULL, &(f1->pathname), &p1);
+ archive_mstring_get_mbs(NULL, &(f2->pathname), &p2);
+ if (p1 == NULL)
+ return (1);
+ if (p2 == NULL)
+ return (-1);
+ return (strcmp(p1, p2));
+}
+
+static int
+cmp_key_mbs(const struct archive_rb_node *n, const void *key)
+{
+ struct match_file *f = (struct match_file *)(uintptr_t)n;
+ const char *p;
+
+ archive_mstring_get_mbs(NULL, &(f->pathname), &p);
+ if (p == NULL)
+ return (-1);
+ return (strcmp(p, (const char *)key));
+}
+
+static int
+cmp_node_wcs(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ struct match_file *f1 = (struct match_file *)(uintptr_t)n1;
+ struct match_file *f2 = (struct match_file *)(uintptr_t)n2;
+ const wchar_t *p1, *p2;
+
+ archive_mstring_get_wcs(NULL, &(f1->pathname), &p1);
+ archive_mstring_get_wcs(NULL, &(f2->pathname), &p2);
+ if (p1 == NULL)
+ return (1);
+ if (p2 == NULL)
+ return (-1);
+ return (wcscmp(p1, p2));
+}
+
+static int
+cmp_key_wcs(const struct archive_rb_node *n, const void *key)
+{
+ struct match_file *f = (struct match_file *)(uintptr_t)n;
+ const wchar_t *p;
+
+ archive_mstring_get_wcs(NULL, &(f->pathname), &p);
+ if (p == NULL)
+ return (-1);
+ return (wcscmp(p, (const wchar_t *)key));
+}
+
+static void
+entry_list_init(struct entry_list *list)
+{
+ list->first = NULL;
+ list->last = &(list->first);
+ list->count = 0;
+}
+
+static void
+entry_list_free(struct entry_list *list)
+{
+ struct match_file *p, *q;
+
+ for (p = list->first; p != NULL; ) {
+ q = p;
+ p = p->next;
+ archive_mstring_clean(&(q->pathname));
+ free(q);
+ }
+}
+
+static void
+entry_list_add(struct entry_list *list, struct match_file *file)
+{
+ *list->last = file;
+ list->last = &(file->next);
+ list->count++;
+}
+
+static int
+add_entry(struct archive_match *a, int flag,
+ struct archive_entry *entry)
+{
+ struct match_file *f;
+ const void *pathname;
+ int r;
+
+ f = calloc(1, sizeof(*f));
+ if (f == NULL)
+ return (error_nomem(a));
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ pathname = archive_entry_pathname_w(entry);
+ if (pathname == NULL) {
+ free(f);
+ archive_set_error(&(a->archive), EINVAL, "pathname is NULL");
+ return (ARCHIVE_FAILED);
+ }
+ archive_mstring_copy_wcs(&(f->pathname), pathname);
+ a->exclusion_tree.rbt_ops = &rb_ops_wcs;
+#else
+ (void)rb_ops_wcs;
+ pathname = archive_entry_pathname(entry);
+ if (pathname == NULL) {
+ free(f);
+ archive_set_error(&(a->archive), EINVAL, "pathname is NULL");
+ return (ARCHIVE_FAILED);
+ }
+ archive_mstring_copy_mbs(&(f->pathname), pathname);
+ a->exclusion_tree.rbt_ops = &rb_ops_mbs;
+#endif
+ f->flag = flag;
+ f->mtime_sec = archive_entry_mtime(entry);
+ f->mtime_nsec = archive_entry_mtime_nsec(entry);
+ f->ctime_sec = archive_entry_ctime(entry);
+ f->ctime_nsec = archive_entry_ctime_nsec(entry);
+ r = __archive_rb_tree_insert_node(&(a->exclusion_tree), &(f->node));
+ if (!r) {
+ struct match_file *f2;
+
+ /* Get the duplicated file. */
+ f2 = (struct match_file *)__archive_rb_tree_find_node(
+ &(a->exclusion_tree), pathname);
+
+ /*
+ * We always overwrite comparison condition.
+ * If you do not want to overwrite it, you should not
+ * call archive_match_exclude_entry(). We cannot know
+ * what behavior you really expect since overwriting
+ * condition might be different with the flag.
+ */
+ if (f2 != NULL) {
+ f2->flag = f->flag;
+ f2->mtime_sec = f->mtime_sec;
+ f2->mtime_nsec = f->mtime_nsec;
+ f2->ctime_sec = f->ctime_sec;
+ f2->ctime_nsec = f->ctime_nsec;
+ }
+ /* Release the duplicated file. */
+ archive_mstring_clean(&(f->pathname));
+ free(f);
+ return (ARCHIVE_OK);
+ }
+ entry_list_add(&(a->exclusion_entry_list), f);
+ a->setflag |= TIME_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Test if entry is excluded by its timestamp.
+ */
+static int
+time_excluded(struct archive_match *a, struct archive_entry *entry)
+{
+ struct match_file *f;
+ const void *pathname;
+ time_t sec;
+ long nsec;
+
+ /*
+ * If this file/dir is excluded by a time comparison, skip it.
+ */
+ if (a->newer_ctime_filter) {
+ /* If ctime is not set, use mtime instead. */
+ if (archive_entry_ctime_is_set(entry))
+ sec = archive_entry_ctime(entry);
+ else
+ sec = archive_entry_mtime(entry);
+ if (sec < a->newer_ctime_sec)
+ return (1); /* Too old, skip it. */
+ if (sec == a->newer_ctime_sec) {
+ if (archive_entry_ctime_is_set(entry))
+ nsec = archive_entry_ctime_nsec(entry);
+ else
+ nsec = archive_entry_mtime_nsec(entry);
+ if (nsec < a->newer_ctime_nsec)
+ return (1); /* Too old, skip it. */
+ if (nsec == a->newer_ctime_nsec &&
+ (a->newer_ctime_filter & ARCHIVE_MATCH_EQUAL)
+ == 0)
+ return (1); /* Equal, skip it. */
+ }
+ }
+ if (a->older_ctime_filter) {
+ /* If ctime is not set, use mtime instead. */
+ if (archive_entry_ctime_is_set(entry))
+ sec = archive_entry_ctime(entry);
+ else
+ sec = archive_entry_mtime(entry);
+ if (sec > a->older_ctime_sec)
+ return (1); /* Too new, skip it. */
+ if (sec == a->older_ctime_sec) {
+ if (archive_entry_ctime_is_set(entry))
+ nsec = archive_entry_ctime_nsec(entry);
+ else
+ nsec = archive_entry_mtime_nsec(entry);
+ if (nsec > a->older_ctime_nsec)
+ return (1); /* Too new, skip it. */
+ if (nsec == a->older_ctime_nsec &&
+ (a->older_ctime_filter & ARCHIVE_MATCH_EQUAL)
+ == 0)
+ return (1); /* Equal, skip it. */
+ }
+ }
+ if (a->newer_mtime_filter) {
+ sec = archive_entry_mtime(entry);
+ if (sec < a->newer_mtime_sec)
+ return (1); /* Too old, skip it. */
+ if (sec == a->newer_mtime_sec) {
+ nsec = archive_entry_mtime_nsec(entry);
+ if (nsec < a->newer_mtime_nsec)
+ return (1); /* Too old, skip it. */
+ if (nsec == a->newer_mtime_nsec &&
+ (a->newer_mtime_filter & ARCHIVE_MATCH_EQUAL)
+ == 0)
+ return (1); /* Equal, skip it. */
+ }
+ }
+ if (a->older_mtime_filter) {
+ sec = archive_entry_mtime(entry);
+ if (sec > a->older_mtime_sec)
+ return (1); /* Too new, skip it. */
+ nsec = archive_entry_mtime_nsec(entry);
+ if (sec == a->older_mtime_sec) {
+ if (nsec > a->older_mtime_nsec)
+ return (1); /* Too new, skip it. */
+ if (nsec == a->older_mtime_nsec &&
+ (a->older_mtime_filter & ARCHIVE_MATCH_EQUAL)
+ == 0)
+ return (1); /* Equal, skip it. */
+ }
+ }
+
+ /* If there is no exclusion list, include the file. */
+ if (a->exclusion_entry_list.count == 0)
+ return (0);
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ pathname = archive_entry_pathname_w(entry);
+ a->exclusion_tree.rbt_ops = &rb_ops_wcs;
+#else
+ (void)rb_ops_wcs;
+ pathname = archive_entry_pathname(entry);
+ a->exclusion_tree.rbt_ops = &rb_ops_mbs;
+#endif
+ if (pathname == NULL)
+ return (0);
+
+ f = (struct match_file *)__archive_rb_tree_find_node(
+ &(a->exclusion_tree), pathname);
+ /* If the file wasn't rejected, include it. */
+ if (f == NULL)
+ return (0);
+
+ if (f->flag & ARCHIVE_MATCH_CTIME) {
+ sec = archive_entry_ctime(entry);
+ if (f->ctime_sec > sec) {
+ if (f->flag & ARCHIVE_MATCH_OLDER)
+ return (1);
+ } else if (f->ctime_sec < sec) {
+ if (f->flag & ARCHIVE_MATCH_NEWER)
+ return (1);
+ } else {
+ nsec = archive_entry_ctime_nsec(entry);
+ if (f->ctime_nsec > nsec) {
+ if (f->flag & ARCHIVE_MATCH_OLDER)
+ return (1);
+ } else if (f->ctime_nsec < nsec) {
+ if (f->flag & ARCHIVE_MATCH_NEWER)
+ return (1);
+ } else if (f->flag & ARCHIVE_MATCH_EQUAL)
+ return (1);
+ }
+ }
+ if (f->flag & ARCHIVE_MATCH_MTIME) {
+ sec = archive_entry_mtime(entry);
+ if (f->mtime_sec > sec) {
+ if (f->flag & ARCHIVE_MATCH_OLDER)
+ return (1);
+ } else if (f->mtime_sec < sec) {
+ if (f->flag & ARCHIVE_MATCH_NEWER)
+ return (1);
+ } else {
+ nsec = archive_entry_mtime_nsec(entry);
+ if (f->mtime_nsec > nsec) {
+ if (f->flag & ARCHIVE_MATCH_OLDER)
+ return (1);
+ } else if (f->mtime_nsec < nsec) {
+ if (f->flag & ARCHIVE_MATCH_NEWER)
+ return (1);
+ } else if (f->flag & ARCHIVE_MATCH_EQUAL)
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Utility functions to manage inclusion owners
+ */
+
+int
+archive_match_include_uid(struct archive *_a, la_int64_t uid)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_uid");
+ a = (struct archive_match *)_a;
+ return (add_owner_id(a, &(a->inclusion_uids), uid));
+}
+
+int
+archive_match_include_gid(struct archive *_a, la_int64_t gid)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_gid");
+ a = (struct archive_match *)_a;
+ return (add_owner_id(a, &(a->inclusion_gids), gid));
+}
+
+int
+archive_match_include_uname(struct archive *_a, const char *uname)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_uname");
+ a = (struct archive_match *)_a;
+ return (add_owner_name(a, &(a->inclusion_unames), 1, uname));
+}
+
+int
+archive_match_include_uname_w(struct archive *_a, const wchar_t *uname)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_uname_w");
+ a = (struct archive_match *)_a;
+ return (add_owner_name(a, &(a->inclusion_unames), 0, uname));
+}
+
+int
+archive_match_include_gname(struct archive *_a, const char *gname)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_gname");
+ a = (struct archive_match *)_a;
+ return (add_owner_name(a, &(a->inclusion_gnames), 1, gname));
+}
+
+int
+archive_match_include_gname_w(struct archive *_a, const wchar_t *gname)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_gname_w");
+ a = (struct archive_match *)_a;
+ return (add_owner_name(a, &(a->inclusion_gnames), 0, gname));
+}
+
+/*
+ * Test function for owner(uid, gid, uname, gname).
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_match_owner_excluded(struct archive *_a,
+ struct archive_entry *entry)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_id_excluded_ae");
+
+ a = (struct archive_match *)_a;
+ if (entry == NULL) {
+ archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* If we don't have inclusion id set at all, the entry is always
+ * not excluded. */
+ if ((a->setflag & ID_IS_SET) == 0)
+ return (0);
+ return (owner_excluded(a, entry));
+}
+
+static int
+add_owner_id(struct archive_match *a, struct id_array *ids, int64_t id)
+{
+ unsigned i;
+
+ if (ids->count + 1 >= ids->size) {
+ void *p;
+
+ if (ids->size == 0)
+ ids->size = 8;
+ else
+ ids->size *= 2;
+ p = realloc(ids->ids, sizeof(*ids->ids) * ids->size);
+ if (p == NULL)
+ return (error_nomem(a));
+ ids->ids = (int64_t *)p;
+ }
+
+ /* Find an insert point. */
+ for (i = 0; i < ids->count; i++) {
+ if (ids->ids[i] >= id)
+ break;
+ }
+
+ /* Add owner id. */
+ if (i == ids->count)
+ ids->ids[ids->count++] = id;
+ else if (ids->ids[i] != id) {
+ memmove(&(ids->ids[i+1]), &(ids->ids[i]),
+ (ids->count - i) * sizeof(ids->ids[0]));
+ ids->ids[i] = id;
+ ids->count++;
+ }
+ a->setflag |= ID_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+static int
+match_owner_id(struct id_array *ids, int64_t id)
+{
+ unsigned b, m, t;
+
+ t = 0;
+ b = (unsigned)ids->count;
+ while (t < b) {
+ m = (t + b)>>1;
+ if (ids->ids[m] == id)
+ return (1);
+ if (ids->ids[m] < id)
+ t = m + 1;
+ else
+ b = m;
+ }
+ return (0);
+}
+
+static int
+add_owner_name(struct archive_match *a, struct match_list *list,
+ int mbs, const void *name)
+{
+ struct match *match;
+
+ match = calloc(1, sizeof(*match));
+ if (match == NULL)
+ return (error_nomem(a));
+ if (mbs)
+ archive_mstring_copy_mbs(&(match->pattern), name);
+ else
+ archive_mstring_copy_wcs(&(match->pattern), name);
+ match_list_add(list, match);
+ a->setflag |= ID_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+static int
+match_owner_name_mbs(struct archive_match *a, struct match_list *list,
+ const char *name)
+{
+ struct match *m;
+ const char *p;
+
+ if (name == NULL || *name == '\0')
+ return (0);
+ for (m = list->first; m; m = m->next) {
+ if (archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p)
+ < 0 && errno == ENOMEM)
+ return (error_nomem(a));
+ if (p != NULL && strcmp(p, name) == 0) {
+ m->matches++;
+ return (1);
+ }
+ }
+ return (0);
+}
+#else
+static int
+match_owner_name_wcs(struct archive_match *a, struct match_list *list,
+ const wchar_t *name)
+{
+ struct match *m;
+ const wchar_t *p;
+
+ if (name == NULL || *name == L'\0')
+ return (0);
+ for (m = list->first; m; m = m->next) {
+ if (archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p)
+ < 0 && errno == ENOMEM)
+ return (error_nomem(a));
+ if (p != NULL && wcscmp(p, name) == 0) {
+ m->matches++;
+ return (1);
+ }
+ }
+ return (0);
+}
+#endif
+
+/*
+ * Test if entry is excluded by uid, gid, uname or gname.
+ */
+static int
+owner_excluded(struct archive_match *a, struct archive_entry *entry)
+{
+ int r;
+
+ if (a->inclusion_uids.count) {
+ if (!match_owner_id(&(a->inclusion_uids),
+ archive_entry_uid(entry)))
+ return (1);
+ }
+
+ if (a->inclusion_gids.count) {
+ if (!match_owner_id(&(a->inclusion_gids),
+ archive_entry_gid(entry)))
+ return (1);
+ }
+
+ if (a->inclusion_unames.count) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ r = match_owner_name_wcs(a, &(a->inclusion_unames),
+ archive_entry_uname_w(entry));
+#else
+ r = match_owner_name_mbs(a, &(a->inclusion_unames),
+ archive_entry_uname(entry));
+#endif
+ if (!r)
+ return (1);
+ else if (r < 0)
+ return (r);
+ }
+
+ if (a->inclusion_gnames.count) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ r = match_owner_name_wcs(a, &(a->inclusion_gnames),
+ archive_entry_gname_w(entry));
+#else
+ r = match_owner_name_mbs(a, &(a->inclusion_gnames),
+ archive_entry_gname(entry));
+#endif
+ if (!r)
+ return (1);
+ else if (r < 0)
+ return (r);
+ }
+ return (0);
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libs/libarchive/libarchive/archive_openssl_evp_private.h
new file mode 100644
index 00000000000..8ac4772808e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_openssl_evp_private.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED
+#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include <openssl/evp.h>
+#include <openssl/opensslv.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
+#include <stdlib.h> /* malloc, free */
+#include <string.h> /* memset */
+static inline EVP_MD_CTX *EVP_MD_CTX_new(void)
+{
+ EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX));
+ return ctx;
+}
+
+static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
+{
+ EVP_MD_CTX_cleanup(ctx);
+ memset(ctx, 0, sizeof(*ctx));
+ free(ctx);
+}
+#endif
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_openssl_hmac_private.h b/contrib/libs/libarchive/libarchive/archive_openssl_hmac_private.h
new file mode 100644
index 00000000000..25c8dda654f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_openssl_hmac_private.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ARCHIVE_OPENSSL_HMAC_PRIVATE_H_INCLUDED
+#define ARCHIVE_OPENSSL_HMAC_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include <openssl/hmac.h>
+#include <openssl/opensslv.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#include <stdlib.h> /* malloc, free */
+#include <string.h> /* memset */
+static inline HMAC_CTX *HMAC_CTX_new(void)
+{
+ HMAC_CTX *ctx = (HMAC_CTX *)calloc(1, sizeof(HMAC_CTX));
+ return ctx;
+}
+
+static inline void HMAC_CTX_free(HMAC_CTX *ctx)
+{
+ HMAC_CTX_cleanup(ctx);
+ memset(ctx, 0, sizeof(*ctx));
+ free(ctx);
+}
+#endif
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_options.c b/contrib/libs/libarchive/libarchive/archive_options.c
new file mode 100644
index 00000000000..6496025a5f6
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_options.c
@@ -0,0 +1,218 @@
+/*-
+ * Copyright (c) 2011 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive_options_private.h"
+
+static const char *
+parse_option(const char **str,
+ const char **mod, const char **opt, const char **val);
+
+int
+_archive_set_option(struct archive *a,
+ const char *m, const char *o, const char *v,
+ int magic, const char *fn, option_handler use_option)
+{
+ const char *mp, *op, *vp;
+ int r;
+
+ archive_check_magic(a, magic, ARCHIVE_STATE_NEW, fn);
+
+ mp = (m != NULL && m[0] != '\0') ? m : NULL;
+ op = (o != NULL && o[0] != '\0') ? o : NULL;
+ vp = (v != NULL && v[0] != '\0') ? v : NULL;
+
+ if (op == NULL && vp == NULL)
+ return (ARCHIVE_OK);
+ if (op == NULL) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC, "Empty option");
+ return (ARCHIVE_FAILED);
+ }
+
+ r = use_option(a, mp, op, vp);
+ if (r == ARCHIVE_WARN - 1) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Unknown module name: `%s'", mp);
+ return (ARCHIVE_FAILED);
+ }
+ if (r == ARCHIVE_WARN) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Undefined option: `%s%s%s%s%s%s'",
+ vp?"":"!", mp?mp:"", mp?":":"", op, vp?"=":"", vp?vp:"");
+ return (ARCHIVE_FAILED);
+ }
+ return (r);
+}
+
+int
+_archive_set_either_option(struct archive *a, const char *m, const char *o, const char *v,
+ option_handler use_format_option, option_handler use_filter_option)
+{
+ int r1, r2;
+
+ if (o == NULL && v == NULL)
+ return (ARCHIVE_OK);
+ if (o == NULL)
+ return (ARCHIVE_FAILED);
+
+ r1 = use_format_option(a, m, o, v);
+ if (r1 == ARCHIVE_FATAL)
+ return (ARCHIVE_FATAL);
+
+ r2 = use_filter_option(a, m, o, v);
+ if (r2 == ARCHIVE_FATAL)
+ return (ARCHIVE_FATAL);
+
+ if (r2 == ARCHIVE_WARN - 1)
+ return r1;
+ return r1 > r2 ? r1 : r2;
+}
+
+int
+_archive_set_options(struct archive *a, const char *options,
+ int magic, const char *fn, option_handler use_option)
+{
+ int allok = 1, anyok = 0, ignore_mod_err = 0, r;
+ char *data;
+ const char *s, *mod, *opt, *val;
+
+ archive_check_magic(a, magic, ARCHIVE_STATE_NEW, fn);
+
+ if (options == NULL || options[0] == '\0')
+ return ARCHIVE_OK;
+
+ if ((data = strdup(options)) == NULL) {
+ archive_set_error(a,
+ ENOMEM, "Out of memory adding file to list");
+ return (ARCHIVE_FATAL);
+ }
+ s = (const char *)data;
+
+ do {
+ mod = opt = val = NULL;
+
+ parse_option(&s, &mod, &opt, &val);
+ if (mod == NULL && opt != NULL &&
+ strcmp("__ignore_wrong_module_name__", opt) == 0) {
+ /* Ignore module name error */
+ if (val != NULL) {
+ ignore_mod_err = 1;
+ anyok = 1;
+ }
+ continue;
+ }
+
+ r = use_option(a, mod, opt, val);
+ if (r == ARCHIVE_FATAL) {
+ free(data);
+ return (ARCHIVE_FATAL);
+ }
+ if (r == ARCHIVE_FAILED && mod != NULL) {
+ free(data);
+ return (ARCHIVE_FAILED);
+ }
+ if (r == ARCHIVE_WARN - 1) {
+ if (ignore_mod_err)
+ continue;
+ /* The module name is wrong. */
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Unknown module name: `%s'", mod);
+ free(data);
+ return (ARCHIVE_FAILED);
+ }
+ if (r == ARCHIVE_WARN) {
+ /* The option name is wrong. No-one used this. */
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Undefined option: `%s%s%s'",
+ mod?mod:"", mod?":":"", opt);
+ free(data);
+ return (ARCHIVE_FAILED);
+ }
+ if (r == ARCHIVE_OK)
+ anyok = 1;
+ else
+ allok = 0;
+ } while (s != NULL);
+
+ free(data);
+ return allok ? ARCHIVE_OK : anyok ? ARCHIVE_WARN : ARCHIVE_FAILED;
+}
+
+static const char *
+parse_option(const char **s, const char **m, const char **o, const char **v)
+{
+ const char *end, *mod, *opt, *val;
+ char *p;
+
+ end = NULL;
+ mod = NULL;
+ opt = *s;
+ val = "1";
+
+ p = strchr(opt, ',');
+
+ if (p != NULL) {
+ *p = '\0';
+ end = ((const char *)p) + 1;
+ }
+
+ if (0 == strlen(opt)) {
+ *s = end;
+ *m = NULL;
+ *o = NULL;
+ *v = NULL;
+ return end;
+ }
+
+ p = strchr(opt, ':');
+ if (p != NULL) {
+ *p = '\0';
+ mod = opt;
+ opt = ++p;
+ }
+
+ p = strchr(opt, '=');
+ if (p != NULL) {
+ *p = '\0';
+ val = ++p;
+ } else if (opt[0] == '!') {
+ ++opt;
+ val = NULL;
+ }
+
+ *s = end;
+ *m = mod;
+ *o = opt;
+ *v = val;
+
+ return end;
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_options_private.h b/contrib/libs/libarchive/libarchive/archive_options_private.h
new file mode 100644
index 00000000000..9a7f8080d2f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_options_private.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 2011 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ARCHIVE_OPTIONS_PRIVATE_H_INCLUDED
+#define ARCHIVE_OPTIONS_PRIVATE_H_INCLUDED
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive_private.h"
+
+typedef int (*option_handler)(struct archive *a,
+ const char *mod, const char *opt, const char *val);
+
+int
+_archive_set_option(struct archive *a,
+ const char *mod, const char *opt, const char *val,
+ int magic, const char *fn, option_handler use_option);
+
+int
+_archive_set_options(struct archive *a, const char *options,
+ int magic, const char *fn, option_handler use_option);
+
+int
+_archive_set_either_option(struct archive *a,
+ const char *m, const char *o, const char *v,
+ option_handler use_format_option, option_handler use_filter_option);
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_pack_dev.c b/contrib/libs/libarchive/libarchive/archive_pack_dev.c
new file mode 100644
index 00000000000..23dc934bc73
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_pack_dev.c
@@ -0,0 +1,337 @@
+/* $NetBSD: pack_dev.c,v 1.12 2013/06/14 16:28:20 tsutsui Exp $ */
+
+/*-
+ * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Charles M. Hannum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Originally from NetBSD's mknod(8) source. */
+
+#include "archive_platform.h"
+
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+#if !defined(lint)
+__RCSID("$NetBSD$");
+#endif /* not lint */
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if MAJOR_IN_MKDEV
+#include <sys/mkdev.h>
+#define HAVE_MAJOR
+#elif MAJOR_IN_SYSMACROS
+#include <sys/sysmacros.h>
+#define HAVE_MAJOR
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive_pack_dev.h"
+
+static pack_t pack_netbsd;
+static pack_t pack_freebsd;
+static pack_t pack_8_8;
+static pack_t pack_12_20;
+static pack_t pack_14_18;
+static pack_t pack_8_24;
+static pack_t pack_bsdos;
+static int __LA_LIBC_CC compare_format(const void *, const void *);
+
+static const char iMajorError[] = "invalid major number";
+static const char iMinorError[] = "invalid minor number";
+static const char tooManyFields[] = "too many fields for format";
+
+/* This is blatantly stolen from libarchive/archive_entry.c,
+ * in an attempt to get this to play nice on MinGW... */
+#if !defined(HAVE_MAJOR) && !defined(major)
+/* Replacement for major/minor/makedev. */
+#define major(x) ((int)(0x00ff & ((x) >> 8)))
+#define minor(x) ((int)(0xffff00ff & (x)))
+#define makedev(maj,min) ((0xff00 & ((maj)<<8)) | (0xffff00ff & (min)))
+#endif
+
+/* Play games to come up with a suitable makedev() definition. */
+#ifdef __QNXNTO__
+/* QNX. <sigh> */
+#error #include <sys/netmgr.h>
+#define apd_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min))
+#elif defined makedev
+/* There's a "makedev" macro. */
+#define apd_makedev(maj, min) makedev((maj), (min))
+#elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__))
+/* Windows. <sigh> */
+#define apd_makedev(maj, min) mkdev((maj), (min))
+#else
+/* There's a "makedev" function. */
+#define apd_makedev(maj, min) makedev((maj), (min))
+#endif
+
+/* exported */
+dev_t
+pack_native(int n, unsigned long numbers[], const char **error)
+{
+ dev_t dev = 0;
+
+ if (n == 2) {
+ dev = apd_makedev(numbers[0], numbers[1]);
+ if ((unsigned long)major(dev) != numbers[0])
+ *error = iMajorError;
+ else if ((unsigned long)minor(dev) != numbers[1])
+ *error = iMinorError;
+ } else
+ *error = tooManyFields;
+ return (dev);
+}
+
+
+static dev_t
+pack_netbsd(int n, unsigned long numbers[], const char **error)
+{
+ dev_t dev = 0;
+
+ if (n == 2) {
+ dev = makedev_netbsd(numbers[0], numbers[1]);
+ if ((unsigned long)major_netbsd(dev) != numbers[0])
+ *error = iMajorError;
+ else if ((unsigned long)minor_netbsd(dev) != numbers[1])
+ *error = iMinorError;
+ } else
+ *error = tooManyFields;
+ return (dev);
+}
+
+
+#define major_freebsd(x) ((int32_t)(((x) & 0x0000ff00) >> 8))
+#define minor_freebsd(x) ((int32_t)(((x) & 0xffff00ff) >> 0))
+#define makedev_freebsd(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \
+ (((y) << 0) & 0xffff00ff)))
+
+static dev_t
+pack_freebsd(int n, unsigned long numbers[], const char **error)
+{
+ dev_t dev = 0;
+
+ if (n == 2) {
+ dev = makedev_freebsd(numbers[0], numbers[1]);
+ if ((unsigned long)major_freebsd(dev) != numbers[0])
+ *error = iMajorError;
+ if ((unsigned long)minor_freebsd(dev) != numbers[1])
+ *error = iMinorError;
+ } else
+ *error = tooManyFields;
+ return (dev);
+}
+
+
+#define major_8_8(x) ((int32_t)(((x) & 0x0000ff00) >> 8))
+#define minor_8_8(x) ((int32_t)(((x) & 0x000000ff) >> 0))
+#define makedev_8_8(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \
+ (((y) << 0) & 0x000000ff)))
+
+static dev_t
+pack_8_8(int n, unsigned long numbers[], const char **error)
+{
+ dev_t dev = 0;
+
+ if (n == 2) {
+ dev = makedev_8_8(numbers[0], numbers[1]);
+ if ((unsigned long)major_8_8(dev) != numbers[0])
+ *error = iMajorError;
+ if ((unsigned long)minor_8_8(dev) != numbers[1])
+ *error = iMinorError;
+ } else
+ *error = tooManyFields;
+ return (dev);
+}
+
+
+#define major_12_20(x) ((int32_t)(((x) & 0xfff00000) >> 20))
+#define minor_12_20(x) ((int32_t)(((x) & 0x000fffff) >> 0))
+#define makedev_12_20(x,y) ((dev_t)((((x) << 20) & 0xfff00000) | \
+ (((y) << 0) & 0x000fffff)))
+
+static dev_t
+pack_12_20(int n, unsigned long numbers[], const char **error)
+{
+ dev_t dev = 0;
+
+ if (n == 2) {
+ dev = makedev_12_20(numbers[0], numbers[1]);
+ if ((unsigned long)major_12_20(dev) != numbers[0])
+ *error = iMajorError;
+ if ((unsigned long)minor_12_20(dev) != numbers[1])
+ *error = iMinorError;
+ } else
+ *error = tooManyFields;
+ return (dev);
+}
+
+
+#define major_14_18(x) ((int32_t)(((x) & 0xfffc0000) >> 18))
+#define minor_14_18(x) ((int32_t)(((x) & 0x0003ffff) >> 0))
+#define makedev_14_18(x,y) ((dev_t)((((x) << 18) & 0xfffc0000) | \
+ (((y) << 0) & 0x0003ffff)))
+
+static dev_t
+pack_14_18(int n, unsigned long numbers[], const char **error)
+{
+ dev_t dev = 0;
+
+ if (n == 2) {
+ dev = makedev_14_18(numbers[0], numbers[1]);
+ if ((unsigned long)major_14_18(dev) != numbers[0])
+ *error = iMajorError;
+ if ((unsigned long)minor_14_18(dev) != numbers[1])
+ *error = iMinorError;
+ } else
+ *error = tooManyFields;
+ return (dev);
+}
+
+
+#define major_8_24(x) ((int32_t)(((x) & 0xff000000) >> 24))
+#define minor_8_24(x) ((int32_t)(((x) & 0x00ffffff) >> 0))
+#define makedev_8_24(x,y) ((dev_t)((((x) << 24) & 0xff000000) | \
+ (((y) << 0) & 0x00ffffff)))
+
+static dev_t
+pack_8_24(int n, unsigned long numbers[], const char **error)
+{
+ dev_t dev = 0;
+
+ if (n == 2) {
+ dev = makedev_8_24(numbers[0], numbers[1]);
+ if ((unsigned long)major_8_24(dev) != numbers[0])
+ *error = iMajorError;
+ if ((unsigned long)minor_8_24(dev) != numbers[1])
+ *error = iMinorError;
+ } else
+ *error = tooManyFields;
+ return (dev);
+}
+
+
+#define major_12_12_8(x) ((int32_t)(((x) & 0xfff00000) >> 20))
+#define unit_12_12_8(x) ((int32_t)(((x) & 0x000fff00) >> 8))
+#define subunit_12_12_8(x) ((int32_t)(((x) & 0x000000ff) >> 0))
+#define makedev_12_12_8(x,y,z) ((dev_t)((((x) << 20) & 0xfff00000) | \
+ (((y) << 8) & 0x000fff00) | \
+ (((z) << 0) & 0x000000ff)))
+
+static dev_t
+pack_bsdos(int n, unsigned long numbers[], const char **error)
+{
+ dev_t dev = 0;
+
+ if (n == 2) {
+ dev = makedev_12_20(numbers[0], numbers[1]);
+ if ((unsigned long)major_12_20(dev) != numbers[0])
+ *error = iMajorError;
+ if ((unsigned long)minor_12_20(dev) != numbers[1])
+ *error = iMinorError;
+ } else if (n == 3) {
+ dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]);
+ if ((unsigned long)major_12_12_8(dev) != numbers[0])
+ *error = iMajorError;
+ if ((unsigned long)unit_12_12_8(dev) != numbers[1])
+ *error = "invalid unit number";
+ if ((unsigned long)subunit_12_12_8(dev) != numbers[2])
+ *error = "invalid subunit number";
+ } else
+ *error = tooManyFields;
+ return (dev);
+}
+
+
+ /* list of formats and pack functions */
+ /* this list must be sorted lexically */
+static const struct format {
+ const char *name;
+ pack_t *pack;
+} formats[] = {
+ {"386bsd", pack_8_8},
+ {"4bsd", pack_8_8},
+ {"bsdos", pack_bsdos},
+ {"freebsd", pack_freebsd},
+ {"hpux", pack_8_24},
+ {"isc", pack_8_8},
+ {"linux", pack_8_8},
+ {"native", pack_native},
+ {"netbsd", pack_netbsd},
+ {"osf1", pack_12_20},
+ {"sco", pack_8_8},
+ {"solaris", pack_14_18},
+ {"sunos", pack_8_8},
+ {"svr3", pack_8_8},
+ {"svr4", pack_14_18},
+ {"ultrix", pack_8_8},
+};
+
+static int
+__LA_LIBC_CC
+compare_format(const void *key, const void *element)
+{
+ const char *name;
+ const struct format *format;
+
+ name = key;
+ format = element;
+
+ return (strcmp(name, format->name));
+}
+
+
+pack_t *
+pack_find(const char *name)
+{
+ struct format *format;
+
+ format = bsearch(name, formats,
+ sizeof(formats)/sizeof(formats[0]),
+ sizeof(formats[0]), compare_format);
+ if (format == 0)
+ return (NULL);
+ return (format->pack);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_pack_dev.h b/contrib/libs/libarchive/libarchive/archive_pack_dev.h
new file mode 100644
index 00000000000..eaf23e3883e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_pack_dev.h
@@ -0,0 +1,49 @@
+/* $NetBSD: pack_dev.h,v 1.8 2013/06/14 16:28:20 tsutsui Exp $ */
+
+/*-
+ * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Charles M. Hannum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Originally from NetBSD's mknod(8) source. */
+
+#ifndef ARCHIVE_PACK_DEV_H
+#define ARCHIVE_PACK_DEV_H
+
+typedef dev_t pack_t(int, unsigned long [], const char **);
+
+pack_t *pack_find(const char *);
+pack_t pack_native;
+
+#define major_netbsd(x) ((int32_t)((((x) & 0x000fff00) >> 8)))
+#define minor_netbsd(x) ((int32_t)((((x) & 0xfff00000) >> 12) | \
+ (((x) & 0x000000ff) >> 0)))
+#define makedev_netbsd(x,y) ((dev_t)((((x) << 8) & 0x000fff00) | \
+ (((y) << 12) & 0xfff00000) | \
+ (((y) << 0) & 0x000000ff)))
+
+#endif /* ARCHIVE_PACK_DEV_H */
diff --git a/contrib/libs/libarchive/libarchive/archive_pathmatch.c b/contrib/libs/libarchive/libarchive/archive_pathmatch.c
new file mode 100644
index 00000000000..0867a268eef
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_pathmatch.c
@@ -0,0 +1,463 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+
+#include "archive_pathmatch.h"
+
+/*
+ * Check whether a character 'c' is matched by a list specification [...]:
+ * * Leading '!' or '^' negates the class.
+ * * <char>-<char> is a range of characters
+ * * \<char> removes any special meaning for <char>
+ *
+ * Some interesting boundary cases:
+ * a-d-e is one range (a-d) followed by two single characters - and e.
+ * \a-\d is same as a-d
+ * a\-d is three single characters: a, d, -
+ * Trailing - is not special (so [a-] is two characters a and -).
+ * Initial - is not special ([a-] is same as [-a] is same as [\\-a])
+ * This function never sees a trailing \.
+ * [] always fails
+ * [!] always succeeds
+ */
+static int
+pm_list(const char *start, const char *end, const char c, int flags)
+{
+ const char *p = start;
+ char rangeStart = '\0', nextRangeStart;
+ int match = 1, nomatch = 0;
+
+ /* This will be used soon... */
+ (void)flags; /* UNUSED */
+
+ /* If this is a negated class, return success for nomatch. */
+ if ((*p == '!' || *p == '^') && p < end) {
+ match = 0;
+ nomatch = 1;
+ ++p;
+ }
+
+ while (p < end) {
+ nextRangeStart = '\0';
+ switch (*p) {
+ case '-':
+ /* Trailing or initial '-' is not special. */
+ if ((rangeStart == '\0') || (p == end - 1)) {
+ if (*p == c)
+ return (match);
+ } else {
+ char rangeEnd = *++p;
+ if (rangeEnd == '\\')
+ rangeEnd = *++p;
+ if ((rangeStart <= c) && (c <= rangeEnd))
+ return (match);
+ }
+ break;
+ case '\\':
+ ++p;
+ /* Fall through */
+ default:
+ if (*p == c)
+ return (match);
+ nextRangeStart = *p; /* Possible start of range. */
+ }
+ rangeStart = nextRangeStart;
+ ++p;
+ }
+ return (nomatch);
+}
+
+static int
+pm_list_w(const wchar_t *start, const wchar_t *end, const wchar_t c, int flags)
+{
+ const wchar_t *p = start;
+ wchar_t rangeStart = L'\0', nextRangeStart;
+ int match = 1, nomatch = 0;
+
+ /* This will be used soon... */
+ (void)flags; /* UNUSED */
+
+ /* If this is a negated class, return success for nomatch. */
+ if ((*p == L'!' || *p == L'^') && p < end) {
+ match = 0;
+ nomatch = 1;
+ ++p;
+ }
+
+ while (p < end) {
+ nextRangeStart = L'\0';
+ switch (*p) {
+ case L'-':
+ /* Trailing or initial '-' is not special. */
+ if ((rangeStart == L'\0') || (p == end - 1)) {
+ if (*p == c)
+ return (match);
+ } else {
+ wchar_t rangeEnd = *++p;
+ if (rangeEnd == L'\\')
+ rangeEnd = *++p;
+ if ((rangeStart <= c) && (c <= rangeEnd))
+ return (match);
+ }
+ break;
+ case L'\\':
+ ++p;
+ /* Fall through */
+ default:
+ if (*p == c)
+ return (match);
+ nextRangeStart = *p; /* Possible start of range. */
+ }
+ rangeStart = nextRangeStart;
+ ++p;
+ }
+ return (nomatch);
+}
+
+/*
+ * If s is pointing to "./", ".//", "./././" or the like, skip it.
+ */
+static const char *
+pm_slashskip(const char *s) {
+ while ((*s == '/')
+ || (s[0] == '.' && s[1] == '/')
+ || (s[0] == '.' && s[1] == '\0'))
+ ++s;
+ return (s);
+}
+
+static const wchar_t *
+pm_slashskip_w(const wchar_t *s) {
+ while ((*s == L'/')
+ || (s[0] == L'.' && s[1] == L'/')
+ || (s[0] == L'.' && s[1] == L'\0'))
+ ++s;
+ return (s);
+}
+
+static int
+pm(const char *p, const char *s, int flags)
+{
+ const char *end;
+
+ /*
+ * Ignore leading './', './/', '././', etc.
+ */
+ if (s[0] == '.' && s[1] == '/')
+ s = pm_slashskip(s + 1);
+ if (p[0] == '.' && p[1] == '/')
+ p = pm_slashskip(p + 1);
+
+ for (;;) {
+ switch (*p) {
+ case '\0':
+ if (s[0] == '/') {
+ if (flags & PATHMATCH_NO_ANCHOR_END)
+ return (1);
+ /* "dir" == "dir/" == "dir/." */
+ s = pm_slashskip(s);
+ }
+ return (*s == '\0');
+ case '?':
+ /* ? always succeeds, unless we hit end of 's' */
+ if (*s == '\0')
+ return (0);
+ break;
+ case '*':
+ /* "*" == "**" == "***" ... */
+ while (*p == '*')
+ ++p;
+ /* Trailing '*' always succeeds. */
+ if (*p == '\0')
+ return (1);
+ while (*s) {
+ if (archive_pathmatch(p, s, flags))
+ return (1);
+ ++s;
+ }
+ return (0);
+ case '[':
+ /*
+ * Find the end of the [...] character class,
+ * ignoring \] that might occur within the class.
+ */
+ end = p + 1;
+ while (*end != '\0' && *end != ']') {
+ if (*end == '\\' && end[1] != '\0')
+ ++end;
+ ++end;
+ }
+ if (*end == ']') {
+ /* We found [...], try to match it. */
+ if (!pm_list(p + 1, end, *s, flags))
+ return (0);
+ p = end; /* Jump to trailing ']' char. */
+ break;
+ } else
+ /* No final ']', so just match '['. */
+ if (*p != *s)
+ return (0);
+ break;
+ case '\\':
+ /* Trailing '\\' matches itself. */
+ if (p[1] == '\0') {
+ if (*s != '\\')
+ return (0);
+ } else {
+ ++p;
+ if (*p != *s)
+ return (0);
+ }
+ break;
+ case '/':
+ if (*s != '/' && *s != '\0')
+ return (0);
+ /* Note: pattern "/\./" won't match "/";
+ * pm_slashskip() correctly stops at backslash. */
+ p = pm_slashskip(p);
+ s = pm_slashskip(s);
+ if (*p == '\0' && (flags & PATHMATCH_NO_ANCHOR_END))
+ return (1);
+ --p; /* Counteract the increment below. */
+ --s;
+ break;
+ case '$':
+ /* '$' is special only at end of pattern and only
+ * if PATHMATCH_NO_ANCHOR_END is specified. */
+ if (p[1] == '\0' && (flags & PATHMATCH_NO_ANCHOR_END)){
+ /* "dir" == "dir/" == "dir/." */
+ return (*pm_slashskip(s) == '\0');
+ }
+ /* Otherwise, '$' is not special. */
+ /* FALL THROUGH */
+ default:
+ if (*p != *s)
+ return (0);
+ break;
+ }
+ ++p;
+ ++s;
+ }
+}
+
+static int
+pm_w(const wchar_t *p, const wchar_t *s, int flags)
+{
+ const wchar_t *end;
+
+ /*
+ * Ignore leading './', './/', '././', etc.
+ */
+ if (s[0] == L'.' && s[1] == L'/')
+ s = pm_slashskip_w(s + 1);
+ if (p[0] == L'.' && p[1] == L'/')
+ p = pm_slashskip_w(p + 1);
+
+ for (;;) {
+ switch (*p) {
+ case L'\0':
+ if (s[0] == L'/') {
+ if (flags & PATHMATCH_NO_ANCHOR_END)
+ return (1);
+ /* "dir" == "dir/" == "dir/." */
+ s = pm_slashskip_w(s);
+ }
+ return (*s == L'\0');
+ case L'?':
+ /* ? always succeeds, unless we hit end of 's' */
+ if (*s == L'\0')
+ return (0);
+ break;
+ case L'*':
+ /* "*" == "**" == "***" ... */
+ while (*p == L'*')
+ ++p;
+ /* Trailing '*' always succeeds. */
+ if (*p == L'\0')
+ return (1);
+ while (*s) {
+ if (archive_pathmatch_w(p, s, flags))
+ return (1);
+ ++s;
+ }
+ return (0);
+ case L'[':
+ /*
+ * Find the end of the [...] character class,
+ * ignoring \] that might occur within the class.
+ */
+ end = p + 1;
+ while (*end != L'\0' && *end != L']') {
+ if (*end == L'\\' && end[1] != L'\0')
+ ++end;
+ ++end;
+ }
+ if (*end == L']') {
+ /* We found [...], try to match it. */
+ if (!pm_list_w(p + 1, end, *s, flags))
+ return (0);
+ p = end; /* Jump to trailing ']' char. */
+ break;
+ } else
+ /* No final ']', so just match '['. */
+ if (*p != *s)
+ return (0);
+ break;
+ case L'\\':
+ /* Trailing '\\' matches itself. */
+ if (p[1] == L'\0') {
+ if (*s != L'\\')
+ return (0);
+ } else {
+ ++p;
+ if (*p != *s)
+ return (0);
+ }
+ break;
+ case L'/':
+ if (*s != L'/' && *s != L'\0')
+ return (0);
+ /* Note: pattern "/\./" won't match "/";
+ * pm_slashskip() correctly stops at backslash. */
+ p = pm_slashskip_w(p);
+ s = pm_slashskip_w(s);
+ if (*p == L'\0' && (flags & PATHMATCH_NO_ANCHOR_END))
+ return (1);
+ --p; /* Counteract the increment below. */
+ --s;
+ break;
+ case L'$':
+ /* '$' is special only at end of pattern and only
+ * if PATHMATCH_NO_ANCHOR_END is specified. */
+ if (p[1] == L'\0' && (flags & PATHMATCH_NO_ANCHOR_END)){
+ /* "dir" == "dir/" == "dir/." */
+ return (*pm_slashskip_w(s) == L'\0');
+ }
+ /* Otherwise, '$' is not special. */
+ /* FALL THROUGH */
+ default:
+ if (*p != *s)
+ return (0);
+ break;
+ }
+ ++p;
+ ++s;
+ }
+}
+
+/* Main entry point. */
+int
+__archive_pathmatch(const char *p, const char *s, int flags)
+{
+ /* Empty pattern only matches the empty string. */
+ if (p == NULL || *p == '\0')
+ return (s == NULL || *s == '\0');
+ else if (s == NULL)
+ return (0);
+
+ /* Leading '^' anchors the start of the pattern. */
+ if (*p == '^') {
+ ++p;
+ flags &= ~PATHMATCH_NO_ANCHOR_START;
+ }
+
+ if (*p == '/' && *s != '/')
+ return (0);
+
+ /* Certain patterns anchor implicitly. */
+ if (*p == '*' || *p == '/') {
+ while (*p == '/')
+ ++p;
+ while (*s == '/')
+ ++s;
+ return (pm(p, s, flags));
+ }
+
+ /* If start is unanchored, try to match start of each path element. */
+ if (flags & PATHMATCH_NO_ANCHOR_START) {
+ for ( ; s != NULL; s = strchr(s, '/')) {
+ if (*s == '/')
+ s++;
+ if (pm(p, s, flags))
+ return (1);
+ }
+ return (0);
+ }
+
+ /* Default: Match from beginning. */
+ return (pm(p, s, flags));
+}
+
+int
+__archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags)
+{
+ /* Empty pattern only matches the empty string. */
+ if (p == NULL || *p == L'\0')
+ return (s == NULL || *s == L'\0');
+ else if (s == NULL)
+ return (0);
+
+ /* Leading '^' anchors the start of the pattern. */
+ if (*p == L'^') {
+ ++p;
+ flags &= ~PATHMATCH_NO_ANCHOR_START;
+ }
+
+ if (*p == L'/' && *s != L'/')
+ return (0);
+
+ /* Certain patterns anchor implicitly. */
+ if (*p == L'*' || *p == L'/') {
+ while (*p == L'/')
+ ++p;
+ while (*s == L'/')
+ ++s;
+ return (pm_w(p, s, flags));
+ }
+
+ /* If start is unanchored, try to match start of each path element. */
+ if (flags & PATHMATCH_NO_ANCHOR_START) {
+ for ( ; s != NULL; s = wcschr(s, L'/')) {
+ if (*s == L'/')
+ s++;
+ if (pm_w(p, s, flags))
+ return (1);
+ }
+ return (0);
+ }
+
+ /* Default: Match from beginning. */
+ return (pm_w(p, s, flags));
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_pathmatch.h b/contrib/libs/libarchive/libarchive/archive_pathmatch.h
new file mode 100644
index 00000000000..9995142921e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_pathmatch.h
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_PATHMATCH_H
+#define ARCHIVE_PATHMATCH_H
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+/* Don't anchor at beginning unless the pattern starts with "^" */
+#define PATHMATCH_NO_ANCHOR_START 1
+/* Don't anchor at end unless the pattern ends with "$" */
+#define PATHMATCH_NO_ANCHOR_END 2
+
+/* Note that "^" and "$" are not special unless you set the corresponding
+ * flag above. */
+
+int __archive_pathmatch(const char *p, const char *s, int flags);
+int __archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags);
+
+#define archive_pathmatch(p, s, f) __archive_pathmatch(p, s, f)
+#define archive_pathmatch_w(p, s, f) __archive_pathmatch_w(p, s, f)
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_platform.h b/contrib/libs/libarchive/libarchive/archive_platform.h
new file mode 100644
index 00000000000..2f134147669
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_platform.h
@@ -0,0 +1,233 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_platform.h 201090 2009-12-28 02:22:04Z kientzle $
+ */
+
+/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */
+
+/*
+ * This header is the first thing included in any of the libarchive
+ * source files. As far as possible, platform-specific issues should
+ * be dealt with here and not within individual source files. I'm
+ * actively trying to minimize #if blocks within the main source,
+ * since they obfuscate the code.
+ */
+
+#ifndef ARCHIVE_PLATFORM_H_INCLUDED
+#define ARCHIVE_PLATFORM_H_INCLUDED
+
+/* archive.h and archive_entry.h require this. */
+#define __LIBARCHIVE_BUILD 1
+
+#if defined(PLATFORM_CONFIG_H)
+/* Use hand-built config.h in environments that need it. */
+#error #include PLATFORM_CONFIG_H
+#elif defined(HAVE_CONFIG_H)
+/* Most POSIX platforms use the 'configure' script to build config.h */
+#include "config.h"
+#else
+/* Warn if the library hasn't been (automatically or manually) configured. */
+#error Oops: No config.h and no pre-built configuration in archive_platform.h.
+#endif
+
+/* On macOS check for some symbols based on the deployment target version. */
+#if defined(__APPLE__)
+# undef HAVE_FUTIMENS
+# undef HAVE_UTIMENSAT
+# include <AvailabilityMacros.h>
+# if MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
+# define HAVE_FUTIMENS 1
+# define HAVE_UTIMENSAT 1
+# endif
+#endif
+
+/* It should be possible to get rid of this by extending the feature-test
+ * macros to cover Windows API functions, probably along with non-trivial
+ * refactoring of code to find structures that sit more cleanly on top of
+ * either Windows or Posix APIs. */
+#if (defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__)
+#include "archive_windows.h"
+/* The C library on Windows specifies a calling convention for callback
+ * functions and exports; when we interact with them (capture pointers,
+ * call and pass function pointers) we need to match their calling
+ * convention.
+ * This only matters when libarchive is built with /Gr, /Gz or /Gv
+ * (which change the default calling convention.) */
+#define __LA_LIBC_CC __cdecl
+#else
+#define la_stat(path,stref) stat(path,stref)
+#define __LA_LIBC_CC
+#endif
+
+/*
+ * The config files define a lot of feature macros. The following
+ * uses those macros to select/define replacements and include key
+ * headers as required.
+ */
+
+/* Get a real definition for __FBSDID or __RCSID if we can */
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+/* If not, define them so as to avoid dangling semicolons. */
+#ifndef __FBSDID
+#define __FBSDID(a) struct _undefined_hack
+#endif
+#ifndef __RCSID
+#define __RCSID(a) struct _undefined_hack
+#endif
+
+/* Try to get standard C99-style integer type definitions. */
+#if HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+/* Borland warns about its own constants! */
+#if defined(__BORLANDC__)
+# if HAVE_DECL_UINT64_MAX
+# undef UINT64_MAX
+# undef HAVE_DECL_UINT64_MAX
+# endif
+# if HAVE_DECL_UINT64_MIN
+# undef UINT64_MIN
+# undef HAVE_DECL_UINT64_MIN
+# endif
+# if HAVE_DECL_INT64_MAX
+# undef INT64_MAX
+# undef HAVE_DECL_INT64_MAX
+# endif
+# if HAVE_DECL_INT64_MIN
+# undef INT64_MIN
+# undef HAVE_DECL_INT64_MIN
+# endif
+#endif
+
+/* Some platforms lack the standard *_MAX definitions. */
+#if !HAVE_DECL_SIZE_MAX
+#define SIZE_MAX (~(size_t)0)
+#endif
+#if !HAVE_DECL_SSIZE_MAX
+#define SSIZE_MAX ((ssize_t)(SIZE_MAX >> 1))
+#endif
+#if !HAVE_DECL_UINT32_MAX
+#define UINT32_MAX (~(uint32_t)0)
+#endif
+#if !HAVE_DECL_INT32_MAX
+#define INT32_MAX ((int32_t)(UINT32_MAX >> 1))
+#endif
+#if !HAVE_DECL_INT32_MIN
+#define INT32_MIN ((int32_t)(~INT32_MAX))
+#endif
+#if !HAVE_DECL_UINT64_MAX
+#define UINT64_MAX (~(uint64_t)0)
+#endif
+#if !HAVE_DECL_INT64_MAX
+#define INT64_MAX ((int64_t)(UINT64_MAX >> 1))
+#endif
+#if !HAVE_DECL_INT64_MIN
+#define INT64_MIN ((int64_t)(~INT64_MAX))
+#endif
+#if !HAVE_DECL_UINTMAX_MAX
+#define UINTMAX_MAX (~(uintmax_t)0)
+#endif
+#if !HAVE_DECL_INTMAX_MAX
+#define INTMAX_MAX ((intmax_t)(UINTMAX_MAX >> 1))
+#endif
+#if !HAVE_DECL_INTMAX_MIN
+#define INTMAX_MIN ((intmax_t)(~INTMAX_MAX))
+#endif
+
+/* Some platforms lack the standard PRIxN/PRIdN definitions. */
+#if !HAVE_INTTYPES_H || !defined(PRIx32) || !defined(PRId32)
+#ifndef PRIx32
+#if SIZEOF_INT == 4
+#define PRIx32 "x"
+#elif SIZEOF_LONG == 4
+#define PRIx32 "lx"
+#else
+#error No suitable 32-bit unsigned integer type found for this platform
+#endif
+#endif // PRIx32
+#ifndef PRId32
+#if SIZEOF_INT == 4
+#define PRId32 "d"
+#elif SIZEOF_LONG == 4
+#define PRId32 "ld"
+#else
+#error No suitable 32-bit signed integer type found for this platform
+#endif
+#endif // PRId32
+#endif // !HAVE_INTTYPES_H || !defined(PRIx32) || !defined(PRId32)
+
+/*
+ * If we can't restore metadata using a file descriptor, then
+ * for compatibility's sake, close files before trying to restore metadata.
+ */
+#if defined(HAVE_FCHMOD) || defined(HAVE_FUTIMES) || defined(HAVE_ACL_SET_FD) || defined(HAVE_ACL_SET_FD_NP) || defined(HAVE_FCHOWN)
+#define CAN_RESTORE_METADATA_FD
+#endif
+
+/*
+ * glibc 2.24 deprecates readdir_r
+ * bionic c deprecates readdir_r too
+ */
+#if defined(HAVE_READDIR_R) && (!defined(__GLIBC__) || !defined(__GLIBC_MINOR__) || __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 24)) && (!defined(__ANDROID__))
+#define USE_READDIR_R 1
+#else
+#undef USE_READDIR_R
+#endif
+
+/* Set up defaults for internal error codes. */
+#ifndef ARCHIVE_ERRNO_FILE_FORMAT
+#if HAVE_EFTYPE
+#define ARCHIVE_ERRNO_FILE_FORMAT EFTYPE
+#else
+#if HAVE_EILSEQ
+#define ARCHIVE_ERRNO_FILE_FORMAT EILSEQ
+#else
+#define ARCHIVE_ERRNO_FILE_FORMAT EINVAL
+#endif
+#endif
+#endif
+
+#ifndef ARCHIVE_ERRNO_PROGRAMMER
+#define ARCHIVE_ERRNO_PROGRAMMER EINVAL
+#endif
+
+#ifndef ARCHIVE_ERRNO_MISC
+#define ARCHIVE_ERRNO_MISC (-1)
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ >= 7)
+#define __LA_FALLTHROUGH __attribute__((fallthrough))
+#else
+#define __LA_FALLTHROUGH
+#endif
+
+#endif /* !ARCHIVE_PLATFORM_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_platform_acl.h b/contrib/libs/libarchive/libarchive/archive_platform_acl.h
new file mode 100644
index 00000000000..264e6de375a
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_platform_acl.h
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 2017 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */
+
+#ifndef ARCHIVE_PLATFORM_ACL_H_INCLUDED
+#define ARCHIVE_PLATFORM_ACL_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST_COMMON
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+/*
+ * Determine what ACL types are supported
+ */
+#if ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_SUNOS || ARCHIVE_ACL_LIBACL
+#define ARCHIVE_ACL_POSIX1E 1
+#endif
+
+#if ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_SUNOS_NFS4 || \
+ ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL
+#define ARCHIVE_ACL_NFS4 1
+#endif
+
+#if ARCHIVE_ACL_POSIX1E || ARCHIVE_ACL_NFS4
+#define ARCHIVE_ACL_SUPPORT 1
+#endif
+
+#endif /* ARCHIVE_PLATFORM_ACL_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_ppmd7.c b/contrib/libs/libarchive/libarchive/archive_ppmd7.c
new file mode 100644
index 00000000000..cc3f778203f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_ppmd7.c
@@ -0,0 +1,1168 @@
+/* Ppmd7.c -- PPMdH codec
+2010-03-12 : Igor Pavlov : Public domain
+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
+
+#include "archive_platform.h"
+
+#include <stdlib.h>
+
+#include "archive_ppmd7_private.h"
+
+#ifdef PPMD_32BIT
+ #define Ppmd7_GetPtr(p, ptr) (ptr)
+ #define Ppmd7_GetContext(p, ptr) (ptr)
+ #define Ppmd7_GetStats(p, ctx) ((ctx)->Stats)
+#else
+ #define Ppmd7_GetPtr(p, offs) ((void *)((p)->Base + (offs)))
+ #define Ppmd7_GetContext(p, offs) ((CPpmd7_Context *)Ppmd7_GetPtr((p), (offs)))
+ #define Ppmd7_GetStats(p, ctx) ((CPpmd_State *)Ppmd7_GetPtr((p), ((ctx)->Stats)))
+#endif
+
+#define Ppmd7_GetBinSumm(p) \
+ &p->BinSumm[Ppmd7Context_OneState(p->MinContext)->Freq - 1][p->PrevSuccess + \
+ p->NS2BSIndx[Ppmd7_GetContext(p, p->MinContext->Suffix)->NumStats - 1] + \
+ (p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol]) + \
+ 2 * p->HB2Flag[Ppmd7Context_OneState(p->MinContext)->Symbol] + \
+ ((p->RunLength >> 26) & 0x20)]
+
+#define kTopValue (1 << 24)
+#define MAX_FREQ 124
+#define UNIT_SIZE 12
+
+#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE)
+#define U2I(nu) (p->Units2Indx[(nu) - 1])
+#define I2U(indx) (p->Indx2Units[indx])
+
+#ifdef PPMD_32BIT
+ #define REF(ptr) (ptr)
+#else
+ #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base))
+#endif
+
+#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr))
+
+#define CTX(ref) ((CPpmd7_Context *)Ppmd7_GetContext(p, ref))
+#define STATS(ctx) Ppmd7_GetStats(p, ctx)
+#define ONE_STATE(ctx) Ppmd7Context_OneState(ctx)
+#define SUFFIX(ctx) CTX((ctx)->Suffix)
+
+static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051};
+static const Byte PPMD7_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
+
+typedef CPpmd7_Context * CTX_PTR;
+
+struct CPpmd7_Node_;
+
+typedef
+ #ifdef PPMD_32BIT
+ struct CPpmd7_Node_ *
+ #else
+ UInt32
+ #endif
+ CPpmd7_Node_Ref;
+
+typedef struct CPpmd7_Node_
+{
+ UInt16 Stamp; /* must be at offset 0 as CPpmd7_Context::NumStats. Stamp=0 means free */
+ UInt16 NU;
+ CPpmd7_Node_Ref Next; /* must be at offset >= 4 */
+ CPpmd7_Node_Ref Prev;
+} CPpmd7_Node;
+
+#ifdef PPMD_32BIT
+ #define NODE(ptr) (ptr)
+#else
+ #define NODE(offs) ((CPpmd7_Node *)(p->Base + (offs)))
+#endif
+
+static void Ppmd7_Update1(CPpmd7 *p);
+static void Ppmd7_Update1_0(CPpmd7 *p);
+static void Ppmd7_Update2(CPpmd7 *p);
+static void Ppmd7_UpdateBin(CPpmd7 *p);
+static CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked,
+ UInt32 *scale);
+
+/* ----------- Base ----------- */
+
+static void Ppmd7_Construct(CPpmd7 *p)
+{
+ unsigned i, k, m;
+
+ p->Base = 0;
+
+ for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++)
+ {
+ unsigned step = (i >= 12 ? 4 : (i >> 2) + 1);
+ do { p->Units2Indx[k++] = (Byte)i; } while(--step);
+ p->Indx2Units[i] = (Byte)k;
+ }
+
+ p->NS2BSIndx[0] = (0 << 1);
+ p->NS2BSIndx[1] = (1 << 1);
+ memset(p->NS2BSIndx + 2, (2 << 1), 9);
+ memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11);
+
+ for (i = 0; i < 3; i++)
+ p->NS2Indx[i] = (Byte)i;
+ for (m = i, k = 1; i < 256; i++)
+ {
+ p->NS2Indx[i] = (Byte)m;
+ if (--k == 0)
+ k = (++m) - 2;
+ }
+
+ memset(p->HB2Flag, 0, 0x40);
+ memset(p->HB2Flag + 0x40, 8, 0x100 - 0x40);
+}
+
+static void Ppmd7_Free(CPpmd7 *p)
+{
+ free(p->Base);
+ p->Size = 0;
+ p->Base = 0;
+}
+
+static Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size)
+{
+ if (p->Base == 0 || p->Size != size)
+ {
+ /* RestartModel() below assumes that p->Size >= UNIT_SIZE
+ (see the calculation of m->MinContext). */
+ if (size < UNIT_SIZE) {
+ return False;
+ }
+ Ppmd7_Free(p);
+ p->AlignOffset =
+ #ifdef PPMD_32BIT
+ (4 - size) & 3;
+ #else
+ 4 - (size & 3);
+ #endif
+ if ((p->Base = (Byte *)malloc(p->AlignOffset + size
+ #ifndef PPMD_32BIT
+ + UNIT_SIZE
+ #endif
+ )) == 0)
+ return False;
+ p->Size = size;
+ }
+ return True;
+}
+
+static void InsertNode(CPpmd7 *p, void *node, unsigned indx)
+{
+ *((CPpmd_Void_Ref *)node) = p->FreeList[indx];
+ p->FreeList[indx] = REF(node);
+}
+
+static void *RemoveNode(CPpmd7 *p, unsigned indx)
+{
+ CPpmd_Void_Ref *node = (CPpmd_Void_Ref *)Ppmd7_GetPtr(p, p->FreeList[indx]);
+ p->FreeList[indx] = *node;
+ return node;
+}
+
+static void SplitBlock(CPpmd7 *p, void *ptr, unsigned oldIndx, unsigned newIndx)
+{
+ unsigned i, nu = I2U(oldIndx) - I2U(newIndx);
+ ptr = (Byte *)ptr + U2B(I2U(newIndx));
+ if (I2U(i = U2I(nu)) != nu)
+ {
+ unsigned k = I2U(--i);
+ InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1);
+ }
+ InsertNode(p, ptr, i);
+}
+
+static void GlueFreeBlocks(CPpmd7 *p)
+{
+ #ifdef PPMD_32BIT
+ CPpmd7_Node headItem;
+ CPpmd7_Node_Ref head = &headItem;
+ #else
+ CPpmd7_Node_Ref head = p->AlignOffset + p->Size;
+ #endif
+
+ CPpmd7_Node_Ref n = head;
+ unsigned i;
+
+ p->GlueCount = 255;
+
+ /* create doubly-linked list of free blocks */
+ for (i = 0; i < PPMD_NUM_INDEXES; i++)
+ {
+ UInt16 nu = I2U(i);
+ CPpmd7_Node_Ref next = (CPpmd7_Node_Ref)p->FreeList[i];
+ p->FreeList[i] = 0;
+ while (next != 0)
+ {
+ CPpmd7_Node *node = NODE(next);
+ node->Next = n;
+ n = NODE(n)->Prev = next;
+ next = *(const CPpmd7_Node_Ref *)node;
+ node->Stamp = 0;
+ node->NU = (UInt16)nu;
+ }
+ }
+ NODE(head)->Stamp = 1;
+ NODE(head)->Next = n;
+ NODE(n)->Prev = head;
+ if (p->LoUnit != p->HiUnit)
+ ((CPpmd7_Node *)p->LoUnit)->Stamp = 1;
+
+ /* Glue free blocks */
+ while (n != head)
+ {
+ CPpmd7_Node *node = NODE(n);
+ UInt32 nu = (UInt32)node->NU;
+ for (;;)
+ {
+ CPpmd7_Node *node2 = NODE(n) + nu;
+ nu += node2->NU;
+ if (node2->Stamp != 0 || nu >= 0x10000)
+ break;
+ NODE(node2->Prev)->Next = node2->Next;
+ NODE(node2->Next)->Prev = node2->Prev;
+ node->NU = (UInt16)nu;
+ }
+ n = node->Next;
+ }
+
+ /* Fill lists of free blocks */
+ for (n = NODE(head)->Next; n != head;)
+ {
+ CPpmd7_Node *node = NODE(n);
+ unsigned nu;
+ CPpmd7_Node_Ref next = node->Next;
+ for (nu = node->NU; nu > 128; nu -= 128, node += 128)
+ InsertNode(p, node, PPMD_NUM_INDEXES - 1);
+ if (I2U(i = U2I(nu)) != nu)
+ {
+ unsigned k = I2U(--i);
+ InsertNode(p, node + k, nu - k - 1);
+ }
+ InsertNode(p, node, i);
+ n = next;
+ }
+}
+
+static void *AllocUnitsRare(CPpmd7 *p, unsigned indx)
+{
+ unsigned i;
+ void *retVal;
+ if (p->GlueCount == 0)
+ {
+ GlueFreeBlocks(p);
+ if (p->FreeList[indx] != 0)
+ return RemoveNode(p, indx);
+ }
+ i = indx;
+ do
+ {
+ if (++i == PPMD_NUM_INDEXES)
+ {
+ UInt32 numBytes = U2B(I2U(indx));
+ p->GlueCount--;
+ return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL);
+ }
+ }
+ while (p->FreeList[i] == 0);
+ retVal = RemoveNode(p, i);
+ SplitBlock(p, retVal, i, indx);
+ return retVal;
+}
+
+static void *AllocUnits(CPpmd7 *p, unsigned indx)
+{
+ UInt32 numBytes;
+ if (p->FreeList[indx] != 0)
+ return RemoveNode(p, indx);
+ numBytes = U2B(I2U(indx));
+ if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit))
+ {
+ void *retVal = p->LoUnit;
+ p->LoUnit += numBytes;
+ return retVal;
+ }
+ return AllocUnitsRare(p, indx);
+}
+
+#define MyMem12Cpy(dest, src, num) \
+ { UInt32 *d = (UInt32 *)dest; const UInt32 *s = (const UInt32 *)src; UInt32 n = num; \
+ do { d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; s += 3; d += 3; } while(--n); }
+
+static void *ShrinkUnits(CPpmd7 *p, void *oldPtr, unsigned oldNU, unsigned newNU)
+{
+ unsigned i0 = U2I(oldNU);
+ unsigned i1 = U2I(newNU);
+ if (i0 == i1)
+ return oldPtr;
+ if (p->FreeList[i1] != 0)
+ {
+ void *ptr = RemoveNode(p, i1);
+ MyMem12Cpy(ptr, oldPtr, newNU);
+ InsertNode(p, oldPtr, i0);
+ return ptr;
+ }
+ SplitBlock(p, oldPtr, i0, i1);
+ return oldPtr;
+}
+
+#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16)))
+
+static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v)
+{
+ (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF);
+ (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF);
+}
+
+static void RestartModel(CPpmd7 *p)
+{
+ unsigned i, k, m;
+
+ memset(p->FreeList, 0, sizeof(p->FreeList));
+ p->Text = p->Base + p->AlignOffset;
+ p->HiUnit = p->Text + p->Size;
+ p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE;
+ p->GlueCount = 0;
+
+ p->OrderFall = p->MaxOrder;
+ p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1;
+ p->PrevSuccess = 0;
+
+ p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */
+ p->MinContext->Suffix = 0;
+ p->MinContext->NumStats = 256;
+ p->MinContext->SummFreq = 256 + 1;
+ p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */
+ p->LoUnit += U2B(256 / 2);
+ p->MinContext->Stats = REF(p->FoundState);
+ for (i = 0; i < 256; i++)
+ {
+ CPpmd_State *s = &p->FoundState[i];
+ s->Symbol = (Byte)i;
+ s->Freq = 1;
+ SetSuccessor(s, 0);
+ }
+
+ for (i = 0; i < 128; i++)
+ for (k = 0; k < 8; k++)
+ {
+ UInt16 *dest = p->BinSumm[i] + k;
+ UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 2));
+ for (m = 0; m < 64; m += 8)
+ dest[m] = val;
+ }
+
+ for (i = 0; i < 25; i++)
+ for (k = 0; k < 16; k++)
+ {
+ CPpmd_See *s = &p->See[i][k];
+ s->Summ = (UInt16)((5 * i + 10) << (s->Shift = PPMD_PERIOD_BITS - 4));
+ s->Count = 4;
+ }
+}
+
+static void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder)
+{
+ p->MaxOrder = maxOrder;
+ RestartModel(p);
+ p->DummySee.Shift = PPMD_PERIOD_BITS;
+ p->DummySee.Summ = 0; /* unused */
+ p->DummySee.Count = 64; /* unused */
+}
+
+static CTX_PTR CreateSuccessors(CPpmd7 *p, Bool skip)
+{
+ CPpmd_State upState;
+ CTX_PTR c = p->MinContext;
+ CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState);
+ CPpmd_State *ps[PPMD7_MAX_ORDER];
+ unsigned numPs = 0;
+
+ if (!skip)
+ ps[numPs++] = p->FoundState;
+
+ while (c->Suffix)
+ {
+ CPpmd_Void_Ref successor;
+ CPpmd_State *s;
+ c = SUFFIX(c);
+ if (c->NumStats != 1)
+ {
+ for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++);
+ }
+ else
+ s = ONE_STATE(c);
+ successor = SUCCESSOR(s);
+ if (successor != upBranch)
+ {
+ c = CTX(successor);
+ if (numPs == 0)
+ return c;
+ break;
+ }
+ ps[numPs++] = s;
+ }
+
+ upState.Symbol = *(const Byte *)Ppmd7_GetPtr(p, upBranch);
+ SetSuccessor(&upState, upBranch + 1);
+
+ if (c->NumStats == 1)
+ upState.Freq = ONE_STATE(c)->Freq;
+ else
+ {
+ UInt32 cf, s0;
+ CPpmd_State *s;
+ for (s = STATS(c); s->Symbol != upState.Symbol; s++);
+ cf = s->Freq - 1;
+ s0 = c->SummFreq - c->NumStats - cf;
+ upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((2 * cf + 3 * s0 - 1) / (2 * s0))));
+ }
+
+ while (numPs != 0)
+ {
+ /* Create Child */
+ CTX_PTR c1; /* = AllocContext(p); */
+ if (p->HiUnit != p->LoUnit)
+ c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE);
+ else if (p->FreeList[0] != 0)
+ c1 = (CTX_PTR)RemoveNode(p, 0);
+ else
+ {
+ c1 = (CTX_PTR)AllocUnitsRare(p, 0);
+ if (!c1)
+ return NULL;
+ }
+ c1->NumStats = 1;
+ *ONE_STATE(c1) = upState;
+ c1->Suffix = REF(c);
+ SetSuccessor(ps[--numPs], REF(c1));
+ c = c1;
+ }
+
+ return c;
+}
+
+static void SwapStates(CPpmd_State *t1, CPpmd_State *t2)
+{
+ CPpmd_State tmp = *t1;
+ *t1 = *t2;
+ *t2 = tmp;
+}
+
+static void UpdateModel(CPpmd7 *p)
+{
+ CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState);
+ CTX_PTR c;
+ unsigned s0, ns;
+
+ if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0)
+ {
+ c = SUFFIX(p->MinContext);
+
+ if (c->NumStats == 1)
+ {
+ CPpmd_State *s = ONE_STATE(c);
+ if (s->Freq < 32)
+ s->Freq++;
+ }
+ else
+ {
+ CPpmd_State *s = STATS(c);
+ if (s->Symbol != p->FoundState->Symbol)
+ {
+ do { s++; } while (s->Symbol != p->FoundState->Symbol);
+ if (s[0].Freq >= s[-1].Freq)
+ {
+ SwapStates(&s[0], &s[-1]);
+ s--;
+ }
+ }
+ if (s->Freq < MAX_FREQ - 9)
+ {
+ s->Freq += 2;
+ c->SummFreq += 2;
+ }
+ }
+ }
+
+ if (p->OrderFall == 0)
+ {
+ p->MinContext = p->MaxContext = CreateSuccessors(p, True);
+ if (p->MinContext == 0)
+ {
+ RestartModel(p);
+ return;
+ }
+ SetSuccessor(p->FoundState, REF(p->MinContext));
+ return;
+ }
+
+ *p->Text++ = p->FoundState->Symbol;
+ successor = REF(p->Text);
+ if (p->Text >= p->UnitsStart)
+ {
+ RestartModel(p);
+ return;
+ }
+
+ if (fSuccessor)
+ {
+ if (fSuccessor <= successor)
+ {
+ CTX_PTR cs = CreateSuccessors(p, False);
+ if (cs == NULL)
+ {
+ RestartModel(p);
+ return;
+ }
+ fSuccessor = REF(cs);
+ }
+ if (--p->OrderFall == 0)
+ {
+ successor = fSuccessor;
+ p->Text -= (p->MaxContext != p->MinContext);
+ }
+ }
+ else
+ {
+ SetSuccessor(p->FoundState, successor);
+ fSuccessor = REF(p->MinContext);
+ }
+
+ s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - (p->FoundState->Freq - 1);
+
+ for (c = p->MaxContext; c != p->MinContext; c = SUFFIX(c))
+ {
+ unsigned ns1;
+ UInt32 cf, sf;
+ if ((ns1 = c->NumStats) != 1)
+ {
+ if ((ns1 & 1) == 0)
+ {
+ /* Expand for one UNIT */
+ unsigned oldNU = ns1 >> 1;
+ unsigned i = U2I(oldNU);
+ if (i != U2I(oldNU + 1))
+ {
+ void *ptr = AllocUnits(p, i + 1);
+ void *oldPtr;
+ if (!ptr)
+ {
+ RestartModel(p);
+ return;
+ }
+ oldPtr = STATS(c);
+ MyMem12Cpy(ptr, oldPtr, oldNU);
+ InsertNode(p, oldPtr, i);
+ c->Stats = STATS_REF(ptr);
+ }
+ }
+ c->SummFreq = (UInt16)(c->SummFreq + (2 * ns1 < ns) + 2 * ((4 * ns1 <= ns) & (c->SummFreq <= 8 * ns1)));
+ }
+ else
+ {
+ CPpmd_State *s = (CPpmd_State*)AllocUnits(p, 0);
+ if (!s)
+ {
+ RestartModel(p);
+ return;
+ }
+ *s = *ONE_STATE(c);
+ c->Stats = REF(s);
+ if (s->Freq < MAX_FREQ / 4 - 1)
+ s->Freq <<= 1;
+ else
+ s->Freq = MAX_FREQ - 4;
+ c->SummFreq = (UInt16)(s->Freq + p->InitEsc + (ns > 3));
+ }
+ cf = 2 * (UInt32)p->FoundState->Freq * (c->SummFreq + 6);
+ sf = (UInt32)s0 + c->SummFreq;
+ if (cf < 6 * sf)
+ {
+ cf = 1 + (cf > sf) + (cf >= 4 * sf);
+ c->SummFreq += 3;
+ }
+ else
+ {
+ cf = 4 + (cf >= 9 * sf) + (cf >= 12 * sf) + (cf >= 15 * sf);
+ c->SummFreq = (UInt16)(c->SummFreq + cf);
+ }
+ {
+ CPpmd_State *s = STATS(c) + ns1;
+ SetSuccessor(s, successor);
+ s->Symbol = p->FoundState->Symbol;
+ s->Freq = (Byte)cf;
+ c->NumStats = (UInt16)(ns1 + 1);
+ }
+ }
+ p->MaxContext = p->MinContext = CTX(fSuccessor);
+}
+
+static void Rescale(CPpmd7 *p)
+{
+ unsigned i, adder, sumFreq, escFreq;
+ CPpmd_State *stats = STATS(p->MinContext);
+ CPpmd_State *s = p->FoundState;
+ {
+ CPpmd_State tmp = *s;
+ for (; s != stats; s--)
+ s[0] = s[-1];
+ *s = tmp;
+ }
+ escFreq = p->MinContext->SummFreq - s->Freq;
+ s->Freq += 4;
+ adder = (p->OrderFall != 0);
+ s->Freq = (Byte)((s->Freq + adder) >> 1);
+ sumFreq = s->Freq;
+
+ i = p->MinContext->NumStats - 1;
+ do
+ {
+ escFreq -= (++s)->Freq;
+ s->Freq = (Byte)((s->Freq + adder) >> 1);
+ sumFreq += s->Freq;
+ if (s[0].Freq > s[-1].Freq)
+ {
+ CPpmd_State *s1 = s;
+ CPpmd_State tmp = *s1;
+ do
+ s1[0] = s1[-1];
+ while (--s1 != stats && tmp.Freq > s1[-1].Freq);
+ *s1 = tmp;
+ }
+ }
+ while (--i);
+
+ if (s->Freq == 0)
+ {
+ unsigned numStats = p->MinContext->NumStats;
+ unsigned n0, n1;
+ do { i++; } while ((--s)->Freq == 0);
+ escFreq += i;
+ p->MinContext->NumStats = (UInt16)(p->MinContext->NumStats - i);
+ if (p->MinContext->NumStats == 1)
+ {
+ CPpmd_State tmp = *stats;
+ do
+ {
+ tmp.Freq = (Byte)(tmp.Freq - (tmp.Freq >> 1));
+ escFreq >>= 1;
+ }
+ while (escFreq > 1);
+ InsertNode(p, stats, U2I(((numStats + 1) >> 1)));
+ *(p->FoundState = ONE_STATE(p->MinContext)) = tmp;
+ return;
+ }
+ n0 = (numStats + 1) >> 1;
+ n1 = (p->MinContext->NumStats + 1) >> 1;
+ if (n0 != n1)
+ p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1));
+ }
+ p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1));
+ p->FoundState = STATS(p->MinContext);
+}
+
+static CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *escFreq)
+{
+ CPpmd_See *see;
+ unsigned nonMasked = p->MinContext->NumStats - numMasked;
+ if (p->MinContext->NumStats != 256)
+ {
+ see = p->See[p->NS2Indx[nonMasked - 1]] +
+ (nonMasked < (unsigned)SUFFIX(p->MinContext)->NumStats - p->MinContext->NumStats) +
+ 2 * (p->MinContext->SummFreq < 11 * p->MinContext->NumStats) +
+ 4 * (numMasked > nonMasked) +
+ p->HiBitsFlag;
+ {
+ unsigned r = (see->Summ >> see->Shift);
+ see->Summ = (UInt16)(see->Summ - r);
+ *escFreq = r + (r == 0);
+ }
+ }
+ else
+ {
+ see = &p->DummySee;
+ *escFreq = 1;
+ }
+ return see;
+}
+
+static void NextContext(CPpmd7 *p)
+{
+ CTX_PTR c = CTX(SUCCESSOR(p->FoundState));
+ if (p->OrderFall == 0 && (Byte *)c > p->Text)
+ p->MinContext = p->MaxContext = c;
+ else
+ UpdateModel(p);
+}
+
+static void Ppmd7_Update1(CPpmd7 *p)
+{
+ CPpmd_State *s = p->FoundState;
+ s->Freq += 4;
+ p->MinContext->SummFreq += 4;
+ if (s[0].Freq > s[-1].Freq)
+ {
+ SwapStates(&s[0], &s[-1]);
+ p->FoundState = --s;
+ if (s->Freq > MAX_FREQ)
+ Rescale(p);
+ }
+ NextContext(p);
+}
+
+static void Ppmd7_Update1_0(CPpmd7 *p)
+{
+ p->PrevSuccess = (2 * p->FoundState->Freq > p->MinContext->SummFreq);
+ p->RunLength += p->PrevSuccess;
+ p->MinContext->SummFreq += 4;
+ if ((p->FoundState->Freq += 4) > MAX_FREQ)
+ Rescale(p);
+ NextContext(p);
+}
+
+static void Ppmd7_UpdateBin(CPpmd7 *p)
+{
+ p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 128 ? 1: 0));
+ p->PrevSuccess = 1;
+ p->RunLength++;
+ NextContext(p);
+}
+
+static void Ppmd7_Update2(CPpmd7 *p)
+{
+ p->MinContext->SummFreq += 4;
+ if ((p->FoundState->Freq += 4) > MAX_FREQ)
+ Rescale(p);
+ p->RunLength = p->InitRL;
+ UpdateModel(p);
+}
+
+/* ---------- Decode ---------- */
+
+static Bool Ppmd_RangeDec_Init(CPpmd7z_RangeDec *p)
+{
+ unsigned i;
+ p->Low = p->Bottom = 0;
+ p->Range = 0xFFFFFFFF;
+ for (i = 0; i < 4; i++)
+ p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream);
+ return (p->Code < 0xFFFFFFFF);
+}
+
+static Bool Ppmd7z_RangeDec_Init(CPpmd7z_RangeDec *p)
+{
+ if (p->Stream->Read((void *)p->Stream) != 0)
+ return False;
+ return Ppmd_RangeDec_Init(p);
+}
+
+static Bool PpmdRAR_RangeDec_Init(CPpmd7z_RangeDec *p)
+{
+ if (!Ppmd_RangeDec_Init(p))
+ return False;
+ p->Bottom = 0x8000;
+ return True;
+}
+
+static UInt32 Range_GetThreshold(void *pp, UInt32 total)
+{
+ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+ return (p->Code - p->Low) / (p->Range /= total);
+}
+
+static void Range_Normalize(CPpmd7z_RangeDec *p)
+{
+ while (1)
+ {
+ if((p->Low ^ (p->Low + p->Range)) >= kTopValue)
+ {
+ if(p->Range >= p->Bottom)
+ break;
+ else
+ p->Range = ((uint32_t)(-(int32_t)p->Low)) & (p->Bottom - 1);
+ }
+ p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream);
+ p->Range <<= 8;
+ p->Low <<= 8;
+ }
+}
+
+static void Range_Decode_7z(void *pp, UInt32 start, UInt32 size)
+{
+ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+ p->Code -= start * p->Range;
+ p->Range *= size;
+ Range_Normalize(p);
+}
+
+static void Range_Decode_RAR(void *pp, UInt32 start, UInt32 size)
+{
+ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+ p->Low += start * p->Range;
+ p->Range *= size;
+ Range_Normalize(p);
+}
+
+static UInt32 Range_DecodeBit_7z(void *pp, UInt32 size0)
+{
+ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+ UInt32 newBound = (p->Range >> 14) * size0;
+ UInt32 symbol;
+ if (p->Code < newBound)
+ {
+ symbol = 0;
+ p->Range = newBound;
+ }
+ else
+ {
+ symbol = 1;
+ p->Code -= newBound;
+ p->Range -= newBound;
+ }
+ Range_Normalize(p);
+ return symbol;
+}
+
+static UInt32 Range_DecodeBit_RAR(void *pp, UInt32 size0)
+{
+ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+ UInt32 bit, value = p->p.GetThreshold(p, PPMD_BIN_SCALE);
+ if(value < size0)
+ {
+ bit = 0;
+ p->p.Decode(p, 0, size0);
+ }
+ else
+ {
+ bit = 1;
+ p->p.Decode(p, size0, PPMD_BIN_SCALE - size0);
+ }
+ return bit;
+}
+
+static void Ppmd7z_RangeDec_CreateVTable(CPpmd7z_RangeDec *p)
+{
+ p->p.GetThreshold = Range_GetThreshold;
+ p->p.Decode = Range_Decode_7z;
+ p->p.DecodeBit = Range_DecodeBit_7z;
+}
+
+static void PpmdRAR_RangeDec_CreateVTable(CPpmd7z_RangeDec *p)
+{
+ p->p.GetThreshold = Range_GetThreshold;
+ p->p.Decode = Range_Decode_RAR;
+ p->p.DecodeBit = Range_DecodeBit_RAR;
+}
+
+#define MASK(sym) ((signed char *)charMask)[sym]
+
+static int Ppmd7_DecodeSymbol(CPpmd7 *p, IPpmd7_RangeDec *rc)
+{
+ size_t charMask[256 / sizeof(size_t)];
+ if (p->MinContext->NumStats != 1)
+ {
+ CPpmd_State *s = Ppmd7_GetStats(p, p->MinContext);
+ unsigned i;
+ UInt32 count, hiCnt;
+ if ((count = rc->GetThreshold(rc, p->MinContext->SummFreq)) < (hiCnt = s->Freq))
+ {
+ Byte symbol;
+ rc->Decode(rc, 0, s->Freq);
+ p->FoundState = s;
+ symbol = s->Symbol;
+ Ppmd7_Update1_0(p);
+ return symbol;
+ }
+ p->PrevSuccess = 0;
+ i = p->MinContext->NumStats - 1;
+ do
+ {
+ if ((hiCnt += (++s)->Freq) > count)
+ {
+ Byte symbol;
+ rc->Decode(rc, hiCnt - s->Freq, s->Freq);
+ p->FoundState = s;
+ symbol = s->Symbol;
+ Ppmd7_Update1(p);
+ return symbol;
+ }
+ }
+ while (--i);
+ if (count >= p->MinContext->SummFreq)
+ return -2;
+ p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol];
+ rc->Decode(rc, hiCnt, p->MinContext->SummFreq - hiCnt);
+ PPMD_SetAllBitsIn256Bytes(charMask);
+ MASK(s->Symbol) = 0;
+ i = p->MinContext->NumStats - 1;
+ do { MASK((--s)->Symbol) = 0; } while (--i);
+ }
+ else
+ {
+ UInt16 *prob = Ppmd7_GetBinSumm(p);
+ if (rc->DecodeBit(rc, *prob) == 0)
+ {
+ Byte symbol;
+ *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob);
+ symbol = (p->FoundState = Ppmd7Context_OneState(p->MinContext))->Symbol;
+ Ppmd7_UpdateBin(p);
+ return symbol;
+ }
+ *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob);
+ p->InitEsc = PPMD7_kExpEscape[*prob >> 10];
+ PPMD_SetAllBitsIn256Bytes(charMask);
+ MASK(Ppmd7Context_OneState(p->MinContext)->Symbol) = 0;
+ p->PrevSuccess = 0;
+ }
+ for (;;)
+ {
+ CPpmd_State *ps[256], *s;
+ UInt32 freqSum, count, hiCnt;
+ CPpmd_See *see;
+ unsigned i, num, numMasked = p->MinContext->NumStats;
+ do
+ {
+ p->OrderFall++;
+ if (!p->MinContext->Suffix)
+ return -1;
+ p->MinContext = Ppmd7_GetContext(p, p->MinContext->Suffix);
+ }
+ while (p->MinContext->NumStats == numMasked);
+ hiCnt = 0;
+ s = Ppmd7_GetStats(p, p->MinContext);
+ i = 0;
+ num = p->MinContext->NumStats - numMasked;
+ do
+ {
+ int k = (int)(MASK(s->Symbol));
+ hiCnt += (s->Freq & k);
+ ps[i] = s++;
+ i -= k;
+ }
+ while (i != num);
+
+ see = Ppmd7_MakeEscFreq(p, numMasked, &freqSum);
+ freqSum += hiCnt;
+ count = rc->GetThreshold(rc, freqSum);
+
+ if (count < hiCnt)
+ {
+ Byte symbol;
+ CPpmd_State **pps = ps;
+ for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++);
+ s = *pps;
+ rc->Decode(rc, hiCnt - s->Freq, s->Freq);
+ Ppmd_See_Update(see);
+ p->FoundState = s;
+ symbol = s->Symbol;
+ Ppmd7_Update2(p);
+ return symbol;
+ }
+ if (count >= freqSum)
+ return -2;
+ rc->Decode(rc, hiCnt, freqSum - hiCnt);
+ see->Summ = (UInt16)(see->Summ + freqSum);
+ do { MASK(ps[--i]->Symbol) = 0; } while (i != 0);
+ }
+}
+
+/* ---------- Encode ---------- Ppmd7Enc.c */
+
+#define kTopValue (1 << 24)
+
+static void Ppmd7z_RangeEnc_Init(CPpmd7z_RangeEnc *p)
+{
+ p->Low = 0;
+ p->Range = 0xFFFFFFFF;
+ p->Cache = 0;
+ p->CacheSize = 1;
+}
+
+static void RangeEnc_ShiftLow(CPpmd7z_RangeEnc *p)
+{
+ if ((UInt32)p->Low < (UInt32)0xFF000000 || (unsigned)(p->Low >> 32) != 0)
+ {
+ Byte temp = p->Cache;
+ do
+ {
+ p->Stream->Write(p->Stream, (Byte)(temp + (Byte)(p->Low >> 32)));
+ temp = 0xFF;
+ }
+ while(--p->CacheSize != 0);
+ p->Cache = (Byte)((UInt32)p->Low >> 24);
+ }
+ p->CacheSize++;
+ p->Low = ((UInt32)p->Low << 8) & 0xFFFFFFFF;
+}
+
+static void RangeEnc_Encode(CPpmd7z_RangeEnc *p, UInt32 start, UInt32 size, UInt32 total)
+{
+ p->Low += (UInt64)start * (UInt64)(p->Range /= total);
+ p->Range *= size;
+ while (p->Range < kTopValue)
+ {
+ p->Range <<= 8;
+ RangeEnc_ShiftLow(p);
+ }
+}
+
+static void RangeEnc_EncodeBit_0(CPpmd7z_RangeEnc *p, UInt32 size0)
+{
+ p->Range = (p->Range >> 14) * size0;
+ while (p->Range < kTopValue)
+ {
+ p->Range <<= 8;
+ RangeEnc_ShiftLow(p);
+ }
+}
+
+static void RangeEnc_EncodeBit_1(CPpmd7z_RangeEnc *p, UInt32 size0)
+{
+ UInt32 newBound = (p->Range >> 14) * size0;
+ p->Low += newBound;
+ p->Range -= newBound;
+ while (p->Range < kTopValue)
+ {
+ p->Range <<= 8;
+ RangeEnc_ShiftLow(p);
+ }
+}
+
+static void Ppmd7z_RangeEnc_FlushData(CPpmd7z_RangeEnc *p)
+{
+ unsigned i;
+ for (i = 0; i < 5; i++)
+ RangeEnc_ShiftLow(p);
+}
+
+
+#define MASK(sym) ((signed char *)charMask)[sym]
+
+static void Ppmd7_EncodeSymbol(CPpmd7 *p, CPpmd7z_RangeEnc *rc, int symbol)
+{
+ size_t charMask[256 / sizeof(size_t)];
+ if (p->MinContext->NumStats != 1)
+ {
+ CPpmd_State *s = Ppmd7_GetStats(p, p->MinContext);
+ UInt32 sum;
+ unsigned i;
+ if (s->Symbol == symbol)
+ {
+ RangeEnc_Encode(rc, 0, s->Freq, p->MinContext->SummFreq);
+ p->FoundState = s;
+ Ppmd7_Update1_0(p);
+ return;
+ }
+ p->PrevSuccess = 0;
+ sum = s->Freq;
+ i = p->MinContext->NumStats - 1;
+ do
+ {
+ if ((++s)->Symbol == symbol)
+ {
+ RangeEnc_Encode(rc, sum, s->Freq, p->MinContext->SummFreq);
+ p->FoundState = s;
+ Ppmd7_Update1(p);
+ return;
+ }
+ sum += s->Freq;
+ }
+ while (--i);
+
+ p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol];
+ PPMD_SetAllBitsIn256Bytes(charMask);
+ MASK(s->Symbol) = 0;
+ i = p->MinContext->NumStats - 1;
+ do { MASK((--s)->Symbol) = 0; } while (--i);
+ RangeEnc_Encode(rc, sum, p->MinContext->SummFreq - sum, p->MinContext->SummFreq);
+ }
+ else
+ {
+ UInt16 *prob = Ppmd7_GetBinSumm(p);
+ CPpmd_State *s = Ppmd7Context_OneState(p->MinContext);
+ if (s->Symbol == symbol)
+ {
+ RangeEnc_EncodeBit_0(rc, *prob);
+ *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob);
+ p->FoundState = s;
+ Ppmd7_UpdateBin(p);
+ return;
+ }
+ else
+ {
+ RangeEnc_EncodeBit_1(rc, *prob);
+ *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob);
+ p->InitEsc = PPMD7_kExpEscape[*prob >> 10];
+ PPMD_SetAllBitsIn256Bytes(charMask);
+ MASK(s->Symbol) = 0;
+ p->PrevSuccess = 0;
+ }
+ }
+ for (;;)
+ {
+ UInt32 escFreq;
+ CPpmd_See *see;
+ CPpmd_State *s;
+ UInt32 sum;
+ unsigned i, numMasked = p->MinContext->NumStats;
+ do
+ {
+ p->OrderFall++;
+ if (!p->MinContext->Suffix)
+ return; /* EndMarker (symbol = -1) */
+ p->MinContext = Ppmd7_GetContext(p, p->MinContext->Suffix);
+ }
+ while (p->MinContext->NumStats == numMasked);
+
+ see = Ppmd7_MakeEscFreq(p, numMasked, &escFreq);
+ s = Ppmd7_GetStats(p, p->MinContext);
+ sum = 0;
+ i = p->MinContext->NumStats;
+ do
+ {
+ int cur = s->Symbol;
+ if (cur == symbol)
+ {
+ UInt32 low = sum;
+ CPpmd_State *s1 = s;
+ do
+ {
+ sum += (s->Freq & (int)(MASK(s->Symbol)));
+ s++;
+ }
+ while (--i);
+ RangeEnc_Encode(rc, low, s1->Freq, sum + escFreq);
+ Ppmd_See_Update(see);
+ p->FoundState = s1;
+ Ppmd7_Update2(p);
+ return;
+ }
+ sum += (s->Freq & (int)(MASK(cur)));
+ MASK(cur) = 0;
+ s++;
+ }
+ while (--i);
+
+ RangeEnc_Encode(rc, sum, escFreq, sum + escFreq);
+ see->Summ = (UInt16)(see->Summ + sum + escFreq);
+ }
+}
+
+const IPpmd7 __archive_ppmd7_functions =
+{
+ &Ppmd7_Construct,
+ &Ppmd7_Alloc,
+ &Ppmd7_Free,
+ &Ppmd7_Init,
+ &Ppmd7z_RangeDec_CreateVTable,
+ &PpmdRAR_RangeDec_CreateVTable,
+ &Ppmd7z_RangeDec_Init,
+ &PpmdRAR_RangeDec_Init,
+ &Ppmd7_DecodeSymbol,
+ &Ppmd7z_RangeEnc_Init,
+ &Ppmd7z_RangeEnc_FlushData,
+ &Ppmd7_EncodeSymbol
+};
diff --git a/contrib/libs/libarchive/libarchive/archive_ppmd7_private.h b/contrib/libs/libarchive/libarchive/archive_ppmd7_private.h
new file mode 100644
index 00000000000..71b954458c6
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_ppmd7_private.h
@@ -0,0 +1,119 @@
+/* Ppmd7.h -- PPMdH compression codec
+2010-03-12 : Igor Pavlov : Public domain
+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
+
+/* This code supports virtual RangeDecoder and includes the implementation
+of RangeCoder from 7z, instead of RangeCoder from original PPMd var.H.
+If you need the compatibility with original PPMd var.H, you can use external RangeDecoder */
+
+#ifndef ARCHIVE_PPMD7_PRIVATE_H_INCLUDED
+#define ARCHIVE_PPMD7_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include "archive_ppmd_private.h"
+
+#define PPMD7_MIN_ORDER 2
+#define PPMD7_MAX_ORDER 64
+
+#define PPMD7_MIN_MEM_SIZE (1 << 11)
+#define PPMD7_MAX_MEM_SIZE (0xFFFFFFFFu - 12 * 3)
+
+struct CPpmd7_Context_;
+
+typedef
+ #ifdef PPMD_32BIT
+ struct CPpmd7_Context_ *
+ #else
+ UInt32
+ #endif
+ CPpmd7_Context_Ref;
+
+typedef struct CPpmd7_Context_
+{
+ UInt16 NumStats;
+ UInt16 SummFreq;
+ CPpmd_State_Ref Stats;
+ CPpmd7_Context_Ref Suffix;
+} CPpmd7_Context;
+
+#define Ppmd7Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq)
+
+typedef struct
+{
+ CPpmd7_Context *MinContext, *MaxContext;
+ CPpmd_State *FoundState;
+ unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder, HiBitsFlag;
+ Int32 RunLength, InitRL; /* must be 32-bit at least */
+
+ UInt32 Size;
+ UInt32 GlueCount;
+ Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart;
+ UInt32 AlignOffset;
+
+ Byte Indx2Units[PPMD_NUM_INDEXES];
+ Byte Units2Indx[128];
+ CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES];
+ Byte NS2Indx[256], NS2BSIndx[256], HB2Flag[256];
+ CPpmd_See DummySee, See[25][16];
+ UInt16 BinSumm[128][64];
+} CPpmd7;
+
+/* ---------- Decode ---------- */
+
+typedef struct
+{
+ UInt32 (*GetThreshold)(void *p, UInt32 total);
+ void (*Decode)(void *p, UInt32 start, UInt32 size);
+ UInt32 (*DecodeBit)(void *p, UInt32 size0);
+} IPpmd7_RangeDec;
+
+typedef struct
+{
+ IPpmd7_RangeDec p;
+ UInt32 Range;
+ UInt32 Code;
+ UInt32 Low;
+ UInt32 Bottom;
+ IByteIn *Stream;
+} CPpmd7z_RangeDec;
+
+/* ---------- Encode ---------- */
+
+typedef struct
+{
+ UInt64 Low;
+ UInt32 Range;
+ Byte Cache;
+ UInt64 CacheSize;
+ IByteOut *Stream;
+} CPpmd7z_RangeEnc;
+
+typedef struct
+{
+ /* Base Functions */
+ void (*Ppmd7_Construct)(CPpmd7 *p);
+ Bool (*Ppmd7_Alloc)(CPpmd7 *p, UInt32 size);
+ void (*Ppmd7_Free)(CPpmd7 *p);
+ void (*Ppmd7_Init)(CPpmd7 *p, unsigned maxOrder);
+ #define Ppmd7_WasAllocated(p) ((p)->Base != NULL)
+
+ /* Decode Functions */
+ void (*Ppmd7z_RangeDec_CreateVTable)(CPpmd7z_RangeDec *p);
+ void (*PpmdRAR_RangeDec_CreateVTable)(CPpmd7z_RangeDec *p);
+ Bool (*Ppmd7z_RangeDec_Init)(CPpmd7z_RangeDec *p);
+ Bool (*PpmdRAR_RangeDec_Init)(CPpmd7z_RangeDec *p);
+ #define Ppmd7z_RangeDec_IsFinishedOK(p) ((p)->Code == 0)
+ int (*Ppmd7_DecodeSymbol)(CPpmd7 *p, IPpmd7_RangeDec *rc);
+
+ /* Encode Functions */
+ void (*Ppmd7z_RangeEnc_Init)(CPpmd7z_RangeEnc *p);
+ void (*Ppmd7z_RangeEnc_FlushData)(CPpmd7z_RangeEnc *p);
+
+ void (*Ppmd7_EncodeSymbol)(CPpmd7 *p, CPpmd7z_RangeEnc *rc, int symbol);
+} IPpmd7;
+
+extern const IPpmd7 __archive_ppmd7_functions;
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_ppmd8.c b/contrib/libs/libarchive/libarchive/archive_ppmd8.c
new file mode 100644
index 00000000000..d1779395dac
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_ppmd8.c
@@ -0,0 +1,1287 @@
+/* Ppmd8.c -- PPMdI codec
+2016-05-21 : Igor Pavlov : Public domain
+This code is based on PPMd var.I (2002): Dmitry Shkarin : Public domain */
+
+#include "archive_platform.h"
+
+#include <string.h>
+
+#include "archive_ppmd8_private.h"
+
+const Byte PPMD8_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
+static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051};
+
+#define MAX_FREQ 124
+#define UNIT_SIZE 12
+
+#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE)
+#define U2I(nu) (p->Units2Indx[(nu) - 1])
+#define I2U(indx) (p->Indx2Units[indx])
+
+#ifdef PPMD_32BIT
+ #define REF(ptr) (ptr)
+#else
+ #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base))
+#endif
+
+#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr))
+
+#define CTX(ref) ((CPpmd8_Context *)Ppmd8_GetContext(p, ref))
+#define STATS(ctx) Ppmd8_GetStats(p, ctx)
+#define ONE_STATE(ctx) Ppmd8Context_OneState(ctx)
+#define SUFFIX(ctx) CTX((ctx)->Suffix)
+
+#define kTop (1 << 24)
+#define kBot (1 << 15)
+
+typedef CPpmd8_Context * CTX_PTR;
+
+struct CPpmd8_Node_;
+
+typedef
+ #ifdef PPMD_32BIT
+ struct CPpmd8_Node_ *
+ #else
+ UInt32
+ #endif
+ CPpmd8_Node_Ref;
+
+typedef struct CPpmd8_Node_
+{
+ UInt32 Stamp;
+ CPpmd8_Node_Ref Next;
+ UInt32 NU;
+} CPpmd8_Node;
+
+#ifdef PPMD_32BIT
+ #define NODE(ptr) (ptr)
+#else
+ #define NODE(offs) ((CPpmd8_Node *)(p->Base + (offs)))
+#endif
+
+#define EMPTY_NODE 0xFFFFFFFF
+
+void Ppmd8_Construct(CPpmd8 *p)
+{
+ unsigned i, k, m;
+
+ p->Base = 0;
+
+ for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++)
+ {
+ unsigned step = (i >= 12 ? 4 : (i >> 2) + 1);
+ do { p->Units2Indx[k++] = (Byte)i; } while (--step);
+ p->Indx2Units[i] = (Byte)k;
+ }
+
+ p->NS2BSIndx[0] = (0 << 1);
+ p->NS2BSIndx[1] = (1 << 1);
+ memset(p->NS2BSIndx + 2, (2 << 1), 9);
+ memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11);
+
+ for (i = 0; i < 5; i++)
+ p->NS2Indx[i] = (Byte)i;
+ for (m = i, k = 1; i < 260; i++)
+ {
+ p->NS2Indx[i] = (Byte)m;
+ if (--k == 0)
+ k = (++m) - 4;
+ }
+}
+
+void Ppmd8_Free(CPpmd8 *p)
+{
+ free(p->Base);
+ p->Size = 0;
+ p->Base = 0;
+}
+
+Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size)
+{
+ if (p->Base == 0 || p->Size != size)
+ {
+ Ppmd8_Free(p);
+ p->AlignOffset =
+ #ifdef PPMD_32BIT
+ (4 - size) & 3;
+ #else
+ 4 - (size & 3);
+ #endif
+ if ((p->Base = (Byte *)malloc(p->AlignOffset + size)) == 0)
+ return False;
+ p->Size = size;
+ }
+ return True;
+}
+
+static void InsertNode(CPpmd8 *p, void *node, unsigned indx)
+{
+ ((CPpmd8_Node *)node)->Stamp = EMPTY_NODE;
+ ((CPpmd8_Node *)node)->Next = (CPpmd8_Node_Ref)p->FreeList[indx];
+ ((CPpmd8_Node *)node)->NU = I2U(indx);
+ p->FreeList[indx] = REF(node);
+ p->Stamps[indx]++;
+}
+
+static void *RemoveNode(CPpmd8 *p, unsigned indx)
+{
+ CPpmd8_Node *node = NODE((CPpmd8_Node_Ref)p->FreeList[indx]);
+ p->FreeList[indx] = node->Next;
+ p->Stamps[indx]--;
+ return node;
+}
+
+static void SplitBlock(CPpmd8 *p, void *ptr, unsigned oldIndx, unsigned newIndx)
+{
+ unsigned i, nu = I2U(oldIndx) - I2U(newIndx);
+ ptr = (Byte *)ptr + U2B(I2U(newIndx));
+ if (I2U(i = U2I(nu)) != nu)
+ {
+ unsigned k = I2U(--i);
+ InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1);
+ }
+ InsertNode(p, ptr, i);
+}
+
+static void GlueFreeBlocks(CPpmd8 *p)
+{
+ CPpmd8_Node_Ref head = 0;
+ CPpmd8_Node_Ref *prev = &head;
+ unsigned i;
+
+ p->GlueCount = 1 << 13;
+ memset(p->Stamps, 0, sizeof(p->Stamps));
+
+ /* Order-0 context is always at top UNIT, so we don't need guard NODE at the end.
+ All blocks up to p->LoUnit can be free, so we need guard NODE at LoUnit. */
+ if (p->LoUnit != p->HiUnit)
+ ((CPpmd8_Node *)p->LoUnit)->Stamp = 0;
+
+ /* Glue free blocks */
+ for (i = 0; i < PPMD_NUM_INDEXES; i++)
+ {
+ CPpmd8_Node_Ref next = (CPpmd8_Node_Ref)p->FreeList[i];
+ p->FreeList[i] = 0;
+ while (next != 0)
+ {
+ CPpmd8_Node *node = NODE(next);
+ if (node->NU != 0)
+ {
+ CPpmd8_Node *node2;
+ *prev = next;
+ prev = &(node->Next);
+ while ((node2 = node + node->NU)->Stamp == EMPTY_NODE)
+ {
+ node->NU += node2->NU;
+ node2->NU = 0;
+ }
+ }
+ next = node->Next;
+ }
+ }
+ *prev = 0;
+
+ /* Fill lists of free blocks */
+ while (head != 0)
+ {
+ CPpmd8_Node *node = NODE(head);
+ unsigned nu;
+ head = node->Next;
+ nu = node->NU;
+ if (nu == 0)
+ continue;
+ for (; nu > 128; nu -= 128, node += 128)
+ InsertNode(p, node, PPMD_NUM_INDEXES - 1);
+ if (I2U(i = U2I(nu)) != nu)
+ {
+ unsigned k = I2U(--i);
+ InsertNode(p, node + k, nu - k - 1);
+ }
+ InsertNode(p, node, i);
+ }
+}
+
+static void *AllocUnitsRare(CPpmd8 *p, unsigned indx)
+{
+ unsigned i;
+ void *retVal;
+ if (p->GlueCount == 0)
+ {
+ GlueFreeBlocks(p);
+ if (p->FreeList[indx] != 0)
+ return RemoveNode(p, indx);
+ }
+ i = indx;
+ do
+ {
+ if (++i == PPMD_NUM_INDEXES)
+ {
+ UInt32 numBytes = U2B(I2U(indx));
+ p->GlueCount--;
+ return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL);
+ }
+ }
+ while (p->FreeList[i] == 0);
+ retVal = RemoveNode(p, i);
+ SplitBlock(p, retVal, i, indx);
+ return retVal;
+}
+
+static void *AllocUnits(CPpmd8 *p, unsigned indx)
+{
+ UInt32 numBytes;
+ if (p->FreeList[indx] != 0)
+ return RemoveNode(p, indx);
+ numBytes = U2B(I2U(indx));
+ if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit))
+ {
+ void *retVal = p->LoUnit;
+ p->LoUnit += numBytes;
+ return retVal;
+ }
+ return AllocUnitsRare(p, indx);
+}
+
+#define MyMem12Cpy(dest, src, num) \
+ { UInt32 *d = (UInt32 *)dest; const UInt32 *z = (const UInt32 *)src; UInt32 n = num; \
+ do { d[0] = z[0]; d[1] = z[1]; d[2] = z[2]; z += 3; d += 3; } while (--n); }
+
+static void *ShrinkUnits(CPpmd8 *p, void *oldPtr, unsigned oldNU, unsigned newNU)
+{
+ unsigned i0 = U2I(oldNU);
+ unsigned i1 = U2I(newNU);
+ if (i0 == i1)
+ return oldPtr;
+ if (p->FreeList[i1] != 0)
+ {
+ void *ptr = RemoveNode(p, i1);
+ MyMem12Cpy(ptr, oldPtr, newNU);
+ InsertNode(p, oldPtr, i0);
+ return ptr;
+ }
+ SplitBlock(p, oldPtr, i0, i1);
+ return oldPtr;
+}
+
+static void FreeUnits(CPpmd8 *p, void *ptr, unsigned nu)
+{
+ InsertNode(p, ptr, U2I(nu));
+}
+
+static void SpecialFreeUnit(CPpmd8 *p, void *ptr)
+{
+ if ((Byte *)ptr != p->UnitsStart)
+ InsertNode(p, ptr, 0);
+ else
+ {
+ #ifdef PPMD8_FREEZE_SUPPORT
+ *(UInt32 *)ptr = EMPTY_NODE; /* it's used for (Flags == 0xFF) check in RemoveBinContexts */
+ #endif
+ p->UnitsStart += UNIT_SIZE;
+ }
+}
+
+static void *MoveUnitsUp(CPpmd8 *p, void *oldPtr, unsigned nu)
+{
+ unsigned indx = U2I(nu);
+ void *ptr;
+ if ((Byte *)oldPtr > p->UnitsStart + 16 * 1024 || REF(oldPtr) > p->FreeList[indx])
+ return oldPtr;
+ ptr = RemoveNode(p, indx);
+ MyMem12Cpy(ptr, oldPtr, nu);
+ if ((Byte*)oldPtr != p->UnitsStart)
+ InsertNode(p, oldPtr, indx);
+ else
+ p->UnitsStart += U2B(I2U(indx));
+ return ptr;
+}
+
+static void ExpandTextArea(CPpmd8 *p)
+{
+ UInt32 count[PPMD_NUM_INDEXES];
+ unsigned i;
+ memset(count, 0, sizeof(count));
+ if (p->LoUnit != p->HiUnit)
+ ((CPpmd8_Node *)p->LoUnit)->Stamp = 0;
+
+ {
+ CPpmd8_Node *node = (CPpmd8_Node *)p->UnitsStart;
+ for (; node->Stamp == EMPTY_NODE; node += node->NU)
+ {
+ node->Stamp = 0;
+ count[U2I(node->NU)]++;
+ }
+ p->UnitsStart = (Byte *)node;
+ }
+
+ for (i = 0; i < PPMD_NUM_INDEXES; i++)
+ {
+ CPpmd8_Node_Ref *next = (CPpmd8_Node_Ref *)&p->FreeList[i];
+ while (count[i] != 0)
+ {
+ CPpmd8_Node *node = NODE(*next);
+ while (node->Stamp == 0)
+ {
+ *next = node->Next;
+ node = NODE(*next);
+ p->Stamps[i]--;
+ if (--count[i] == 0)
+ break;
+ }
+ next = &node->Next;
+ }
+ }
+}
+
+#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16)))
+
+static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v)
+{
+ (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF);
+ (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF);
+}
+
+#define RESET_TEXT(offs) { p->Text = p->Base + p->AlignOffset + (offs); }
+
+static void RestartModel(CPpmd8 *p)
+{
+ unsigned i, k, m, r;
+
+ memset(p->FreeList, 0, sizeof(p->FreeList));
+ memset(p->Stamps, 0, sizeof(p->Stamps));
+ RESET_TEXT(0);
+ p->HiUnit = p->Text + p->Size;
+ p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE;
+ p->GlueCount = 0;
+
+ p->OrderFall = p->MaxOrder;
+ p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1;
+ p->PrevSuccess = 0;
+
+ p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */
+ p->MinContext->Suffix = 0;
+ p->MinContext->NumStats = 255;
+ p->MinContext->Flags = 0;
+ p->MinContext->SummFreq = 256 + 1;
+ p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */
+ p->LoUnit += U2B(256 / 2);
+ p->MinContext->Stats = REF(p->FoundState);
+ for (i = 0; i < 256; i++)
+ {
+ CPpmd_State *s = &p->FoundState[i];
+ s->Symbol = (Byte)i;
+ s->Freq = 1;
+ SetSuccessor(s, 0);
+ }
+
+ for (i = m = 0; m < 25; m++)
+ {
+ while (p->NS2Indx[i] == m)
+ i++;
+ for (k = 0; k < 8; k++)
+ {
+ UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 1));
+ UInt16 *dest = p->BinSumm[m] + k;
+ for (r = 0; r < 64; r += 8)
+ dest[r] = val;
+ }
+ }
+
+ for (i = m = 0; m < 24; m++)
+ {
+ while (p->NS2Indx[i + 3] == m + 3)
+ i++;
+ for (k = 0; k < 32; k++)
+ {
+ CPpmd_See *s = &p->See[m][k];
+ s->Summ = (UInt16)((2 * i + 5) << (s->Shift = PPMD_PERIOD_BITS - 4));
+ s->Count = 7;
+ }
+ }
+}
+
+void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod)
+{
+ p->MaxOrder = maxOrder;
+ p->RestoreMethod = restoreMethod;
+ RestartModel(p);
+ p->DummySee.Shift = PPMD_PERIOD_BITS;
+ p->DummySee.Summ = 0; /* unused */
+ p->DummySee.Count = 64; /* unused */
+}
+
+static void Refresh(CPpmd8 *p, CTX_PTR ctx, unsigned oldNU, unsigned scale)
+{
+ unsigned i = ctx->NumStats, escFreq, sumFreq, flags;
+ CPpmd_State *s = (CPpmd_State *)ShrinkUnits(p, STATS(ctx), oldNU, (i + 2) >> 1);
+ ctx->Stats = REF(s);
+ #ifdef PPMD8_FREEZE_SUPPORT
+ /* fixed over Shkarin's code. Fixed code is not compatible with original code for some files in FREEZE mode. */
+ scale |= (ctx->SummFreq >= ((UInt32)1 << 15));
+ #endif
+ flags = (ctx->Flags & (0x10 + 0x04 * scale)) + 0x08 * (s->Symbol >= 0x40);
+ escFreq = ctx->SummFreq - s->Freq;
+ sumFreq = (s->Freq = (Byte)((s->Freq + scale) >> scale));
+ do
+ {
+ escFreq -= (++s)->Freq;
+ sumFreq += (s->Freq = (Byte)((s->Freq + scale) >> scale));
+ flags |= 0x08 * (s->Symbol >= 0x40);
+ }
+ while (--i);
+ ctx->SummFreq = (UInt16)(sumFreq + ((escFreq + scale) >> scale));
+ ctx->Flags = (Byte)flags;
+}
+
+static void SwapStates(CPpmd_State *t1, CPpmd_State *t2)
+{
+ CPpmd_State tmp = *t1;
+ *t1 = *t2;
+ *t2 = tmp;
+}
+
+static CPpmd_Void_Ref CutOff(CPpmd8 *p, CTX_PTR ctx, unsigned order)
+{
+ int i;
+ unsigned tmp;
+ CPpmd_State *s;
+
+ if (!ctx->NumStats)
+ {
+ s = ONE_STATE(ctx);
+ if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart)
+ {
+ if (order < p->MaxOrder)
+ SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1));
+ else
+ SetSuccessor(s, 0);
+ if (SUCCESSOR(s) || order <= 9) /* O_BOUND */
+ return REF(ctx);
+ }
+ SpecialFreeUnit(p, ctx);
+ return 0;
+ }
+
+ ctx->Stats = STATS_REF(MoveUnitsUp(p, STATS(ctx), tmp = ((unsigned)ctx->NumStats + 2) >> 1));
+
+ for (s = STATS(ctx) + (i = ctx->NumStats); s >= STATS(ctx); s--)
+ if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) < p->UnitsStart)
+ {
+ CPpmd_State *s2 = STATS(ctx) + (i--);
+ SetSuccessor(s, 0);
+ SwapStates(s, s2);
+ }
+ else if (order < p->MaxOrder)
+ SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1));
+ else
+ SetSuccessor(s, 0);
+
+ if (i != ctx->NumStats && order)
+ {
+ ctx->NumStats = (Byte)i;
+ s = STATS(ctx);
+ if (i < 0)
+ {
+ FreeUnits(p, s, tmp);
+ SpecialFreeUnit(p, ctx);
+ return 0;
+ }
+ if (i == 0)
+ {
+ ctx->Flags = (Byte)((ctx->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40));
+ *ONE_STATE(ctx) = *s;
+ FreeUnits(p, s, tmp);
+ /* 9.31: the code was fixed. It's was not BUG, if Freq <= MAX_FREQ = 124 */
+ ONE_STATE(ctx)->Freq = (Byte)(((unsigned)ONE_STATE(ctx)->Freq + 11) >> 3);
+ }
+ else
+ Refresh(p, ctx, tmp, ctx->SummFreq > 16 * i);
+ }
+ return REF(ctx);
+}
+
+#ifdef PPMD8_FREEZE_SUPPORT
+static CPpmd_Void_Ref RemoveBinContexts(CPpmd8 *p, CTX_PTR ctx, unsigned order)
+{
+ CPpmd_State *s;
+ if (!ctx->NumStats)
+ {
+ s = ONE_STATE(ctx);
+ if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder)
+ SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1));
+ else
+ SetSuccessor(s, 0);
+ /* Suffix context can be removed already, since different (high-order)
+ Successors may refer to same context. So we check Flags == 0xFF (Stamp == EMPTY_NODE) */
+ if (!SUCCESSOR(s) && (!SUFFIX(ctx)->NumStats || SUFFIX(ctx)->Flags == 0xFF))
+ {
+ FreeUnits(p, ctx, 1);
+ return 0;
+ }
+ else
+ return REF(ctx);
+ }
+
+ for (s = STATS(ctx) + ctx->NumStats; s >= STATS(ctx); s--)
+ if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder)
+ SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1));
+ else
+ SetSuccessor(s, 0);
+
+ return REF(ctx);
+}
+#endif
+
+static UInt32 GetUsedMemory(const CPpmd8 *p)
+{
+ UInt32 v = 0;
+ unsigned i;
+ for (i = 0; i < PPMD_NUM_INDEXES; i++)
+ v += p->Stamps[i] * I2U(i);
+ return p->Size - (UInt32)(p->HiUnit - p->LoUnit) - (UInt32)(p->UnitsStart - p->Text) - U2B(v);
+}
+
+#ifdef PPMD8_FREEZE_SUPPORT
+ #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1, fSuccessor)
+#else
+ #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1)
+#endif
+
+static void RestoreModel(CPpmd8 *p, CTX_PTR c1
+ #ifdef PPMD8_FREEZE_SUPPORT
+ , CTX_PTR fSuccessor
+ #endif
+ )
+{
+ CTX_PTR c;
+ CPpmd_State *s;
+ RESET_TEXT(0);
+ for (c = p->MaxContext; c != c1; c = SUFFIX(c))
+ if (--(c->NumStats) == 0)
+ {
+ s = STATS(c);
+ c->Flags = (Byte)((c->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40));
+ *ONE_STATE(c) = *s;
+ SpecialFreeUnit(p, s);
+ ONE_STATE(c)->Freq = (Byte)(((unsigned)ONE_STATE(c)->Freq + 11) >> 3);
+ }
+ else
+ Refresh(p, c, (c->NumStats+3) >> 1, 0);
+
+ for (; c != p->MinContext; c = SUFFIX(c))
+ if (!c->NumStats)
+ ONE_STATE(c)->Freq = (Byte)(ONE_STATE(c)->Freq - (ONE_STATE(c)->Freq >> 1));
+ else if ((c->SummFreq += 4) > 128 + 4 * c->NumStats)
+ Refresh(p, c, (c->NumStats + 2) >> 1, 1);
+
+ #ifdef PPMD8_FREEZE_SUPPORT
+ if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
+ {
+ p->MaxContext = fSuccessor;
+ p->GlueCount += !(p->Stamps[1] & 1);
+ }
+ else if (p->RestoreMethod == PPMD8_RESTORE_METHOD_FREEZE)
+ {
+ while (p->MaxContext->Suffix)
+ p->MaxContext = SUFFIX(p->MaxContext);
+ RemoveBinContexts(p, p->MaxContext, 0);
+ p->RestoreMethod++;
+ p->GlueCount = 0;
+ p->OrderFall = p->MaxOrder;
+ }
+ else
+ #endif
+ if (p->RestoreMethod == PPMD8_RESTORE_METHOD_RESTART || GetUsedMemory(p) < (p->Size >> 1))
+ RestartModel(p);
+ else
+ {
+ while (p->MaxContext->Suffix)
+ p->MaxContext = SUFFIX(p->MaxContext);
+ do
+ {
+ CutOff(p, p->MaxContext, 0);
+ ExpandTextArea(p);
+ }
+ while (GetUsedMemory(p) > 3 * (p->Size >> 2));
+ p->GlueCount = 0;
+ p->OrderFall = p->MaxOrder;
+ }
+}
+
+static CTX_PTR CreateSuccessors(CPpmd8 *p, Bool skip, CPpmd_State *s1, CTX_PTR c)
+{
+ CPpmd_State upState;
+ Byte flags;
+ CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState);
+ /* fixed over Shkarin's code. Maybe it could work without + 1 too. */
+ CPpmd_State *ps[PPMD8_MAX_ORDER + 1];
+ unsigned numPs = 0;
+
+ if (!skip)
+ ps[numPs++] = p->FoundState;
+
+ while (c->Suffix)
+ {
+ CPpmd_Void_Ref successor;
+ CPpmd_State *s;
+ c = SUFFIX(c);
+ if (s1)
+ {
+ s = s1;
+ s1 = NULL;
+ }
+ else if (c->NumStats != 0)
+ {
+ for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++);
+ if (s->Freq < MAX_FREQ - 9)
+ {
+ s->Freq++;
+ c->SummFreq++;
+ }
+ }
+ else
+ {
+ s = ONE_STATE(c);
+ s->Freq = (Byte)(s->Freq + (!SUFFIX(c)->NumStats & (s->Freq < 24)));
+ }
+ successor = SUCCESSOR(s);
+ if (successor != upBranch)
+ {
+ c = CTX(successor);
+ if (numPs == 0)
+ return c;
+ break;
+ }
+ ps[numPs++] = s;
+ }
+
+ upState.Symbol = *(const Byte *)Ppmd8_GetPtr(p, upBranch);
+ SetSuccessor(&upState, upBranch + 1);
+ flags = (Byte)(0x10 * (p->FoundState->Symbol >= 0x40) + 0x08 * (upState.Symbol >= 0x40));
+
+ if (c->NumStats == 0)
+ upState.Freq = ONE_STATE(c)->Freq;
+ else
+ {
+ UInt32 cf, s0;
+ CPpmd_State *s;
+ for (s = STATS(c); s->Symbol != upState.Symbol; s++);
+ cf = s->Freq - 1;
+ s0 = c->SummFreq - c->NumStats - cf;
+ upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((cf + 2 * s0 - 3) / s0)));
+ }
+
+ do
+ {
+ /* Create Child */
+ CTX_PTR c1; /* = AllocContext(p); */
+ if (p->HiUnit != p->LoUnit)
+ c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE);
+ else if (p->FreeList[0] != 0)
+ c1 = (CTX_PTR)RemoveNode(p, 0);
+ else
+ {
+ c1 = (CTX_PTR)AllocUnitsRare(p, 0);
+ if (!c1)
+ return NULL;
+ }
+ c1->NumStats = 0;
+ c1->Flags = flags;
+ *ONE_STATE(c1) = upState;
+ c1->Suffix = REF(c);
+ SetSuccessor(ps[--numPs], REF(c1));
+ c = c1;
+ }
+ while (numPs != 0);
+
+ return c;
+}
+
+static CTX_PTR ReduceOrder(CPpmd8 *p, CPpmd_State *s1, CTX_PTR c)
+{
+ CPpmd_State *s = NULL;
+ CTX_PTR c1 = c;
+ CPpmd_Void_Ref upBranch = REF(p->Text);
+
+ #ifdef PPMD8_FREEZE_SUPPORT
+ /* The BUG in Shkarin's code was fixed: ps could overflow in CUT_OFF mode. */
+ CPpmd_State *ps[PPMD8_MAX_ORDER + 1];
+ unsigned numPs = 0;
+ ps[numPs++] = p->FoundState;
+ #endif
+
+ SetSuccessor(p->FoundState, upBranch);
+ p->OrderFall++;
+
+ for (;;)
+ {
+ if (s1)
+ {
+ c = SUFFIX(c);
+ s = s1;
+ s1 = NULL;
+ }
+ else
+ {
+ if (!c->Suffix)
+ {
+ #ifdef PPMD8_FREEZE_SUPPORT
+ if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
+ {
+ do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs);
+ RESET_TEXT(1);
+ p->OrderFall = 1;
+ }
+ #endif
+ return c;
+ }
+ c = SUFFIX(c);
+ if (c->NumStats)
+ {
+ if ((s = STATS(c))->Symbol != p->FoundState->Symbol)
+ do { s++; } while (s->Symbol != p->FoundState->Symbol);
+ if (s->Freq < MAX_FREQ - 9)
+ {
+ s->Freq += 2;
+ c->SummFreq += 2;
+ }
+ }
+ else
+ {
+ s = ONE_STATE(c);
+ s->Freq = (Byte)(s->Freq + (s->Freq < 32));
+ }
+ }
+ if (SUCCESSOR(s))
+ break;
+ #ifdef PPMD8_FREEZE_SUPPORT
+ ps[numPs++] = s;
+ #endif
+ SetSuccessor(s, upBranch);
+ p->OrderFall++;
+ }
+
+ #ifdef PPMD8_FREEZE_SUPPORT
+ if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
+ {
+ c = CTX(SUCCESSOR(s));
+ do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs);
+ RESET_TEXT(1);
+ p->OrderFall = 1;
+ return c;
+ }
+ else
+ #endif
+ if (SUCCESSOR(s) <= upBranch)
+ {
+ CTX_PTR successor;
+ CPpmd_State *s2 = p->FoundState;
+ p->FoundState = s;
+
+ successor = CreateSuccessors(p, False, NULL, c);
+ if (successor == NULL)
+ SetSuccessor(s, 0);
+ else
+ SetSuccessor(s, REF(successor));
+ p->FoundState = s2;
+ }
+
+ if (p->OrderFall == 1 && c1 == p->MaxContext)
+ {
+ SetSuccessor(p->FoundState, SUCCESSOR(s));
+ p->Text--;
+ }
+ if (SUCCESSOR(s) == 0)
+ return NULL;
+ return CTX(SUCCESSOR(s));
+}
+
+static void UpdateModel(CPpmd8 *p)
+{
+ CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState);
+ CTX_PTR c;
+ unsigned s0, ns, fFreq = p->FoundState->Freq;
+ Byte flag, fSymbol = p->FoundState->Symbol;
+ CPpmd_State *s = NULL;
+
+ if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0)
+ {
+ c = SUFFIX(p->MinContext);
+
+ if (c->NumStats == 0)
+ {
+ s = ONE_STATE(c);
+ if (s->Freq < 32)
+ s->Freq++;
+ }
+ else
+ {
+ s = STATS(c);
+ if (s->Symbol != p->FoundState->Symbol)
+ {
+ do { s++; } while (s->Symbol != p->FoundState->Symbol);
+ if (s[0].Freq >= s[-1].Freq)
+ {
+ SwapStates(&s[0], &s[-1]);
+ s--;
+ }
+ }
+ if (s->Freq < MAX_FREQ - 9)
+ {
+ s->Freq += 2;
+ c->SummFreq += 2;
+ }
+ }
+ }
+
+ c = p->MaxContext;
+ if (p->OrderFall == 0 && fSuccessor)
+ {
+ CTX_PTR cs = CreateSuccessors(p, True, s, p->MinContext);
+ if (cs == 0)
+ {
+ SetSuccessor(p->FoundState, 0);
+ RESTORE_MODEL(c, CTX(fSuccessor));
+ }
+ else
+ {
+ SetSuccessor(p->FoundState, REF(cs));
+ p->MaxContext = cs;
+ }
+ return;
+ }
+
+ *p->Text++ = p->FoundState->Symbol;
+ successor = REF(p->Text);
+ if (p->Text >= p->UnitsStart)
+ {
+ RESTORE_MODEL(c, CTX(fSuccessor)); /* check it */
+ return;
+ }
+
+ if (!fSuccessor)
+ {
+ CTX_PTR cs = ReduceOrder(p, s, p->MinContext);
+ if (cs == NULL)
+ {
+ RESTORE_MODEL(c, 0);
+ return;
+ }
+ fSuccessor = REF(cs);
+ }
+ else if ((Byte *)Ppmd8_GetPtr(p, fSuccessor) < p->UnitsStart)
+ {
+ CTX_PTR cs = CreateSuccessors(p, False, s, p->MinContext);
+ if (cs == NULL)
+ {
+ RESTORE_MODEL(c, 0);
+ return;
+ }
+ fSuccessor = REF(cs);
+ }
+
+ if (--p->OrderFall == 0)
+ {
+ successor = fSuccessor;
+ p->Text -= (p->MaxContext != p->MinContext);
+ }
+ #ifdef PPMD8_FREEZE_SUPPORT
+ else if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
+ {
+ successor = fSuccessor;
+ RESET_TEXT(0);
+ p->OrderFall = 0;
+ }
+ #endif
+
+ s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - fFreq;
+ flag = (Byte)(0x08 * (fSymbol >= 0x40));
+
+ for (; c != p->MinContext; c = SUFFIX(c))
+ {
+ unsigned ns1;
+ UInt32 cf, sf;
+ if ((ns1 = c->NumStats) != 0)
+ {
+ if ((ns1 & 1) != 0)
+ {
+ /* Expand for one UNIT */
+ unsigned oldNU = (ns1 + 1) >> 1;
+ unsigned i = U2I(oldNU);
+ if (i != U2I(oldNU + 1))
+ {
+ void *ptr = AllocUnits(p, i + 1);
+ void *oldPtr;
+ if (!ptr)
+ {
+ RESTORE_MODEL(c, CTX(fSuccessor));
+ return;
+ }
+ oldPtr = STATS(c);
+ MyMem12Cpy(ptr, oldPtr, oldNU);
+ InsertNode(p, oldPtr, i);
+ c->Stats = STATS_REF(ptr);
+ }
+ }
+ c->SummFreq = (UInt16)(c->SummFreq + (3 * ns1 + 1 < ns));
+ }
+ else
+ {
+ CPpmd_State *s2 = (CPpmd_State*)AllocUnits(p, 0);
+ if (!s2)
+ {
+ RESTORE_MODEL(c, CTX(fSuccessor));
+ return;
+ }
+ *s2 = *ONE_STATE(c);
+ c->Stats = REF(s2);
+ if (s2->Freq < MAX_FREQ / 4 - 1)
+ s2->Freq <<= 1;
+ else
+ s2->Freq = MAX_FREQ - 4;
+ c->SummFreq = (UInt16)(s2->Freq + p->InitEsc + (ns > 2));
+ }
+ cf = 2 * fFreq * (c->SummFreq + 6);
+ sf = (UInt32)s0 + c->SummFreq;
+ if (cf < 6 * sf)
+ {
+ cf = 1 + (cf > sf) + (cf >= 4 * sf);
+ c->SummFreq += 4;
+ }
+ else
+ {
+ cf = 4 + (cf > 9 * sf) + (cf > 12 * sf) + (cf > 15 * sf);
+ c->SummFreq = (UInt16)(c->SummFreq + cf);
+ }
+ {
+ CPpmd_State *s2 = STATS(c) + ns1 + 1;
+ SetSuccessor(s2, successor);
+ s2->Symbol = fSymbol;
+ s2->Freq = (Byte)cf;
+ c->Flags |= flag;
+ c->NumStats = (Byte)(ns1 + 1);
+ }
+ }
+ p->MaxContext = p->MinContext = CTX(fSuccessor);
+}
+
+static void Rescale(CPpmd8 *p)
+{
+ unsigned i, adder, sumFreq, escFreq;
+ CPpmd_State *stats = STATS(p->MinContext);
+ CPpmd_State *s = p->FoundState;
+ {
+ CPpmd_State tmp = *s;
+ for (; s != stats; s--)
+ s[0] = s[-1];
+ *s = tmp;
+ }
+ escFreq = p->MinContext->SummFreq - s->Freq;
+ s->Freq += 4;
+ adder = (p->OrderFall != 0
+ #ifdef PPMD8_FREEZE_SUPPORT
+ || p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE
+ #endif
+ );
+ s->Freq = (Byte)((s->Freq + adder) >> 1);
+ sumFreq = s->Freq;
+
+ i = p->MinContext->NumStats;
+ do
+ {
+ escFreq -= (++s)->Freq;
+ s->Freq = (Byte)((s->Freq + adder) >> 1);
+ sumFreq += s->Freq;
+ if (s[0].Freq > s[-1].Freq)
+ {
+ CPpmd_State *s1 = s;
+ CPpmd_State tmp = *s1;
+ do
+ s1[0] = s1[-1];
+ while (--s1 != stats && tmp.Freq > s1[-1].Freq);
+ *s1 = tmp;
+ }
+ }
+ while (--i);
+
+ if (s->Freq == 0)
+ {
+ unsigned numStats = p->MinContext->NumStats;
+ unsigned n0, n1;
+ do { i++; } while ((--s)->Freq == 0);
+ escFreq += i;
+ p->MinContext->NumStats = (Byte)(p->MinContext->NumStats - i);
+ if (p->MinContext->NumStats == 0)
+ {
+ CPpmd_State tmp = *stats;
+ tmp.Freq = (Byte)((2 * tmp.Freq + escFreq - 1) / escFreq);
+ if (tmp.Freq > MAX_FREQ / 3)
+ tmp.Freq = MAX_FREQ / 3;
+ InsertNode(p, stats, U2I((numStats + 2) >> 1));
+ p->MinContext->Flags = (Byte)((p->MinContext->Flags & 0x10) + 0x08 * (tmp.Symbol >= 0x40));
+ *(p->FoundState = ONE_STATE(p->MinContext)) = tmp;
+ return;
+ }
+ n0 = (numStats + 2) >> 1;
+ n1 = (p->MinContext->NumStats + 2) >> 1;
+ if (n0 != n1)
+ p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1));
+ p->MinContext->Flags &= ~0x08;
+ p->MinContext->Flags |= 0x08 * ((s = STATS(p->MinContext))->Symbol >= 0x40);
+ i = p->MinContext->NumStats;
+ do { p->MinContext->Flags |= 0x08*((++s)->Symbol >= 0x40); } while (--i);
+ }
+ p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1));
+ p->MinContext->Flags |= 0x4;
+ p->FoundState = STATS(p->MinContext);
+}
+
+CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked1, UInt32 *escFreq)
+{
+ CPpmd_See *see;
+ if (p->MinContext->NumStats != 0xFF)
+ {
+ see = p->See[(unsigned)p->NS2Indx[(unsigned)p->MinContext->NumStats + 2] - 3] +
+ (p->MinContext->SummFreq > 11 * ((unsigned)p->MinContext->NumStats + 1)) +
+ 2 * (unsigned)(2 * (unsigned)p->MinContext->NumStats <
+ ((unsigned)SUFFIX(p->MinContext)->NumStats + numMasked1)) +
+ p->MinContext->Flags;
+ {
+ unsigned r = (see->Summ >> see->Shift);
+ see->Summ = (UInt16)(see->Summ - r);
+ *escFreq = r + (r == 0);
+ }
+ }
+ else
+ {
+ see = &p->DummySee;
+ *escFreq = 1;
+ }
+ return see;
+}
+
+static void NextContext(CPpmd8 *p)
+{
+ CTX_PTR c = CTX(SUCCESSOR(p->FoundState));
+ if (p->OrderFall == 0 && (Byte *)c >= p->UnitsStart)
+ p->MinContext = p->MaxContext = c;
+ else
+ {
+ UpdateModel(p);
+ p->MinContext = p->MaxContext;
+ }
+}
+
+void Ppmd8_Update1(CPpmd8 *p)
+{
+ CPpmd_State *s = p->FoundState;
+ s->Freq += 4;
+ p->MinContext->SummFreq += 4;
+ if (s[0].Freq > s[-1].Freq)
+ {
+ SwapStates(&s[0], &s[-1]);
+ p->FoundState = --s;
+ if (s->Freq > MAX_FREQ)
+ Rescale(p);
+ }
+ NextContext(p);
+}
+
+void Ppmd8_Update1_0(CPpmd8 *p)
+{
+ p->PrevSuccess = (2 * p->FoundState->Freq >= p->MinContext->SummFreq);
+ p->RunLength += p->PrevSuccess;
+ p->MinContext->SummFreq += 4;
+ if ((p->FoundState->Freq += 4) > MAX_FREQ)
+ Rescale(p);
+ NextContext(p);
+}
+
+void Ppmd8_UpdateBin(CPpmd8 *p)
+{
+ p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 196));
+ p->PrevSuccess = 1;
+ p->RunLength++;
+ NextContext(p);
+}
+
+void Ppmd8_Update2(CPpmd8 *p)
+{
+ p->MinContext->SummFreq += 4;
+ if ((p->FoundState->Freq += 4) > MAX_FREQ)
+ Rescale(p);
+ p->RunLength = p->InitRL;
+ UpdateModel(p);
+ p->MinContext = p->MaxContext;
+}
+
+/* Ppmd8Dec.c -- PPMdI Decoder
+2010-04-16 : Igor Pavlov : Public domain
+This code is based on:
+ PPMd var.I (2002): Dmitry Shkarin : Public domain
+ Carryless rangecoder (1999): Dmitry Subbotin : Public domain */
+
+Bool Ppmd8_RangeDec_Init(CPpmd8 *p)
+{
+ unsigned i;
+ p->Low = 0;
+ p->Range = 0xFFFFFFFF;
+ p->Code = 0;
+ for (i = 0; i < 4; i++)
+ p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In);
+ return (p->Code < 0xFFFFFFFF);
+}
+
+static UInt32 RangeDec_GetThreshold(CPpmd8 *p, UInt32 total)
+{
+ return p->Code / (p->Range /= total);
+}
+
+static void RangeDec_Decode(CPpmd8 *p, UInt32 start, UInt32 size)
+{
+ start *= p->Range;
+ p->Low += start;
+ p->Code -= start;
+ p->Range *= size;
+
+ while ((p->Low ^ (p->Low + p->Range)) < kTop ||
+ (p->Range < kBot && ((p->Range = (0 - p->Low) & (kBot - 1)), 1)))
+ {
+ p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In);
+ p->Range <<= 8;
+ p->Low <<= 8;
+ }
+}
+
+#define MASK(sym) ((signed char *)charMask)[sym]
+
+int Ppmd8_DecodeSymbol(CPpmd8 *p)
+{
+ size_t charMask[256 / sizeof(size_t)];
+ if (p->MinContext->NumStats != 0)
+ {
+ CPpmd_State *s = Ppmd8_GetStats(p, p->MinContext);
+ unsigned i;
+ UInt32 count, hiCnt;
+ if ((count = RangeDec_GetThreshold(p, p->MinContext->SummFreq)) < (hiCnt = s->Freq))
+ {
+ Byte symbol;
+ RangeDec_Decode(p, 0, s->Freq);
+ p->FoundState = s;
+ symbol = s->Symbol;
+ Ppmd8_Update1_0(p);
+ return symbol;
+ }
+ p->PrevSuccess = 0;
+ i = p->MinContext->NumStats;
+ do
+ {
+ if ((hiCnt += (++s)->Freq) > count)
+ {
+ Byte symbol;
+ RangeDec_Decode(p, hiCnt - s->Freq, s->Freq);
+ p->FoundState = s;
+ symbol = s->Symbol;
+ Ppmd8_Update1(p);
+ return symbol;
+ }
+ }
+ while (--i);
+ if (count >= p->MinContext->SummFreq)
+ return -2;
+ RangeDec_Decode(p, hiCnt, p->MinContext->SummFreq - hiCnt);
+ PPMD_SetAllBitsIn256Bytes(charMask);
+ MASK(s->Symbol) = 0;
+ i = p->MinContext->NumStats;
+ do { MASK((--s)->Symbol) = 0; } while (--i);
+ }
+ else
+ {
+ UInt16 *prob = Ppmd8_GetBinSumm(p);
+ if (((p->Code / (p->Range >>= 14)) < *prob))
+ {
+ Byte symbol;
+ RangeDec_Decode(p, 0, *prob);
+ *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob);
+ symbol = (p->FoundState = Ppmd8Context_OneState(p->MinContext))->Symbol;
+ Ppmd8_UpdateBin(p);
+ return symbol;
+ }
+ RangeDec_Decode(p, *prob, (1 << 14) - *prob);
+ *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob);
+ p->InitEsc = PPMD8_kExpEscape[*prob >> 10];
+ PPMD_SetAllBitsIn256Bytes(charMask);
+ MASK(Ppmd8Context_OneState(p->MinContext)->Symbol) = 0;
+ p->PrevSuccess = 0;
+ }
+ for (;;)
+ {
+ CPpmd_State *ps[256], *s;
+ UInt32 freqSum, count, hiCnt;
+ CPpmd_See *see;
+ unsigned i, num, numMasked = p->MinContext->NumStats;
+ do
+ {
+ p->OrderFall++;
+ if (!p->MinContext->Suffix)
+ return -1;
+ p->MinContext = Ppmd8_GetContext(p, p->MinContext->Suffix);
+ }
+ while (p->MinContext->NumStats == numMasked);
+ hiCnt = 0;
+ s = Ppmd8_GetStats(p, p->MinContext);
+ i = 0;
+ num = p->MinContext->NumStats - numMasked;
+ do
+ {
+ int k = (int)(MASK(s->Symbol));
+ hiCnt += (s->Freq & k);
+ ps[i] = s++;
+ i -= k;
+ }
+ while (i != num);
+
+ see = Ppmd8_MakeEscFreq(p, numMasked, &freqSum);
+ freqSum += hiCnt;
+ count = RangeDec_GetThreshold(p, freqSum);
+
+ if (count < hiCnt)
+ {
+ Byte symbol;
+ CPpmd_State **pps = ps;
+ for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++);
+ s = *pps;
+ RangeDec_Decode(p, hiCnt - s->Freq, s->Freq);
+ Ppmd_See_Update(see);
+ p->FoundState = s;
+ symbol = s->Symbol;
+ Ppmd8_Update2(p);
+ return symbol;
+ }
+ if (count >= freqSum)
+ return -2;
+ RangeDec_Decode(p, hiCnt, freqSum - hiCnt);
+ see->Summ = (UInt16)(see->Summ + freqSum);
+ do { MASK(ps[--i]->Symbol) = 0; } while (i != 0);
+ }
+}
+
+/* H->I changes:
+ NS2Indx
+ GlewCount, and Glue method
+ BinSum
+ See / EscFreq
+ CreateSuccessors updates more suffix contexts
+ UpdateModel consts.
+ PrevSuccess Update
+*/
+
+const IPpmd8 __archive_ppmd8_functions =
+{
+ &Ppmd8_Construct,
+ &Ppmd8_Alloc,
+ &Ppmd8_Free,
+ &Ppmd8_Init,
+ &Ppmd8_RangeDec_Init,
+ &Ppmd8_DecodeSymbol,
+};
diff --git a/contrib/libs/libarchive/libarchive/archive_ppmd8_private.h b/contrib/libs/libarchive/libarchive/archive_ppmd8_private.h
new file mode 100644
index 00000000000..454b75f41f2
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_ppmd8_private.h
@@ -0,0 +1,148 @@
+/* Ppmd8.h -- PPMdI codec
+2011-01-27 : Igor Pavlov : Public domain
+This code is based on:
+ PPMd var.I (2002): Dmitry Shkarin : Public domain
+ Carryless rangecoder (1999): Dmitry Subbotin : Public domain */
+
+#ifndef ARCHIVE_PPMD8_PRIVATE_H_INCLUDED
+#define ARCHIVE_PPMD8_PRIVATE_H_INCLUDED
+
+#include "archive_ppmd_private.h"
+
+#define PPMD8_MIN_ORDER 2
+#define PPMD8_MAX_ORDER 16
+
+struct CPpmd8_Context_;
+
+typedef
+ #ifdef PPMD_32BIT
+ struct CPpmd8_Context_ *
+ #else
+ UInt32
+ #endif
+ CPpmd8_Context_Ref;
+
+#pragma pack(push, 1)
+
+typedef struct CPpmd8_Context_
+{
+ Byte NumStats;
+ Byte Flags;
+ UInt16 SummFreq;
+ CPpmd_State_Ref Stats;
+ CPpmd8_Context_Ref Suffix;
+} CPpmd8_Context;
+
+#pragma pack(pop)
+
+#define Ppmd8Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq)
+
+/* The BUG in Shkarin's code for FREEZE mode was fixed, but that fixed
+ code is not compatible with original code for some files compressed
+ in FREEZE mode. So we disable FREEZE mode support. */
+
+enum
+{
+ PPMD8_RESTORE_METHOD_RESTART,
+ PPMD8_RESTORE_METHOD_CUT_OFF
+ #ifdef PPMD8_FREEZE_SUPPORT
+ , PPMD8_RESTORE_METHOD_FREEZE
+ #endif
+};
+
+typedef struct
+{
+ CPpmd8_Context *MinContext, *MaxContext;
+ CPpmd_State *FoundState;
+ unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder;
+ Int32 RunLength, InitRL; /* must be 32-bit at least */
+
+ UInt32 Size;
+ UInt32 GlueCount;
+ Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart;
+ UInt32 AlignOffset;
+ unsigned RestoreMethod;
+
+ /* Range Coder */
+ UInt32 Range;
+ UInt32 Code;
+ UInt32 Low;
+ union
+ {
+ IByteIn *In;
+ IByteOut *Out;
+ } Stream;
+
+ Byte Indx2Units[PPMD_NUM_INDEXES];
+ Byte Units2Indx[128];
+ CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES];
+ UInt32 Stamps[PPMD_NUM_INDEXES];
+
+ Byte NS2BSIndx[256], NS2Indx[260];
+ CPpmd_See DummySee, See[24][32];
+ UInt16 BinSumm[25][64];
+} CPpmd8;
+
+void Ppmd8_Construct(CPpmd8 *p);
+Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size);
+void Ppmd8_Free(CPpmd8 *p);
+void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod);
+#define Ppmd8_WasAllocated(p) ((p)->Base != NULL)
+
+
+/* ---------- Internal Functions ---------- */
+
+extern const Byte PPMD8_kExpEscape[16];
+
+#ifdef PPMD_32BIT
+ #define Ppmd8_GetPtr(p, ptr) (ptr)
+ #define Ppmd8_GetContext(p, ptr) (ptr)
+ #define Ppmd8_GetStats(p, ctx) ((ctx)->Stats)
+#else
+ #define Ppmd8_GetPtr(p, offs) ((void *)((p)->Base + (offs)))
+ #define Ppmd8_GetContext(p, offs) ((CPpmd8_Context *)Ppmd8_GetPtr((p), (offs)))
+ #define Ppmd8_GetStats(p, ctx) ((CPpmd_State *)Ppmd8_GetPtr((p), ((ctx)->Stats)))
+#endif
+
+void Ppmd8_Update1(CPpmd8 *p);
+void Ppmd8_Update1_0(CPpmd8 *p);
+void Ppmd8_Update2(CPpmd8 *p);
+void Ppmd8_UpdateBin(CPpmd8 *p);
+
+#define Ppmd8_GetBinSumm(p) \
+ &p->BinSumm[p->NS2Indx[Ppmd8Context_OneState(p->MinContext)->Freq - 1]][ \
+ p->NS2BSIndx[Ppmd8_GetContext(p, p->MinContext->Suffix)->NumStats] + \
+ p->PrevSuccess + p->MinContext->Flags + ((p->RunLength >> 26) & 0x20)]
+
+CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked, UInt32 *scale);
+
+
+/* ---------- Decode ---------- */
+
+Bool Ppmd8_RangeDec_Init(CPpmd8 *p);
+#define Ppmd8_RangeDec_IsFinishedOK(p) ((p)->Code == 0)
+int Ppmd8_DecodeSymbol(CPpmd8 *p); /* returns: -1 as EndMarker, -2 as DataError */
+
+/* ---------- Encode ---------- */
+
+#define Ppmd8_RangeEnc_Init(p) { (p)->Low = 0; (p)->Range = 0xFFFFFFFF; }
+void Ppmd8_RangeEnc_FlushData(CPpmd8 *p);
+void Ppmd8_EncodeSymbol(CPpmd8 *p, int symbol); /* symbol = -1 means EndMarker */
+
+typedef struct
+{
+ /* Base Functions */
+ void (*Ppmd8_Construct)(CPpmd8 *p);
+ Bool (*Ppmd8_Alloc)(CPpmd8 *p, UInt32 size);
+ void (*Ppmd8_Free)(CPpmd8 *p);
+ void (*Ppmd8_Init)(CPpmd8 *p, unsigned max_order, unsigned restore_method);
+ #define Ppmd7_WasAllocated(p) ((p)->Base != NULL)
+
+ /* Decode Functions */
+ int (*Ppmd8_RangeDec_Init)(CPpmd8 *p);
+ int (*Ppmd8_DecodeSymbol)(CPpmd8 *p);
+} IPpmd8;
+
+extern const IPpmd8 __archive_ppmd8_functions;
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_ppmd_private.h b/contrib/libs/libarchive/libarchive/archive_ppmd_private.h
new file mode 100644
index 00000000000..582803e5fd0
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_ppmd_private.h
@@ -0,0 +1,151 @@
+/* Ppmd.h -- PPMD codec common code
+2010-03-12 : Igor Pavlov : Public domain
+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
+
+#ifndef ARCHIVE_PPMD_PRIVATE_H_INCLUDED
+#define ARCHIVE_PPMD_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include <stddef.h>
+
+#include "archive_read_private.h"
+
+/*** Begin defined in Types.h ***/
+
+#if !defined(ZCONF_H)
+typedef unsigned char Byte;
+#endif
+typedef short Int16;
+typedef unsigned short UInt16;
+
+#ifdef _LZMA_UINT32_IS_ULONG
+typedef long Int32;
+typedef unsigned long UInt32;
+#else
+typedef int Int32;
+typedef unsigned int UInt32;
+#endif
+
+#ifdef _SZ_NO_INT_64
+
+/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers.
+ NOTES: Some code will work incorrectly in that case! */
+
+typedef long Int64;
+typedef unsigned long UInt64;
+
+#else
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+typedef __int64 Int64;
+typedef unsigned __int64 UInt64;
+#define UINT64_CONST(n) n
+#else
+typedef long long int Int64;
+typedef unsigned long long int UInt64;
+#define UINT64_CONST(n) n ## ULL
+#endif
+
+#endif
+
+typedef int Bool;
+#define True 1
+#define False 0
+
+/* The following interfaces use first parameter as pointer to structure */
+
+typedef struct
+{
+ struct archive_read *a;
+ Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */
+} IByteIn;
+
+typedef struct
+{
+ struct archive_write *a;
+ void (*Write)(void *p, Byte b);
+} IByteOut;
+
+/*** End defined in Types.h ***/
+/*** Begin defined in CpuArch.h ***/
+
+#if defined(_M_IX86) || defined(__i386__)
+#define MY_CPU_X86
+#endif
+
+#if defined(MY_CPU_X86) || defined(_M_ARM)
+#define MY_CPU_32BIT
+#endif
+
+#ifdef MY_CPU_32BIT
+#define PPMD_32BIT
+#endif
+
+/*** End defined in CpuArch.h ***/
+
+#define PPMD_INT_BITS 7
+#define PPMD_PERIOD_BITS 7
+#define PPMD_BIN_SCALE (1 << (PPMD_INT_BITS + PPMD_PERIOD_BITS))
+
+#define PPMD_GET_MEAN_SPEC(summ, shift, round) (((summ) + (1 << ((shift) - (round)))) >> (shift))
+#define PPMD_GET_MEAN(summ) PPMD_GET_MEAN_SPEC((summ), PPMD_PERIOD_BITS, 2)
+#define PPMD_UPDATE_PROB_0(prob) ((prob) + (1 << PPMD_INT_BITS) - PPMD_GET_MEAN(prob))
+#define PPMD_UPDATE_PROB_1(prob) ((prob) - PPMD_GET_MEAN(prob))
+
+#define PPMD_N1 4
+#define PPMD_N2 4
+#define PPMD_N3 4
+#define PPMD_N4 ((128 + 3 - 1 * PPMD_N1 - 2 * PPMD_N2 - 3 * PPMD_N3) / 4)
+#define PPMD_NUM_INDEXES (PPMD_N1 + PPMD_N2 + PPMD_N3 + PPMD_N4)
+
+/* SEE-contexts for PPM-contexts with masked symbols */
+typedef struct
+{
+ UInt16 Summ; /* Freq */
+ Byte Shift; /* Speed of Freq change; low Shift is for fast change */
+ Byte Count; /* Count to next change of Shift */
+} CPpmd_See;
+
+#define Ppmd_See_Update(p) if ((p)->Shift < PPMD_PERIOD_BITS && --(p)->Count == 0) \
+ { (p)->Summ <<= 1; (p)->Count = (Byte)(3 << (p)->Shift++); }
+
+typedef struct
+{
+ Byte Symbol;
+ Byte Freq;
+ UInt16 SuccessorLow;
+ UInt16 SuccessorHigh;
+} CPpmd_State;
+
+typedef
+ #ifdef PPMD_32BIT
+ CPpmd_State *
+ #else
+ UInt32
+ #endif
+ CPpmd_State_Ref;
+
+typedef
+ #ifdef PPMD_32BIT
+ void *
+ #else
+ UInt32
+ #endif
+ CPpmd_Void_Ref;
+
+typedef
+ #ifdef PPMD_32BIT
+ Byte *
+ #else
+ UInt32
+ #endif
+ CPpmd_Byte_Ref;
+
+#define PPMD_SetAllBitsIn256Bytes(p) \
+ { unsigned j; for (j = 0; j < 256 / sizeof(p[0]); j += 8) { \
+ p[j+7] = p[j+6] = p[j+5] = p[j+4] = p[j+3] = p[j+2] = p[j+1] = p[j+0] = ~(size_t)0; }}
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_private.h b/contrib/libs/libarchive/libarchive/archive_private.h
new file mode 100644
index 00000000000..b2a2cda250e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_private.h
@@ -0,0 +1,180 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_private.h 201098 2009-12-28 02:58:14Z kientzle $
+ */
+
+#ifndef ARCHIVE_PRIVATE_H_INCLUDED
+#define ARCHIVE_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#if HAVE_ICONV_H
+#include <iconv.h>
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+
+#if defined(__GNUC__) && (__GNUC__ > 2 || \
+ (__GNUC__ == 2 && __GNUC_MINOR__ >= 5))
+#define __LA_DEAD __attribute__((__noreturn__))
+#else
+#define __LA_DEAD
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ > 2 || \
+ (__GNUC__ == 2 && __GNUC_MINOR__ >= 7))
+#define __LA_UNUSED __attribute__((__unused__))
+#else
+#define __LA_UNUSED
+#endif
+
+#define ARCHIVE_WRITE_MAGIC (0xb0c5c0deU)
+#define ARCHIVE_READ_MAGIC (0xdeb0c5U)
+#define ARCHIVE_WRITE_DISK_MAGIC (0xc001b0c5U)
+#define ARCHIVE_READ_DISK_MAGIC (0xbadb0c5U)
+#define ARCHIVE_MATCH_MAGIC (0xcad11c9U)
+
+#define ARCHIVE_STATE_NEW 1U
+#define ARCHIVE_STATE_HEADER 2U
+#define ARCHIVE_STATE_DATA 4U
+#define ARCHIVE_STATE_EOF 0x10U
+#define ARCHIVE_STATE_CLOSED 0x20U
+#define ARCHIVE_STATE_FATAL 0x8000U
+#define ARCHIVE_STATE_ANY (0xFFFFU & ~ARCHIVE_STATE_FATAL)
+
+struct archive_vtable {
+ int (*archive_close)(struct archive *);
+ int (*archive_free)(struct archive *);
+ int (*archive_write_header)(struct archive *,
+ struct archive_entry *);
+ int (*archive_write_finish_entry)(struct archive *);
+ ssize_t (*archive_write_data)(struct archive *,
+ const void *, size_t);
+ ssize_t (*archive_write_data_block)(struct archive *,
+ const void *, size_t, int64_t);
+
+ int (*archive_read_next_header)(struct archive *,
+ struct archive_entry **);
+ int (*archive_read_next_header2)(struct archive *,
+ struct archive_entry *);
+ int (*archive_read_data_block)(struct archive *,
+ const void **, size_t *, int64_t *);
+
+ int (*archive_filter_count)(struct archive *);
+ int64_t (*archive_filter_bytes)(struct archive *, int);
+ int (*archive_filter_code)(struct archive *, int);
+ const char * (*archive_filter_name)(struct archive *, int);
+};
+
+struct archive_string_conv;
+
+struct archive {
+ /*
+ * The magic/state values are used to sanity-check the
+ * client's usage. If an API function is called at a
+ * ridiculous time, or the client passes us an invalid
+ * pointer, these values allow me to catch that.
+ */
+ unsigned int magic;
+ unsigned int state;
+
+ /*
+ * Some public API functions depend on the "real" type of the
+ * archive object.
+ */
+ const struct archive_vtable *vtable;
+
+ int archive_format;
+ const char *archive_format_name;
+
+ /* Number of file entries processed. */
+ int file_count;
+
+ int archive_error_number;
+ const char *error;
+ struct archive_string error_string;
+
+ char *current_code;
+ unsigned current_codepage; /* Current ACP(ANSI CodePage). */
+ unsigned current_oemcp; /* Current OEMCP(OEM CodePage). */
+ struct archive_string_conv *sconv;
+
+ /*
+ * Used by archive_read_data() to track blocks and copy
+ * data to client buffers, filling gaps with zero bytes.
+ */
+ const char *read_data_block;
+ int64_t read_data_offset;
+ int64_t read_data_output_offset;
+ size_t read_data_remaining;
+
+ /*
+ * Used by formats/filters to determine the amount of data
+ * requested from a call to archive_read_data(). This is only
+ * useful when the format/filter has seek support.
+ */
+ char read_data_is_posix_read;
+ size_t read_data_requested;
+};
+
+/* Check magic value and state; return(ARCHIVE_FATAL) if it isn't valid. */
+int __archive_check_magic(struct archive *, unsigned int magic,
+ unsigned int state, const char *func);
+#define archive_check_magic(a, expected_magic, allowed_states, function_name) \
+ do { \
+ int magic_test = __archive_check_magic((a), (expected_magic), \
+ (allowed_states), (function_name)); \
+ if (magic_test == ARCHIVE_FATAL) \
+ return ARCHIVE_FATAL; \
+ } while (0)
+
+void __archive_errx(int retvalue, const char *msg) __LA_DEAD;
+
+void __archive_ensure_cloexec_flag(int fd);
+int __archive_mktemp(const char *tmpdir);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+int __archive_mkstemp(wchar_t *template);
+#else
+int __archive_mkstemp(char *template);
+#endif
+
+int __archive_clean(struct archive *);
+
+void __archive_reset_read_data(struct archive *);
+
+#define err_combine(a,b) ((a) < (b) ? (a) : (b))
+
+#if defined(__BORLANDC__) || (defined(_MSC_VER) && _MSC_VER <= 1300)
+# define ARCHIVE_LITERAL_LL(x) x##i64
+# define ARCHIVE_LITERAL_ULL(x) x##ui64
+#else
+# define ARCHIVE_LITERAL_LL(x) x##ll
+# define ARCHIVE_LITERAL_ULL(x) x##ull
+#endif
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_random.c b/contrib/libs/libarchive/libarchive/archive_random.c
new file mode 100644
index 00000000000..301765acd83
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_random.c
@@ -0,0 +1,301 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__))
+
+#ifdef HAVE_FCNTL
+#include <fcntl.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+static void la_arc4random_buf(void *, size_t);
+
+#endif /* HAVE_ARC4RANDOM_BUF */
+
+#include "archive.h"
+#include "archive_random_private.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+/* don't use bcrypt when XP needs to be supported */
+#include <bcrypt.h>
+
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#endif
+
+#elif defined(HAVE_WINCRYPT_H)
+#include <wincrypt.h>
+#endif
+#endif
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+/*
+ * Random number generator function.
+ * This simply calls arc4random_buf function if the platform provides it.
+ */
+
+int
+archive_random(void *buf, size_t nbytes)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+# if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ NTSTATUS status;
+ BCRYPT_ALG_HANDLE hAlg;
+
+ status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, NULL, 0);
+ if (!BCRYPT_SUCCESS(status))
+ return ARCHIVE_FAILED;
+ status = BCryptGenRandom(hAlg, buf, nbytes, 0);
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ if (!BCRYPT_SUCCESS(status))
+ return ARCHIVE_FAILED;
+
+ return ARCHIVE_OK;
+# else
+ HCRYPTPROV hProv;
+ BOOL success;
+
+ success = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT);
+ if (!success && GetLastError() == (DWORD)NTE_BAD_KEYSET) {
+ success = CryptAcquireContext(&hProv, NULL, NULL,
+ PROV_RSA_FULL, CRYPT_NEWKEYSET);
+ }
+ if (success) {
+ success = CryptGenRandom(hProv, (DWORD)nbytes, (BYTE*)buf);
+ CryptReleaseContext(hProv, 0);
+ if (success)
+ return ARCHIVE_OK;
+ }
+ /* TODO: Does this case really happen? */
+ return ARCHIVE_FAILED;
+# endif
+#elif !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__))
+ la_arc4random_buf(buf, nbytes);
+ return ARCHIVE_OK;
+#else
+ arc4random_buf(buf, nbytes);
+ return ARCHIVE_OK;
+#endif
+}
+
+#if !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__))
+
+/* $OpenBSD: arc4random.c,v 1.24 2013/06/11 16:59:50 deraadt Exp $ */
+/*
+ * Copyright (c) 1996, David Mazieres <dm@uun.org>
+ * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Arc4 random number generator for OpenBSD.
+ *
+ * This code is derived from section 17.1 of Applied Cryptography,
+ * second edition, which describes a stream cipher allegedly
+ * compatible with RSA Labs "RC4" cipher (the actual description of
+ * which is a trade secret). The same algorithm is used as a stream
+ * cipher called "arcfour" in Tatu Ylonen's ssh package.
+ *
+ * RC4 is a registered trademark of RSA Laboratories.
+ */
+
+#ifdef __GNUC__
+#define inline __inline
+#else /* !__GNUC__ */
+#define inline
+#endif /* !__GNUC__ */
+
+struct arc4_stream {
+ uint8_t i;
+ uint8_t j;
+ uint8_t s[256];
+};
+
+#define RANDOMDEV "/dev/urandom"
+#define KEYSIZE 128
+#ifdef HAVE_PTHREAD_H
+static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER;
+#define _ARC4_LOCK() pthread_mutex_lock(&arc4random_mtx);
+#define _ARC4_UNLOCK() pthread_mutex_unlock(&arc4random_mtx);
+#else
+#define _ARC4_LOCK()
+#define _ARC4_UNLOCK()
+#endif
+
+static int rs_initialized;
+static struct arc4_stream rs;
+static pid_t arc4_stir_pid;
+static int arc4_count;
+
+static inline uint8_t arc4_getbyte(void);
+static void arc4_stir(void);
+
+static inline void
+arc4_init(void)
+{
+ int n;
+
+ for (n = 0; n < 256; n++)
+ rs.s[n] = n;
+ rs.i = 0;
+ rs.j = 0;
+}
+
+static inline void
+arc4_addrandom(uint8_t *dat, int datlen)
+{
+ int n;
+ uint8_t si;
+
+ rs.i--;
+ for (n = 0; n < 256; n++) {
+ rs.i = (rs.i + 1);
+ si = rs.s[rs.i];
+ rs.j = (rs.j + si + dat[n % datlen]);
+ rs.s[rs.i] = rs.s[rs.j];
+ rs.s[rs.j] = si;
+ }
+ rs.j = rs.i;
+}
+
+static void
+arc4_stir(void)
+{
+ int done, fd, i;
+ struct {
+ struct timeval tv;
+ pid_t pid;
+ uint8_t rnd[KEYSIZE];
+ } rdat;
+
+ if (!rs_initialized) {
+ arc4_init();
+ rs_initialized = 1;
+ }
+ done = 0;
+ fd = open(RANDOMDEV, O_RDONLY | O_CLOEXEC, 0);
+ if (fd >= 0) {
+ if (read(fd, &rdat, KEYSIZE) == KEYSIZE)
+ done = 1;
+ (void)close(fd);
+ }
+ if (!done) {
+ (void)gettimeofday(&rdat.tv, NULL);
+ rdat.pid = getpid();
+ /* We'll just take whatever was on the stack too... */
+ }
+
+ arc4_addrandom((uint8_t *)&rdat, KEYSIZE);
+
+ /*
+ * Discard early keystream, as per recommendations in:
+ * "(Not So) Random Shuffles of RC4" by Ilya Mironov.
+ * As per the Network Operations Division, cryptographic requirements
+ * published on wikileaks on March 2017.
+ */
+
+ for (i = 0; i < 3072; i++)
+ (void)arc4_getbyte();
+ arc4_count = 1600000;
+}
+
+static void
+arc4_stir_if_needed(void)
+{
+ pid_t pid = getpid();
+
+ if (arc4_count <= 0 || !rs_initialized || arc4_stir_pid != pid) {
+ arc4_stir_pid = pid;
+ arc4_stir();
+ }
+}
+
+static inline uint8_t
+arc4_getbyte(void)
+{
+ uint8_t si, sj;
+
+ rs.i = (rs.i + 1);
+ si = rs.s[rs.i];
+ rs.j = (rs.j + si);
+ sj = rs.s[rs.j];
+ rs.s[rs.i] = sj;
+ rs.s[rs.j] = si;
+ return (rs.s[(si + sj) & 0xff]);
+}
+
+static void
+la_arc4random_buf(void *_buf, size_t n)
+{
+ uint8_t *buf = (uint8_t *)_buf;
+ _ARC4_LOCK();
+ arc4_stir_if_needed();
+ while (n--) {
+ if (--arc4_count <= 0)
+ arc4_stir();
+ buf[n] = arc4_getbyte();
+ }
+ _ARC4_UNLOCK();
+}
+
+#endif /* !HAVE_ARC4RANDOM_BUF */
diff --git a/contrib/libs/libarchive/libarchive/archive_random_private.h b/contrib/libs/libarchive/libarchive/archive_random_private.h
new file mode 100644
index 00000000000..08b91b3b7a3
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_random_private.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ARCHIVE_RANDOM_PRIVATE_H_INCLUDED
+#define ARCHIVE_RANDOM_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+/* Random number generator. */
+int archive_random(void *buf, size_t nbytes);
+
+#endif /* ARCHIVE_RANDOM_PRIVATE_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_rb.c b/contrib/libs/libarchive/libarchive/archive_rb.c
new file mode 100644
index 00000000000..cf58ac3354d
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_rb.c
@@ -0,0 +1,709 @@
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Matt Thomas <matt@3am-software.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Based on: NetBSD: rb.c,v 1.6 2010/04/30 13:58:09 joerg Exp
+ */
+
+#include "archive_platform.h"
+
+#include <stddef.h>
+
+#include "archive_rb.h"
+
+/* Keep in sync with archive_rb.h */
+#define RB_DIR_LEFT 0
+#define RB_DIR_RIGHT 1
+#define RB_DIR_OTHER 1
+#define rb_left rb_nodes[RB_DIR_LEFT]
+#define rb_right rb_nodes[RB_DIR_RIGHT]
+
+#define RB_FLAG_POSITION 0x2
+#define RB_FLAG_RED 0x1
+#define RB_FLAG_MASK (RB_FLAG_POSITION|RB_FLAG_RED)
+#define RB_FATHER(rb) \
+ ((struct archive_rb_node *)((rb)->rb_info & ~RB_FLAG_MASK))
+#define RB_SET_FATHER(rb, father) \
+ ((void)((rb)->rb_info = (uintptr_t)(father)|((rb)->rb_info & RB_FLAG_MASK)))
+
+#define RB_SENTINEL_P(rb) ((rb) == NULL)
+#define RB_LEFT_SENTINEL_P(rb) RB_SENTINEL_P((rb)->rb_left)
+#define RB_RIGHT_SENTINEL_P(rb) RB_SENTINEL_P((rb)->rb_right)
+#define RB_FATHER_SENTINEL_P(rb) RB_SENTINEL_P(RB_FATHER((rb)))
+#define RB_CHILDLESS_P(rb) \
+ (RB_SENTINEL_P(rb) || (RB_LEFT_SENTINEL_P(rb) && RB_RIGHT_SENTINEL_P(rb)))
+#define RB_TWOCHILDREN_P(rb) \
+ (!RB_SENTINEL_P(rb) && !RB_LEFT_SENTINEL_P(rb) && !RB_RIGHT_SENTINEL_P(rb))
+
+#define RB_POSITION(rb) \
+ (((rb)->rb_info & RB_FLAG_POSITION) ? RB_DIR_RIGHT : RB_DIR_LEFT)
+#define RB_RIGHT_P(rb) (RB_POSITION(rb) == RB_DIR_RIGHT)
+#define RB_LEFT_P(rb) (RB_POSITION(rb) == RB_DIR_LEFT)
+#define RB_RED_P(rb) (!RB_SENTINEL_P(rb) && ((rb)->rb_info & RB_FLAG_RED) != 0)
+#define RB_BLACK_P(rb) (RB_SENTINEL_P(rb) || ((rb)->rb_info & RB_FLAG_RED) == 0)
+#define RB_MARK_RED(rb) ((void)((rb)->rb_info |= RB_FLAG_RED))
+#define RB_MARK_BLACK(rb) ((void)((rb)->rb_info &= ~RB_FLAG_RED))
+#define RB_INVERT_COLOR(rb) ((void)((rb)->rb_info ^= RB_FLAG_RED))
+#define RB_ROOT_P(rbt, rb) ((rbt)->rbt_root == (rb))
+#define RB_SET_POSITION(rb, position) \
+ ((void)((position) ? ((rb)->rb_info |= RB_FLAG_POSITION) : \
+ ((rb)->rb_info &= ~RB_FLAG_POSITION)))
+#define RB_ZERO_PROPERTIES(rb) ((void)((rb)->rb_info &= ~RB_FLAG_MASK))
+#define RB_COPY_PROPERTIES(dst, src) \
+ ((void)((dst)->rb_info ^= ((dst)->rb_info ^ (src)->rb_info) & RB_FLAG_MASK))
+#define RB_SWAP_PROPERTIES(a, b) do { \
+ uintptr_t xorinfo = ((a)->rb_info ^ (b)->rb_info) & RB_FLAG_MASK; \
+ (a)->rb_info ^= xorinfo; \
+ (b)->rb_info ^= xorinfo; \
+ } while (/*CONSTCOND*/ 0)
+
+static void __archive_rb_tree_insert_rebalance(struct archive_rb_tree *,
+ struct archive_rb_node *);
+static void __archive_rb_tree_removal_rebalance(struct archive_rb_tree *,
+ struct archive_rb_node *, unsigned int);
+
+#define RB_SENTINEL_NODE NULL
+
+#define T 1
+#define F 0
+
+void
+__archive_rb_tree_init(struct archive_rb_tree *rbt,
+ const struct archive_rb_tree_ops *ops)
+{
+ rbt->rbt_ops = ops;
+ *((struct archive_rb_node **)&rbt->rbt_root) = RB_SENTINEL_NODE;
+}
+
+struct archive_rb_node *
+__archive_rb_tree_find_node(struct archive_rb_tree *rbt, const void *key)
+{
+ archive_rbto_compare_key_fn compare_key = rbt->rbt_ops->rbto_compare_key;
+ struct archive_rb_node *parent = rbt->rbt_root;
+
+ while (!RB_SENTINEL_P(parent)) {
+ const signed int diff = (*compare_key)(parent, key);
+ if (diff == 0)
+ return parent;
+ parent = parent->rb_nodes[diff > 0];
+ }
+
+ return NULL;
+}
+
+struct archive_rb_node *
+__archive_rb_tree_find_node_geq(struct archive_rb_tree *rbt, const void *key)
+{
+ archive_rbto_compare_key_fn compare_key = rbt->rbt_ops->rbto_compare_key;
+ struct archive_rb_node *parent = rbt->rbt_root;
+ struct archive_rb_node *last = NULL;
+
+ while (!RB_SENTINEL_P(parent)) {
+ const signed int diff = (*compare_key)(parent, key);
+ if (diff == 0)
+ return parent;
+ if (diff < 0)
+ last = parent;
+ parent = parent->rb_nodes[diff > 0];
+ }
+
+ return last;
+}
+
+struct archive_rb_node *
+__archive_rb_tree_find_node_leq(struct archive_rb_tree *rbt, const void *key)
+{
+ archive_rbto_compare_key_fn compare_key = rbt->rbt_ops->rbto_compare_key;
+ struct archive_rb_node *parent = rbt->rbt_root;
+ struct archive_rb_node *last = NULL;
+
+ while (!RB_SENTINEL_P(parent)) {
+ const signed int diff = (*compare_key)(parent, key);
+ if (diff == 0)
+ return parent;
+ if (diff > 0)
+ last = parent;
+ parent = parent->rb_nodes[diff > 0];
+ }
+
+ return last;
+}
+
+int
+__archive_rb_tree_insert_node(struct archive_rb_tree *rbt,
+ struct archive_rb_node *self)
+{
+ archive_rbto_compare_nodes_fn compare_nodes = rbt->rbt_ops->rbto_compare_nodes;
+ struct archive_rb_node *parent, *tmp;
+ unsigned int position;
+ int rebalance;
+
+ tmp = rbt->rbt_root;
+ /*
+ * This is a hack. Because rbt->rbt_root is just a
+ * struct archive_rb_node *, just like rb_node->rb_nodes[RB_DIR_LEFT],
+ * we can use this fact to avoid a lot of tests for root and know
+ * that even at root, updating
+ * RB_FATHER(rb_node)->rb_nodes[RB_POSITION(rb_node)] will
+ * update rbt->rbt_root.
+ */
+ parent = (struct archive_rb_node *)(void *)&rbt->rbt_root;
+ position = RB_DIR_LEFT;
+
+ /*
+ * Find out where to place this new leaf.
+ */
+ while (!RB_SENTINEL_P(tmp)) {
+ const signed int diff = (*compare_nodes)(tmp, self);
+ if (diff == 0) {
+ /*
+ * Node already exists; don't insert.
+ */
+ return F;
+ }
+ parent = tmp;
+ position = (diff > 0);
+ tmp = parent->rb_nodes[position];
+ }
+
+ /*
+ * Initialize the node and insert as a leaf into the tree.
+ */
+ RB_SET_FATHER(self, parent);
+ RB_SET_POSITION(self, position);
+ if (parent == (struct archive_rb_node *)(void *)&rbt->rbt_root) {
+ RB_MARK_BLACK(self); /* root is always black */
+ rebalance = F;
+ } else {
+ /*
+ * All new nodes are colored red. We only need to rebalance
+ * if our parent is also red.
+ */
+ RB_MARK_RED(self);
+ rebalance = RB_RED_P(parent);
+ }
+ self->rb_left = parent->rb_nodes[position];
+ self->rb_right = parent->rb_nodes[position];
+ parent->rb_nodes[position] = self;
+
+ /*
+ * Rebalance tree after insertion
+ */
+ if (rebalance)
+ __archive_rb_tree_insert_rebalance(rbt, self);
+
+ return T;
+}
+
+/*
+ * Swap the location and colors of 'self' and its child @ which. The child
+ * can not be a sentinel node. This is our rotation function. However,
+ * since it preserves coloring, it great simplifies both insertion and
+ * removal since rotation almost always involves the exchanging of colors
+ * as a separate step.
+ */
+/*ARGSUSED*/
+static void
+__archive_rb_tree_reparent_nodes(
+ struct archive_rb_node *old_father, const unsigned int which)
+{
+ const unsigned int other = which ^ RB_DIR_OTHER;
+ struct archive_rb_node * const grandpa = RB_FATHER(old_father);
+ struct archive_rb_node * const old_child = old_father->rb_nodes[which];
+ struct archive_rb_node * const new_father = old_child;
+ struct archive_rb_node * const new_child = old_father;
+
+ if (new_father == NULL)
+ return;
+ /*
+ * Exchange descendant linkages.
+ */
+ grandpa->rb_nodes[RB_POSITION(old_father)] = new_father;
+ new_child->rb_nodes[which] = old_child->rb_nodes[other];
+ new_father->rb_nodes[other] = new_child;
+
+ /*
+ * Update ancestor linkages
+ */
+ RB_SET_FATHER(new_father, grandpa);
+ RB_SET_FATHER(new_child, new_father);
+
+ /*
+ * Exchange properties between new_father and new_child. The only
+ * change is that new_child's position is now on the other side.
+ */
+ RB_SWAP_PROPERTIES(new_father, new_child);
+ RB_SET_POSITION(new_child, other);
+
+ /*
+ * Make sure to reparent the new child to ourself.
+ */
+ if (!RB_SENTINEL_P(new_child->rb_nodes[which])) {
+ RB_SET_FATHER(new_child->rb_nodes[which], new_child);
+ RB_SET_POSITION(new_child->rb_nodes[which], which);
+ }
+
+}
+
+static void
+__archive_rb_tree_insert_rebalance(struct archive_rb_tree *rbt,
+ struct archive_rb_node *self)
+{
+ struct archive_rb_node * father = RB_FATHER(self);
+ struct archive_rb_node * grandpa;
+ struct archive_rb_node * uncle;
+ unsigned int which;
+ unsigned int other;
+
+ for (;;) {
+ /*
+ * We are red and our parent is red, therefore we must have a
+ * grandfather and he must be black.
+ */
+ grandpa = RB_FATHER(father);
+ which = (father == grandpa->rb_right);
+ other = which ^ RB_DIR_OTHER;
+ uncle = grandpa->rb_nodes[other];
+
+ if (RB_BLACK_P(uncle))
+ break;
+
+ /*
+ * Case 1: our uncle is red
+ * Simply invert the colors of our parent and
+ * uncle and make our grandparent red. And
+ * then solve the problem up at his level.
+ */
+ RB_MARK_BLACK(uncle);
+ RB_MARK_BLACK(father);
+ if (RB_ROOT_P(rbt, grandpa)) {
+ /*
+ * If our grandpa is root, don't bother
+ * setting him to red, just return.
+ */
+ return;
+ }
+ RB_MARK_RED(grandpa);
+ self = grandpa;
+ father = RB_FATHER(self);
+ if (RB_BLACK_P(father)) {
+ /*
+ * If our great-grandpa is black, we're done.
+ */
+ return;
+ }
+ }
+
+ /*
+ * Case 2&3: our uncle is black.
+ */
+ if (self == father->rb_nodes[other]) {
+ /*
+ * Case 2: we are on the same side as our uncle
+ * Swap ourselves with our parent so this case
+ * becomes case 3. Basically our parent becomes our
+ * child.
+ */
+ __archive_rb_tree_reparent_nodes(father, other);
+ }
+ /*
+ * Case 3: we are opposite a child of a black uncle.
+ * Swap our parent and grandparent. Since our grandfather
+ * is black, our father will become black and our new sibling
+ * (former grandparent) will become red.
+ */
+ __archive_rb_tree_reparent_nodes(grandpa, which);
+
+ /*
+ * Final step: Set the root to black.
+ */
+ RB_MARK_BLACK(rbt->rbt_root);
+}
+
+static void
+__archive_rb_tree_prune_node(struct archive_rb_tree *rbt,
+ struct archive_rb_node *self, int rebalance)
+{
+ const unsigned int which = RB_POSITION(self);
+ struct archive_rb_node *father = RB_FATHER(self);
+
+ /*
+ * Since we are childless, we know that self->rb_left is pointing
+ * to the sentinel node.
+ */
+ father->rb_nodes[which] = self->rb_left;
+
+ /*
+ * Rebalance if requested.
+ */
+ if (rebalance)
+ __archive_rb_tree_removal_rebalance(rbt, father, which);
+}
+
+/*
+ * When deleting an interior node
+ */
+static void
+__archive_rb_tree_swap_prune_and_rebalance(struct archive_rb_tree *rbt,
+ struct archive_rb_node *self, struct archive_rb_node *standin)
+{
+ const unsigned int standin_which = RB_POSITION(standin);
+ unsigned int standin_other = standin_which ^ RB_DIR_OTHER;
+ struct archive_rb_node *standin_son;
+ struct archive_rb_node *standin_father = RB_FATHER(standin);
+ int rebalance = RB_BLACK_P(standin);
+
+ if (standin_father == self) {
+ /*
+ * As a child of self, any children would be opposite of
+ * our parent.
+ */
+ standin_son = standin->rb_nodes[standin_which];
+ } else {
+ /*
+ * Since we aren't a child of self, any children would be
+ * on the same side as our parent.
+ */
+ standin_son = standin->rb_nodes[standin_other];
+ }
+
+ if (RB_RED_P(standin_son)) {
+ /*
+ * We know we have a red child so if we flip it to black
+ * we don't have to rebalance.
+ */
+ RB_MARK_BLACK(standin_son);
+ rebalance = F;
+
+ if (standin_father != self) {
+ /*
+ * Change the son's parentage to point to his grandpa.
+ */
+ RB_SET_FATHER(standin_son, standin_father);
+ RB_SET_POSITION(standin_son, standin_which);
+ }
+ }
+
+ if (standin_father == self) {
+ /*
+ * If we are about to delete the standin's father, then when
+ * we call rebalance, we need to use ourselves as our father.
+ * Otherwise remember our original father. Also, since we are
+ * our standin's father we only need to reparent the standin's
+ * brother.
+ *
+ * | R --> S |
+ * | Q S --> Q T |
+ * | t --> |
+ *
+ * Have our son/standin adopt his brother as his new son.
+ */
+ standin_father = standin;
+ } else {
+ /*
+ * | R --> S . |
+ * | / \ | T --> / \ | / |
+ * | ..... | S --> ..... | T |
+ *
+ * Sever standin's connection to his father.
+ */
+ standin_father->rb_nodes[standin_which] = standin_son;
+ /*
+ * Adopt the far son.
+ */
+ standin->rb_nodes[standin_other] = self->rb_nodes[standin_other];
+ RB_SET_FATHER(standin->rb_nodes[standin_other], standin);
+ /*
+ * Use standin_other because we need to preserve standin_which
+ * for the removal_rebalance.
+ */
+ standin_other = standin_which;
+ }
+
+ /*
+ * Move the only remaining son to our standin. If our standin is our
+ * son, this will be the only son needed to be moved.
+ */
+ standin->rb_nodes[standin_other] = self->rb_nodes[standin_other];
+ RB_SET_FATHER(standin->rb_nodes[standin_other], standin);
+
+ /*
+ * Now copy the result of self to standin and then replace
+ * self with standin in the tree.
+ */
+ RB_COPY_PROPERTIES(standin, self);
+ RB_SET_FATHER(standin, RB_FATHER(self));
+ RB_FATHER(standin)->rb_nodes[RB_POSITION(standin)] = standin;
+
+ if (rebalance)
+ __archive_rb_tree_removal_rebalance(rbt, standin_father, standin_which);
+}
+
+/*
+ * We could do this by doing
+ * __archive_rb_tree_node_swap(rbt, self, which);
+ * __archive_rb_tree_prune_node(rbt, self, F);
+ *
+ * But it's more efficient to just evaluate and recolor the child.
+ */
+static void
+__archive_rb_tree_prune_blackred_branch(
+ struct archive_rb_node *self, unsigned int which)
+{
+ struct archive_rb_node *father = RB_FATHER(self);
+ struct archive_rb_node *son = self->rb_nodes[which];
+
+ /*
+ * Remove ourselves from the tree and give our former child our
+ * properties (position, color, root).
+ */
+ RB_COPY_PROPERTIES(son, self);
+ father->rb_nodes[RB_POSITION(son)] = son;
+ RB_SET_FATHER(son, father);
+}
+/*
+ *
+ */
+void
+__archive_rb_tree_remove_node(struct archive_rb_tree *rbt,
+ struct archive_rb_node *self)
+{
+ struct archive_rb_node *standin;
+ unsigned int which;
+
+ /*
+ * In the following diagrams, we (the node to be removed) are S. Red
+ * nodes are lowercase. T could be either red or black.
+ *
+ * Remember the major axiom of the red-black tree: the number of
+ * black nodes from the root to each leaf is constant across all
+ * leaves, only the number of red nodes varies.
+ *
+ * Thus removing a red leaf doesn't require any other changes to a
+ * red-black tree. So if we must remove a node, attempt to rearrange
+ * the tree so we can remove a red node.
+ *
+ * The simplest case is a childless red node or a childless root node:
+ *
+ * | T --> T | or | R --> * |
+ * | s --> * |
+ */
+ if (RB_CHILDLESS_P(self)) {
+ const int rebalance = RB_BLACK_P(self) && !RB_ROOT_P(rbt, self);
+ __archive_rb_tree_prune_node(rbt, self, rebalance);
+ return;
+ }
+ if (!RB_TWOCHILDREN_P(self)) {
+ /*
+ * The next simplest case is the node we are deleting is
+ * black and has one red child.
+ *
+ * | T --> T --> T |
+ * | S --> R --> R |
+ * | r --> s --> * |
+ */
+ which = RB_LEFT_SENTINEL_P(self) ? RB_DIR_RIGHT : RB_DIR_LEFT;
+ __archive_rb_tree_prune_blackred_branch(self, which);
+ return;
+ }
+
+ /*
+ * We invert these because we prefer to remove from the inside of
+ * the tree.
+ */
+ which = RB_POSITION(self) ^ RB_DIR_OTHER;
+
+ /*
+ * Let's find the node closes to us opposite of our parent
+ * Now swap it with ourself, "prune" it, and rebalance, if needed.
+ */
+ standin = __archive_rb_tree_iterate(rbt, self, which);
+ __archive_rb_tree_swap_prune_and_rebalance(rbt, self, standin);
+}
+
+static void
+__archive_rb_tree_removal_rebalance(struct archive_rb_tree *rbt,
+ struct archive_rb_node *parent, unsigned int which)
+{
+
+ while (RB_BLACK_P(parent->rb_nodes[which])) {
+ unsigned int other = which ^ RB_DIR_OTHER;
+ struct archive_rb_node *brother = parent->rb_nodes[other];
+
+ if (brother == NULL)
+ return;/* The tree may be broken. */
+ /*
+ * For cases 1, 2a, and 2b, our brother's children must
+ * be black and our father must be black
+ */
+ if (RB_BLACK_P(parent)
+ && RB_BLACK_P(brother->rb_left)
+ && RB_BLACK_P(brother->rb_right)) {
+ if (RB_RED_P(brother)) {
+ /*
+ * Case 1: Our brother is red, swap its
+ * position (and colors) with our parent.
+ * This should now be case 2b (unless C or E
+ * has a red child which is case 3; thus no
+ * explicit branch to case 2b).
+ *
+ * B -> D
+ * A d -> b E
+ * C E -> A C
+ */
+ __archive_rb_tree_reparent_nodes(parent, other);
+ brother = parent->rb_nodes[other];
+ if (brother == NULL)
+ return;/* The tree may be broken. */
+ } else {
+ /*
+ * Both our parent and brother are black.
+ * Change our brother to red, advance up rank
+ * and go through the loop again.
+ *
+ * B -> *B
+ * *A D -> A d
+ * C E -> C E
+ */
+ RB_MARK_RED(brother);
+ if (RB_ROOT_P(rbt, parent))
+ return; /* root == parent == black */
+ which = RB_POSITION(parent);
+ parent = RB_FATHER(parent);
+ continue;
+ }
+ }
+ /*
+ * Avoid an else here so that case 2a above can hit either
+ * case 2b, 3, or 4.
+ */
+ if (RB_RED_P(parent)
+ && RB_BLACK_P(brother)
+ && RB_BLACK_P(brother->rb_left)
+ && RB_BLACK_P(brother->rb_right)) {
+ /*
+ * We are black, our father is red, our brother and
+ * both nephews are black. Simply invert/exchange the
+ * colors of our father and brother (to black and red
+ * respectively).
+ *
+ * | f --> F |
+ * | * B --> * b |
+ * | N N --> N N |
+ */
+ RB_MARK_BLACK(parent);
+ RB_MARK_RED(brother);
+ break; /* We're done! */
+ } else {
+ /*
+ * Our brother must be black and have at least one
+ * red child (it may have two).
+ */
+ if (RB_BLACK_P(brother->rb_nodes[other])) {
+ /*
+ * Case 3: our brother is black, our near
+ * nephew is red, and our far nephew is black.
+ * Swap our brother with our near nephew.
+ * This result in a tree that matches case 4.
+ * (Our father could be red or black).
+ *
+ * | F --> F |
+ * | x B --> x B |
+ * | n --> n |
+ */
+ __archive_rb_tree_reparent_nodes(brother, which);
+ brother = parent->rb_nodes[other];
+ }
+ /*
+ * Case 4: our brother is black and our far nephew
+ * is red. Swap our father and brother locations and
+ * change our far nephew to black. (these can be
+ * done in either order so we change the color first).
+ * The result is a valid red-black tree and is a
+ * terminal case. (again we don't care about the
+ * father's color)
+ *
+ * If the father is red, we will get a red-black-black
+ * tree:
+ * | f -> f --> b |
+ * | B -> B --> F N |
+ * | n -> N --> |
+ *
+ * If the father is black, we will get an all black
+ * tree:
+ * | F -> F --> B |
+ * | B -> B --> F N |
+ * | n -> N --> |
+ *
+ * If we had two red nephews, then after the swap,
+ * our former father would have a red grandson.
+ */
+ if (brother->rb_nodes[other] == NULL)
+ return;/* The tree may be broken. */
+ RB_MARK_BLACK(brother->rb_nodes[other]);
+ __archive_rb_tree_reparent_nodes(parent, other);
+ break; /* We're done! */
+ }
+ }
+}
+
+struct archive_rb_node *
+__archive_rb_tree_iterate(struct archive_rb_tree *rbt,
+ struct archive_rb_node *self, const unsigned int direction)
+{
+ const unsigned int other = direction ^ RB_DIR_OTHER;
+
+ if (self == NULL) {
+ self = rbt->rbt_root;
+ if (RB_SENTINEL_P(self))
+ return NULL;
+ while (!RB_SENTINEL_P(self->rb_nodes[direction]))
+ self = self->rb_nodes[direction];
+ return self;
+ }
+ /*
+ * We can't go any further in this direction. We proceed up in the
+ * opposite direction until our parent is in direction we want to go.
+ */
+ if (RB_SENTINEL_P(self->rb_nodes[direction])) {
+ while (!RB_ROOT_P(rbt, self)) {
+ if (other == (unsigned int)RB_POSITION(self))
+ return RB_FATHER(self);
+ self = RB_FATHER(self);
+ }
+ return NULL;
+ }
+
+ /*
+ * Advance down one in current direction and go down as far as possible
+ * in the opposite direction.
+ */
+ self = self->rb_nodes[direction];
+ while (!RB_SENTINEL_P(self->rb_nodes[other]))
+ self = self->rb_nodes[other];
+ return self;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_rb.h b/contrib/libs/libarchive/libarchive/archive_rb.h
new file mode 100644
index 00000000000..8851f108186
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_rb.h
@@ -0,0 +1,113 @@
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Matt Thomas <matt@3am-software.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Based on NetBSD: rb.h,v 1.13 2009/08/16 10:57:01 yamt Exp
+ */
+
+#ifndef ARCHIVE_RB_H_INCLUDED
+#define ARCHIVE_RB_H_INCLUDED
+
+struct archive_rb_node {
+ struct archive_rb_node *rb_nodes[2];
+ /*
+ * rb_info contains the two flags and the parent back pointer.
+ * We put the two flags in the low two bits since we know that
+ * rb_node will have an alignment of 4 or 8 bytes.
+ */
+ uintptr_t rb_info;
+};
+
+#define ARCHIVE_RB_DIR_LEFT 0
+#define ARCHIVE_RB_DIR_RIGHT 1
+
+#define ARCHIVE_RB_TREE_MIN(T) \
+ __archive_rb_tree_iterate((T), NULL, ARCHIVE_RB_DIR_LEFT)
+#define ARCHIVE_RB_TREE_MAX(T) \
+ __archive_rb_tree_iterate((T), NULL, ARCHIVE_RB_DIR_RIGHT)
+#define ARCHIVE_RB_TREE_NEXT(T, N) \
+ __archive_rb_tree_iterate((T), (N), ARCHIVE_RB_DIR_RIGHT)
+#define ARCHIVE_RB_TREE_PREV(T, N) \
+ __archive_rb_tree_iterate((T), (N), ARCHIVE_RB_DIR_LEFT)
+#define ARCHIVE_RB_TREE_FOREACH(N, T) \
+ for ((N) = ARCHIVE_RB_TREE_MIN(T); (N); \
+ (N) = ARCHIVE_RB_TREE_NEXT((T), (N)))
+#define ARCHIVE_RB_TREE_FOREACH_REVERSE(N, T) \
+ for ((N) = ARCHIVE_RB_TREE_MAX(T); (N); \
+ (N) = ARCHIVE_RB_TREE_PREV((T), (N)))
+#define ARCHIVE_RB_TREE_FOREACH_SAFE(N, T, S) \
+ for ((N) = ARCHIVE_RB_TREE_MIN(T); \
+ (N) && ((S) = ARCHIVE_RB_TREE_NEXT((T), (N)), 1); \
+ (N) = (S))
+#define ARCHIVE_RB_TREE_FOREACH_REVERSE_SAFE(N, T, S) \
+ for ((N) = ARCHIVE_RB_TREE_MAX(T); \
+ (N) && ((S) = ARCHIVE_RB_TREE_PREV((T), (N)), 1); \
+ (N) = (S))
+
+/*
+ * archive_rbto_compare_nodes_fn:
+ * return a positive value if the first node < the second node.
+ * return a negative value if the first node > the second node.
+ * return 0 if they are considered same.
+ *
+ * archive_rbto_compare_key_fn:
+ * return a positive value if the node < the key.
+ * return a negative value if the node > the key.
+ * return 0 if they are considered same.
+ */
+
+typedef signed int (*const archive_rbto_compare_nodes_fn)(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+typedef signed int (*const archive_rbto_compare_key_fn)(const struct archive_rb_node *,
+ const void *);
+
+struct archive_rb_tree_ops {
+ archive_rbto_compare_nodes_fn rbto_compare_nodes;
+ archive_rbto_compare_key_fn rbto_compare_key;
+};
+
+struct archive_rb_tree {
+ struct archive_rb_node *rbt_root;
+ const struct archive_rb_tree_ops *rbt_ops;
+};
+
+void __archive_rb_tree_init(struct archive_rb_tree *,
+ const struct archive_rb_tree_ops *);
+int __archive_rb_tree_insert_node(struct archive_rb_tree *,
+ struct archive_rb_node *);
+struct archive_rb_node *
+ __archive_rb_tree_find_node(struct archive_rb_tree *, const void *);
+struct archive_rb_node *
+ __archive_rb_tree_find_node_geq(struct archive_rb_tree *, const void *);
+struct archive_rb_node *
+ __archive_rb_tree_find_node_leq(struct archive_rb_tree *, const void *);
+void __archive_rb_tree_remove_node(struct archive_rb_tree *, struct archive_rb_node *);
+struct archive_rb_node *
+ __archive_rb_tree_iterate(struct archive_rb_tree *,
+ struct archive_rb_node *, const unsigned int);
+
+#endif /* ARCHIVE_RB_H_*/
diff --git a/contrib/libs/libarchive/libarchive/archive_read.c b/contrib/libs/libarchive/libarchive/archive_read.c
new file mode 100644
index 00000000000..45a38aed02b
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read.c
@@ -0,0 +1,1756 @@
+/*-
+ * Copyright (c) 2003-2011 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This file contains the "essential" portions of the read API, that
+ * is, stuff that will probably always be used by any client that
+ * actually needs to read an archive. Optional pieces have been, as
+ * far as possible, separated out into separate files to avoid
+ * needlessly bloating statically-linked clients.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read.c 201157 2009-12-29 05:30:23Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#define minimum(a, b) (a < b ? a : b)
+
+static int choose_filters(struct archive_read *);
+static int choose_format(struct archive_read *);
+static int close_filters(struct archive_read *);
+static int64_t _archive_filter_bytes(struct archive *, int);
+static int _archive_filter_code(struct archive *, int);
+static const char *_archive_filter_name(struct archive *, int);
+static int _archive_filter_count(struct archive *);
+static int _archive_read_close(struct archive *);
+static int _archive_read_data_block(struct archive *,
+ const void **, size_t *, int64_t *);
+static int _archive_read_free(struct archive *);
+static int _archive_read_next_header(struct archive *,
+ struct archive_entry **);
+static int _archive_read_next_header2(struct archive *,
+ struct archive_entry *);
+static int64_t advance_file_pointer(struct archive_read_filter *, int64_t);
+
+static const struct archive_vtable
+archive_read_vtable = {
+ .archive_filter_bytes = _archive_filter_bytes,
+ .archive_filter_code = _archive_filter_code,
+ .archive_filter_name = _archive_filter_name,
+ .archive_filter_count = _archive_filter_count,
+ .archive_read_data_block = _archive_read_data_block,
+ .archive_read_next_header = _archive_read_next_header,
+ .archive_read_next_header2 = _archive_read_next_header2,
+ .archive_free = _archive_read_free,
+ .archive_close = _archive_read_close,
+};
+
+/*
+ * Allocate, initialize and return a struct archive object.
+ */
+struct archive *
+archive_read_new(void)
+{
+ struct archive_read *a;
+
+ a = (struct archive_read *)calloc(1, sizeof(*a));
+ if (a == NULL)
+ return (NULL);
+ a->archive.magic = ARCHIVE_READ_MAGIC;
+
+ a->archive.state = ARCHIVE_STATE_NEW;
+ a->entry = archive_entry_new2(&a->archive);
+ a->archive.vtable = &archive_read_vtable;
+
+ a->passphrases.last = &a->passphrases.first;
+
+ return (&a->archive);
+}
+
+/*
+ * Record the do-not-extract-to file. This belongs in archive_read_extract.c.
+ */
+void
+archive_read_extract_set_skip_file(struct archive *_a, la_int64_t d,
+ la_int64_t i)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_extract_set_skip_file"))
+ return;
+ a->skip_file_set = 1;
+ a->skip_file_dev = d;
+ a->skip_file_ino = i;
+}
+
+/*
+ * Open the archive
+ */
+int
+archive_read_open(struct archive *a, void *client_data,
+ archive_open_callback *client_opener, archive_read_callback *client_reader,
+ archive_close_callback *client_closer)
+{
+ /* Old archive_read_open() is just a thin shell around
+ * archive_read_open1. */
+ archive_read_set_open_callback(a, client_opener);
+ archive_read_set_read_callback(a, client_reader);
+ archive_read_set_close_callback(a, client_closer);
+ archive_read_set_callback_data(a, client_data);
+ return archive_read_open1(a);
+}
+
+
+int
+archive_read_open2(struct archive *a, void *client_data,
+ archive_open_callback *client_opener,
+ archive_read_callback *client_reader,
+ archive_skip_callback *client_skipper,
+ archive_close_callback *client_closer)
+{
+ /* Old archive_read_open2() is just a thin shell around
+ * archive_read_open1. */
+ archive_read_set_callback_data(a, client_data);
+ archive_read_set_open_callback(a, client_opener);
+ archive_read_set_read_callback(a, client_reader);
+ archive_read_set_skip_callback(a, client_skipper);
+ archive_read_set_close_callback(a, client_closer);
+ return archive_read_open1(a);
+}
+
+static ssize_t
+client_read_proxy(struct archive_read_filter *self, const void **buff)
+{
+ ssize_t r;
+ r = (self->archive->client.reader)(&self->archive->archive,
+ self->data, buff);
+ return (r);
+}
+
+static int64_t
+client_skip_proxy(struct archive_read_filter *self, int64_t request)
+{
+ if (request < 0)
+ __archive_errx(1, "Negative skip requested.");
+ if (request == 0)
+ return 0;
+
+ if (self->archive->client.skipper != NULL) {
+ /* Seek requests over 1GiB are broken down into
+ * multiple seeks. This avoids overflows when the
+ * requests get passed through 32-bit arguments. */
+ int64_t skip_limit = (int64_t)1 << 30;
+ int64_t total = 0;
+ for (;;) {
+ int64_t get, ask = request;
+ if (ask > skip_limit)
+ ask = skip_limit;
+ get = (self->archive->client.skipper)
+ (&self->archive->archive, self->data, ask);
+ total += get;
+ if (get == 0 || get == request)
+ return (total);
+ if (get > request)
+ return ARCHIVE_FATAL;
+ request -= get;
+ }
+ } else if (self->archive->client.seeker != NULL
+ && request > 64 * 1024) {
+ /* If the client provided a seeker but not a skipper,
+ * we can use the seeker to skip forward.
+ *
+ * Note: This isn't always a good idea. The client
+ * skipper is allowed to skip by less than requested
+ * if it needs to maintain block alignment. The
+ * seeker is not allowed to play such games, so using
+ * the seeker here may be a performance loss compared
+ * to just reading and discarding. That's why we
+ * only do this for skips of over 64k.
+ */
+ int64_t before = self->position;
+ int64_t after = (self->archive->client.seeker)
+ (&self->archive->archive, self->data, request, SEEK_CUR);
+ if (after != before + request)
+ return ARCHIVE_FATAL;
+ return after - before;
+ }
+ return 0;
+}
+
+static int64_t
+client_seek_proxy(struct archive_read_filter *self, int64_t offset, int whence)
+{
+ /* DO NOT use the skipper here! If we transparently handled
+ * forward seek here by using the skipper, that will break
+ * other libarchive code that assumes a successful forward
+ * seek means it can also seek backwards.
+ */
+ if (self->archive->client.seeker == NULL) {
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "Current client reader does not support seeking a device");
+ return (ARCHIVE_FAILED);
+ }
+ return (self->archive->client.seeker)(&self->archive->archive,
+ self->data, offset, whence);
+}
+
+static int
+read_client_close_proxy(struct archive_read *a)
+{
+ int r = ARCHIVE_OK, r2;
+ unsigned int i;
+
+ if (a->client.closer == NULL)
+ return (r);
+ for (i = 0; i < a->client.nodes; i++)
+ {
+ r2 = (a->client.closer)
+ ((struct archive *)a, a->client.dataset[i].data);
+ if (r > r2)
+ r = r2;
+ }
+ return (r);
+}
+
+static int
+client_close_proxy(struct archive_read_filter *self)
+{
+ return read_client_close_proxy(self->archive);
+}
+
+static int
+client_open_proxy(struct archive_read_filter *self)
+{
+ int r = ARCHIVE_OK;
+ if (self->archive->client.opener != NULL)
+ r = (self->archive->client.opener)(
+ (struct archive *)self->archive, self->data);
+ return (r);
+}
+
+static int
+client_switch_proxy(struct archive_read_filter *self, unsigned int iindex)
+{
+ int r1 = ARCHIVE_OK, r2 = ARCHIVE_OK;
+ void *data2 = NULL;
+
+ /* Don't do anything if already in the specified data node */
+ if (self->archive->client.cursor == iindex)
+ return (ARCHIVE_OK);
+
+ self->archive->client.cursor = iindex;
+ data2 = self->archive->client.dataset[self->archive->client.cursor].data;
+ if (self->archive->client.switcher != NULL)
+ {
+ r1 = r2 = (self->archive->client.switcher)
+ ((struct archive *)self->archive, self->data, data2);
+ self->data = data2;
+ }
+ else
+ {
+ /* Attempt to call close and open instead */
+ if (self->archive->client.closer != NULL)
+ r1 = (self->archive->client.closer)
+ ((struct archive *)self->archive, self->data);
+ self->data = data2;
+ r2 = client_open_proxy(self);
+ }
+ return (r1 < r2) ? r1 : r2;
+}
+
+int
+archive_read_set_open_callback(struct archive *_a,
+ archive_open_callback *client_opener)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_set_open_callback");
+ a->client.opener = client_opener;
+ return ARCHIVE_OK;
+}
+
+int
+archive_read_set_read_callback(struct archive *_a,
+ archive_read_callback *client_reader)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_set_read_callback");
+ a->client.reader = client_reader;
+ return ARCHIVE_OK;
+}
+
+int
+archive_read_set_skip_callback(struct archive *_a,
+ archive_skip_callback *client_skipper)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_set_skip_callback");
+ a->client.skipper = client_skipper;
+ return ARCHIVE_OK;
+}
+
+int
+archive_read_set_seek_callback(struct archive *_a,
+ archive_seek_callback *client_seeker)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_set_seek_callback");
+ a->client.seeker = client_seeker;
+ return ARCHIVE_OK;
+}
+
+int
+archive_read_set_close_callback(struct archive *_a,
+ archive_close_callback *client_closer)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_set_close_callback");
+ a->client.closer = client_closer;
+ return ARCHIVE_OK;
+}
+
+int
+archive_read_set_switch_callback(struct archive *_a,
+ archive_switch_callback *client_switcher)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_set_switch_callback");
+ a->client.switcher = client_switcher;
+ return ARCHIVE_OK;
+}
+
+int
+archive_read_set_callback_data(struct archive *_a, void *client_data)
+{
+ return archive_read_set_callback_data2(_a, client_data, 0);
+}
+
+int
+archive_read_set_callback_data2(struct archive *_a, void *client_data,
+ unsigned int iindex)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_set_callback_data2");
+
+ if (a->client.nodes == 0)
+ {
+ a->client.dataset = (struct archive_read_data_node *)
+ calloc(1, sizeof(*a->client.dataset));
+ if (a->client.dataset == NULL)
+ {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory.");
+ return ARCHIVE_FATAL;
+ }
+ a->client.nodes = 1;
+ }
+
+ if (iindex > a->client.nodes - 1)
+ {
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid index specified.");
+ return ARCHIVE_FATAL;
+ }
+ a->client.dataset[iindex].data = client_data;
+ a->client.dataset[iindex].begin_position = -1;
+ a->client.dataset[iindex].total_size = -1;
+ return ARCHIVE_OK;
+}
+
+int
+archive_read_add_callback_data(struct archive *_a, void *client_data,
+ unsigned int iindex)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ void *p;
+ unsigned int i;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_add_callback_data");
+ if (iindex > a->client.nodes) {
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid index specified.");
+ return ARCHIVE_FATAL;
+ }
+ p = realloc(a->client.dataset, sizeof(*a->client.dataset)
+ * (++(a->client.nodes)));
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory.");
+ return ARCHIVE_FATAL;
+ }
+ a->client.dataset = (struct archive_read_data_node *)p;
+ for (i = a->client.nodes - 1; i > iindex; i--) {
+ a->client.dataset[i].data = a->client.dataset[i-1].data;
+ a->client.dataset[i].begin_position = -1;
+ a->client.dataset[i].total_size = -1;
+ }
+ a->client.dataset[iindex].data = client_data;
+ a->client.dataset[iindex].begin_position = -1;
+ a->client.dataset[iindex].total_size = -1;
+ return ARCHIVE_OK;
+}
+
+int
+archive_read_append_callback_data(struct archive *_a, void *client_data)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ return archive_read_add_callback_data(_a, client_data, a->client.nodes);
+}
+
+int
+archive_read_prepend_callback_data(struct archive *_a, void *client_data)
+{
+ return archive_read_add_callback_data(_a, client_data, 0);
+}
+
+static const struct archive_read_filter_vtable
+none_reader_vtable = {
+ .read = client_read_proxy,
+ .close = client_close_proxy,
+};
+
+int
+archive_read_open1(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct archive_read_filter *filter, *tmp;
+ int slot, e = ARCHIVE_OK;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_open");
+ archive_clear_error(&a->archive);
+
+ if (a->client.reader == NULL) {
+ archive_set_error(&a->archive, EINVAL,
+ "No reader function provided to archive_read_open");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Open data source. */
+ if (a->client.opener != NULL) {
+ e = (a->client.opener)(&a->archive, a->client.dataset[0].data);
+ if (e != 0) {
+ /* If the open failed, call the closer to clean up. */
+ read_client_close_proxy(a);
+ return (e);
+ }
+ }
+
+ filter = calloc(1, sizeof(*filter));
+ if (filter == NULL)
+ return (ARCHIVE_FATAL);
+ filter->bidder = NULL;
+ filter->upstream = NULL;
+ filter->archive = a;
+ filter->data = a->client.dataset[0].data;
+ filter->vtable = &none_reader_vtable;
+ filter->name = "none";
+ filter->code = ARCHIVE_FILTER_NONE;
+ filter->can_skip = 1;
+ filter->can_seek = 1;
+
+ a->client.dataset[0].begin_position = 0;
+ if (!a->filter || !a->bypass_filter_bidding)
+ {
+ a->filter = filter;
+ /* Build out the input pipeline. */
+ e = choose_filters(a);
+ if (e < ARCHIVE_WARN) {
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ }
+ else
+ {
+ /* Need to add "NONE" type filter at the end of the filter chain */
+ tmp = a->filter;
+ while (tmp->upstream)
+ tmp = tmp->upstream;
+ tmp->upstream = filter;
+ }
+
+ if (!a->format)
+ {
+ slot = choose_format(a);
+ if (slot < 0) {
+ close_filters(a);
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ a->format = &(a->formats[slot]);
+ }
+
+ a->archive.state = ARCHIVE_STATE_HEADER;
+
+ /* Ensure libarchive starts from the first node in a multivolume set */
+ client_switch_proxy(a->filter, 0);
+ return (e);
+}
+
+/*
+ * Allow each registered stream transform to bid on whether
+ * it wants to handle this stream. Repeat until we've finished
+ * building the pipeline.
+ */
+
+/* We won't build a filter pipeline with more stages than this. */
+#define MAX_NUMBER_FILTERS 25
+
+static int
+choose_filters(struct archive_read *a)
+{
+ int number_bidders, i, bid, best_bid, number_filters;
+ struct archive_read_filter_bidder *bidder, *best_bidder;
+ struct archive_read_filter *filter;
+ ssize_t avail;
+ int r;
+
+ for (number_filters = 0; number_filters < MAX_NUMBER_FILTERS; ++number_filters) {
+ number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);
+
+ best_bid = 0;
+ best_bidder = NULL;
+
+ bidder = a->bidders;
+ for (i = 0; i < number_bidders; i++, bidder++) {
+ if (bidder->vtable == NULL)
+ continue;
+ bid = (bidder->vtable->bid)(bidder, a->filter);
+ if (bid > best_bid) {
+ best_bid = bid;
+ best_bidder = bidder;
+ }
+ }
+
+ /* If no bidder, we're done. */
+ if (best_bidder == NULL) {
+ /* Verify the filter by asking it for some data. */
+ __archive_read_filter_ahead(a->filter, 1, &avail);
+ if (avail < 0) {
+ __archive_read_free_filters(a);
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+ }
+
+ filter
+ = (struct archive_read_filter *)calloc(1, sizeof(*filter));
+ if (filter == NULL)
+ return (ARCHIVE_FATAL);
+ filter->bidder = best_bidder;
+ filter->archive = a;
+ filter->upstream = a->filter;
+ a->filter = filter;
+ r = (best_bidder->vtable->init)(a->filter);
+ if (r != ARCHIVE_OK) {
+ __archive_read_free_filters(a);
+ return (ARCHIVE_FATAL);
+ }
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Input requires too many filters for decoding");
+ return (ARCHIVE_FATAL);
+}
+
+int
+__archive_read_header(struct archive_read *a, struct archive_entry *entry)
+{
+ if (!a->filter->vtable->read_header)
+ return (ARCHIVE_OK);
+ return a->filter->vtable->read_header(a->filter, entry);
+}
+
+/*
+ * Read header of next entry.
+ */
+static int
+_archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ int r1 = ARCHIVE_OK, r2;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_read_next_header");
+
+ archive_entry_clear(entry);
+ archive_clear_error(&a->archive);
+
+ /*
+ * If client didn't consume entire data, skip any remainder
+ * (This is especially important for GNU incremental directories.)
+ */
+ if (a->archive.state == ARCHIVE_STATE_DATA) {
+ r1 = archive_read_data_skip(&a->archive);
+ if (r1 == ARCHIVE_EOF)
+ archive_set_error(&a->archive, EIO,
+ "Premature end-of-file.");
+ if (r1 == ARCHIVE_EOF || r1 == ARCHIVE_FATAL) {
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ /* Record start-of-header offset in uncompressed stream. */
+ a->header_position = a->filter->position;
+
+ ++_a->file_count;
+ r2 = (a->format->read_header)(a, entry);
+
+ /*
+ * EOF and FATAL are persistent at this layer. By
+ * modifying the state, we guarantee that future calls to
+ * read a header or read data will fail.
+ */
+ switch (r2) {
+ case ARCHIVE_EOF:
+ a->archive.state = ARCHIVE_STATE_EOF;
+ --_a->file_count;/* Revert a file counter. */
+ break;
+ case ARCHIVE_OK:
+ a->archive.state = ARCHIVE_STATE_DATA;
+ break;
+ case ARCHIVE_WARN:
+ a->archive.state = ARCHIVE_STATE_DATA;
+ break;
+ case ARCHIVE_RETRY:
+ break;
+ case ARCHIVE_FATAL:
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ break;
+ }
+
+ __archive_reset_read_data(&a->archive);
+
+ a->data_start_node = a->client.cursor;
+ /* EOF always wins; otherwise return the worst error. */
+ return (r2 < r1 || r2 == ARCHIVE_EOF) ? r2 : r1;
+}
+
+static int
+_archive_read_next_header(struct archive *_a, struct archive_entry **entryp)
+{
+ int ret;
+ struct archive_read *a = (struct archive_read *)_a;
+ *entryp = NULL;
+ ret = _archive_read_next_header2(_a, a->entry);
+ *entryp = a->entry;
+ return ret;
+}
+
+/*
+ * Allow each registered format to bid on whether it wants to handle
+ * the next entry. Return index of winning bidder.
+ */
+static int
+choose_format(struct archive_read *a)
+{
+ int slots;
+ int i;
+ int bid, best_bid;
+ int best_bid_slot;
+
+ slots = sizeof(a->formats) / sizeof(a->formats[0]);
+ best_bid = -1;
+ best_bid_slot = -1;
+
+ /* Set up a->format for convenience of bidders. */
+ a->format = &(a->formats[0]);
+ for (i = 0; i < slots; i++, a->format++) {
+ if (a->format->bid) {
+ bid = (a->format->bid)(a, best_bid);
+ if (bid == ARCHIVE_FATAL)
+ return (ARCHIVE_FATAL);
+ if (a->filter->position != 0)
+ __archive_read_seek(a, 0, SEEK_SET);
+ if ((bid > best_bid) || (best_bid_slot < 0)) {
+ best_bid = bid;
+ best_bid_slot = i;
+ }
+ }
+ }
+
+ /*
+ * There were no bidders; this is a serious programmer error
+ * and demands a quick and definitive abort.
+ */
+ if (best_bid_slot < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "No formats registered");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * There were bidders, but no non-zero bids; this means we
+ * can't support this stream.
+ */
+ if (best_bid < 1) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unrecognized archive format");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (best_bid_slot);
+}
+
+/*
+ * Return the file offset (within the uncompressed data stream) where
+ * the last header started.
+ */
+la_int64_t
+archive_read_header_position(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_header_position");
+ return (a->header_position);
+}
+
+/*
+ * Returns 1 if the archive contains at least one encrypted entry.
+ * If the archive format not support encryption at all
+ * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned.
+ * If for any other reason (e.g. not enough data read so far)
+ * we cannot say whether there are encrypted entries, then
+ * ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW is returned.
+ * In general, this function will return values below zero when the
+ * reader is uncertain or totally incapable of encryption support.
+ * When this function returns 0 you can be sure that the reader
+ * supports encryption detection but no encrypted entries have
+ * been found yet.
+ *
+ * NOTE: If the metadata/header of an archive is also encrypted, you
+ * cannot rely on the number of encrypted entries. That is why this
+ * function does not return the number of encrypted entries but#
+ * just shows that there are some.
+ */
+int
+archive_read_has_encrypted_entries(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ int format_supports_encryption = archive_read_format_capabilities(_a)
+ & (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
+
+ if (!_a || !format_supports_encryption) {
+ /* Format in general doesn't support encryption */
+ return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED;
+ }
+
+ /* A reader potentially has read enough data now. */
+ if (a->format && a->format->has_encrypted_entries) {
+ return (a->format->has_encrypted_entries)(a);
+ }
+
+ /* For any other reason we cannot say how many entries are there. */
+ return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+}
+
+/*
+ * Returns a bitmask of capabilities that are supported by the archive format reader.
+ * If the reader has no special capabilities, ARCHIVE_READ_FORMAT_CAPS_NONE is returned.
+ */
+int
+archive_read_format_capabilities(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ if (a && a->format && a->format->format_capabilties) {
+ return (a->format->format_capabilties)(a);
+ }
+ return ARCHIVE_READ_FORMAT_CAPS_NONE;
+}
+
+/*
+ * Read data from an archive entry, using a read(2)-style interface.
+ * This is a convenience routine that just calls
+ * archive_read_data_block and copies the results into the client
+ * buffer, filling any gaps with zero bytes. Clients using this
+ * API can be completely ignorant of sparse-file issues; sparse files
+ * will simply be padded with nulls.
+ *
+ * DO NOT intermingle calls to this function and archive_read_data_block
+ * to read a single entry body.
+ */
+la_ssize_t
+archive_read_data(struct archive *_a, void *buff, size_t s)
+{
+ struct archive *a = (struct archive *)_a;
+ char *dest;
+ const void *read_buf;
+ size_t bytes_read;
+ size_t len;
+ int r;
+
+ bytes_read = 0;
+ dest = (char *)buff;
+
+ while (s > 0) {
+ if (a->read_data_offset == a->read_data_output_offset &&
+ a->read_data_remaining == 0) {
+ read_buf = a->read_data_block;
+ a->read_data_is_posix_read = 1;
+ a->read_data_requested = s;
+ r = archive_read_data_block(a, &read_buf,
+ &a->read_data_remaining, &a->read_data_offset);
+ a->read_data_block = read_buf;
+ if (r == ARCHIVE_EOF)
+ return (bytes_read);
+ /*
+ * Error codes are all negative, so the status
+ * return here cannot be confused with a valid
+ * byte count. (ARCHIVE_OK is zero.)
+ */
+ if (r < ARCHIVE_OK)
+ return (r);
+ }
+
+ if (a->read_data_offset < a->read_data_output_offset) {
+ archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Encountered out-of-order sparse blocks");
+ return (ARCHIVE_RETRY);
+ }
+
+ /* Compute the amount of zero padding needed. */
+ if (a->read_data_output_offset + (int64_t)s <
+ a->read_data_offset) {
+ len = s;
+ } else if (a->read_data_output_offset <
+ a->read_data_offset) {
+ len = (size_t)(a->read_data_offset -
+ a->read_data_output_offset);
+ } else
+ len = 0;
+
+ /* Add zeroes. */
+ memset(dest, 0, len);
+ s -= len;
+ a->read_data_output_offset += len;
+ dest += len;
+ bytes_read += len;
+
+ /* Copy data if there is any space left. */
+ if (s > 0) {
+ len = a->read_data_remaining;
+ if (len > s)
+ len = s;
+ if (len) {
+ memcpy(dest, a->read_data_block, len);
+ s -= len;
+ a->read_data_block += len;
+ a->read_data_remaining -= len;
+ a->read_data_output_offset += len;
+ a->read_data_offset += len;
+ dest += len;
+ bytes_read += len;
+ }
+ }
+ }
+ a->read_data_is_posix_read = 0;
+ a->read_data_requested = 0;
+ return (bytes_read);
+}
+
+/*
+ * Reset the read_data_* variables, used for starting a new entry.
+ */
+void __archive_reset_read_data(struct archive * a)
+{
+ a->read_data_output_offset = 0;
+ a->read_data_remaining = 0;
+ a->read_data_is_posix_read = 0;
+ a->read_data_requested = 0;
+
+ /* extra resets, from rar.c */
+ a->read_data_block = NULL;
+ a->read_data_offset = 0;
+}
+
+/*
+ * Skip over all remaining data in this entry.
+ */
+int
+archive_read_data_skip(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ int r;
+ const void *buff;
+ size_t size;
+ int64_t offset;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_data_skip");
+
+ if (a->format->read_data_skip != NULL)
+ r = (a->format->read_data_skip)(a);
+ else {
+ while ((r = archive_read_data_block(&a->archive,
+ &buff, &size, &offset))
+ == ARCHIVE_OK)
+ ;
+ }
+
+ if (r == ARCHIVE_EOF)
+ r = ARCHIVE_OK;
+
+ a->archive.state = ARCHIVE_STATE_HEADER;
+ return (r);
+}
+
+la_int64_t
+archive_seek_data(struct archive *_a, int64_t offset, int whence)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_seek_data_block");
+
+ if (a->format->seek_data == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal error: "
+ "No format_seek_data_block function registered");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (a->format->seek_data)(a, offset, whence);
+}
+
+/*
+ * Read the next block of entry data from the archive.
+ * This is a zero-copy interface; the client receives a pointer,
+ * size, and file offset of the next available block of data.
+ *
+ * Returns ARCHIVE_OK if the operation is successful, ARCHIVE_EOF if
+ * the end of entry is encountered.
+ */
+static int
+_archive_read_data_block(struct archive *_a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_data_block");
+
+ if (a->format->read_data == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal error: "
+ "No format->read_data function registered");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (a->format->read_data)(a, buff, size, offset);
+}
+
+static int
+close_filters(struct archive_read *a)
+{
+ struct archive_read_filter *f = a->filter;
+ int r = ARCHIVE_OK;
+ /* Close each filter in the pipeline. */
+ while (f != NULL) {
+ struct archive_read_filter *t = f->upstream;
+ if (!f->closed && f->vtable != NULL) {
+ int r1 = (f->vtable->close)(f);
+ f->closed = 1;
+ if (r1 < r)
+ r = r1;
+ }
+ free(f->buffer);
+ f->buffer = NULL;
+ f = t;
+ }
+ return r;
+}
+
+void
+__archive_read_free_filters(struct archive_read *a)
+{
+ /* Make sure filters are closed and their buffers are freed */
+ close_filters(a);
+
+ while (a->filter != NULL) {
+ struct archive_read_filter *t = a->filter->upstream;
+ free(a->filter);
+ a->filter = t;
+ }
+}
+
+/*
+ * return the count of # of filters in use
+ */
+static int
+_archive_filter_count(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct archive_read_filter *p = a->filter;
+ int count = 0;
+ while(p) {
+ count++;
+ p = p->upstream;
+ }
+ return count;
+}
+
+/*
+ * Close the file and all I/O.
+ */
+static int
+_archive_read_close(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ int r = ARCHIVE_OK, r1 = ARCHIVE_OK;
+
+ archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close");
+ if (a->archive.state == ARCHIVE_STATE_CLOSED)
+ return (ARCHIVE_OK);
+ archive_clear_error(&a->archive);
+ a->archive.state = ARCHIVE_STATE_CLOSED;
+
+ /* TODO: Clean up the formatters. */
+
+ /* Release the filter objects. */
+ r1 = close_filters(a);
+ if (r1 < r)
+ r = r1;
+
+ return (r);
+}
+
+/*
+ * Release memory and other resources.
+ */
+static int
+_archive_read_free(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct archive_read_passphrase *p;
+ int i, n;
+ int slots;
+ int r = ARCHIVE_OK;
+
+ if (_a == NULL)
+ return (ARCHIVE_OK);
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free");
+ if (a->archive.state != ARCHIVE_STATE_CLOSED
+ && a->archive.state != ARCHIVE_STATE_FATAL)
+ r = archive_read_close(&a->archive);
+
+ /* Call cleanup functions registered by optional components. */
+ if (a->cleanup_archive_extract != NULL)
+ r = (a->cleanup_archive_extract)(a);
+
+ /* Cleanup format-specific data. */
+ slots = sizeof(a->formats) / sizeof(a->formats[0]);
+ for (i = 0; i < slots; i++) {
+ a->format = &(a->formats[i]);
+ if (a->formats[i].cleanup)
+ (a->formats[i].cleanup)(a);
+ }
+
+ /* Free the filters */
+ __archive_read_free_filters(a);
+
+ /* Release the bidder objects. */
+ n = sizeof(a->bidders)/sizeof(a->bidders[0]);
+ for (i = 0; i < n; i++) {
+ if (a->bidders[i].vtable == NULL ||
+ a->bidders[i].vtable->free == NULL)
+ continue;
+ (a->bidders[i].vtable->free)(&a->bidders[i]);
+ }
+
+ /* Release passphrase list. */
+ p = a->passphrases.first;
+ while (p != NULL) {
+ struct archive_read_passphrase *np = p->next;
+
+ /* A passphrase should be cleaned. */
+ memset(p->passphrase, 0, strlen(p->passphrase));
+ free(p->passphrase);
+ free(p);
+ p = np;
+ }
+
+ archive_string_free(&a->archive.error_string);
+ archive_entry_free(a->entry);
+ a->archive.magic = 0;
+ __archive_clean(&a->archive);
+ free(a->client.dataset);
+ free(a);
+ return (r);
+}
+
+static struct archive_read_filter *
+get_filter(struct archive *_a, int n)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct archive_read_filter *f = a->filter;
+ /* We use n == -1 for 'the last filter', which is always the
+ * client proxy. */
+ if (n == -1 && f != NULL) {
+ struct archive_read_filter *last = f;
+ f = f->upstream;
+ while (f != NULL) {
+ last = f;
+ f = f->upstream;
+ }
+ return (last);
+ }
+ if (n < 0)
+ return NULL;
+ while (n > 0 && f != NULL) {
+ f = f->upstream;
+ --n;
+ }
+ return (f);
+}
+
+static int
+_archive_filter_code(struct archive *_a, int n)
+{
+ struct archive_read_filter *f = get_filter(_a, n);
+ return f == NULL ? -1 : f->code;
+}
+
+static const char *
+_archive_filter_name(struct archive *_a, int n)
+{
+ struct archive_read_filter *f = get_filter(_a, n);
+ return f != NULL ? f->name : NULL;
+}
+
+static int64_t
+_archive_filter_bytes(struct archive *_a, int n)
+{
+ struct archive_read_filter *f = get_filter(_a, n);
+ return f == NULL ? -1 : f->position;
+}
+
+/*
+ * Used internally by read format handlers to register their bid and
+ * initialization functions.
+ */
+int
+__archive_read_register_format(struct archive_read *a,
+ void *format_data,
+ const char *name,
+ int (*bid)(struct archive_read *, int),
+ int (*options)(struct archive_read *, const char *, const char *),
+ int (*read_header)(struct archive_read *, struct archive_entry *),
+ int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *),
+ int (*read_data_skip)(struct archive_read *),
+ int64_t (*seek_data)(struct archive_read *, int64_t, int),
+ int (*cleanup)(struct archive_read *),
+ int (*format_capabilities)(struct archive_read *),
+ int (*has_encrypted_entries)(struct archive_read *))
+{
+ int i, number_slots;
+
+ archive_check_magic(&a->archive,
+ ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "__archive_read_register_format");
+
+ number_slots = sizeof(a->formats) / sizeof(a->formats[0]);
+
+ for (i = 0; i < number_slots; i++) {
+ if (a->formats[i].bid == bid)
+ return (ARCHIVE_WARN); /* We've already installed */
+ if (a->formats[i].bid == NULL) {
+ a->formats[i].bid = bid;
+ a->formats[i].options = options;
+ a->formats[i].read_header = read_header;
+ a->formats[i].read_data = read_data;
+ a->formats[i].read_data_skip = read_data_skip;
+ a->formats[i].seek_data = seek_data;
+ a->formats[i].cleanup = cleanup;
+ a->formats[i].data = format_data;
+ a->formats[i].name = name;
+ a->formats[i].format_capabilties = format_capabilities;
+ a->formats[i].has_encrypted_entries = has_encrypted_entries;
+ return (ARCHIVE_OK);
+ }
+ }
+
+ archive_set_error(&a->archive, ENOMEM,
+ "Not enough slots for format registration");
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Used internally by decompression routines to register their bid and
+ * initialization functions.
+ */
+int
+__archive_read_register_bidder(struct archive_read *a,
+ void *bidder_data,
+ const char *name,
+ const struct archive_read_filter_bidder_vtable *vtable)
+{
+ struct archive_read_filter_bidder *bidder;
+ int i, number_slots;
+
+ archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "__archive_read_register_bidder");
+
+ number_slots = sizeof(a->bidders) / sizeof(a->bidders[0]);
+
+ for (i = 0; i < number_slots; i++) {
+ if (a->bidders[i].vtable != NULL)
+ continue;
+ memset(a->bidders + i, 0, sizeof(a->bidders[0]));
+ bidder = (a->bidders + i);
+ bidder->data = bidder_data;
+ bidder->name = name;
+ bidder->vtable = vtable;
+ if (bidder->vtable->bid == NULL || bidder->vtable->init == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal error: "
+ "no bid/init for filter bidder");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (ARCHIVE_OK);
+ }
+
+ archive_set_error(&a->archive, ENOMEM,
+ "Not enough slots for filter registration");
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * The next section implements the peek/consume internal I/O
+ * system used by archive readers. This system allows simple
+ * read-ahead for consumers while preserving zero-copy operation
+ * most of the time.
+ *
+ * The two key operations:
+ * * The read-ahead function returns a pointer to a block of data
+ * that satisfies a minimum request.
+ * * The consume function advances the file pointer.
+ *
+ * In the ideal case, filters generate blocks of data
+ * and __archive_read_ahead() just returns pointers directly into
+ * those blocks. Then __archive_read_consume() just bumps those
+ * pointers. Only if your request would span blocks does the I/O
+ * layer use a copy buffer to provide you with a contiguous block of
+ * data.
+ *
+ * A couple of useful idioms:
+ * * "I just want some data." Ask for 1 byte and pay attention to
+ * the "number of bytes available" from __archive_read_ahead().
+ * Consume whatever you actually use.
+ * * "I want to output a large block of data." As above, ask for 1 byte,
+ * emit all that's available (up to whatever limit you have), consume
+ * it all, then repeat until you're done. This effectively means that
+ * you're passing along the blocks that came from your provider.
+ * * "I want to peek ahead by a large amount." Ask for 4k or so, then
+ * double and repeat until you get an error or have enough. Note
+ * that the I/O layer will likely end up expanding its copy buffer
+ * to fit your request, so use this technique cautiously. This
+ * technique is used, for example, by some of the format tasting
+ * code that has uncertain look-ahead needs.
+ */
+
+/*
+ * Looks ahead in the input stream:
+ * * If 'avail' pointer is provided, that returns number of bytes available
+ * in the current buffer, which may be much larger than requested.
+ * * If end-of-file, *avail gets set to zero.
+ * * If error, *avail gets error code.
+ * * If request can be met, returns pointer to data.
+ * * If minimum request cannot be met, returns NULL.
+ *
+ * Note: If you just want "some data", ask for 1 byte and pay attention
+ * to *avail, which will have the actual amount available. If you
+ * know exactly how many bytes you need, just ask for that and treat
+ * a NULL return as an error.
+ *
+ * Important: This does NOT move the file pointer. See
+ * __archive_read_consume() below.
+ */
+const void *
+__archive_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)
+{
+ return (__archive_read_filter_ahead(a->filter, min, avail));
+}
+
+const void *
+__archive_read_filter_ahead(struct archive_read_filter *filter,
+ size_t min, ssize_t *avail)
+{
+ ssize_t bytes_read;
+ size_t tocopy;
+
+ if (filter->fatal) {
+ if (avail)
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+
+ /*
+ * Keep pulling more data until we can satisfy the request.
+ */
+ for (;;) {
+
+ /*
+ * If we can satisfy from the copy buffer (and the
+ * copy buffer isn't empty), we're done. In particular,
+ * note that min == 0 is a perfectly well-defined
+ * request.
+ */
+ if (filter->avail >= min && filter->avail > 0) {
+ if (avail != NULL)
+ *avail = filter->avail;
+ return (filter->next);
+ }
+
+ /*
+ * We can satisfy directly from client buffer if everything
+ * currently in the copy buffer is still in the client buffer.
+ */
+ if (filter->client_total >= filter->client_avail + filter->avail
+ && filter->client_avail + filter->avail >= min) {
+ /* "Roll back" to client buffer. */
+ filter->client_avail += filter->avail;
+ filter->client_next -= filter->avail;
+ /* Copy buffer is now empty. */
+ filter->avail = 0;
+ filter->next = filter->buffer;
+ /* Return data from client buffer. */
+ if (avail != NULL)
+ *avail = filter->client_avail;
+ return (filter->client_next);
+ }
+
+ /* Move data forward in copy buffer if necessary. */
+ if (filter->next > filter->buffer &&
+ filter->next + min > filter->buffer + filter->buffer_size) {
+ if (filter->avail > 0)
+ memmove(filter->buffer, filter->next,
+ filter->avail);
+ filter->next = filter->buffer;
+ }
+
+ /* If we've used up the client data, get more. */
+ if (filter->client_avail <= 0) {
+ if (filter->end_of_file) {
+ if (avail != NULL)
+ *avail = 0;
+ return (NULL);
+ }
+ bytes_read = (filter->vtable->read)(filter,
+ &filter->client_buff);
+ if (bytes_read < 0) { /* Read error. */
+ filter->client_total = filter->client_avail = 0;
+ filter->client_next =
+ filter->client_buff = NULL;
+ filter->fatal = 1;
+ if (avail != NULL)
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ if (bytes_read == 0) {
+ /* Check for another client object first */
+ if (filter->archive->client.cursor !=
+ filter->archive->client.nodes - 1) {
+ if (client_switch_proxy(filter,
+ filter->archive->client.cursor + 1)
+ == ARCHIVE_OK)
+ continue;
+ }
+ /* Premature end-of-file. */
+ filter->client_total = filter->client_avail = 0;
+ filter->client_next =
+ filter->client_buff = NULL;
+ filter->end_of_file = 1;
+ /* Return whatever we do have. */
+ if (avail != NULL)
+ *avail = filter->avail;
+ return (NULL);
+ }
+ filter->client_total = bytes_read;
+ filter->client_avail = filter->client_total;
+ filter->client_next = filter->client_buff;
+ } else {
+ /*
+ * We can't satisfy the request from the copy
+ * buffer or the existing client data, so we
+ * need to copy more client data over to the
+ * copy buffer.
+ */
+
+ /* Ensure the buffer is big enough. */
+ if (min > filter->buffer_size) {
+ size_t s, t;
+ char *p;
+
+ /* Double the buffer; watch for overflow. */
+ s = t = filter->buffer_size;
+ if (s == 0)
+ s = min;
+ while (s < min) {
+ t *= 2;
+ if (t <= s) { /* Integer overflow! */
+ archive_set_error(
+ &filter->archive->archive,
+ ENOMEM,
+ "Unable to allocate copy"
+ " buffer");
+ filter->fatal = 1;
+ if (avail != NULL)
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ s = t;
+ }
+ /* Now s >= min, so allocate a new buffer. */
+ p = (char *)malloc(s);
+ if (p == NULL) {
+ archive_set_error(
+ &filter->archive->archive,
+ ENOMEM,
+ "Unable to allocate copy buffer");
+ filter->fatal = 1;
+ if (avail != NULL)
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ /* Move data into newly-enlarged buffer. */
+ if (filter->avail > 0)
+ memmove(p, filter->next, filter->avail);
+ free(filter->buffer);
+ filter->next = filter->buffer = p;
+ filter->buffer_size = s;
+ }
+
+ /* We can add client data to copy buffer. */
+ /* First estimate: copy to fill rest of buffer. */
+ tocopy = (filter->buffer + filter->buffer_size)
+ - (filter->next + filter->avail);
+ /* Don't waste time buffering more than we need to. */
+ if (tocopy + filter->avail > min)
+ tocopy = min - filter->avail;
+ /* Don't copy more than is available. */
+ if (tocopy > filter->client_avail)
+ tocopy = filter->client_avail;
+
+ memcpy(filter->next + filter->avail,
+ filter->client_next, tocopy);
+ /* Remove this data from client buffer. */
+ filter->client_next += tocopy;
+ filter->client_avail -= tocopy;
+ /* add it to copy buffer. */
+ filter->avail += tocopy;
+ }
+ }
+}
+
+/*
+ * Move the file pointer forward.
+ */
+int64_t
+__archive_read_consume(struct archive_read *a, int64_t request)
+{
+ return (__archive_read_filter_consume(a->filter, request));
+}
+
+int64_t
+__archive_read_filter_consume(struct archive_read_filter * filter,
+ int64_t request)
+{
+ int64_t skipped;
+
+ if (request < 0)
+ return ARCHIVE_FATAL;
+ if (request == 0)
+ return 0;
+
+ skipped = advance_file_pointer(filter, request);
+ if (skipped == request)
+ return (skipped);
+ /* We hit EOF before we satisfied the skip request. */
+ if (skipped < 0) /* Map error code to 0 for error message below. */
+ skipped = 0;
+ archive_set_error(&filter->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Truncated input file (needed %jd bytes, only %jd available)",
+ (intmax_t)request, (intmax_t)skipped);
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Advance the file pointer by the amount requested.
+ * Returns the amount actually advanced, which may be less than the
+ * request if EOF is encountered first.
+ * Returns a negative value if there's an I/O error.
+ */
+static int64_t
+advance_file_pointer(struct archive_read_filter *filter, int64_t request)
+{
+ int64_t bytes_skipped, total_bytes_skipped = 0;
+ ssize_t bytes_read;
+ size_t min;
+
+ if (filter->fatal)
+ return (-1);
+
+ /* Use up the copy buffer first. */
+ if (filter->avail > 0) {
+ min = (size_t)minimum(request, (int64_t)filter->avail);
+ filter->next += min;
+ filter->avail -= min;
+ request -= min;
+ filter->position += min;
+ total_bytes_skipped += min;
+ }
+
+ /* Then use up the client buffer. */
+ if (filter->client_avail > 0) {
+ min = (size_t)minimum(request, (int64_t)filter->client_avail);
+ filter->client_next += min;
+ filter->client_avail -= min;
+ request -= min;
+ filter->position += min;
+ total_bytes_skipped += min;
+ }
+ if (request == 0)
+ return (total_bytes_skipped);
+
+ /* If there's an optimized skip function, use it. */
+ if (filter->can_skip != 0) {
+ bytes_skipped = client_skip_proxy(filter, request);
+ if (bytes_skipped < 0) { /* error */
+ filter->fatal = 1;
+ return (bytes_skipped);
+ }
+ filter->position += bytes_skipped;
+ total_bytes_skipped += bytes_skipped;
+ request -= bytes_skipped;
+ if (request == 0)
+ return (total_bytes_skipped);
+ }
+
+ /* Use ordinary reads as necessary to complete the request. */
+ for (;;) {
+ bytes_read = (filter->vtable->read)(filter, &filter->client_buff);
+ if (bytes_read < 0) {
+ filter->client_buff = NULL;
+ filter->fatal = 1;
+ return (bytes_read);
+ }
+
+ if (bytes_read == 0) {
+ if (filter->archive->client.cursor !=
+ filter->archive->client.nodes - 1) {
+ if (client_switch_proxy(filter,
+ filter->archive->client.cursor + 1)
+ == ARCHIVE_OK)
+ continue;
+ }
+ filter->client_buff = NULL;
+ filter->end_of_file = 1;
+ return (total_bytes_skipped);
+ }
+
+ if (bytes_read >= request) {
+ filter->client_next =
+ ((const char *)filter->client_buff) + request;
+ filter->client_avail = (size_t)(bytes_read - request);
+ filter->client_total = bytes_read;
+ total_bytes_skipped += request;
+ filter->position += request;
+ return (total_bytes_skipped);
+ }
+
+ filter->position += bytes_read;
+ total_bytes_skipped += bytes_read;
+ request -= bytes_read;
+ }
+}
+
+/**
+ * Returns ARCHIVE_FAILED if seeking isn't supported.
+ */
+int64_t
+__archive_read_seek(struct archive_read *a, int64_t offset, int whence)
+{
+ return __archive_read_filter_seek(a->filter, offset, whence);
+}
+
+int64_t
+__archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset,
+ int whence)
+{
+ struct archive_read_client *client;
+ int64_t r;
+ unsigned int cursor;
+
+ if (filter->closed || filter->fatal)
+ return (ARCHIVE_FATAL);
+ if (filter->can_seek == 0)
+ return (ARCHIVE_FAILED);
+
+ client = &(filter->archive->client);
+ switch (whence) {
+ case SEEK_CUR:
+ /* Adjust the offset and use SEEK_SET instead */
+ offset += filter->position;
+ __LA_FALLTHROUGH;
+ case SEEK_SET:
+ cursor = 0;
+ while (1)
+ {
+ if (client->dataset[cursor].begin_position < 0 ||
+ client->dataset[cursor].total_size < 0 ||
+ client->dataset[cursor].begin_position +
+ client->dataset[cursor].total_size - 1 > offset ||
+ cursor + 1 >= client->nodes)
+ break;
+ r = client->dataset[cursor].begin_position +
+ client->dataset[cursor].total_size;
+ client->dataset[++cursor].begin_position = r;
+ }
+ while (1) {
+ r = client_switch_proxy(filter, cursor);
+ if (r != ARCHIVE_OK)
+ return r;
+ if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0)
+ return r;
+ client->dataset[cursor].total_size = r;
+ if (client->dataset[cursor].begin_position +
+ client->dataset[cursor].total_size - 1 > offset ||
+ cursor + 1 >= client->nodes)
+ break;
+ r = client->dataset[cursor].begin_position +
+ client->dataset[cursor].total_size;
+ client->dataset[++cursor].begin_position = r;
+ }
+ offset -= client->dataset[cursor].begin_position;
+ if (offset < 0
+ || offset > client->dataset[cursor].total_size)
+ return ARCHIVE_FATAL;
+ if ((r = client_seek_proxy(filter, offset, SEEK_SET)) < 0)
+ return r;
+ break;
+
+ case SEEK_END:
+ cursor = 0;
+ while (1) {
+ if (client->dataset[cursor].begin_position < 0 ||
+ client->dataset[cursor].total_size < 0 ||
+ cursor + 1 >= client->nodes)
+ break;
+ r = client->dataset[cursor].begin_position +
+ client->dataset[cursor].total_size;
+ client->dataset[++cursor].begin_position = r;
+ }
+ while (1) {
+ r = client_switch_proxy(filter, cursor);
+ if (r != ARCHIVE_OK)
+ return r;
+ if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0)
+ return r;
+ client->dataset[cursor].total_size = r;
+ r = client->dataset[cursor].begin_position +
+ client->dataset[cursor].total_size;
+ if (cursor + 1 >= client->nodes)
+ break;
+ client->dataset[++cursor].begin_position = r;
+ }
+ while (1) {
+ if (r + offset >=
+ client->dataset[cursor].begin_position)
+ break;
+ offset += client->dataset[cursor].total_size;
+ if (cursor == 0)
+ break;
+ cursor--;
+ r = client->dataset[cursor].begin_position +
+ client->dataset[cursor].total_size;
+ }
+ offset = (r + offset) - client->dataset[cursor].begin_position;
+ if ((r = client_switch_proxy(filter, cursor)) != ARCHIVE_OK)
+ return r;
+ r = client_seek_proxy(filter, offset, SEEK_SET);
+ if (r < ARCHIVE_OK)
+ return r;
+ break;
+
+ default:
+ return (ARCHIVE_FATAL);
+ }
+ r += client->dataset[cursor].begin_position;
+
+ if (r >= 0) {
+ /*
+ * Ouch. Clearing the buffer like this hurts, especially
+ * at bid time. A lot of our efficiency at bid time comes
+ * from having bidders reuse the data we've already read.
+ *
+ * TODO: If the seek request is in data we already
+ * have, then don't call the seek callback.
+ *
+ * TODO: Zip seeks to end-of-file at bid time. If
+ * other formats also start doing this, we may need to
+ * find a way for clients to fudge the seek offset to
+ * a block boundary.
+ *
+ * Hmmm... If whence was SEEK_END, we know the file
+ * size is (r - offset). Can we use that to simplify
+ * the TODO items above?
+ */
+ filter->avail = filter->client_avail = 0;
+ filter->next = filter->buffer;
+ filter->position = r;
+ filter->end_of_file = 0;
+ }
+ return r;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_add_passphrase.c b/contrib/libs/libarchive/libarchive/archive_read_add_passphrase.c
new file mode 100644
index 00000000000..f0b1ab93300
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_add_passphrase.c
@@ -0,0 +1,190 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "archive_read_private.h"
+
+static void
+add_passphrase_to_tail(struct archive_read *a,
+ struct archive_read_passphrase *p)
+{
+ *a->passphrases.last = p;
+ a->passphrases.last = &p->next;
+ p->next = NULL;
+}
+
+static struct archive_read_passphrase *
+remove_passphrases_from_head(struct archive_read *a)
+{
+ struct archive_read_passphrase *p;
+
+ p = a->passphrases.first;
+ if (p != NULL)
+ a->passphrases.first = p->next;
+ return (p);
+}
+
+static void
+insert_passphrase_to_head(struct archive_read *a,
+ struct archive_read_passphrase *p)
+{
+ p->next = a->passphrases.first;
+ a->passphrases.first = p;
+ if (&a->passphrases.first == a->passphrases.last) {
+ a->passphrases.last = &p->next;
+ p->next = NULL;
+ }
+}
+
+static struct archive_read_passphrase *
+new_read_passphrase(struct archive_read *a, const char *passphrase)
+{
+ struct archive_read_passphrase *p;
+
+ p = malloc(sizeof(*p));
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (NULL);
+ }
+ p->passphrase = strdup(passphrase);
+ if (p->passphrase == NULL) {
+ free(p);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (NULL);
+ }
+ return (p);
+}
+
+int
+archive_read_add_passphrase(struct archive *_a, const char *passphrase)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct archive_read_passphrase *p;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_add_passphrase");
+
+ if (passphrase == NULL || passphrase[0] == '\0') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Empty passphrase is unacceptable");
+ return (ARCHIVE_FAILED);
+ }
+
+ p = new_read_passphrase(a, passphrase);
+ if (p == NULL)
+ return (ARCHIVE_FATAL);
+ add_passphrase_to_tail(a, p);
+
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_set_passphrase_callback(struct archive *_a, void *client_data,
+ archive_passphrase_callback *cb)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_set_passphrase_callback");
+
+ a->passphrases.callback = cb;
+ a->passphrases.client_data = client_data;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Call this in advance when you start to get a passphrase for decryption
+ * for a entry.
+ */
+void
+__archive_read_reset_passphrase(struct archive_read *a)
+{
+
+ a->passphrases.candidate = -1;
+}
+
+/*
+ * Get a passphrase for decryption.
+ */
+const char *
+__archive_read_next_passphrase(struct archive_read *a)
+{
+ struct archive_read_passphrase *p;
+ const char *passphrase;
+
+ if (a->passphrases.candidate < 0) {
+ /* Count out how many passphrases we have. */
+ int cnt = 0;
+
+ for (p = a->passphrases.first; p != NULL; p = p->next)
+ cnt++;
+ a->passphrases.candidate = cnt;
+ p = a->passphrases.first;
+ } else if (a->passphrases.candidate > 1) {
+ /* Rotate a passphrase list. */
+ a->passphrases.candidate--;
+ p = remove_passphrases_from_head(a);
+ add_passphrase_to_tail(a, p);
+ /* Pick a new passphrase candidate up. */
+ p = a->passphrases.first;
+ } else if (a->passphrases.candidate == 1) {
+ /* This case is that all candidates failed to decrypt. */
+ a->passphrases.candidate = 0;
+ if (a->passphrases.first->next != NULL) {
+ /* Rotate a passphrase list. */
+ p = remove_passphrases_from_head(a);
+ add_passphrase_to_tail(a, p);
+ }
+ p = NULL;
+ } else /* There is no passphrase candidate. */
+ p = NULL;
+
+ if (p != NULL)
+ passphrase = p->passphrase;
+ else if (a->passphrases.callback != NULL) {
+ /* Get a passphrase through a call-back function
+ * since we tried all passphrases out or we don't
+ * have it. */
+ passphrase = a->passphrases.callback(&a->archive,
+ a->passphrases.client_data);
+ if (passphrase != NULL) {
+ p = new_read_passphrase(a, passphrase);
+ if (p == NULL)
+ return (NULL);
+ insert_passphrase_to_head(a, p);
+ a->passphrases.candidate = 1;
+ }
+ } else
+ passphrase = NULL;
+
+ return (passphrase);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_append_filter.c b/contrib/libs/libarchive/libarchive/archive_read_append_filter.c
new file mode 100644
index 00000000000..25dc4b2a2b7
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_append_filter.c
@@ -0,0 +1,204 @@
+/*-
+ * Copyright (c) 2003-2012 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+int
+archive_read_append_filter(struct archive *_a, int code)
+{
+ int r1, r2, number_bidders, i;
+ char str[20];
+ struct archive_read_filter_bidder *bidder;
+ struct archive_read_filter *filter;
+ struct archive_read *a = (struct archive_read *)_a;
+
+ r2 = (ARCHIVE_OK);
+ switch (code)
+ {
+ case ARCHIVE_FILTER_NONE:
+ /* No filter to add, so do nothing.
+ * NOTE: An initial "NONE" type filter is always set at the end of the
+ * filter chain.
+ */
+ r1 = (ARCHIVE_OK);
+ break;
+ case ARCHIVE_FILTER_GZIP:
+ strcpy(str, "gzip");
+ r1 = archive_read_support_filter_gzip(_a);
+ break;
+ case ARCHIVE_FILTER_BZIP2:
+ strcpy(str, "bzip2");
+ r1 = archive_read_support_filter_bzip2(_a);
+ break;
+ case ARCHIVE_FILTER_COMPRESS:
+ strcpy(str, "compress (.Z)");
+ r1 = archive_read_support_filter_compress(_a);
+ break;
+ case ARCHIVE_FILTER_PROGRAM:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Cannot append program filter using archive_read_append_filter");
+ return (ARCHIVE_FATAL);
+ case ARCHIVE_FILTER_LZMA:
+ strcpy(str, "lzma");
+ r1 = archive_read_support_filter_lzma(_a);
+ break;
+ case ARCHIVE_FILTER_XZ:
+ strcpy(str, "xz");
+ r1 = archive_read_support_filter_xz(_a);
+ break;
+ case ARCHIVE_FILTER_UU:
+ strcpy(str, "uu");
+ r1 = archive_read_support_filter_uu(_a);
+ break;
+ case ARCHIVE_FILTER_RPM:
+ strcpy(str, "rpm");
+ r1 = archive_read_support_filter_rpm(_a);
+ break;
+ case ARCHIVE_FILTER_LZ4:
+ strcpy(str, "lz4");
+ r1 = archive_read_support_filter_lz4(_a);
+ break;
+ case ARCHIVE_FILTER_ZSTD:
+ strcpy(str, "zstd");
+ r1 = archive_read_support_filter_zstd(_a);
+ break;
+ case ARCHIVE_FILTER_LZIP:
+ strcpy(str, "lzip");
+ r1 = archive_read_support_filter_lzip(_a);
+ break;
+ case ARCHIVE_FILTER_LRZIP:
+ strcpy(str, "lrzip");
+ r1 = archive_read_support_filter_lrzip(_a);
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Invalid filter code specified");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (code != ARCHIVE_FILTER_NONE)
+ {
+ number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);
+
+ bidder = a->bidders;
+ for (i = 0; i < number_bidders; i++, bidder++)
+ {
+ if (!bidder->name || !strcmp(bidder->name, str))
+ break;
+ }
+ if (!bidder->name || strcmp(bidder->name, str))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal error: Unable to append filter");
+ return (ARCHIVE_FATAL);
+ }
+
+ filter
+ = (struct archive_read_filter *)calloc(1, sizeof(*filter));
+ if (filter == NULL)
+ {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ filter->bidder = bidder;
+ filter->archive = a;
+ filter->upstream = a->filter;
+ a->filter = filter;
+ r2 = (bidder->vtable->init)(a->filter);
+ if (r2 != ARCHIVE_OK) {
+ __archive_read_free_filters(a);
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ a->bypass_filter_bidding = 1;
+ return (r1 < r2) ? r1 : r2;
+}
+
+int
+archive_read_append_filter_program(struct archive *_a, const char *cmd)
+{
+ return (archive_read_append_filter_program_signature(_a, cmd, NULL, 0));
+}
+
+int
+archive_read_append_filter_program_signature(struct archive *_a,
+ const char *cmd, const void *signature, size_t signature_len)
+{
+ int r, number_bidders, i;
+ struct archive_read_filter_bidder *bidder;
+ struct archive_read_filter *filter;
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (archive_read_support_filter_program_signature(_a, cmd, signature,
+ signature_len) != (ARCHIVE_OK))
+ return (ARCHIVE_FATAL);
+
+ number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);
+
+ bidder = a->bidders;
+ for (i = 0; i < number_bidders; i++, bidder++)
+ {
+ /* Program bidder name set to filter name after initialization */
+ if (bidder->data && !bidder->name)
+ break;
+ }
+ if (!bidder->data)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal error: Unable to append program filter");
+ return (ARCHIVE_FATAL);
+ }
+
+ filter
+ = (struct archive_read_filter *)calloc(1, sizeof(*filter));
+ if (filter == NULL)
+ {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ filter->bidder = bidder;
+ filter->archive = a;
+ filter->upstream = a->filter;
+ a->filter = filter;
+ r = (bidder->vtable->init)(a->filter);
+ if (r != ARCHIVE_OK) {
+ __archive_read_free_filters(a);
+ return (ARCHIVE_FATAL);
+ }
+ bidder->name = a->filter->name;
+
+ a->bypass_filter_bidding = 1;
+ return r;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_data_into_fd.c b/contrib/libs/libarchive/libarchive/archive_read_data_into_fd.c
new file mode 100644
index 00000000000..f16ca5c82b1
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_data_into_fd.c
@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_data_into_fd.c,v 1.16 2008/05/23 05:01:29 cperciva Exp $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+/* Maximum amount of data to write at one time. */
+#define MAX_WRITE (1024 * 1024)
+
+/*
+ * This implementation minimizes copying of data and is sparse-file aware.
+ */
+static int
+pad_to(struct archive *a, int fd, int can_lseek,
+ size_t nulls_size, const char *nulls,
+ int64_t target_offset, int64_t actual_offset)
+{
+ size_t to_write;
+ ssize_t bytes_written;
+
+ if (can_lseek) {
+ actual_offset = lseek(fd,
+ target_offset - actual_offset, SEEK_CUR);
+ if (actual_offset != target_offset) {
+ archive_set_error(a, errno, "Seek error");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+ }
+ while (target_offset > actual_offset) {
+ to_write = nulls_size;
+ if (target_offset < actual_offset + (int64_t)nulls_size)
+ to_write = (size_t)(target_offset - actual_offset);
+ bytes_written = write(fd, nulls, to_write);
+ if (bytes_written < 0) {
+ archive_set_error(a, errno, "Write error");
+ return (ARCHIVE_FATAL);
+ }
+ actual_offset += bytes_written;
+ }
+ return (ARCHIVE_OK);
+}
+
+
+int
+archive_read_data_into_fd(struct archive *a, int fd)
+{
+ struct stat st;
+ int r, r2;
+ const void *buff;
+ size_t size, bytes_to_write;
+ ssize_t bytes_written;
+ int64_t target_offset;
+ int64_t actual_offset = 0;
+ int can_lseek;
+ char *nulls = NULL;
+ size_t nulls_size = 16384;
+
+ archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_data_into_fd");
+
+ can_lseek = (fstat(fd, &st) == 0) && S_ISREG(st.st_mode);
+ if (!can_lseek) {
+ nulls = calloc(1, nulls_size);
+ if (!nulls) {
+ r = ARCHIVE_FATAL;
+ goto cleanup;
+ }
+ }
+
+ while ((r = archive_read_data_block(a, &buff, &size, &target_offset)) ==
+ ARCHIVE_OK) {
+ const char *p = buff;
+ if (target_offset > actual_offset) {
+ r = pad_to(a, fd, can_lseek, nulls_size, nulls,
+ target_offset, actual_offset);
+ if (r != ARCHIVE_OK)
+ break;
+ actual_offset = target_offset;
+ }
+ while (size > 0) {
+ bytes_to_write = size;
+ if (bytes_to_write > MAX_WRITE)
+ bytes_to_write = MAX_WRITE;
+ bytes_written = write(fd, p, bytes_to_write);
+ if (bytes_written < 0) {
+ archive_set_error(a, errno, "Write error");
+ r = ARCHIVE_FATAL;
+ goto cleanup;
+ }
+ actual_offset += bytes_written;
+ p += bytes_written;
+ size -= bytes_written;
+ }
+ }
+
+ if (r == ARCHIVE_EOF && target_offset > actual_offset) {
+ r2 = pad_to(a, fd, can_lseek, nulls_size, nulls,
+ target_offset, actual_offset);
+ if (r2 != ARCHIVE_OK)
+ r = r2;
+ }
+
+cleanup:
+ free(nulls);
+ if (r != ARCHIVE_EOF)
+ return (r);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_disk_entry_from_file.c b/contrib/libs/libarchive/libarchive/archive_read_disk_entry_from_file.c
new file mode 100644
index 00000000000..bff25b627c1
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_disk_entry_from_file.c
@@ -0,0 +1,1086 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2016 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD");
+
+/* This is the tree-walking code for POSIX systems. */
+#if !defined(_WIN32) || defined(__CYGWIN__)
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_EXTATTR_H
+#include <sys/extattr.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if defined(HAVE_SYS_XATTR_H)
+#include <sys/xattr.h>
+#elif defined(HAVE_ATTR_XATTR_H)
+#include <attr/xattr.h>
+#endif
+#ifdef HAVE_SYS_EA_H
+#error #include <sys/ea.h>
+#endif
+#ifdef HAVE_COPYFILE_H
+#include <copyfile.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+#ifdef HAVE_LINUX_FIEMAP_H
+#include <linux/fiemap.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#error #include <linux/ext2_fs.h> /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#error #include <ext2fs/ext2_fs.h> /* Linux file flags, broken on Cygwin */
+#endif
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+static int setup_mac_metadata(struct archive_read_disk *,
+ struct archive_entry *, int *fd);
+#ifdef ARCHIVE_XATTR_FREEBSD
+static int setup_xattrs_namespace(struct archive_read_disk *,
+ struct archive_entry *, int *, int);
+#endif
+static int setup_xattrs(struct archive_read_disk *,
+ struct archive_entry *, int *fd);
+static int setup_sparse(struct archive_read_disk *,
+ struct archive_entry *, int *fd);
+#if defined(HAVE_LINUX_FIEMAP_H)
+static int setup_sparse_fiemap(struct archive_read_disk *,
+ struct archive_entry *, int *fd);
+#endif
+
+#if !ARCHIVE_ACL_SUPPORT
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ (void)a; /* UNUSED */
+ (void)entry; /* UNUSED */
+ (void)fd; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+#endif
+
+/*
+ * Enter working directory and return working pathname of archive_entry.
+ * If a pointer to an integer is provided and its value is below zero
+ * open a file descriptor on this pathname.
+ */
+const char *
+archive_read_disk_entry_setup_path(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ const char *path;
+
+ path = archive_entry_sourcepath(entry);
+
+ if (path == NULL || (a->tree != NULL &&
+ a->tree_enter_working_dir(a->tree) != 0))
+ path = archive_entry_pathname(entry);
+ if (path == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Couldn't determine path");
+ } else if (fd != NULL && *fd < 0 && a->tree != NULL &&
+ (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK)) {
+ *fd = a->open_on_current_dir(a->tree, path,
+ O_RDONLY | O_NONBLOCK);
+ }
+ return (path);
+}
+
+int
+archive_read_disk_entry_from_file(struct archive *_a,
+ struct archive_entry *entry,
+ int fd,
+ const struct stat *st)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ const char *path, *name;
+ struct stat s;
+ int initial_fd = fd;
+ int r, r1;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY,
+ "archive_read_disk_entry_from_file");
+
+ archive_clear_error(_a);
+ path = archive_entry_sourcepath(entry);
+ if (path == NULL)
+ path = archive_entry_pathname(entry);
+
+ if (a->tree == NULL) {
+ if (st == NULL) {
+#if HAVE_FSTAT
+ if (fd >= 0) {
+ if (fstat(fd, &s) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't fstat");
+ return (ARCHIVE_FAILED);
+ }
+ } else
+#endif
+#if HAVE_LSTAT
+ if (!a->follow_symlinks) {
+ if (lstat(path, &s) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't lstat %s", path);
+ return (ARCHIVE_FAILED);
+ }
+ } else
+#endif
+ if (la_stat(path, &s) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't stat %s", path);
+ return (ARCHIVE_FAILED);
+ }
+ st = &s;
+ }
+ archive_entry_copy_stat(entry, st);
+ }
+
+ /* Lookup uname/gname */
+ name = archive_read_disk_uname(_a, archive_entry_uid(entry));
+ if (name != NULL)
+ archive_entry_copy_uname(entry, name);
+ name = archive_read_disk_gname(_a, archive_entry_gid(entry));
+ if (name != NULL)
+ archive_entry_copy_gname(entry, name);
+
+#ifdef HAVE_STRUCT_STAT_ST_FLAGS
+ /* On FreeBSD, we get flags for free with the stat. */
+ /* TODO: Does this belong in copy_stat()? */
+ if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 && st->st_flags != 0)
+ archive_entry_set_fflags(entry, st->st_flags, 0);
+#endif
+
+#if (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \
+ (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))
+ /* Linux requires an extra ioctl to pull the flags. Although
+ * this is an extra step, it has a nice side-effect: We get an
+ * open file descriptor which we can use in the subsequent lookups. */
+ if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 &&
+ (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {
+ if (fd < 0) {
+ if (a->tree != NULL)
+ fd = a->open_on_current_dir(a->tree, path,
+ O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+ else
+ fd = open(path, O_RDONLY | O_NONBLOCK |
+ O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
+ }
+ if (fd >= 0) {
+ int stflags;
+ r = ioctl(fd,
+#if defined(FS_IOC_GETFLAGS)
+ FS_IOC_GETFLAGS,
+#else
+ EXT2_IOC_GETFLAGS,
+#endif
+ &stflags);
+ if (r == 0 && stflags != 0)
+ archive_entry_set_fflags(entry, stflags, 0);
+ }
+ }
+#endif
+
+#if defined(HAVE_READLINK) || defined(HAVE_READLINKAT)
+ if (S_ISLNK(st->st_mode)) {
+ size_t linkbuffer_len = st->st_size;
+ char *linkbuffer;
+ int lnklen;
+
+ linkbuffer = malloc(linkbuffer_len + 1);
+ if (linkbuffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't read link data");
+ return (ARCHIVE_FAILED);
+ }
+ if (a->tree != NULL) {
+#ifdef HAVE_READLINKAT
+ lnklen = readlinkat(a->tree_current_dir_fd(a->tree),
+ path, linkbuffer, linkbuffer_len);
+#else
+ if (a->tree_enter_working_dir(a->tree) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't read link data");
+ free(linkbuffer);
+ return (ARCHIVE_FAILED);
+ }
+ lnklen = readlink(path, linkbuffer, linkbuffer_len);
+#endif /* HAVE_READLINKAT */
+ } else
+ lnklen = readlink(path, linkbuffer, linkbuffer_len);
+ if (lnklen < 0) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't read link data");
+ free(linkbuffer);
+ return (ARCHIVE_FAILED);
+ }
+ linkbuffer[lnklen] = '\0';
+ archive_entry_set_symlink(entry, linkbuffer);
+ free(linkbuffer);
+ }
+#endif /* HAVE_READLINK || HAVE_READLINKAT */
+
+ r = 0;
+ if ((a->flags & ARCHIVE_READDISK_NO_ACL) == 0)
+ r = archive_read_disk_entry_setup_acls(a, entry, &fd);
+ if ((a->flags & ARCHIVE_READDISK_NO_XATTR) == 0) {
+ r1 = setup_xattrs(a, entry, &fd);
+ if (r1 < r)
+ r = r1;
+ }
+ if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) {
+ r1 = setup_mac_metadata(a, entry, &fd);
+ if (r1 < r)
+ r = r1;
+ }
+ if ((a->flags & ARCHIVE_READDISK_NO_SPARSE) == 0) {
+ r1 = setup_sparse(a, entry, &fd);
+ if (r1 < r)
+ r = r1;
+ }
+
+ /* If we opened the file earlier in this function, close it. */
+ if (initial_fd != fd)
+ close(fd);
+ return (r);
+}
+
+#if defined(__APPLE__) && defined(HAVE_COPYFILE_H)
+/*
+ * The Mac OS "copyfile()" API copies the extended metadata for a
+ * file into a separate file in AppleDouble format (see RFC 1740).
+ *
+ * Mac OS tar and cpio implementations store this extended
+ * metadata as a separate entry just before the regular entry
+ * with a "._" prefix added to the filename.
+ *
+ * Note that this is currently done unconditionally; the tar program has
+ * an option to discard this information before the archive is written.
+ *
+ * TODO: If there's a failure, report it and return ARCHIVE_WARN.
+ */
+static int
+setup_mac_metadata(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ int tempfd = -1;
+ int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR;
+ struct stat copyfile_stat;
+ int ret = ARCHIVE_OK;
+ void *buff = NULL;
+ int have_attrs;
+ const char *name, *tempdir;
+ struct archive_string tempfile;
+
+ (void)fd; /* UNUSED */
+
+ name = archive_read_disk_entry_setup_path(a, entry, NULL);
+ if (name == NULL)
+ return (ARCHIVE_WARN);
+
+ /* Short-circuit if there's nothing to do. */
+ have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK);
+ if (have_attrs == -1) {
+ archive_set_error(&a->archive, errno,
+ "Could not check extended attributes");
+ return (ARCHIVE_WARN);
+ }
+ if (have_attrs == 0)
+ return (ARCHIVE_OK);
+
+ tempdir = NULL;
+ if (issetugid() == 0)
+ tempdir = getenv("TMPDIR");
+ if (tempdir == NULL)
+ tempdir = _PATH_TMP;
+ archive_string_init(&tempfile);
+ archive_strcpy(&tempfile, tempdir);
+ archive_strcat(&tempfile, "tar.md.XXXXXX");
+ tempfd = mkstemp(tempfile.s);
+ if (tempfd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Could not open extended attribute file");
+ ret = ARCHIVE_WARN;
+ goto cleanup;
+ }
+ __archive_ensure_cloexec_flag(tempfd);
+
+ /* XXX I wish copyfile() could pack directly to a memory
+ * buffer; that would avoid the temp file here. For that
+ * matter, it would be nice if fcopyfile() actually worked,
+ * that would reduce the many open/close races here. */
+ if (copyfile(name, tempfile.s, 0, copyfile_flags | COPYFILE_PACK)) {
+ archive_set_error(&a->archive, errno,
+ "Could not pack extended attributes");
+ ret = ARCHIVE_WARN;
+ goto cleanup;
+ }
+ if (fstat(tempfd, &copyfile_stat)) {
+ archive_set_error(&a->archive, errno,
+ "Could not check size of extended attributes");
+ ret = ARCHIVE_WARN;
+ goto cleanup;
+ }
+ buff = malloc(copyfile_stat.st_size);
+ if (buff == NULL) {
+ archive_set_error(&a->archive, errno,
+ "Could not allocate memory for extended attributes");
+ ret = ARCHIVE_WARN;
+ goto cleanup;
+ }
+ if (copyfile_stat.st_size != read(tempfd, buff, copyfile_stat.st_size)) {
+ archive_set_error(&a->archive, errno,
+ "Could not read extended attributes into memory");
+ ret = ARCHIVE_WARN;
+ goto cleanup;
+ }
+ archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size);
+
+cleanup:
+ if (tempfd >= 0) {
+ close(tempfd);
+ unlink(tempfile.s);
+ }
+ archive_string_free(&tempfile);
+ free(buff);
+ return (ret);
+}
+
+#else
+
+/*
+ * Stub implementation for non-Mac systems.
+ */
+static int
+setup_mac_metadata(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ (void)a; /* UNUSED */
+ (void)entry; /* UNUSED */
+ (void)fd; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+#endif
+
+#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX
+
+/*
+ * Linux, Darwin and AIX extended attribute support.
+ *
+ * TODO: By using a stack-allocated buffer for the first
+ * call to getxattr(), we might be able to avoid the second
+ * call entirely. We only need the second call if the
+ * stack-allocated buffer is too small. But a modest buffer
+ * of 1024 bytes or so will often be big enough. Same applies
+ * to listxattr().
+ */
+
+
+static int
+setup_xattr(struct archive_read_disk *a,
+ struct archive_entry *entry, const char *name, int fd, const char *accpath)
+{
+ ssize_t size;
+ void *value = NULL;
+
+
+ if (fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
+ size = fgetxattr(fd, name, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+ size = fgetxattr(fd, name, NULL, 0, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+ size = fgetea(fd, name, NULL, 0);
+#endif
+ } else if (!a->follow_symlinks) {
+#if ARCHIVE_XATTR_LINUX
+ size = lgetxattr(accpath, name, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+ size = getxattr(accpath, name, NULL, 0, 0, XATTR_NOFOLLOW);
+#elif ARCHIVE_XATTR_AIX
+ size = lgetea(accpath, name, NULL, 0);
+#endif
+ } else {
+#if ARCHIVE_XATTR_LINUX
+ size = getxattr(accpath, name, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+ size = getxattr(accpath, name, NULL, 0, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+ size = getea(accpath, name, NULL, 0);
+#endif
+ }
+
+ if (size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't query extended attribute");
+ return (ARCHIVE_WARN);
+ }
+
+ if (size > 0 && (value = malloc(size)) == NULL) {
+ archive_set_error(&a->archive, errno, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+
+
+ if (fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
+ size = fgetxattr(fd, name, value, size);
+#elif ARCHIVE_XATTR_DARWIN
+ size = fgetxattr(fd, name, value, size, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+ size = fgetea(fd, name, value, size);
+#endif
+ } else if (!a->follow_symlinks) {
+#if ARCHIVE_XATTR_LINUX
+ size = lgetxattr(accpath, name, value, size);
+#elif ARCHIVE_XATTR_DARWIN
+ size = getxattr(accpath, name, value, size, 0, XATTR_NOFOLLOW);
+#elif ARCHIVE_XATTR_AIX
+ size = lgetea(accpath, name, value, size);
+#endif
+ } else {
+#if ARCHIVE_XATTR_LINUX
+ size = getxattr(accpath, name, value, size);
+#elif ARCHIVE_XATTR_DARWIN
+ size = getxattr(accpath, name, value, size, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+ size = getea(accpath, name, value, size);
+#endif
+ }
+
+ if (size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't read extended attribute");
+ return (ARCHIVE_WARN);
+ }
+
+ archive_entry_xattr_add_entry(entry, name, value, size);
+
+ free(value);
+ return (ARCHIVE_OK);
+}
+
+static int
+setup_xattrs(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ char *list, *p;
+ const char *path;
+ ssize_t list_size;
+
+ path = NULL;
+
+ if (*fd < 0) {
+ path = archive_read_disk_entry_setup_path(a, entry, fd);
+ if (path == NULL)
+ return (ARCHIVE_WARN);
+ }
+
+ if (*fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
+ list_size = flistxattr(*fd, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+ list_size = flistxattr(*fd, NULL, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+ list_size = flistea(*fd, NULL, 0);
+#endif
+ } else if (!a->follow_symlinks) {
+#if ARCHIVE_XATTR_LINUX
+ list_size = llistxattr(path, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+ list_size = listxattr(path, NULL, 0, XATTR_NOFOLLOW);
+#elif ARCHIVE_XATTR_AIX
+ list_size = llistea(path, NULL, 0);
+#endif
+ } else {
+#if ARCHIVE_XATTR_LINUX
+ list_size = listxattr(path, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+ list_size = listxattr(path, NULL, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+ list_size = listea(path, NULL, 0);
+#endif
+ }
+
+ if (list_size == -1) {
+ if (errno == ENOTSUP || errno == ENOSYS)
+ return (ARCHIVE_OK);
+ archive_set_error(&a->archive, errno,
+ "Couldn't list extended attributes");
+ return (ARCHIVE_WARN);
+ }
+
+ if (list_size == 0)
+ return (ARCHIVE_OK);
+
+ if ((list = malloc(list_size)) == NULL) {
+ archive_set_error(&a->archive, errno, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (*fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
+ list_size = flistxattr(*fd, list, list_size);
+#elif ARCHIVE_XATTR_DARWIN
+ list_size = flistxattr(*fd, list, list_size, 0);
+#elif ARCHIVE_XATTR_AIX
+ list_size = flistea(*fd, list, list_size);
+#endif
+ } else if (!a->follow_symlinks) {
+#if ARCHIVE_XATTR_LINUX
+ list_size = llistxattr(path, list, list_size);
+#elif ARCHIVE_XATTR_DARWIN
+ list_size = listxattr(path, list, list_size, XATTR_NOFOLLOW);
+#elif ARCHIVE_XATTR_AIX
+ list_size = llistea(path, list, list_size);
+#endif
+ } else {
+#if ARCHIVE_XATTR_LINUX
+ list_size = listxattr(path, list, list_size);
+#elif ARCHIVE_XATTR_DARWIN
+ list_size = listxattr(path, list, list_size, 0);
+#elif ARCHIVE_XATTR_AIX
+ list_size = listea(path, list, list_size);
+#endif
+ }
+
+ if (list_size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't retrieve extended attributes");
+ free(list);
+ return (ARCHIVE_WARN);
+ }
+
+ for (p = list; (p - list) < list_size; p += strlen(p) + 1) {
+#if ARCHIVE_XATTR_LINUX
+ /* Linux: skip POSIX.1e ACL extended attributes */
+ if (strncmp(p, "system.", 7) == 0 &&
+ (strcmp(p + 7, "posix_acl_access") == 0 ||
+ strcmp(p + 7, "posix_acl_default") == 0))
+ continue;
+ if (strncmp(p, "trusted.SGI_", 12) == 0 &&
+ (strcmp(p + 12, "ACL_DEFAULT") == 0 ||
+ strcmp(p + 12, "ACL_FILE") == 0))
+ continue;
+
+ /* Linux: xfsroot namespace is obsolete and unsupported */
+ if (strncmp(p, "xfsroot.", 8) == 0)
+ continue;
+#endif
+ setup_xattr(a, entry, p, *fd, path);
+ }
+
+ free(list);
+ return (ARCHIVE_OK);
+}
+
+#elif ARCHIVE_XATTR_FREEBSD
+
+/*
+ * FreeBSD extattr interface.
+ */
+
+/* TODO: Implement this. Follow the Linux model above, but
+ * with FreeBSD-specific system calls, of course. Be careful
+ * to not include the system extattrs that hold ACLs; we handle
+ * those separately.
+ */
+static int
+setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
+ int namespace, const char *name, const char *fullname, int fd,
+ const char *path);
+
+static int
+setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
+ int namespace, const char *name, const char *fullname, int fd,
+ const char *accpath)
+{
+ ssize_t size;
+ void *value = NULL;
+
+ if (fd >= 0)
+ size = extattr_get_fd(fd, namespace, name, NULL, 0);
+ else if (!a->follow_symlinks)
+ size = extattr_get_link(accpath, namespace, name, NULL, 0);
+ else
+ size = extattr_get_file(accpath, namespace, name, NULL, 0);
+
+ if (size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't query extended attribute");
+ return (ARCHIVE_WARN);
+ }
+
+ if (size > 0 && (value = malloc(size)) == NULL) {
+ archive_set_error(&a->archive, errno, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (fd >= 0)
+ size = extattr_get_fd(fd, namespace, name, value, size);
+ else if (!a->follow_symlinks)
+ size = extattr_get_link(accpath, namespace, name, value, size);
+ else
+ size = extattr_get_file(accpath, namespace, name, value, size);
+
+ if (size == -1) {
+ free(value);
+ archive_set_error(&a->archive, errno,
+ "Couldn't read extended attribute");
+ return (ARCHIVE_WARN);
+ }
+
+ archive_entry_xattr_add_entry(entry, fullname, value, size);
+
+ free(value);
+ return (ARCHIVE_OK);
+}
+
+static int
+setup_xattrs_namespace(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd, int namespace)
+{
+ char buff[512];
+ char *list, *p;
+ ssize_t list_size;
+ const char *path;
+
+ path = NULL;
+
+ if (*fd < 0) {
+ path = archive_read_disk_entry_setup_path(a, entry, fd);
+ if (path == NULL)
+ return (ARCHIVE_WARN);
+ }
+
+ if (*fd >= 0)
+ list_size = extattr_list_fd(*fd, namespace, NULL, 0);
+ else if (!a->follow_symlinks)
+ list_size = extattr_list_link(path, namespace, NULL, 0);
+ else
+ list_size = extattr_list_file(path, namespace, NULL, 0);
+
+ if (list_size == -1 && errno == EOPNOTSUPP)
+ return (ARCHIVE_OK);
+ if (list_size == -1 && errno == EPERM)
+ return (ARCHIVE_OK);
+ if (list_size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't list extended attributes");
+ return (ARCHIVE_WARN);
+ }
+
+ if (list_size == 0)
+ return (ARCHIVE_OK);
+
+ if ((list = malloc(list_size)) == NULL) {
+ archive_set_error(&a->archive, errno, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (*fd >= 0)
+ list_size = extattr_list_fd(*fd, namespace, list, list_size);
+ else if (!a->follow_symlinks)
+ list_size = extattr_list_link(path, namespace, list, list_size);
+ else
+ list_size = extattr_list_file(path, namespace, list, list_size);
+
+ if (list_size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't retrieve extended attributes");
+ free(list);
+ return (ARCHIVE_WARN);
+ }
+
+ p = list;
+ while ((p - list) < list_size) {
+ size_t len = 255 & (int)*p;
+ char *name;
+
+ if (namespace == EXTATTR_NAMESPACE_SYSTEM) {
+ if (!strcmp(p + 1, "nfs4.acl") ||
+ !strcmp(p + 1, "posix1e.acl_access") ||
+ !strcmp(p + 1, "posix1e.acl_default")) {
+ p += 1 + len;
+ continue;
+ }
+ strcpy(buff, "system.");
+ } else {
+ strcpy(buff, "user.");
+ }
+ name = buff + strlen(buff);
+ memcpy(name, p + 1, len);
+ name[len] = '\0';
+ setup_xattr(a, entry, namespace, name, buff, *fd, path);
+ p += 1 + len;
+ }
+
+ free(list);
+ return (ARCHIVE_OK);
+}
+
+static int
+setup_xattrs(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ int namespaces[2];
+ int i, res;
+
+ namespaces[0] = EXTATTR_NAMESPACE_USER;
+ namespaces[1] = EXTATTR_NAMESPACE_SYSTEM;
+
+ for (i = 0; i < 2; i++) {
+ res = setup_xattrs_namespace(a, entry, fd,
+ namespaces[i]);
+ switch (res) {
+ case (ARCHIVE_OK):
+ case (ARCHIVE_WARN):
+ break;
+ default:
+ return (res);
+ }
+ }
+
+ return (ARCHIVE_OK);
+}
+
+#else
+
+/*
+ * Generic (stub) extended attribute support.
+ */
+static int
+setup_xattrs(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ (void)a; /* UNUSED */
+ (void)entry; /* UNUSED */
+ (void)fd; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+#endif
+
+#if defined(HAVE_LINUX_FIEMAP_H)
+
+/*
+ * Linux FIEMAP sparse interface.
+ *
+ * The FIEMAP ioctl returns an "extent" for each physical allocation
+ * on disk. We need to process those to generate a more compact list
+ * of logical file blocks. We also need to be very careful to use
+ * FIEMAP_FLAG_SYNC here, since there are reports that Linux sometimes
+ * does not report allocations for newly-written data that hasn't
+ * been synced to disk.
+ *
+ * It's important to return a minimal sparse file list because we want
+ * to not trigger sparse file extensions if we don't have to, since
+ * not all readers support them.
+ */
+
+static int
+setup_sparse_fiemap(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ char buff[4096];
+ struct fiemap *fm;
+ struct fiemap_extent *fe;
+ int64_t size;
+ int count, do_fiemap, iters;
+ int exit_sts = ARCHIVE_OK;
+ const char *path;
+
+ if (archive_entry_filetype(entry) != AE_IFREG
+ || archive_entry_size(entry) <= 0
+ || archive_entry_hardlink(entry) != NULL)
+ return (ARCHIVE_OK);
+
+ if (*fd < 0) {
+ path = archive_read_disk_entry_setup_path(a, entry, NULL);
+ if (path == NULL)
+ return (ARCHIVE_FAILED);
+
+ if (a->tree != NULL)
+ *fd = a->open_on_current_dir(a->tree, path,
+ O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+ else
+ *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+ if (*fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't open `%s'", path);
+ return (ARCHIVE_FAILED);
+ }
+ __archive_ensure_cloexec_flag(*fd);
+ }
+
+ /* Initialize buffer to avoid the error valgrind complains about. */
+ memset(buff, 0, sizeof(buff));
+ count = (sizeof(buff) - sizeof(*fm))/sizeof(*fe);
+ fm = (struct fiemap *)buff;
+ fm->fm_start = 0;
+ fm->fm_length = ~0ULL;;
+ fm->fm_flags = FIEMAP_FLAG_SYNC;
+ fm->fm_extent_count = count;
+ do_fiemap = 1;
+ size = archive_entry_size(entry);
+ for (iters = 0; ; ++iters) {
+ int i, r;
+
+ r = ioctl(*fd, FS_IOC_FIEMAP, fm);
+ if (r < 0) {
+ /* When something error happens, it is better we
+ * should return ARCHIVE_OK because an earlier
+ * version(<2.6.28) cannot perform FS_IOC_FIEMAP. */
+ goto exit_setup_sparse_fiemap;
+ }
+ if (fm->fm_mapped_extents == 0) {
+ if (iters == 0) {
+ /* Fully sparse file; insert a zero-length "data" entry */
+ archive_entry_sparse_add_entry(entry, 0, 0);
+ }
+ break;
+ }
+ fe = fm->fm_extents;
+ for (i = 0; i < (int)fm->fm_mapped_extents; i++, fe++) {
+ if (!(fe->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) {
+ /* The fe_length of the last block does not
+ * adjust itself to its size files. */
+ int64_t length = fe->fe_length;
+ if (fe->fe_logical + length > (uint64_t)size)
+ length -= fe->fe_logical + length - size;
+ if (fe->fe_logical == 0 && length == size) {
+ /* This is not sparse. */
+ do_fiemap = 0;
+ break;
+ }
+ if (length > 0)
+ archive_entry_sparse_add_entry(entry,
+ fe->fe_logical, length);
+ }
+ if (fe->fe_flags & FIEMAP_EXTENT_LAST)
+ do_fiemap = 0;
+ }
+ if (do_fiemap) {
+ fe = fm->fm_extents + fm->fm_mapped_extents -1;
+ fm->fm_start = fe->fe_logical + fe->fe_length;
+ } else
+ break;
+ }
+exit_setup_sparse_fiemap:
+ return (exit_sts);
+}
+
+#if !defined(SEEK_HOLE) || !defined(SEEK_DATA)
+static int
+setup_sparse(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ return setup_sparse_fiemap(a, entry, fd);
+}
+#endif
+#endif /* defined(HAVE_LINUX_FIEMAP_H) */
+
+#if defined(SEEK_HOLE) && defined(SEEK_DATA)
+
+/*
+ * SEEK_HOLE sparse interface (FreeBSD, Linux, Solaris)
+ */
+
+static int
+setup_sparse(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ int64_t size;
+ off_t initial_off;
+ off_t off_s, off_e;
+ int exit_sts = ARCHIVE_OK;
+ int check_fully_sparse = 0;
+ const char *path;
+
+ if (archive_entry_filetype(entry) != AE_IFREG
+ || archive_entry_size(entry) <= 0
+ || archive_entry_hardlink(entry) != NULL)
+ return (ARCHIVE_OK);
+
+ /* Does filesystem support the reporting of hole ? */
+ if (*fd < 0)
+ path = archive_read_disk_entry_setup_path(a, entry, fd);
+ else
+ path = NULL;
+
+ if (*fd >= 0) {
+#ifdef _PC_MIN_HOLE_SIZE
+ if (fpathconf(*fd, _PC_MIN_HOLE_SIZE) <= 0)
+ return (ARCHIVE_OK);
+#endif
+ initial_off = lseek(*fd, 0, SEEK_CUR);
+ if (initial_off != 0)
+ lseek(*fd, 0, SEEK_SET);
+ } else {
+ if (path == NULL)
+ return (ARCHIVE_FAILED);
+#ifdef _PC_MIN_HOLE_SIZE
+ if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0)
+ return (ARCHIVE_OK);
+#endif
+ *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+ if (*fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't open `%s'", path);
+ return (ARCHIVE_FAILED);
+ }
+ __archive_ensure_cloexec_flag(*fd);
+ initial_off = 0;
+ }
+
+#ifndef _PC_MIN_HOLE_SIZE
+ /* Check if the underlying filesystem supports seek hole */
+ off_s = lseek(*fd, 0, SEEK_HOLE);
+ if (off_s < 0)
+#if defined(HAVE_LINUX_FIEMAP_H)
+ return setup_sparse_fiemap(a, entry, fd);
+#else
+ goto exit_setup_sparse;
+#endif
+ else if (off_s > 0)
+ lseek(*fd, 0, SEEK_SET);
+#endif
+
+ off_s = 0;
+ size = archive_entry_size(entry);
+ while (off_s < size) {
+ off_s = lseek(*fd, off_s, SEEK_DATA);
+ if (off_s == (off_t)-1) {
+ if (errno == ENXIO) {
+ /* no more hole */
+ if (archive_entry_sparse_count(entry) == 0) {
+ /* Potentially a fully-sparse file. */
+ check_fully_sparse = 1;
+ }
+ break;
+ }
+ archive_set_error(&a->archive, errno,
+ "lseek(SEEK_HOLE) failed");
+ exit_sts = ARCHIVE_FAILED;
+ goto exit_setup_sparse;
+ }
+ off_e = lseek(*fd, off_s, SEEK_HOLE);
+ if (off_e == (off_t)-1) {
+ if (errno == ENXIO) {
+ off_e = lseek(*fd, 0, SEEK_END);
+ if (off_e != (off_t)-1)
+ break;/* no more data */
+ }
+ archive_set_error(&a->archive, errno,
+ "lseek(SEEK_DATA) failed");
+ exit_sts = ARCHIVE_FAILED;
+ goto exit_setup_sparse;
+ }
+ if (off_s == 0 && off_e == size)
+ break;/* This is not sparse. */
+ archive_entry_sparse_add_entry(entry, off_s,
+ off_e - off_s);
+ off_s = off_e;
+ }
+
+ if (check_fully_sparse) {
+ if (lseek(*fd, 0, SEEK_HOLE) == 0 &&
+ lseek(*fd, 0, SEEK_END) == size) {
+ /* Fully sparse file; insert a zero-length "data" entry */
+ archive_entry_sparse_add_entry(entry, 0, 0);
+ }
+ }
+exit_setup_sparse:
+ lseek(*fd, initial_off, SEEK_SET);
+ return (exit_sts);
+}
+
+#elif !defined(HAVE_LINUX_FIEMAP_H)
+
+/*
+ * Generic (stub) sparse support.
+ */
+static int
+setup_sparse(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ (void)a; /* UNUSED */
+ (void)entry; /* UNUSED */
+ (void)fd; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+#endif
+
+#endif /* !defined(_WIN32) || defined(__CYGWIN__) */
+
diff --git a/contrib/libs/libarchive/libarchive/archive_read_disk_posix.c b/contrib/libs/libarchive/libarchive/archive_read_disk_posix.c
new file mode 100644
index 00000000000..fbed06adf21
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_disk_posix.c
@@ -0,0 +1,2760 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* This is the tree-walking code for POSIX systems. */
+#if !defined(_WIN32) || defined(__CYGWIN__)
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_STATFS_H
+#include <sys/statfs.h>
+#endif
+#ifdef HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_LINUX_MAGIC_H
+#include <linux/magic.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>
+#elif HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#error #include <linux/ext2_fs.h> /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#error #include <ext2fs/ext2_fs.h> /* Linux file flags, broken on Cygwin */
+#endif
+#ifdef HAVE_DIRECT_H
+#include <direct.h>
+#endif
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+
+#ifndef HAVE_FCHDIR
+#error fchdir function required.
+#endif
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#if defined(__hpux) && !defined(HAVE_DIRFD)
+#define dirfd(x) ((x)->__dd_fd)
+#define HAVE_DIRFD
+#endif
+
+/*-
+ * This is a new directory-walking system that addresses a number
+ * of problems I've had with fts(3). In particular, it has no
+ * pathname-length limits (other than the size of 'int'), handles
+ * deep logical traversals, uses considerably less memory, and has
+ * an opaque interface (easier to modify in the future).
+ *
+ * Internally, it keeps a single list of "tree_entry" items that
+ * represent filesystem objects that require further attention.
+ * Non-directories are not kept in memory: they are pulled from
+ * readdir(), returned to the client, then freed as soon as possible.
+ * Any directory entry to be traversed gets pushed onto the stack.
+ *
+ * There is surprisingly little information that needs to be kept for
+ * each item on the stack. Just the name, depth (represented here as the
+ * string length of the parent directory's pathname), and some markers
+ * indicating how to get back to the parent (via chdir("..") for a
+ * regular dir or via fchdir(2) for a symlink).
+ */
+/*
+ * TODO:
+ * 1) Loop checking.
+ * 3) Arbitrary logical traversals by closing/reopening intermediate fds.
+ */
+
+struct restore_time {
+ const char *name;
+ time_t mtime;
+ long mtime_nsec;
+ time_t atime;
+ long atime_nsec;
+ mode_t filetype;
+ int noatime;
+};
+
+struct tree_entry {
+ int depth;
+ struct tree_entry *next;
+ struct tree_entry *parent;
+ struct archive_string name;
+ size_t dirname_length;
+ int64_t dev;
+ int64_t ino;
+ int flags;
+ int filesystem_id;
+ /* How to return back to the parent of a symlink. */
+ int symlink_parent_fd;
+ /* How to restore time of a directory. */
+ struct restore_time restore_time;
+};
+
+struct filesystem {
+ int64_t dev;
+ int synthetic;
+ int remote;
+ int noatime;
+#if defined(USE_READDIR_R)
+ size_t name_max;
+#endif
+ long incr_xfer_size;
+ long max_xfer_size;
+ long min_xfer_size;
+ long xfer_align;
+
+ /*
+ * Buffer used for reading file contents.
+ */
+ /* Exactly allocated memory pointer. */
+ unsigned char *allocation_ptr;
+ /* Pointer adjusted to the filesystem alignment . */
+ unsigned char *buff;
+ size_t buff_size;
+};
+
+/* Definitions for tree_entry.flags bitmap. */
+#define isDir 1 /* This entry is a regular directory. */
+#define isDirLink 2 /* This entry is a symbolic link to a directory. */
+#define needsFirstVisit 4 /* This is an initial entry. */
+#define needsDescent 8 /* This entry needs to be previsited. */
+#define needsOpen 16 /* This is a directory that needs to be opened. */
+#define needsAscent 32 /* This entry needs to be postvisited. */
+
+/*
+ * Local data for this package.
+ */
+struct tree {
+ struct tree_entry *stack;
+ struct tree_entry *current;
+ DIR *d;
+#define INVALID_DIR_HANDLE NULL
+ struct dirent *de;
+#if defined(USE_READDIR_R)
+ struct dirent *dirent;
+ size_t dirent_allocated;
+#endif
+ int flags;
+ int visit_type;
+ /* Error code from last failed operation. */
+ int tree_errno;
+
+ /* Dynamically-sized buffer for holding path */
+ struct archive_string path;
+
+ /* Last path element */
+ const char *basename;
+ /* Leading dir length */
+ size_t dirname_length;
+
+ int depth;
+ int openCount;
+ int maxOpenCount;
+ int initial_dir_fd;
+ int working_dir_fd;
+
+ struct stat lst;
+ struct stat st;
+ int descend;
+ int nlink;
+ /* How to restore time of a file. */
+ struct restore_time restore_time;
+
+ struct entry_sparse {
+ int64_t length;
+ int64_t offset;
+ } *sparse_list, *current_sparse;
+ int sparse_count;
+ int sparse_list_size;
+
+ char initial_symlink_mode;
+ char symlink_mode;
+ struct filesystem *current_filesystem;
+ struct filesystem *filesystem_table;
+ int initial_filesystem_id;
+ int current_filesystem_id;
+ int max_filesystem_id;
+ int allocated_filesystem;
+
+ int entry_fd;
+ int entry_eof;
+ int64_t entry_remaining_bytes;
+ int64_t entry_total;
+ unsigned char *entry_buff;
+ size_t entry_buff_size;
+};
+
+/* Definitions for tree.flags bitmap. */
+#define hasStat 16 /* The st entry is valid. */
+#define hasLstat 32 /* The lst entry is valid. */
+#define onWorkingDir 64 /* We are on the working dir where we are
+ * reading directory entry at this time. */
+#define needsRestoreTimes 128
+#define onInitialDir 256 /* We are on the initial dir. */
+
+static int
+tree_dir_next_posix(struct tree *t);
+
+#ifdef HAVE_DIRENT_D_NAMLEN
+/* BSD extension; avoids need for a strlen() call. */
+#define D_NAMELEN(dp) (dp)->d_namlen
+#else
+#define D_NAMELEN(dp) (strlen((dp)->d_name))
+#endif
+
+/* Initiate/terminate a tree traversal. */
+static struct tree *tree_open(const char *, int, int);
+static struct tree *tree_reopen(struct tree *, const char *, int);
+static void tree_close(struct tree *);
+static void tree_free(struct tree *);
+static void tree_push(struct tree *, const char *, int, int64_t, int64_t,
+ struct restore_time *);
+static int tree_enter_initial_dir(struct tree *);
+static int tree_enter_working_dir(struct tree *);
+static int tree_current_dir_fd(struct tree *);
+
+/*
+ * tree_next() returns Zero if there is no next entry, non-zero if
+ * there is. Note that directories are visited three times.
+ * Directories are always visited first as part of enumerating their
+ * parent; that is a "regular" visit. If tree_descend() is invoked at
+ * that time, the directory is added to a work list and will
+ * subsequently be visited two more times: once just after descending
+ * into the directory ("postdescent") and again just after ascending
+ * back to the parent ("postascent").
+ *
+ * TREE_ERROR_DIR is returned if the descent failed (because the
+ * directory couldn't be opened, for instance). This is returned
+ * instead of TREE_POSTDESCENT/TREE_POSTASCENT. TREE_ERROR_DIR is not a
+ * fatal error, but it does imply that the relevant subtree won't be
+ * visited. TREE_ERROR_FATAL is returned for an error that left the
+ * traversal completely hosed. Right now, this is only returned for
+ * chdir() failures during ascent.
+ */
+#define TREE_REGULAR 1
+#define TREE_POSTDESCENT 2
+#define TREE_POSTASCENT 3
+#define TREE_ERROR_DIR -1
+#define TREE_ERROR_FATAL -2
+
+static int tree_next(struct tree *);
+
+/*
+ * Return information about the current entry.
+ */
+
+/*
+ * The current full pathname, length of the full pathname, and a name
+ * that can be used to access the file. Because tree does use chdir
+ * extensively, the access path is almost never the same as the full
+ * current path.
+ *
+ * TODO: On platforms that support it, use openat()-style operations
+ * to eliminate the chdir() operations entirely while still supporting
+ * arbitrarily deep traversals. This makes access_path troublesome to
+ * support, of course, which means we'll need a rich enough interface
+ * that clients can function without it. (In particular, we'll need
+ * tree_current_open() that returns an open file descriptor.)
+ *
+ */
+static const char *tree_current_path(struct tree *);
+static const char *tree_current_access_path(struct tree *);
+
+/*
+ * Request the lstat() or stat() data for the current path. Since the
+ * tree package needs to do some of this anyway, and caches the
+ * results, you should take advantage of it here if you need it rather
+ * than make a redundant stat() or lstat() call of your own.
+ */
+static const struct stat *tree_current_stat(struct tree *);
+static const struct stat *tree_current_lstat(struct tree *);
+static int tree_current_is_symblic_link_target(struct tree *);
+
+/* The following functions use tricks to avoid a certain number of
+ * stat()/lstat() calls. */
+/* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */
+static int tree_current_is_physical_dir(struct tree *);
+/* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */
+static int tree_current_is_dir(struct tree *);
+static int update_current_filesystem(struct archive_read_disk *a,
+ int64_t dev);
+static int setup_current_filesystem(struct archive_read_disk *);
+static int tree_target_is_same_as_parent(struct tree *, const struct stat *);
+
+static int _archive_read_disk_open(struct archive *, const char *);
+static int _archive_read_free(struct archive *);
+static int _archive_read_close(struct archive *);
+static int _archive_read_data_block(struct archive *,
+ const void **, size_t *, int64_t *);
+static int _archive_read_next_header(struct archive *,
+ struct archive_entry **);
+static int _archive_read_next_header2(struct archive *,
+ struct archive_entry *);
+static const char *trivial_lookup_gname(void *, int64_t gid);
+static const char *trivial_lookup_uname(void *, int64_t uid);
+static int setup_sparse(struct archive_read_disk *, struct archive_entry *);
+static int close_and_restore_time(int fd, struct tree *,
+ struct restore_time *);
+static int open_on_current_dir(struct tree *, const char *, int);
+static int tree_dup(int);
+
+
+static const struct archive_vtable
+archive_read_disk_vtable = {
+ .archive_free = _archive_read_free,
+ .archive_close = _archive_read_close,
+ .archive_read_data_block = _archive_read_data_block,
+ .archive_read_next_header = _archive_read_next_header,
+ .archive_read_next_header2 = _archive_read_next_header2,
+};
+
+const char *
+archive_read_disk_gname(struct archive *_a, la_int64_t gid)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_gname"))
+ return (NULL);
+ if (a->lookup_gname == NULL)
+ return (NULL);
+ return ((*a->lookup_gname)(a->lookup_gname_data, gid));
+}
+
+const char *
+archive_read_disk_uname(struct archive *_a, la_int64_t uid)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_uname"))
+ return (NULL);
+ if (a->lookup_uname == NULL)
+ return (NULL);
+ return ((*a->lookup_uname)(a->lookup_uname_data, uid));
+}
+
+int
+archive_read_disk_set_gname_lookup(struct archive *_a,
+ void *private_data,
+ const char * (*lookup_gname)(void *private, la_int64_t gid),
+ void (*cleanup_gname)(void *private))
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup");
+
+ if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
+ (a->cleanup_gname)(a->lookup_gname_data);
+
+ a->lookup_gname = lookup_gname;
+ a->cleanup_gname = cleanup_gname;
+ a->lookup_gname_data = private_data;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_uname_lookup(struct archive *_a,
+ void *private_data,
+ const char * (*lookup_uname)(void *private, la_int64_t uid),
+ void (*cleanup_uname)(void *private))
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup");
+
+ if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
+ (a->cleanup_uname)(a->lookup_uname_data);
+
+ a->lookup_uname = lookup_uname;
+ a->cleanup_uname = cleanup_uname;
+ a->lookup_uname_data = private_data;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Create a new archive_read_disk object and initialize it with global state.
+ */
+struct archive *
+archive_read_disk_new(void)
+{
+ struct archive_read_disk *a;
+
+ a = (struct archive_read_disk *)calloc(1, sizeof(*a));
+ if (a == NULL)
+ return (NULL);
+ a->archive.magic = ARCHIVE_READ_DISK_MAGIC;
+ a->archive.state = ARCHIVE_STATE_NEW;
+ a->archive.vtable = &archive_read_disk_vtable;
+ a->entry = archive_entry_new2(&a->archive);
+ a->lookup_uname = trivial_lookup_uname;
+ a->lookup_gname = trivial_lookup_gname;
+ a->flags = ARCHIVE_READDISK_MAC_COPYFILE;
+ a->open_on_current_dir = open_on_current_dir;
+ a->tree_current_dir_fd = tree_current_dir_fd;
+ a->tree_enter_working_dir = tree_enter_working_dir;
+ return (&a->archive);
+}
+
+static int
+_archive_read_free(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ int r;
+
+ if (_a == NULL)
+ return (ARCHIVE_OK);
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free");
+
+ if (a->archive.state != ARCHIVE_STATE_CLOSED)
+ r = _archive_read_close(&a->archive);
+ else
+ r = ARCHIVE_OK;
+
+ tree_free(a->tree);
+ if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
+ (a->cleanup_gname)(a->lookup_gname_data);
+ if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
+ (a->cleanup_uname)(a->lookup_uname_data);
+ archive_string_free(&a->archive.error_string);
+ archive_entry_free(a->entry);
+ a->archive.magic = 0;
+ __archive_clean(&a->archive);
+ free(a);
+ return (r);
+}
+
+static int
+_archive_read_close(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close");
+
+ if (a->archive.state != ARCHIVE_STATE_FATAL)
+ a->archive.state = ARCHIVE_STATE_CLOSED;
+
+ tree_close(a->tree);
+
+ return (ARCHIVE_OK);
+}
+
+static void
+setup_symlink_mode(struct archive_read_disk *a, char symlink_mode,
+ int follow_symlinks)
+{
+ a->symlink_mode = symlink_mode;
+ a->follow_symlinks = follow_symlinks;
+ if (a->tree != NULL) {
+ a->tree->initial_symlink_mode = a->symlink_mode;
+ a->tree->symlink_mode = a->symlink_mode;
+ }
+}
+
+int
+archive_read_disk_set_symlink_logical(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_logical");
+ setup_symlink_mode(a, 'L', 1);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_symlink_physical(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_physical");
+ setup_symlink_mode(a, 'P', 0);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_symlink_hybrid(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_hybrid");
+ setup_symlink_mode(a, 'H', 1);/* Follow symlinks initially. */
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_atime_restored(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_restore_atime");
+#ifdef HAVE_UTIMES
+ a->flags |= ARCHIVE_READDISK_RESTORE_ATIME;
+ if (a->tree != NULL)
+ a->tree->flags |= needsRestoreTimes;
+ return (ARCHIVE_OK);
+#else
+ /* Display warning and unset flag */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot restore access time on this system");
+ a->flags &= ~ARCHIVE_READDISK_RESTORE_ATIME;
+ return (ARCHIVE_WARN);
+#endif
+}
+
+int
+archive_read_disk_set_behavior(struct archive *_a, int flags)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ int r = ARCHIVE_OK;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump");
+
+ a->flags = flags;
+
+ if (flags & ARCHIVE_READDISK_RESTORE_ATIME)
+ r = archive_read_disk_set_atime_restored(_a);
+ else {
+ if (a->tree != NULL)
+ a->tree->flags &= ~needsRestoreTimes;
+ }
+ return (r);
+}
+
+/*
+ * Trivial implementations of gname/uname lookup functions.
+ * These are normally overridden by the client, but these stub
+ * versions ensure that we always have something that works.
+ */
+static const char *
+trivial_lookup_gname(void *private_data, int64_t gid)
+{
+ (void)private_data; /* UNUSED */
+ (void)gid; /* UNUSED */
+ return (NULL);
+}
+
+static const char *
+trivial_lookup_uname(void *private_data, int64_t uid)
+{
+ (void)private_data; /* UNUSED */
+ (void)uid; /* UNUSED */
+ return (NULL);
+}
+
+/*
+ * Allocate memory for the reading buffer adjusted to the filesystem
+ * alignment.
+ */
+static int
+setup_suitable_read_buffer(struct archive_read_disk *a)
+{
+ struct tree *t = a->tree;
+ struct filesystem *cf = t->current_filesystem;
+ size_t asize;
+ size_t s;
+
+ if (cf->allocation_ptr == NULL) {
+ /* If we couldn't get a filesystem alignment,
+ * we use 4096 as default value but we won't use
+ * O_DIRECT to open() and openat() operations. */
+ long xfer_align = (cf->xfer_align == -1)?4096:cf->xfer_align;
+
+ if (cf->max_xfer_size != -1)
+ asize = cf->max_xfer_size + xfer_align;
+ else {
+ long incr = cf->incr_xfer_size;
+ /* Some platform does not set a proper value to
+ * incr_xfer_size.*/
+ if (incr < 0)
+ incr = cf->min_xfer_size;
+ if (cf->min_xfer_size < 0) {
+ incr = xfer_align;
+ asize = xfer_align;
+ } else
+ asize = cf->min_xfer_size;
+
+ /* Increase a buffer size up to 64K bytes in
+ * a proper increment size. */
+ while (asize < 1024*64)
+ asize += incr;
+ /* Take a margin to adjust to the filesystem
+ * alignment. */
+ asize += xfer_align;
+ }
+ cf->allocation_ptr = malloc(asize);
+ if (cf->allocation_ptr == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Calculate proper address for the filesystem.
+ */
+ s = (uintptr_t)cf->allocation_ptr;
+ s %= xfer_align;
+ if (s > 0)
+ s = xfer_align - s;
+
+ /*
+ * Set a read buffer pointer in the proper alignment of
+ * the current filesystem.
+ */
+ cf->buff = cf->allocation_ptr + s;
+ cf->buff_size = asize - xfer_align;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+_archive_read_data_block(struct archive *_a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct tree *t = a->tree;
+ int r;
+ ssize_t bytes;
+ int64_t sparse_bytes;
+ size_t buffbytes;
+ int empty_sparse_region = 0;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_data_block");
+
+ if (t->entry_eof || t->entry_remaining_bytes <= 0) {
+ r = ARCHIVE_EOF;
+ goto abort_read_data;
+ }
+
+ /*
+ * Open the current file.
+ */
+ if (t->entry_fd < 0) {
+ int flags = O_RDONLY | O_BINARY | O_CLOEXEC;
+
+ /*
+ * Eliminate or reduce cache effects if we can.
+ *
+ * Carefully consider this to be enabled.
+ */
+#if defined(O_DIRECT) && 0/* Disabled for now */
+ if (t->current_filesystem->xfer_align != -1 &&
+ t->nlink == 1)
+ flags |= O_DIRECT;
+#endif
+#if defined(O_NOATIME)
+ /*
+ * Linux has O_NOATIME flag; use it if we need.
+ */
+ if ((t->flags & needsRestoreTimes) != 0 &&
+ t->restore_time.noatime == 0)
+ flags |= O_NOATIME;
+#endif
+ t->entry_fd = open_on_current_dir(t,
+ tree_current_access_path(t), flags);
+ __archive_ensure_cloexec_flag(t->entry_fd);
+#if defined(O_NOATIME)
+ /*
+ * When we did open the file with O_NOATIME flag,
+ * if successful, set 1 to t->restore_time.noatime
+ * not to restore an atime of the file later.
+ * if failed by EPERM, retry it without O_NOATIME flag.
+ */
+ if (flags & O_NOATIME) {
+ if (t->entry_fd >= 0)
+ t->restore_time.noatime = 1;
+ else if (errno == EPERM)
+ flags &= ~O_NOATIME;
+ }
+#endif
+ if (t->entry_fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't open %s", tree_current_path(t));
+ r = ARCHIVE_FAILED;
+ tree_enter_initial_dir(t);
+ goto abort_read_data;
+ }
+ tree_enter_initial_dir(t);
+ }
+
+ /*
+ * Allocate read buffer if not allocated.
+ */
+ if (t->current_filesystem->allocation_ptr == NULL) {
+ r = setup_suitable_read_buffer(a);
+ if (r != ARCHIVE_OK) {
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ goto abort_read_data;
+ }
+ }
+ t->entry_buff = t->current_filesystem->buff;
+ t->entry_buff_size = t->current_filesystem->buff_size;
+
+ buffbytes = t->entry_buff_size;
+ if ((int64_t)buffbytes > t->current_sparse->length)
+ buffbytes = t->current_sparse->length;
+
+ if (t->current_sparse->length == 0)
+ empty_sparse_region = 1;
+
+ /*
+ * Skip hole.
+ * TODO: Should we consider t->current_filesystem->xfer_align?
+ */
+ if (t->current_sparse->offset > t->entry_total) {
+ if (lseek(t->entry_fd,
+ (off_t)t->current_sparse->offset, SEEK_SET) < 0) {
+ archive_set_error(&a->archive, errno, "Seek error");
+ r = ARCHIVE_FATAL;
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ goto abort_read_data;
+ }
+ sparse_bytes = t->current_sparse->offset - t->entry_total;
+ t->entry_remaining_bytes -= sparse_bytes;
+ t->entry_total += sparse_bytes;
+ }
+
+ /*
+ * Read file contents.
+ */
+ if (buffbytes > 0) {
+ bytes = read(t->entry_fd, t->entry_buff, buffbytes);
+ if (bytes < 0) {
+ archive_set_error(&a->archive, errno, "Read error");
+ r = ARCHIVE_FATAL;
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ goto abort_read_data;
+ }
+ } else
+ bytes = 0;
+ /*
+ * Return an EOF unless we've read a leading empty sparse region, which
+ * is used to represent fully-sparse files.
+ */
+ if (bytes == 0 && !empty_sparse_region) {
+ /* Get EOF */
+ t->entry_eof = 1;
+ r = ARCHIVE_EOF;
+ goto abort_read_data;
+ }
+ *buff = t->entry_buff;
+ *size = bytes;
+ *offset = t->entry_total;
+ t->entry_total += bytes;
+ t->entry_remaining_bytes -= bytes;
+ if (t->entry_remaining_bytes == 0) {
+ /* Close the current file descriptor */
+ close_and_restore_time(t->entry_fd, t, &t->restore_time);
+ t->entry_fd = -1;
+ t->entry_eof = 1;
+ }
+ t->current_sparse->offset += bytes;
+ t->current_sparse->length -= bytes;
+ if (t->current_sparse->length == 0 && !t->entry_eof)
+ t->current_sparse++;
+ return (ARCHIVE_OK);
+
+abort_read_data:
+ *buff = NULL;
+ *size = 0;
+ *offset = t->entry_total;
+ if (t->entry_fd >= 0) {
+ /* Close the current file descriptor */
+ close_and_restore_time(t->entry_fd, t, &t->restore_time);
+ t->entry_fd = -1;
+ }
+ return (r);
+}
+
+static int
+next_entry(struct archive_read_disk *a, struct tree *t,
+ struct archive_entry *entry)
+{
+ const struct stat *st; /* info to use for this entry */
+ const struct stat *lst;/* lstat() information */
+ const char *name;
+ int delayed, delayed_errno, descend, r;
+ struct archive_string delayed_str;
+
+ delayed = ARCHIVE_OK;
+ delayed_errno = 0;
+ archive_string_init(&delayed_str);
+
+ st = NULL;
+ lst = NULL;
+ t->descend = 0;
+ do {
+ switch (tree_next(t)) {
+ case TREE_ERROR_FATAL:
+ archive_set_error(&a->archive, t->tree_errno,
+ "%s: Unable to continue traversing directory tree",
+ tree_current_path(t));
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ tree_enter_initial_dir(t);
+ return (ARCHIVE_FATAL);
+ case TREE_ERROR_DIR:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: Couldn't visit directory",
+ tree_current_path(t));
+ tree_enter_initial_dir(t);
+ return (ARCHIVE_FAILED);
+ case 0:
+ tree_enter_initial_dir(t);
+ return (ARCHIVE_EOF);
+ case TREE_POSTDESCENT:
+ case TREE_POSTASCENT:
+ break;
+ case TREE_REGULAR:
+ lst = tree_current_lstat(t);
+ if (lst == NULL) {
+ if (errno == ENOENT && t->depth > 0) {
+ delayed = ARCHIVE_WARN;
+ delayed_errno = errno;
+ if (delayed_str.length == 0) {
+ archive_string_sprintf(&delayed_str,
+ "%s", tree_current_path(t));
+ } else {
+ archive_string_sprintf(&delayed_str,
+ " %s", tree_current_path(t));
+ }
+ } else {
+ archive_set_error(&a->archive, errno,
+ "%s: Cannot stat",
+ tree_current_path(t));
+ tree_enter_initial_dir(t);
+ return (ARCHIVE_FAILED);
+ }
+ }
+ break;
+ }
+ } while (lst == NULL);
+
+#ifdef __APPLE__
+ if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) {
+ /* If we're using copyfile(), ignore "._XXX" files. */
+ const char *bname = strrchr(tree_current_path(t), '/');
+ if (bname == NULL)
+ bname = tree_current_path(t);
+ else
+ ++bname;
+ if (bname[0] == '.' && bname[1] == '_')
+ return (ARCHIVE_RETRY);
+ }
+#endif
+
+ archive_entry_copy_pathname(entry, tree_current_path(t));
+ /*
+ * Perform path matching.
+ */
+ if (a->matching) {
+ r = archive_match_path_excluded(a->matching, entry);
+ if (r < 0) {
+ archive_set_error(&(a->archive), errno,
+ "Failed : %s", archive_error_string(a->matching));
+ return (r);
+ }
+ if (r) {
+ if (a->excluded_cb_func)
+ a->excluded_cb_func(&(a->archive),
+ a->excluded_cb_data, entry);
+ return (ARCHIVE_RETRY);
+ }
+ }
+
+ /*
+ * Distinguish 'L'/'P'/'H' symlink following.
+ */
+ switch(t->symlink_mode) {
+ case 'H':
+ /* 'H': After the first item, rest like 'P'. */
+ t->symlink_mode = 'P';
+ /* 'H': First item (from command line) like 'L'. */
+ /* FALLTHROUGH */
+ case 'L':
+ /* 'L': Do descend through a symlink to dir. */
+ descend = tree_current_is_dir(t);
+ /* 'L': Follow symlinks to files. */
+ a->symlink_mode = 'L';
+ a->follow_symlinks = 1;
+ /* 'L': Archive symlinks as targets, if we can. */
+ st = tree_current_stat(t);
+ if (st != NULL && !tree_target_is_same_as_parent(t, st))
+ break;
+ /* If stat fails, we have a broken symlink;
+ * in that case, don't follow the link. */
+ /* FALLTHROUGH */
+ default:
+ /* 'P': Don't descend through a symlink to dir. */
+ descend = tree_current_is_physical_dir(t);
+ /* 'P': Don't follow symlinks to files. */
+ a->symlink_mode = 'P';
+ a->follow_symlinks = 0;
+ /* 'P': Archive symlinks as symlinks. */
+ st = lst;
+ break;
+ }
+
+ if (update_current_filesystem(a, st->st_dev) != ARCHIVE_OK) {
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ tree_enter_initial_dir(t);
+ return (ARCHIVE_FATAL);
+ }
+ if (t->initial_filesystem_id == -1)
+ t->initial_filesystem_id = t->current_filesystem_id;
+ if (a->flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) {
+ if (t->initial_filesystem_id != t->current_filesystem_id)
+ descend = 0;
+ }
+ t->descend = descend;
+
+ /*
+ * Honor nodump flag.
+ * If the file is marked with nodump flag, do not return this entry.
+ */
+ if (a->flags & ARCHIVE_READDISK_HONOR_NODUMP) {
+#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
+ if (st->st_flags & UF_NODUMP)
+ return (ARCHIVE_RETRY);
+#elif (defined(FS_IOC_GETFLAGS) && defined(FS_NODUMP_FL) && \
+ defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \
+ (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) && \
+ defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))
+ if (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) {
+ int stflags;
+
+ t->entry_fd = open_on_current_dir(t,
+ tree_current_access_path(t),
+ O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(t->entry_fd);
+ if (t->entry_fd >= 0) {
+ r = ioctl(t->entry_fd,
+#ifdef FS_IOC_GETFLAGS
+ FS_IOC_GETFLAGS,
+#else
+ EXT2_IOC_GETFLAGS,
+#endif
+ &stflags);
+#ifdef FS_NODUMP_FL
+ if (r == 0 && (stflags & FS_NODUMP_FL) != 0)
+#else
+ if (r == 0 && (stflags & EXT2_NODUMP_FL) != 0)
+#endif
+ return (ARCHIVE_RETRY);
+ }
+ }
+#endif
+ }
+
+ archive_entry_copy_stat(entry, st);
+
+ /* Save the times to be restored. This must be in before
+ * calling archive_read_disk_descend() or any chance of it,
+ * especially, invoking a callback. */
+ t->restore_time.mtime = archive_entry_mtime(entry);
+ t->restore_time.mtime_nsec = archive_entry_mtime_nsec(entry);
+ t->restore_time.atime = archive_entry_atime(entry);
+ t->restore_time.atime_nsec = archive_entry_atime_nsec(entry);
+ t->restore_time.filetype = archive_entry_filetype(entry);
+ t->restore_time.noatime = t->current_filesystem->noatime;
+
+ /*
+ * Perform time matching.
+ */
+ if (a->matching) {
+ r = archive_match_time_excluded(a->matching, entry);
+ if (r < 0) {
+ archive_set_error(&(a->archive), errno,
+ "Failed : %s", archive_error_string(a->matching));
+ return (r);
+ }
+ if (r) {
+ if (a->excluded_cb_func)
+ a->excluded_cb_func(&(a->archive),
+ a->excluded_cb_data, entry);
+ return (ARCHIVE_RETRY);
+ }
+ }
+
+ /* Lookup uname/gname */
+ name = archive_read_disk_uname(&(a->archive), archive_entry_uid(entry));
+ if (name != NULL)
+ archive_entry_copy_uname(entry, name);
+ name = archive_read_disk_gname(&(a->archive), archive_entry_gid(entry));
+ if (name != NULL)
+ archive_entry_copy_gname(entry, name);
+
+ /*
+ * Perform owner matching.
+ */
+ if (a->matching) {
+ r = archive_match_owner_excluded(a->matching, entry);
+ if (r < 0) {
+ archive_set_error(&(a->archive), errno,
+ "Failed : %s", archive_error_string(a->matching));
+ return (r);
+ }
+ if (r) {
+ if (a->excluded_cb_func)
+ a->excluded_cb_func(&(a->archive),
+ a->excluded_cb_data, entry);
+ return (ARCHIVE_RETRY);
+ }
+ }
+
+ /*
+ * Invoke a meta data filter callback.
+ */
+ if (a->metadata_filter_func) {
+ if (!a->metadata_filter_func(&(a->archive),
+ a->metadata_filter_data, entry))
+ return (ARCHIVE_RETRY);
+ }
+
+ /*
+ * Populate the archive_entry with metadata from the disk.
+ */
+ archive_entry_copy_sourcepath(entry, tree_current_access_path(t));
+ r = archive_read_disk_entry_from_file(&(a->archive), entry,
+ t->entry_fd, st);
+
+ if (r == ARCHIVE_OK) {
+ r = delayed;
+ if (r != ARCHIVE_OK) {
+ archive_string_sprintf(&delayed_str, ": %s",
+ "File removed before we read it");
+ archive_set_error(&(a->archive), delayed_errno,
+ "%s", delayed_str.s);
+ }
+ }
+ archive_string_free(&delayed_str);
+
+ return (r);
+}
+
+static int
+_archive_read_next_header(struct archive *_a, struct archive_entry **entryp)
+{
+ int ret;
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ *entryp = NULL;
+ ret = _archive_read_next_header2(_a, a->entry);
+ *entryp = a->entry;
+ return ret;
+}
+
+static int
+_archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct tree *t;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_read_next_header2");
+
+ t = a->tree;
+ if (t->entry_fd >= 0) {
+ close_and_restore_time(t->entry_fd, t, &t->restore_time);
+ t->entry_fd = -1;
+ }
+
+ archive_entry_clear(entry);
+
+ for (;;) {
+ r = next_entry(a, t, entry);
+ if (t->entry_fd >= 0) {
+ close(t->entry_fd);
+ t->entry_fd = -1;
+ }
+
+ if (r == ARCHIVE_RETRY) {
+ archive_entry_clear(entry);
+ continue;
+ }
+ break;
+ }
+
+ /* Return to the initial directory. */
+ tree_enter_initial_dir(t);
+
+ /*
+ * EOF and FATAL are persistent at this layer. By
+ * modifying the state, we guarantee that future calls to
+ * read a header or read data will fail.
+ */
+ switch (r) {
+ case ARCHIVE_EOF:
+ a->archive.state = ARCHIVE_STATE_EOF;
+ break;
+ case ARCHIVE_OK:
+ case ARCHIVE_WARN:
+ /* Overwrite the sourcepath based on the initial directory. */
+ archive_entry_copy_sourcepath(entry, tree_current_path(t));
+ t->entry_total = 0;
+ if (archive_entry_filetype(entry) == AE_IFREG) {
+ t->nlink = archive_entry_nlink(entry);
+ t->entry_remaining_bytes = archive_entry_size(entry);
+ t->entry_eof = (t->entry_remaining_bytes == 0)? 1: 0;
+ if (!t->entry_eof &&
+ setup_sparse(a, entry) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } else {
+ t->entry_remaining_bytes = 0;
+ t->entry_eof = 1;
+ }
+ a->archive.state = ARCHIVE_STATE_DATA;
+ break;
+ case ARCHIVE_RETRY:
+ break;
+ case ARCHIVE_FATAL:
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ break;
+ }
+
+ __archive_reset_read_data(&a->archive);
+ return (r);
+}
+
+static int
+setup_sparse(struct archive_read_disk *a, struct archive_entry *entry)
+{
+ struct tree *t = a->tree;
+ int64_t length, offset;
+ int i;
+
+ t->sparse_count = archive_entry_sparse_reset(entry);
+ if (t->sparse_count+1 > t->sparse_list_size) {
+ free(t->sparse_list);
+ t->sparse_list_size = t->sparse_count + 1;
+ t->sparse_list = malloc(sizeof(t->sparse_list[0]) *
+ t->sparse_list_size);
+ if (t->sparse_list == NULL) {
+ t->sparse_list_size = 0;
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate data");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ }
+ for (i = 0; i < t->sparse_count; i++) {
+ archive_entry_sparse_next(entry, &offset, &length);
+ t->sparse_list[i].offset = offset;
+ t->sparse_list[i].length = length;
+ }
+ if (i == 0) {
+ t->sparse_list[i].offset = 0;
+ t->sparse_list[i].length = archive_entry_size(entry);
+ } else {
+ t->sparse_list[i].offset = archive_entry_size(entry);
+ t->sparse_list[i].length = 0;
+ }
+ t->current_sparse = t->sparse_list;
+
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_matching(struct archive *_a, struct archive *_ma,
+ void (*_excluded_func)(struct archive *, void *, struct archive_entry *),
+ void *_client_data)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_matching");
+ a->matching = _ma;
+ a->excluded_cb_func = _excluded_func;
+ a->excluded_cb_data = _client_data;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_metadata_filter_callback(struct archive *_a,
+ int (*_metadata_filter_func)(struct archive *, void *,
+ struct archive_entry *), void *_client_data)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY,
+ "archive_read_disk_set_metadata_filter_callback");
+
+ a->metadata_filter_func = _metadata_filter_func;
+ a->metadata_filter_data = _client_data;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_can_descend(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct tree *t = a->tree;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_read_disk_can_descend");
+
+ return (t->visit_type == TREE_REGULAR && t->descend);
+}
+
+/*
+ * Called by the client to mark the directory just returned from
+ * tree_next() as needing to be visited.
+ */
+int
+archive_read_disk_descend(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct tree *t = a->tree;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_read_disk_descend");
+
+ if (!archive_read_disk_can_descend(_a))
+ return (ARCHIVE_OK);
+
+ /*
+ * We must not treat the initial specified path as a physical dir,
+ * because if we do then we will try and ascend out of it by opening
+ * ".." which is (a) wrong and (b) causes spurious permissions errors
+ * if ".." is not readable by us. Instead, treat it as if it were a
+ * symlink. (This uses an extra fd, but it can only happen once at the
+ * top level of a traverse.) But we can't necessarily assume t->st is
+ * valid here (though t->lst is), which complicates the logic a
+ * little.
+ */
+ if (tree_current_is_physical_dir(t)) {
+ tree_push(t, t->basename, t->current_filesystem_id,
+ t->lst.st_dev, t->lst.st_ino, &t->restore_time);
+ if (t->stack->parent->parent != NULL)
+ t->stack->flags |= isDir;
+ else
+ t->stack->flags |= isDirLink;
+ } else if (tree_current_is_dir(t)) {
+ tree_push(t, t->basename, t->current_filesystem_id,
+ t->st.st_dev, t->st.st_ino, &t->restore_time);
+ t->stack->flags |= isDirLink;
+ }
+ t->descend = 0;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_open(struct archive *_a, const char *pathname)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED,
+ "archive_read_disk_open");
+ archive_clear_error(&a->archive);
+
+ return (_archive_read_disk_open(_a, pathname));
+}
+
+int
+archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct archive_string path;
+ int ret;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED,
+ "archive_read_disk_open_w");
+ archive_clear_error(&a->archive);
+
+ /* Make a char string from a wchar_t string. */
+ archive_string_init(&path);
+ if (archive_string_append_from_wcs(&path, pathname,
+ wcslen(pathname)) != 0) {
+ if (errno == ENOMEM)
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't convert a path to a char string");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ ret = ARCHIVE_FATAL;
+ } else
+ ret = _archive_read_disk_open(_a, path.s);
+
+ archive_string_free(&path);
+ return (ret);
+}
+
+static int
+_archive_read_disk_open(struct archive *_a, const char *pathname)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ if (a->tree != NULL)
+ a->tree = tree_reopen(a->tree, pathname,
+ a->flags & ARCHIVE_READDISK_RESTORE_ATIME);
+ else
+ a->tree = tree_open(pathname, a->symlink_mode,
+ a->flags & ARCHIVE_READDISK_RESTORE_ATIME);
+ if (a->tree == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate tar data");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ a->archive.state = ARCHIVE_STATE_HEADER;
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Return a current filesystem ID which is index of the filesystem entry
+ * you've visited through archive_read_disk.
+ */
+int
+archive_read_disk_current_filesystem(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_disk_current_filesystem");
+
+ return (a->tree->current_filesystem_id);
+}
+
+static int
+update_current_filesystem(struct archive_read_disk *a, int64_t dev)
+{
+ struct tree *t = a->tree;
+ int i, fid;
+
+ if (t->current_filesystem != NULL &&
+ t->current_filesystem->dev == dev)
+ return (ARCHIVE_OK);
+
+ for (i = 0; i < t->max_filesystem_id; i++) {
+ if (t->filesystem_table[i].dev == dev) {
+ /* There is the filesystem ID we've already generated. */
+ t->current_filesystem_id = i;
+ t->current_filesystem = &(t->filesystem_table[i]);
+ return (ARCHIVE_OK);
+ }
+ }
+
+ /*
+ * This is the new filesystem which we have to generate a new ID for.
+ */
+ fid = t->max_filesystem_id++;
+ if (t->max_filesystem_id > t->allocated_filesystem) {
+ size_t s;
+ void *p;
+
+ s = t->max_filesystem_id * 2;
+ p = realloc(t->filesystem_table,
+ s * sizeof(*t->filesystem_table));
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate tar data");
+ return (ARCHIVE_FATAL);
+ }
+ t->filesystem_table = (struct filesystem *)p;
+ t->allocated_filesystem = s;
+ }
+ t->current_filesystem_id = fid;
+ t->current_filesystem = &(t->filesystem_table[fid]);
+ t->current_filesystem->dev = dev;
+ t->current_filesystem->allocation_ptr = NULL;
+ t->current_filesystem->buff = NULL;
+
+ /* Setup the current filesystem properties which depend on
+ * platform specific. */
+ return (setup_current_filesystem(a));
+}
+
+/*
+ * Returns 1 if current filesystem is generated filesystem, 0 if it is not
+ * or -1 if it is unknown.
+ */
+int
+archive_read_disk_current_filesystem_is_synthetic(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_disk_current_filesystem");
+
+ return (a->tree->current_filesystem->synthetic);
+}
+
+/*
+ * Returns 1 if current filesystem is remote filesystem, 0 if it is not
+ * or -1 if it is unknown.
+ */
+int
+archive_read_disk_current_filesystem_is_remote(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_disk_current_filesystem");
+
+ return (a->tree->current_filesystem->remote);
+}
+
+#if defined(_PC_REC_INCR_XFER_SIZE) && defined(_PC_REC_MAX_XFER_SIZE) &&\
+ defined(_PC_REC_MIN_XFER_SIZE) && defined(_PC_REC_XFER_ALIGN)
+static int
+get_xfer_size(struct tree *t, int fd, const char *path)
+{
+ t->current_filesystem->xfer_align = -1;
+ errno = 0;
+ if (fd >= 0) {
+ t->current_filesystem->incr_xfer_size =
+ fpathconf(fd, _PC_REC_INCR_XFER_SIZE);
+ t->current_filesystem->max_xfer_size =
+ fpathconf(fd, _PC_REC_MAX_XFER_SIZE);
+ t->current_filesystem->min_xfer_size =
+ fpathconf(fd, _PC_REC_MIN_XFER_SIZE);
+ t->current_filesystem->xfer_align =
+ fpathconf(fd, _PC_REC_XFER_ALIGN);
+ } else if (path != NULL) {
+ t->current_filesystem->incr_xfer_size =
+ pathconf(path, _PC_REC_INCR_XFER_SIZE);
+ t->current_filesystem->max_xfer_size =
+ pathconf(path, _PC_REC_MAX_XFER_SIZE);
+ t->current_filesystem->min_xfer_size =
+ pathconf(path, _PC_REC_MIN_XFER_SIZE);
+ t->current_filesystem->xfer_align =
+ pathconf(path, _PC_REC_XFER_ALIGN);
+ }
+ /* At least we need an alignment size. */
+ if (t->current_filesystem->xfer_align == -1)
+ return ((errno == EINVAL)?1:-1);
+ else
+ return (0);
+}
+#else
+static int
+get_xfer_size(struct tree *t, int fd, const char *path)
+{
+ (void)t; /* UNUSED */
+ (void)fd; /* UNUSED */
+ (void)path; /* UNUSED */
+ return (1);/* Not supported */
+}
+#endif
+
+#if defined(HAVE_STATVFS)
+static inline __LA_UNUSED void
+set_statvfs_transfer_size(struct filesystem *fs, const struct statvfs *sfs)
+{
+ fs->xfer_align = sfs->f_frsize > 0 ? (long)sfs->f_frsize : -1;
+ fs->max_xfer_size = -1;
+#if defined(HAVE_STRUCT_STATVFS_F_IOSIZE)
+ fs->min_xfer_size = sfs->f_iosize > 0 ? (long)sfs->f_iosize : -1;
+ fs->incr_xfer_size = sfs->f_iosize > 0 ? (long)sfs->f_iosize : -1;
+#else
+ fs->min_xfer_size = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1;
+ fs->incr_xfer_size = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1;
+#endif
+}
+#endif
+
+#if defined(HAVE_STRUCT_STATFS)
+static inline __LA_UNUSED void
+set_statfs_transfer_size(struct filesystem *fs, const struct statfs *sfs)
+{
+ fs->xfer_align = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1;
+ fs->max_xfer_size = -1;
+#if defined(HAVE_STRUCT_STATFS_F_IOSIZE)
+ fs->min_xfer_size = sfs->f_iosize > 0 ? (long)sfs->f_iosize : -1;
+ fs->incr_xfer_size = sfs->f_iosize > 0 ? (long)sfs->f_iosize : -1;
+#else
+ fs->min_xfer_size = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1;
+ fs->incr_xfer_size = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1;
+#endif
+}
+#endif
+
+#if defined(HAVE_STRUCT_STATFS) && defined(HAVE_STATFS) && \
+ defined(HAVE_FSTATFS) && defined(MNT_LOCAL) && !defined(ST_LOCAL)
+
+/*
+ * Gather current filesystem properties on FreeBSD, OpenBSD and Mac OS X.
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+ struct tree *t = a->tree;
+ struct statfs sfs;
+#if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC)
+/* TODO: configure should set GETVFSBYNAME_ARG_TYPE to make
+ * this accurate; some platforms have both and we need the one that's
+ * used by getvfsbyname()
+ *
+ * Then the following would become:
+ * #if defined(GETVFSBYNAME_ARG_TYPE)
+ * GETVFSBYNAME_ARG_TYPE vfc;
+ * #endif
+ */
+# if defined(HAVE_STRUCT_XVFSCONF)
+ struct xvfsconf vfc;
+# else
+ struct vfsconf vfc;
+# endif
+#endif
+ int r, xr = 0;
+#if !defined(HAVE_STRUCT_STATFS_F_NAMEMAX)
+ long nm;
+#endif
+
+ t->current_filesystem->synthetic = -1;
+ t->current_filesystem->remote = -1;
+ if (tree_current_is_symblic_link_target(t)) {
+#if defined(HAVE_OPENAT)
+ /*
+ * Get file system statistics on any directory
+ * where current is.
+ */
+ int fd = openat(tree_current_dir_fd(t),
+ tree_current_access_path(t), O_RDONLY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
+ if (fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "openat failed");
+ return (ARCHIVE_FAILED);
+ }
+ r = fstatfs(fd, &sfs);
+ if (r == 0)
+ xr = get_xfer_size(t, fd, NULL);
+ close(fd);
+#else
+ if (tree_enter_working_dir(t) != 0) {
+ archive_set_error(&a->archive, errno, "fchdir failed");
+ return (ARCHIVE_FAILED);
+ }
+ r = statfs(tree_current_access_path(t), &sfs);
+ if (r == 0)
+ xr = get_xfer_size(t, -1, tree_current_access_path(t));
+#endif
+ } else {
+ r = fstatfs(tree_current_dir_fd(t), &sfs);
+ if (r == 0)
+ xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
+ }
+ if (r == -1 || xr == -1) {
+ archive_set_error(&a->archive, errno, "statfs failed");
+ return (ARCHIVE_FAILED);
+ } else if (xr == 1) {
+ /* pathconf(_PC_REX_*) operations are not supported. */
+ set_statfs_transfer_size(t->current_filesystem, &sfs);
+ }
+ if (sfs.f_flags & MNT_LOCAL)
+ t->current_filesystem->remote = 0;
+ else
+ t->current_filesystem->remote = 1;
+
+#if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC)
+ r = getvfsbyname(sfs.f_fstypename, &vfc);
+ if (r == -1) {
+ archive_set_error(&a->archive, errno, "getvfsbyname failed");
+ return (ARCHIVE_FAILED);
+ }
+ if (vfc.vfc_flags & VFCF_SYNTHETIC)
+ t->current_filesystem->synthetic = 1;
+ else
+ t->current_filesystem->synthetic = 0;
+#endif
+
+#if defined(MNT_NOATIME)
+ if (sfs.f_flags & MNT_NOATIME)
+ t->current_filesystem->noatime = 1;
+ else
+#endif
+ t->current_filesystem->noatime = 0;
+
+#if defined(USE_READDIR_R)
+ /* Set maximum filename length. */
+#if defined(HAVE_STRUCT_STATFS_F_NAMEMAX)
+ t->current_filesystem->name_max = sfs.f_namemax;
+#else
+# if defined(_PC_NAME_MAX)
+ /* Mac OS X does not have f_namemax in struct statfs. */
+ if (tree_current_is_symblic_link_target(t)) {
+ if (tree_enter_working_dir(t) != 0) {
+ archive_set_error(&a->archive, errno, "fchdir failed");
+ return (ARCHIVE_FAILED);
+ }
+ nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX);
+ } else
+ nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX);
+# else
+ nm = -1;
+# endif
+ if (nm == -1)
+ t->current_filesystem->name_max = NAME_MAX;
+ else
+ t->current_filesystem->name_max = nm;
+#endif
+ if (t->current_filesystem->name_max == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot determine name_max");
+ return (ARCHIVE_FAILED);
+ }
+#endif /* USE_READDIR_R */
+ return (ARCHIVE_OK);
+}
+
+#elif (defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS)) && defined(ST_LOCAL)
+
+/*
+ * Gather current filesystem properties on NetBSD
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+ struct tree *t = a->tree;
+ struct statvfs svfs;
+ int r, xr = 0;
+
+ t->current_filesystem->synthetic = -1;
+ if (tree_enter_working_dir(t) != 0) {
+ archive_set_error(&a->archive, errno, "fchdir failed");
+ return (ARCHIVE_FAILED);
+ }
+ if (tree_current_is_symblic_link_target(t)) {
+ r = statvfs(tree_current_access_path(t), &svfs);
+ if (r == 0)
+ xr = get_xfer_size(t, -1, tree_current_access_path(t));
+ } else {
+#ifdef HAVE_FSTATVFS
+ r = fstatvfs(tree_current_dir_fd(t), &svfs);
+ if (r == 0)
+ xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
+#else
+ r = statvfs(".", &svfs);
+ if (r == 0)
+ xr = get_xfer_size(t, -1, ".");
+#endif
+ }
+ if (r == -1 || xr == -1) {
+ t->current_filesystem->remote = -1;
+ archive_set_error(&a->archive, errno, "statvfs failed");
+ return (ARCHIVE_FAILED);
+ } else if (xr == 1) {
+ /* Usually come here unless NetBSD supports _PC_REC_XFER_ALIGN
+ * for pathconf() function. */
+ set_statvfs_transfer_size(t->current_filesystem, &svfs);
+ }
+ if (svfs.f_flag & ST_LOCAL)
+ t->current_filesystem->remote = 0;
+ else
+ t->current_filesystem->remote = 1;
+
+#if defined(ST_NOATIME)
+ if (svfs.f_flag & ST_NOATIME)
+ t->current_filesystem->noatime = 1;
+ else
+#endif
+ t->current_filesystem->noatime = 0;
+
+ /* Set maximum filename length. */
+ t->current_filesystem->name_max = svfs.f_namemax;
+ return (ARCHIVE_OK);
+}
+
+#elif defined(HAVE_SYS_STATFS_H) && defined(HAVE_LINUX_MAGIC_H) &&\
+ defined(HAVE_STATFS) && defined(HAVE_FSTATFS)
+/*
+ * Note: statfs is deprecated since LSB 3.2
+ */
+
+#ifndef CIFS_SUPER_MAGIC
+#define CIFS_SUPER_MAGIC 0xFF534D42
+#endif
+#ifndef DEVFS_SUPER_MAGIC
+#define DEVFS_SUPER_MAGIC 0x1373
+#endif
+
+/*
+ * Gather current filesystem properties on Linux
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+ struct tree *t = a->tree;
+ struct statfs sfs;
+#if defined(HAVE_STATVFS)
+ struct statvfs svfs;
+#endif
+ int r, vr = 0, xr = 0;
+
+ if (tree_current_is_symblic_link_target(t)) {
+#if defined(HAVE_OPENAT)
+ /*
+ * Get file system statistics on any directory
+ * where current is.
+ */
+ int fd = openat(tree_current_dir_fd(t),
+ tree_current_access_path(t), O_RDONLY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
+ if (fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "openat failed");
+ return (ARCHIVE_FAILED);
+ }
+#if defined(HAVE_FSTATVFS)
+ vr = fstatvfs(fd, &svfs);/* for f_flag, mount flags */
+#endif
+ r = fstatfs(fd, &sfs);
+ if (r == 0)
+ xr = get_xfer_size(t, fd, NULL);
+ close(fd);
+#else
+ if (tree_enter_working_dir(t) != 0) {
+ archive_set_error(&a->archive, errno, "fchdir failed");
+ return (ARCHIVE_FAILED);
+ }
+#if defined(HAVE_STATVFS)
+ vr = statvfs(tree_current_access_path(t), &svfs);
+#endif
+ r = statfs(tree_current_access_path(t), &sfs);
+ if (r == 0)
+ xr = get_xfer_size(t, -1, tree_current_access_path(t));
+#endif
+ } else {
+#ifdef HAVE_FSTATFS
+#if defined(HAVE_FSTATVFS)
+ vr = fstatvfs(tree_current_dir_fd(t), &svfs);
+#endif
+ r = fstatfs(tree_current_dir_fd(t), &sfs);
+ if (r == 0)
+ xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
+#else
+ if (tree_enter_working_dir(t) != 0) {
+ archive_set_error(&a->archive, errno, "fchdir failed");
+ return (ARCHIVE_FAILED);
+ }
+#if defined(HAVE_STATVFS)
+ vr = statvfs(".", &svfs);
+#endif
+ r = statfs(".", &sfs);
+ if (r == 0)
+ xr = get_xfer_size(t, -1, ".");
+#endif
+ }
+ if (r == -1 || xr == -1 || vr == -1) {
+ t->current_filesystem->synthetic = -1;
+ t->current_filesystem->remote = -1;
+ archive_set_error(&a->archive, errno, "statfs failed");
+ return (ARCHIVE_FAILED);
+ } else if (xr == 1) {
+ /* pathconf(_PC_REX_*) operations are not supported. */
+#if defined(HAVE_STATVFS)
+ set_statvfs_transfer_size(t->current_filesystem, &svfs);
+#else
+ set_statfs_transfer_size(t->current_filesystem, &sfs);
+#endif
+ }
+ switch (sfs.f_type) {
+ case AFS_SUPER_MAGIC:
+ case CIFS_SUPER_MAGIC:
+ case CODA_SUPER_MAGIC:
+ case NCP_SUPER_MAGIC:/* NetWare */
+ case NFS_SUPER_MAGIC:
+ case SMB_SUPER_MAGIC:
+ t->current_filesystem->remote = 1;
+ t->current_filesystem->synthetic = 0;
+ break;
+ case DEVFS_SUPER_MAGIC:
+ case PROC_SUPER_MAGIC:
+ case USBDEVICE_SUPER_MAGIC:
+ t->current_filesystem->remote = 0;
+ t->current_filesystem->synthetic = 1;
+ break;
+ default:
+ t->current_filesystem->remote = 0;
+ t->current_filesystem->synthetic = 0;
+ break;
+ }
+
+#if defined(ST_NOATIME)
+#if defined(HAVE_STATVFS)
+ if (svfs.f_flag & ST_NOATIME)
+#else
+ if (sfs.f_flags & ST_NOATIME)
+#endif
+ t->current_filesystem->noatime = 1;
+ else
+#endif
+ t->current_filesystem->noatime = 0;
+
+#if defined(USE_READDIR_R)
+ /* Set maximum filename length. */
+#if defined(HAVE_STATVFS)
+ t->current_filesystem->name_max = svfs.f_namemax;
+#else
+ t->current_filesystem->name_max = sfs.f_namelen;
+#endif
+ if (t->current_filesystem->name_max == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot determine name_max");
+ return (ARCHIVE_FAILED);
+ }
+#endif
+ return (ARCHIVE_OK);
+}
+
+#elif defined(HAVE_SYS_STATVFS_H) &&\
+ (defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS))
+
+/*
+ * Gather current filesystem properties on other posix platform.
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+ struct tree *t = a->tree;
+ struct statvfs svfs;
+ int r, xr = 0;
+
+ t->current_filesystem->synthetic = -1;/* Not supported */
+ t->current_filesystem->remote = -1;/* Not supported */
+ if (tree_current_is_symblic_link_target(t)) {
+#if defined(HAVE_OPENAT)
+ /*
+ * Get file system statistics on any directory
+ * where current is.
+ */
+ int fd = openat(tree_current_dir_fd(t),
+ tree_current_access_path(t), O_RDONLY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
+ if (fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "openat failed");
+ return (ARCHIVE_FAILED);
+ }
+ r = fstatvfs(fd, &svfs);
+ if (r == 0)
+ xr = get_xfer_size(t, fd, NULL);
+ close(fd);
+#else
+ if (tree_enter_working_dir(t) != 0) {
+ archive_set_error(&a->archive, errno, "fchdir failed");
+ return (ARCHIVE_FAILED);
+ }
+ r = statvfs(tree_current_access_path(t), &svfs);
+ if (r == 0)
+ xr = get_xfer_size(t, -1, tree_current_access_path(t));
+#endif
+ } else {
+#ifdef HAVE_FSTATVFS
+ r = fstatvfs(tree_current_dir_fd(t), &svfs);
+ if (r == 0)
+ xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
+#else
+ if (tree_enter_working_dir(t) != 0) {
+ archive_set_error(&a->archive, errno, "fchdir failed");
+ return (ARCHIVE_FAILED);
+ }
+ r = statvfs(".", &svfs);
+ if (r == 0)
+ xr = get_xfer_size(t, -1, ".");
+#endif
+ }
+ if (r == -1 || xr == -1) {
+ t->current_filesystem->synthetic = -1;
+ t->current_filesystem->remote = -1;
+ archive_set_error(&a->archive, errno, "statvfs failed");
+ return (ARCHIVE_FAILED);
+ } else if (xr == 1) {
+ /* pathconf(_PC_REX_*) operations are not supported. */
+ set_statvfs_transfer_size(t->current_filesystem, &svfs);
+ }
+
+#if defined(ST_NOATIME)
+ if (svfs.f_flag & ST_NOATIME)
+ t->current_filesystem->noatime = 1;
+ else
+#endif
+ t->current_filesystem->noatime = 0;
+
+#if defined(USE_READDIR_R)
+ /* Set maximum filename length. */
+ t->current_filesystem->name_max = svfs.f_namemax;
+ if (t->current_filesystem->name_max == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot determine name_max");
+ return (ARCHIVE_FAILED);
+ }
+#endif
+ return (ARCHIVE_OK);
+}
+
+#else
+
+/*
+ * Generic: Gather current filesystem properties.
+ * TODO: Is this generic function really needed?
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+ struct tree *t = a->tree;
+#if defined(_PC_NAME_MAX) && defined(USE_READDIR_R)
+ long nm;
+#endif
+ t->current_filesystem->synthetic = -1;/* Not supported */
+ t->current_filesystem->remote = -1;/* Not supported */
+ t->current_filesystem->noatime = 0;
+ (void)get_xfer_size(t, -1, ".");/* Dummy call to avoid build error. */
+ t->current_filesystem->xfer_align = -1;/* Unknown */
+ t->current_filesystem->max_xfer_size = -1;
+ t->current_filesystem->min_xfer_size = -1;
+ t->current_filesystem->incr_xfer_size = -1;
+
+#if defined(USE_READDIR_R)
+ /* Set maximum filename length. */
+# if defined(_PC_NAME_MAX)
+ if (tree_current_is_symblic_link_target(t)) {
+ if (tree_enter_working_dir(t) != 0) {
+ archive_set_error(&a->archive, errno, "fchdir failed");
+ return (ARCHIVE_FAILED);
+ }
+ nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX);
+ } else
+ nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX);
+ if (nm == -1)
+# endif /* _PC_NAME_MAX */
+ /*
+ * Some systems (HP-UX or others?) incorrectly defined
+ * NAME_MAX macro to be a smaller value.
+ */
+# if defined(NAME_MAX) && NAME_MAX >= 255
+ t->current_filesystem->name_max = NAME_MAX;
+# else
+ /* No way to get a trusted value of maximum filename
+ * length. */
+ t->current_filesystem->name_max = PATH_MAX;
+# endif /* NAME_MAX */
+# if defined(_PC_NAME_MAX)
+ else
+ t->current_filesystem->name_max = nm;
+# endif /* _PC_NAME_MAX */
+ if (t->current_filesystem->name_max == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot determine name_max");
+ return (ARCHIVE_FAILED);
+ }
+#endif /* USE_READDIR_R */
+ return (ARCHIVE_OK);
+}
+
+#endif
+
+static int
+close_and_restore_time(int fd, struct tree *t, struct restore_time *rt)
+{
+#ifndef HAVE_UTIMES
+ (void)t; /* UNUSED */
+ (void)rt; /* UNUSED */
+ return (close(fd));
+#else
+#if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__)
+ struct timespec timespecs[2];
+#endif
+ struct timeval times[2];
+
+ if ((t->flags & needsRestoreTimes) == 0 || rt->noatime) {
+ if (fd >= 0)
+ return (close(fd));
+ else
+ return (0);
+ }
+
+#if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__)
+ timespecs[1].tv_sec = rt->mtime;
+ timespecs[1].tv_nsec = rt->mtime_nsec;
+
+ timespecs[0].tv_sec = rt->atime;
+ timespecs[0].tv_nsec = rt->atime_nsec;
+ /* futimens() is defined in POSIX.1-2008. */
+ if (futimens(fd, timespecs) == 0)
+ return (close(fd));
+#endif
+
+ times[1].tv_sec = rt->mtime;
+ times[1].tv_usec = rt->mtime_nsec / 1000;
+
+ times[0].tv_sec = rt->atime;
+ times[0].tv_usec = rt->atime_nsec / 1000;
+
+#if !defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES) && !defined(__CYGWIN__)
+ if (futimes(fd, times) == 0)
+ return (close(fd));
+#endif
+ close(fd);
+#if defined(HAVE_FUTIMESAT)
+ if (futimesat(tree_current_dir_fd(t), rt->name, times) == 0)
+ return (0);
+#endif
+#ifdef HAVE_LUTIMES
+ if (lutimes(rt->name, times) != 0)
+#else
+ if (AE_IFLNK != rt->filetype && utimes(rt->name, times) != 0)
+#endif
+ return (-1);
+#endif
+ return (0);
+}
+
+static int
+open_on_current_dir(struct tree *t, const char *path, int flags)
+{
+#ifdef HAVE_OPENAT
+ return (openat(tree_current_dir_fd(t), path, flags));
+#else
+ if (tree_enter_working_dir(t) != 0)
+ return (-1);
+ return (open(path, flags));
+#endif
+}
+
+static int
+tree_dup(int fd)
+{
+ int new_fd;
+#ifdef F_DUPFD_CLOEXEC
+ static volatile int can_dupfd_cloexec = 1;
+
+ if (can_dupfd_cloexec) {
+ new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+ if (new_fd != -1)
+ return (new_fd);
+ /* Linux 2.6.18 - 2.6.23 declare F_DUPFD_CLOEXEC,
+ * but it cannot be used. So we have to try dup(). */
+ /* We won't try F_DUPFD_CLOEXEC. */
+ can_dupfd_cloexec = 0;
+ }
+#endif /* F_DUPFD_CLOEXEC */
+ new_fd = dup(fd);
+ __archive_ensure_cloexec_flag(new_fd);
+ return (new_fd);
+}
+
+/*
+ * Add a directory path to the current stack.
+ */
+static void
+tree_push(struct tree *t, const char *path, int filesystem_id,
+ int64_t dev, int64_t ino, struct restore_time *rt)
+{
+ struct tree_entry *te;
+
+ te = calloc(1, sizeof(*te));
+ if (te == NULL)
+ __archive_errx(1, "Out of memory");
+ te->next = t->stack;
+ te->parent = t->current;
+ if (te->parent)
+ te->depth = te->parent->depth + 1;
+ t->stack = te;
+ archive_string_init(&te->name);
+ te->symlink_parent_fd = -1;
+ archive_strcpy(&te->name, path);
+ te->flags = needsDescent | needsOpen | needsAscent;
+ te->filesystem_id = filesystem_id;
+ te->dev = dev;
+ te->ino = ino;
+ te->dirname_length = t->dirname_length;
+ te->restore_time.name = te->name.s;
+ if (rt != NULL) {
+ te->restore_time.mtime = rt->mtime;
+ te->restore_time.mtime_nsec = rt->mtime_nsec;
+ te->restore_time.atime = rt->atime;
+ te->restore_time.atime_nsec = rt->atime_nsec;
+ te->restore_time.filetype = rt->filetype;
+ te->restore_time.noatime = rt->noatime;
+ }
+}
+
+/*
+ * Append a name to the current dir path.
+ */
+static void
+tree_append(struct tree *t, const char *name, size_t name_length)
+{
+ size_t size_needed;
+
+ t->path.s[t->dirname_length] = '\0';
+ t->path.length = t->dirname_length;
+ /* Strip trailing '/' from name, unless entire name is "/". */
+ while (name_length > 1 && name[name_length - 1] == '/')
+ name_length--;
+
+ /* Resize pathname buffer as needed. */
+ size_needed = name_length + t->dirname_length + 2;
+ archive_string_ensure(&t->path, size_needed);
+ /* Add a separating '/' if it's needed. */
+ if (t->dirname_length > 0 && t->path.s[archive_strlen(&t->path)-1] != '/')
+ archive_strappend_char(&t->path, '/');
+ t->basename = t->path.s + archive_strlen(&t->path);
+ archive_strncat(&t->path, name, name_length);
+ t->restore_time.name = t->basename;
+}
+
+/*
+ * Open a directory tree for traversal.
+ */
+static struct tree *
+tree_open(const char *path, int symlink_mode, int restore_time)
+{
+ struct tree *t;
+
+ if ((t = calloc(1, sizeof(*t))) == NULL)
+ return (NULL);
+ archive_string_init(&t->path);
+ archive_string_ensure(&t->path, 31);
+ t->initial_symlink_mode = symlink_mode;
+ return (tree_reopen(t, path, restore_time));
+}
+
+static struct tree *
+tree_reopen(struct tree *t, const char *path, int restore_time)
+{
+#if defined(O_PATH)
+ /* Linux */
+ const int o_flag = O_PATH;
+#elif defined(O_SEARCH)
+ /* SunOS */
+ const int o_flag = O_SEARCH;
+#elif defined(__FreeBSD__) && defined(O_EXEC)
+ /* FreeBSD */
+ const int o_flag = O_EXEC;
+#endif
+
+ t->flags = (restore_time != 0)?needsRestoreTimes:0;
+ t->flags |= onInitialDir;
+ t->visit_type = 0;
+ t->tree_errno = 0;
+ t->dirname_length = 0;
+ t->depth = 0;
+ t->descend = 0;
+ t->current = NULL;
+ t->d = INVALID_DIR_HANDLE;
+ t->symlink_mode = t->initial_symlink_mode;
+ archive_string_empty(&t->path);
+ t->entry_fd = -1;
+ t->entry_eof = 0;
+ t->entry_remaining_bytes = 0;
+ t->initial_filesystem_id = -1;
+
+ /* First item is set up a lot like a symlink traversal. */
+ tree_push(t, path, 0, 0, 0, NULL);
+ t->stack->flags = needsFirstVisit;
+ t->maxOpenCount = t->openCount = 1;
+ t->initial_dir_fd = open(".", O_RDONLY | O_CLOEXEC);
+#if defined(O_PATH) || defined(O_SEARCH) || \
+ (defined(__FreeBSD__) && defined(O_EXEC))
+ /*
+ * Most likely reason to fail opening "." is that it's not readable,
+ * so try again for execute. The consequences of not opening this are
+ * unhelpful and unnecessary errors later.
+ */
+ if (t->initial_dir_fd < 0)
+ t->initial_dir_fd = open(".", o_flag | O_CLOEXEC);
+#endif
+ __archive_ensure_cloexec_flag(t->initial_dir_fd);
+ t->working_dir_fd = tree_dup(t->initial_dir_fd);
+ return (t);
+}
+
+static int
+tree_descent(struct tree *t)
+{
+ int flag, new_fd, r = 0;
+
+ t->dirname_length = archive_strlen(&t->path);
+ flag = O_RDONLY | O_CLOEXEC;
+#if defined(O_DIRECTORY)
+ flag |= O_DIRECTORY;
+#endif
+ new_fd = open_on_current_dir(t, t->stack->name.s, flag);
+ __archive_ensure_cloexec_flag(new_fd);
+ if (new_fd < 0) {
+ t->tree_errno = errno;
+ r = TREE_ERROR_DIR;
+ } else {
+ t->depth++;
+ /* If it is a link, set up fd for the ascent. */
+ if (t->stack->flags & isDirLink) {
+ t->stack->symlink_parent_fd = t->working_dir_fd;
+ t->openCount++;
+ if (t->openCount > t->maxOpenCount)
+ t->maxOpenCount = t->openCount;
+ } else
+ close(t->working_dir_fd);
+ /* Renew the current working directory. */
+ t->working_dir_fd = new_fd;
+ t->flags &= ~onWorkingDir;
+ }
+ return (r);
+}
+
+/*
+ * We've finished a directory; ascend back to the parent.
+ */
+static int
+tree_ascend(struct tree *t)
+{
+ struct tree_entry *te;
+ int new_fd, r = 0, prev_dir_fd;
+
+ te = t->stack;
+ prev_dir_fd = t->working_dir_fd;
+ if (te->flags & isDirLink)
+ new_fd = te->symlink_parent_fd;
+ else {
+ new_fd = open_on_current_dir(t, "..", O_RDONLY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(new_fd);
+ }
+ if (new_fd < 0) {
+ t->tree_errno = errno;
+ r = TREE_ERROR_FATAL;
+ } else {
+ /* Renew the current working directory. */
+ t->working_dir_fd = new_fd;
+ t->flags &= ~onWorkingDir;
+ /* Current directory has been changed, we should
+ * close an fd of previous working directory. */
+ close_and_restore_time(prev_dir_fd, t, &te->restore_time);
+ if (te->flags & isDirLink) {
+ t->openCount--;
+ te->symlink_parent_fd = -1;
+ }
+ t->depth--;
+ }
+ return (r);
+}
+
+/*
+ * Return to the initial directory where tree_open() was performed.
+ */
+static int
+tree_enter_initial_dir(struct tree *t)
+{
+ int r = 0;
+
+ if ((t->flags & onInitialDir) == 0) {
+ r = fchdir(t->initial_dir_fd);
+ if (r == 0) {
+ t->flags &= ~onWorkingDir;
+ t->flags |= onInitialDir;
+ }
+ }
+ return (r);
+}
+
+/*
+ * Restore working directory of directory traversals.
+ */
+static int
+tree_enter_working_dir(struct tree *t)
+{
+ int r = 0;
+
+ /*
+ * Change the current directory if really needed.
+ * Sometimes this is unneeded when we did not do
+ * descent.
+ */
+ if (t->depth > 0 && (t->flags & onWorkingDir) == 0) {
+ r = fchdir(t->working_dir_fd);
+ if (r == 0) {
+ t->flags &= ~onInitialDir;
+ t->flags |= onWorkingDir;
+ }
+ }
+ return (r);
+}
+
+static int
+tree_current_dir_fd(struct tree *t)
+{
+ return (t->working_dir_fd);
+}
+
+/*
+ * Pop the working stack.
+ */
+static void
+tree_pop(struct tree *t)
+{
+ struct tree_entry *te;
+
+ t->path.s[t->dirname_length] = '\0';
+ t->path.length = t->dirname_length;
+ if (t->stack == t->current && t->current != NULL)
+ t->current = t->current->parent;
+ te = t->stack;
+ t->stack = te->next;
+ t->dirname_length = te->dirname_length;
+ t->basename = t->path.s + t->dirname_length;
+ while (t->basename[0] == '/')
+ t->basename++;
+ archive_string_free(&te->name);
+ free(te);
+}
+
+/*
+ * Get the next item in the tree traversal.
+ */
+static int
+tree_next(struct tree *t)
+{
+ int r;
+
+ while (t->stack != NULL) {
+ /* If there's an open dir, get the next entry from there. */
+ if (t->d != INVALID_DIR_HANDLE) {
+ r = tree_dir_next_posix(t);
+ if (r == 0)
+ continue;
+ return (r);
+ }
+
+ if (t->stack->flags & needsFirstVisit) {
+ /* Top stack item needs a regular visit. */
+ t->current = t->stack;
+ tree_append(t, t->stack->name.s,
+ archive_strlen(&(t->stack->name)));
+ /* t->dirname_length = t->path_length; */
+ /* tree_pop(t); */
+ t->stack->flags &= ~needsFirstVisit;
+ return (t->visit_type = TREE_REGULAR);
+ } else if (t->stack->flags & needsDescent) {
+ /* Top stack item is dir to descend into. */
+ t->current = t->stack;
+ tree_append(t, t->stack->name.s,
+ archive_strlen(&(t->stack->name)));
+ t->stack->flags &= ~needsDescent;
+ r = tree_descent(t);
+ if (r != 0) {
+ tree_pop(t);
+ t->visit_type = r;
+ } else
+ t->visit_type = TREE_POSTDESCENT;
+ return (t->visit_type);
+ } else if (t->stack->flags & needsOpen) {
+ t->stack->flags &= ~needsOpen;
+ r = tree_dir_next_posix(t);
+ if (r == 0)
+ continue;
+ return (r);
+ } else if (t->stack->flags & needsAscent) {
+ /* Top stack item is dir and we're done with it. */
+ r = tree_ascend(t);
+ tree_pop(t);
+ t->visit_type = r != 0 ? r : TREE_POSTASCENT;
+ return (t->visit_type);
+ } else {
+ /* Top item on stack is dead. */
+ tree_pop(t);
+ t->flags &= ~hasLstat;
+ t->flags &= ~hasStat;
+ }
+ }
+ return (t->visit_type = 0);
+}
+
+static int
+tree_dir_next_posix(struct tree *t)
+{
+ int r;
+ const char *name;
+ size_t namelen;
+
+ if (t->d == NULL) {
+#if defined(USE_READDIR_R)
+ size_t dirent_size;
+#endif
+
+#if defined(HAVE_FDOPENDIR)
+ t->d = fdopendir(tree_dup(t->working_dir_fd));
+#else /* HAVE_FDOPENDIR */
+ if (tree_enter_working_dir(t) == 0) {
+ t->d = opendir(".");
+#ifdef HAVE_DIRFD
+ __archive_ensure_cloexec_flag(dirfd(t->d));
+#endif
+ }
+#endif /* HAVE_FDOPENDIR */
+ if (t->d == NULL) {
+ r = tree_ascend(t); /* Undo "chdir" */
+ tree_pop(t);
+ t->tree_errno = errno;
+ t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
+ return (t->visit_type);
+ }
+#if defined(USE_READDIR_R)
+ dirent_size = offsetof(struct dirent, d_name) +
+ t->filesystem_table[t->current->filesystem_id].name_max + 1;
+ if (t->dirent == NULL || t->dirent_allocated < dirent_size) {
+ free(t->dirent);
+ t->dirent = malloc(dirent_size);
+ if (t->dirent == NULL) {
+ closedir(t->d);
+ t->d = INVALID_DIR_HANDLE;
+ (void)tree_ascend(t);
+ tree_pop(t);
+ t->tree_errno = ENOMEM;
+ t->visit_type = TREE_ERROR_DIR;
+ return (t->visit_type);
+ }
+ t->dirent_allocated = dirent_size;
+ }
+#endif /* USE_READDIR_R */
+ }
+ for (;;) {
+ errno = 0;
+#if defined(USE_READDIR_R)
+ r = readdir_r(t->d, t->dirent, &t->de);
+#ifdef _AIX
+ /* Note: According to the man page, return value 9 indicates
+ * that the readdir_r was not successful and the error code
+ * is set to the global errno variable. And then if the end
+ * of directory entries was reached, the return value is 9
+ * and the third parameter is set to NULL and errno is
+ * unchanged. */
+ if (r == 9)
+ r = errno;
+#endif /* _AIX */
+ if (r != 0 || t->de == NULL) {
+#else
+ t->de = readdir(t->d);
+ if (t->de == NULL) {
+ r = errno;
+#endif
+ closedir(t->d);
+ t->d = INVALID_DIR_HANDLE;
+ if (r != 0) {
+ t->tree_errno = r;
+ t->visit_type = TREE_ERROR_DIR;
+ return (t->visit_type);
+ } else
+ return (0);
+ }
+ name = t->de->d_name;
+ namelen = D_NAMELEN(t->de);
+ t->flags &= ~hasLstat;
+ t->flags &= ~hasStat;
+ if (name[0] == '.' && name[1] == '\0')
+ continue;
+ if (name[0] == '.' && name[1] == '.' && name[2] == '\0')
+ continue;
+ tree_append(t, name, namelen);
+ return (t->visit_type = TREE_REGULAR);
+ }
+}
+
+
+/*
+ * Get the stat() data for the entry just returned from tree_next().
+ */
+static const struct stat *
+tree_current_stat(struct tree *t)
+{
+ if (!(t->flags & hasStat)) {
+#ifdef HAVE_FSTATAT
+ if (fstatat(tree_current_dir_fd(t),
+ tree_current_access_path(t), &t->st, 0) != 0)
+#else
+ if (tree_enter_working_dir(t) != 0)
+ return NULL;
+ if (la_stat(tree_current_access_path(t), &t->st) != 0)
+#endif
+ return NULL;
+ t->flags |= hasStat;
+ }
+ return (&t->st);
+}
+
+/*
+ * Get the lstat() data for the entry just returned from tree_next().
+ */
+static const struct stat *
+tree_current_lstat(struct tree *t)
+{
+ if (!(t->flags & hasLstat)) {
+#ifdef HAVE_FSTATAT
+ if (fstatat(tree_current_dir_fd(t),
+ tree_current_access_path(t), &t->lst,
+ AT_SYMLINK_NOFOLLOW) != 0)
+#else
+ if (tree_enter_working_dir(t) != 0)
+ return NULL;
+#ifdef HAVE_LSTAT
+ if (lstat(tree_current_access_path(t), &t->lst) != 0)
+#else
+ if (la_stat(tree_current_access_path(t), &t->lst) != 0)
+#endif
+#endif
+ return NULL;
+ t->flags |= hasLstat;
+ }
+ return (&t->lst);
+}
+
+/*
+ * Test whether current entry is a dir or link to a dir.
+ */
+static int
+tree_current_is_dir(struct tree *t)
+{
+ const struct stat *st;
+ /*
+ * If we already have lstat() info, then try some
+ * cheap tests to determine if this is a dir.
+ */
+ if (t->flags & hasLstat) {
+ /* If lstat() says it's a dir, it must be a dir. */
+ st = tree_current_lstat(t);
+ if (st == NULL)
+ return 0;
+ if (S_ISDIR(st->st_mode))
+ return 1;
+ /* Not a dir; might be a link to a dir. */
+ /* If it's not a link, then it's not a link to a dir. */
+ if (!S_ISLNK(st->st_mode))
+ return 0;
+ /*
+ * It's a link, but we don't know what it's a link to,
+ * so we'll have to use stat().
+ */
+ }
+
+ st = tree_current_stat(t);
+ /* If we can't stat it, it's not a dir. */
+ if (st == NULL)
+ return 0;
+ /* Use the definitive test. Hopefully this is cached. */
+ return (S_ISDIR(st->st_mode));
+}
+
+/*
+ * Test whether current entry is a physical directory. Usually, we
+ * already have at least one of stat() or lstat() in memory, so we
+ * use tricks to try to avoid an extra trip to the disk.
+ */
+static int
+tree_current_is_physical_dir(struct tree *t)
+{
+ const struct stat *st;
+
+ /*
+ * If stat() says it isn't a dir, then it's not a dir.
+ * If stat() data is cached, this check is free, so do it first.
+ */
+ if (t->flags & hasStat) {
+ st = tree_current_stat(t);
+ if (st == NULL)
+ return (0);
+ if (!S_ISDIR(st->st_mode))
+ return (0);
+ }
+
+ /*
+ * Either stat() said it was a dir (in which case, we have
+ * to determine whether it's really a link to a dir) or
+ * stat() info wasn't available. So we use lstat(), which
+ * hopefully is already cached.
+ */
+
+ st = tree_current_lstat(t);
+ /* If we can't stat it, it's not a dir. */
+ if (st == NULL)
+ return 0;
+ /* Use the definitive test. Hopefully this is cached. */
+ return (S_ISDIR(st->st_mode));
+}
+
+/*
+ * Test whether the same file has been in the tree as its parent.
+ */
+static int
+tree_target_is_same_as_parent(struct tree *t, const struct stat *st)
+{
+ struct tree_entry *te;
+
+ for (te = t->current->parent; te != NULL; te = te->parent) {
+ if (te->dev == (int64_t)st->st_dev &&
+ te->ino == (int64_t)st->st_ino)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Test whether the current file is symbolic link target and
+ * on the other filesystem.
+ */
+static int
+tree_current_is_symblic_link_target(struct tree *t)
+{
+ static const struct stat *lst, *st;
+
+ lst = tree_current_lstat(t);
+ st = tree_current_stat(t);
+ return (st != NULL && lst != NULL &&
+ (int64_t)st->st_dev == t->current_filesystem->dev &&
+ st->st_dev != lst->st_dev);
+}
+
+/*
+ * Return the access path for the entry just returned from tree_next().
+ */
+static const char *
+tree_current_access_path(struct tree *t)
+{
+ return (t->basename);
+}
+
+/*
+ * Return the full path for the entry just returned from tree_next().
+ */
+static const char *
+tree_current_path(struct tree *t)
+{
+ return (t->path.s);
+}
+
+/*
+ * Terminate the traversal.
+ */
+static void
+tree_close(struct tree *t)
+{
+
+ if (t == NULL)
+ return;
+ if (t->entry_fd >= 0) {
+ close_and_restore_time(t->entry_fd, t, &t->restore_time);
+ t->entry_fd = -1;
+ }
+ /* Close the handle of readdir(). */
+ if (t->d != INVALID_DIR_HANDLE) {
+ closedir(t->d);
+ t->d = INVALID_DIR_HANDLE;
+ }
+ /* Release anything remaining in the stack. */
+ while (t->stack != NULL) {
+ if (t->stack->flags & isDirLink)
+ close(t->stack->symlink_parent_fd);
+ tree_pop(t);
+ }
+ if (t->working_dir_fd >= 0) {
+ close(t->working_dir_fd);
+ t->working_dir_fd = -1;
+ }
+ if (t->initial_dir_fd >= 0) {
+ close(t->initial_dir_fd);
+ t->initial_dir_fd = -1;
+ }
+}
+
+/*
+ * Release any resources.
+ */
+static void
+tree_free(struct tree *t)
+{
+ int i;
+
+ if (t == NULL)
+ return;
+ archive_string_free(&t->path);
+#if defined(USE_READDIR_R)
+ free(t->dirent);
+#endif
+ free(t->sparse_list);
+ for (i = 0; i < t->max_filesystem_id; i++)
+ free(t->filesystem_table[i].allocation_ptr);
+ free(t->filesystem_table);
+ free(t);
+}
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_read_disk_private.h b/contrib/libs/libarchive/libarchive/archive_read_disk_private.h
new file mode 100644
index 00000000000..bc8abc15d15
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_disk_private.h
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_read_disk_private.h 201105 2009-12-28 03:20:54Z kientzle $
+ */
+
+#ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
+#define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include "archive_platform_acl.h"
+
+struct tree;
+struct archive_entry;
+
+struct archive_read_disk {
+ struct archive archive;
+
+ /* Reused by archive_read_next_header() */
+ struct archive_entry *entry;
+
+ /*
+ * Symlink mode is one of 'L'ogical, 'P'hysical, or 'H'ybrid,
+ * following an old BSD convention. 'L' follows all symlinks,
+ * 'P' follows none, 'H' follows symlinks only for the first
+ * item.
+ */
+ char symlink_mode;
+
+ /*
+ * Since symlink interaction changes, we need to track whether
+ * we're following symlinks for the current item. 'L' mode above
+ * sets this true, 'P' sets it false, 'H' changes it as we traverse.
+ */
+ char follow_symlinks; /* Either 'L' or 'P'. */
+
+ /* Directory traversals. */
+ struct tree *tree;
+ int (*open_on_current_dir)(struct tree*, const char *, int);
+ int (*tree_current_dir_fd)(struct tree*);
+ int (*tree_enter_working_dir)(struct tree*);
+
+ /* Bitfield with ARCHIVE_READDISK_* tunables */
+ int flags;
+
+ const char * (*lookup_gname)(void *private, int64_t gid);
+ void (*cleanup_gname)(void *private);
+ void *lookup_gname_data;
+ const char * (*lookup_uname)(void *private, int64_t uid);
+ void (*cleanup_uname)(void *private);
+ void *lookup_uname_data;
+
+ int (*metadata_filter_func)(struct archive *, void *,
+ struct archive_entry *);
+ void *metadata_filter_data;
+
+ /* ARCHIVE_MATCH object. */
+ struct archive *matching;
+ /* Callback function, this will be invoked when ARCHIVE_MATCH
+ * archive_match_*_excluded_ae return true. */
+ void (*excluded_cb_func)(struct archive *, void *,
+ struct archive_entry *);
+ void *excluded_cb_data;
+};
+
+const char *
+archive_read_disk_entry_setup_path(struct archive_read_disk *,
+ struct archive_entry *, int *);
+
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *,
+ struct archive_entry *, int *);
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_read_disk_set_standard_lookup.c b/contrib/libs/libarchive/libarchive/archive_read_disk_set_standard_lookup.c
new file mode 100644
index 00000000000..c7fd2471ecb
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_disk_set_standard_lookup.c
@@ -0,0 +1,313 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_set_standard_lookup.c 201109 2009-12-28 03:30:31Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+int
+archive_read_disk_set_standard_lookup(struct archive *a)
+{
+ archive_set_error(a, -1, "Standard lookups not available on Windows");
+ return (ARCHIVE_FATAL);
+}
+#else /* ! (_WIN32 && !__CYGWIN__) */
+#define name_cache_size 127
+
+static const char * const NO_NAME = "(noname)";
+
+struct name_cache {
+ struct archive *archive;
+ char *buff;
+ size_t buff_size;
+ int probes;
+ int hits;
+ size_t size;
+ struct {
+ id_t id;
+ const char *name;
+ } cache[name_cache_size];
+};
+
+static const char * lookup_gname(void *, int64_t);
+static const char * lookup_uname(void *, int64_t);
+static void cleanup(void *);
+static const char * lookup_gname_helper(struct name_cache *, id_t gid);
+static const char * lookup_uname_helper(struct name_cache *, id_t uid);
+
+/*
+ * Installs functions that use getpwuid()/getgrgid()---along with
+ * a simple cache to accelerate such lookups---into the archive_read_disk
+ * object. This is in a separate file because getpwuid()/getgrgid()
+ * can pull in a LOT of library code (including NIS/LDAP functions, which
+ * pull in DNS resolvers, etc). This can easily top 500kB, which makes
+ * it inappropriate for some space-constrained applications.
+ *
+ * Applications that are size-sensitive may want to just use the
+ * real default functions (defined in archive_read_disk.c) that just
+ * use the uid/gid without the lookup. Or define your own custom functions
+ * if you prefer.
+ */
+int
+archive_read_disk_set_standard_lookup(struct archive *a)
+{
+ struct name_cache *ucache = malloc(sizeof(struct name_cache));
+ struct name_cache *gcache = malloc(sizeof(struct name_cache));
+
+ if (ucache == NULL || gcache == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate uname/gname lookup cache");
+ free(ucache);
+ free(gcache);
+ return (ARCHIVE_FATAL);
+ }
+
+ memset(ucache, 0, sizeof(*ucache));
+ ucache->archive = a;
+ ucache->size = name_cache_size;
+ memset(gcache, 0, sizeof(*gcache));
+ gcache->archive = a;
+ gcache->size = name_cache_size;
+
+ archive_read_disk_set_gname_lookup(a, gcache, lookup_gname, cleanup);
+ archive_read_disk_set_uname_lookup(a, ucache, lookup_uname, cleanup);
+
+ return (ARCHIVE_OK);
+}
+
+static void
+cleanup(void *data)
+{
+ struct name_cache *cache = (struct name_cache *)data;
+ size_t i;
+
+ if (cache != NULL) {
+ for (i = 0; i < cache->size; i++) {
+ if (cache->cache[i].name != NULL &&
+ cache->cache[i].name != NO_NAME)
+ free((void *)(uintptr_t)cache->cache[i].name);
+ }
+ free(cache->buff);
+ free(cache);
+ }
+}
+
+/*
+ * Lookup uid/gid from uname/gname, return NULL if no match.
+ */
+static const char *
+lookup_name(struct name_cache *cache,
+ const char * (*lookup_fn)(struct name_cache *, id_t), id_t id)
+{
+ const char *name;
+ int slot;
+
+
+ cache->probes++;
+
+ slot = id % cache->size;
+ if (cache->cache[slot].name != NULL) {
+ if (cache->cache[slot].id == id) {
+ cache->hits++;
+ if (cache->cache[slot].name == NO_NAME)
+ return (NULL);
+ return (cache->cache[slot].name);
+ }
+ if (cache->cache[slot].name != NO_NAME)
+ free((void *)(uintptr_t)cache->cache[slot].name);
+ cache->cache[slot].name = NULL;
+ }
+
+ name = (lookup_fn)(cache, id);
+ if (name == NULL) {
+ /* Cache and return the negative response. */
+ cache->cache[slot].name = NO_NAME;
+ cache->cache[slot].id = id;
+ return (NULL);
+ }
+
+ cache->cache[slot].name = name;
+ cache->cache[slot].id = id;
+ return (cache->cache[slot].name);
+}
+
+static const char *
+lookup_uname(void *data, int64_t uid)
+{
+ struct name_cache *uname_cache = (struct name_cache *)data;
+ return (lookup_name(uname_cache,
+ &lookup_uname_helper, (id_t)uid));
+}
+
+#if HAVE_GETPWUID_R
+static const char *
+lookup_uname_helper(struct name_cache *cache, id_t id)
+{
+ struct passwd pwent, *result;
+ char * nbuff;
+ size_t nbuff_size;
+ int r;
+
+ if (cache->buff_size == 0) {
+ cache->buff_size = 256;
+ cache->buff = malloc(cache->buff_size);
+ }
+ if (cache->buff == NULL)
+ return (NULL);
+ for (;;) {
+ result = &pwent; /* Old getpwuid_r ignores last arg. */
+ r = getpwuid_r((uid_t)id, &pwent,
+ cache->buff, cache->buff_size, &result);
+ if (r == 0)
+ break;
+ if (r != ERANGE)
+ break;
+ /* ERANGE means our buffer was too small, but POSIX
+ * doesn't tell us how big the buffer should be, so
+ * we just double it and try again. Because the buffer
+ * is kept around in the cache object, we shouldn't
+ * have to do this very often. */
+ nbuff_size = cache->buff_size * 2;
+ nbuff = realloc(cache->buff, nbuff_size);
+ if (nbuff == NULL)
+ break;
+ cache->buff = nbuff;
+ cache->buff_size = nbuff_size;
+ }
+ if (r != 0) {
+ archive_set_error(cache->archive, errno,
+ "Can't lookup user for id %d", (int)id);
+ return (NULL);
+ }
+ if (result == NULL)
+ return (NULL);
+
+ return strdup(result->pw_name);
+}
+#else
+static const char *
+lookup_uname_helper(struct name_cache *cache, id_t id)
+{
+ struct passwd *result;
+ (void)cache; /* UNUSED */
+
+ result = getpwuid((uid_t)id);
+
+ if (result == NULL)
+ return (NULL);
+
+ return strdup(result->pw_name);
+}
+#endif
+
+static const char *
+lookup_gname(void *data, int64_t gid)
+{
+ struct name_cache *gname_cache = (struct name_cache *)data;
+ return (lookup_name(gname_cache,
+ &lookup_gname_helper, (id_t)gid));
+}
+
+#if HAVE_GETGRGID_R
+static const char *
+lookup_gname_helper(struct name_cache *cache, id_t id)
+{
+ struct group grent, *result;
+ char * nbuff;
+ size_t nbuff_size;
+ int r;
+
+ if (cache->buff_size == 0) {
+ cache->buff_size = 256;
+ cache->buff = malloc(cache->buff_size);
+ }
+ if (cache->buff == NULL)
+ return (NULL);
+ for (;;) {
+ result = &grent; /* Old getgrgid_r ignores last arg. */
+ r = getgrgid_r((gid_t)id, &grent,
+ cache->buff, cache->buff_size, &result);
+ if (r == 0)
+ break;
+ if (r != ERANGE)
+ break;
+ /* ERANGE means our buffer was too small, but POSIX
+ * doesn't tell us how big the buffer should be, so
+ * we just double it and try again. */
+ nbuff_size = cache->buff_size * 2;
+ nbuff = realloc(cache->buff, nbuff_size);
+ if (nbuff == NULL)
+ break;
+ cache->buff = nbuff;
+ cache->buff_size = nbuff_size;
+ }
+ if (r != 0) {
+ archive_set_error(cache->archive, errno,
+ "Can't lookup group for id %d", (int)id);
+ return (NULL);
+ }
+ if (result == NULL)
+ return (NULL);
+
+ return strdup(result->gr_name);
+}
+#else
+static const char *
+lookup_gname_helper(struct name_cache *cache, id_t id)
+{
+ struct group *result;
+ (void)cache; /* UNUSED */
+
+ result = getgrgid((gid_t)id);
+
+ if (result == NULL)
+ return (NULL);
+
+ return strdup(result->gr_name);
+}
+#endif
+
+#endif /* ! (_WIN32 && !__CYGWIN__) */
diff --git a/contrib/libs/libarchive/libarchive/archive_read_disk_windows.c b/contrib/libs/libarchive/libarchive/archive_read_disk_windows.c
new file mode 100644
index 00000000000..f92a78a21ed
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_disk_windows.c
@@ -0,0 +1,2547 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <winioctl.h>
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef IO_REPARSE_TAG_SYMLINK
+/* Old SDKs do not provide IO_REPARSE_TAG_SYMLINK */
+#define IO_REPARSE_TAG_SYMLINK 0xA000000CL
+#endif
+
+/*-
+ * This is a new directory-walking system that addresses a number
+ * of problems I've had with fts(3). In particular, it has no
+ * pathname-length limits (other than the size of 'int'), handles
+ * deep logical traversals, uses considerably less memory, and has
+ * an opaque interface (easier to modify in the future).
+ *
+ * Internally, it keeps a single list of "tree_entry" items that
+ * represent filesystem objects that require further attention.
+ * Non-directories are not kept in memory: they are pulled from
+ * readdir(), returned to the client, then freed as soon as possible.
+ * Any directory entry to be traversed gets pushed onto the stack.
+ *
+ * There is surprisingly little information that needs to be kept for
+ * each item on the stack. Just the name, depth (represented here as the
+ * string length of the parent directory's pathname), and some markers
+ * indicating how to get back to the parent (via chdir("..") for a
+ * regular dir or via fchdir(2) for a symlink).
+ */
+
+struct restore_time {
+ const wchar_t *full_path;
+ FILETIME lastWriteTime;
+ FILETIME lastAccessTime;
+ mode_t filetype;
+};
+
+struct tree_entry {
+ int depth;
+ struct tree_entry *next;
+ struct tree_entry *parent;
+ size_t full_path_dir_length;
+ struct archive_wstring name;
+ struct archive_wstring full_path;
+ size_t dirname_length;
+ int64_t dev;
+ int64_t ino;
+ int flags;
+ int filesystem_id;
+ /* How to restore time of a directory. */
+ struct restore_time restore_time;
+};
+
+struct filesystem {
+ int64_t dev;
+ int synthetic;
+ int remote;
+ DWORD bytesPerSector;
+};
+
+/* Definitions for tree_entry.flags bitmap. */
+#define isDir 1 /* This entry is a regular directory. */
+#define isDirLink 2 /* This entry is a symbolic link to a directory. */
+#define needsFirstVisit 4 /* This is an initial entry. */
+#define needsDescent 8 /* This entry needs to be previsited. */
+#define needsOpen 16 /* This is a directory that needs to be opened. */
+#define needsAscent 32 /* This entry needs to be postvisited. */
+
+/*
+ * On Windows, "first visit" is handled as a pattern to be handed to
+ * _findfirst(). This is consistent with Windows conventions that
+ * file patterns are handled within the application. On Posix,
+ * "first visit" is just returned to the client.
+ */
+
+#define MAX_OVERLAPPED 8
+#define READ_BUFFER_SIZE (1024 * 64) /* Default to 64KB per https://technet.microsoft.com/en-us/library/cc938632.aspx */
+#define DIRECT_IO 0/* Disabled */
+#define ASYNC_IO 1/* Enabled */
+
+/*
+ * Local data for this package.
+ */
+struct tree {
+ struct tree_entry *stack;
+ struct tree_entry *current;
+ HANDLE d;
+ WIN32_FIND_DATAW _findData;
+ WIN32_FIND_DATAW *findData;
+ int flags;
+ int visit_type;
+ /* Error code from last failed operation. */
+ int tree_errno;
+
+ /* A full path with "\\?\" prefix. */
+ struct archive_wstring full_path;
+ size_t full_path_dir_length;
+ /* Dynamically-sized buffer for holding path */
+ struct archive_wstring path;
+
+ /* Last path element */
+ const wchar_t *basename;
+ /* Leading dir length */
+ size_t dirname_length;
+
+ int depth;
+
+ BY_HANDLE_FILE_INFORMATION lst;
+ BY_HANDLE_FILE_INFORMATION st;
+ int descend;
+ /* How to restore time of a file. */
+ struct restore_time restore_time;
+
+ struct entry_sparse {
+ int64_t length;
+ int64_t offset;
+ } *sparse_list, *current_sparse;
+ int sparse_count;
+ int sparse_list_size;
+
+ char initial_symlink_mode;
+ char symlink_mode;
+ struct filesystem *current_filesystem;
+ struct filesystem *filesystem_table;
+ int initial_filesystem_id;
+ int current_filesystem_id;
+ int max_filesystem_id;
+ int allocated_filesystem;
+
+ HANDLE entry_fh;
+ int entry_eof;
+ int64_t entry_remaining_bytes;
+ int64_t entry_total;
+
+ int ol_idx_doing;
+ int ol_idx_done;
+ int ol_num_doing;
+ int ol_num_done;
+ int64_t ol_remaining_bytes;
+ int64_t ol_total;
+ struct la_overlapped {
+ OVERLAPPED ol;
+ struct archive * _a;
+ unsigned char *buff;
+ size_t buff_size;
+ int64_t offset;
+ size_t bytes_expected;
+ size_t bytes_transferred;
+ } ol[MAX_OVERLAPPED];
+ int direct_io;
+ int async_io;
+};
+
+#define bhfi_dev(bhfi) ((bhfi)->dwVolumeSerialNumber)
+/* Treat FileIndex as i-node. We should remove a sequence number
+ * which is high-16-bits of nFileIndexHigh. */
+#define bhfi_ino(bhfi) \
+ ((((int64_t)((bhfi)->nFileIndexHigh & 0x0000FFFFUL)) << 32) \
+ + (bhfi)->nFileIndexLow)
+
+/* Definitions for tree.flags bitmap. */
+#define hasStat 16 /* The st entry is valid. */
+#define hasLstat 32 /* The lst entry is valid. */
+#define needsRestoreTimes 128
+
+static int
+tree_dir_next_windows(struct tree *t, const wchar_t *pattern);
+
+/* Initiate/terminate a tree traversal. */
+static struct tree *tree_open(const wchar_t *, int, int);
+static struct tree *tree_reopen(struct tree *, const wchar_t *, int);
+static void tree_close(struct tree *);
+static void tree_free(struct tree *);
+static void tree_push(struct tree *, const wchar_t *, const wchar_t *,
+ int, int64_t, int64_t, struct restore_time *);
+
+/*
+ * tree_next() returns Zero if there is no next entry, non-zero if
+ * there is. Note that directories are visited three times.
+ * Directories are always visited first as part of enumerating their
+ * parent; that is a "regular" visit. If tree_descend() is invoked at
+ * that time, the directory is added to a work list and will
+ * subsequently be visited two more times: once just after descending
+ * into the directory ("postdescent") and again just after ascending
+ * back to the parent ("postascent").
+ *
+ * TREE_ERROR_DIR is returned if the descent failed (because the
+ * directory couldn't be opened, for instance). This is returned
+ * instead of TREE_POSTDESCENT/TREE_POSTASCENT. TREE_ERROR_DIR is not a
+ * fatal error, but it does imply that the relevant subtree won't be
+ * visited. TREE_ERROR_FATAL is returned for an error that left the
+ * traversal completely hosed. Right now, this is only returned for
+ * chdir() failures during ascent.
+ */
+#define TREE_REGULAR 1
+#define TREE_POSTDESCENT 2
+#define TREE_POSTASCENT 3
+#define TREE_ERROR_DIR -1
+#define TREE_ERROR_FATAL -2
+
+static int tree_next(struct tree *);
+
+/*
+ * Return information about the current entry.
+ */
+
+/*
+ * The current full pathname, length of the full pathname, and a name
+ * that can be used to access the file. Because tree does use chdir
+ * extensively, the access path is almost never the same as the full
+ * current path.
+ *
+ */
+static const wchar_t *tree_current_path(struct tree *);
+static const wchar_t *tree_current_access_path(struct tree *);
+
+/*
+ * Request the lstat() or stat() data for the current path. Since the
+ * tree package needs to do some of this anyway, and caches the
+ * results, you should take advantage of it here if you need it rather
+ * than make a redundant stat() or lstat() call of your own.
+ */
+static const BY_HANDLE_FILE_INFORMATION *tree_current_stat(struct tree *);
+static const BY_HANDLE_FILE_INFORMATION *tree_current_lstat(struct tree *);
+
+/* The following functions use tricks to avoid a certain number of
+ * stat()/lstat() calls. */
+/* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */
+static int tree_current_is_physical_dir(struct tree *);
+/* "is_physical_link" is equivalent to S_ISLNK(tree_current_lstat()->st_mode) */
+static int tree_current_is_physical_link(struct tree *);
+/* Instead of archive_entry_copy_stat for BY_HANDLE_FILE_INFORMATION */
+static void tree_archive_entry_copy_bhfi(struct archive_entry *,
+ struct tree *, const BY_HANDLE_FILE_INFORMATION *);
+/* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */
+static int tree_current_is_dir(struct tree *);
+static int update_current_filesystem(struct archive_read_disk *a,
+ int64_t dev);
+static int setup_current_filesystem(struct archive_read_disk *);
+static int tree_target_is_same_as_parent(struct tree *,
+ const BY_HANDLE_FILE_INFORMATION *);
+
+static int _archive_read_disk_open_w(struct archive *, const wchar_t *);
+static int _archive_read_free(struct archive *);
+static int _archive_read_close(struct archive *);
+static int _archive_read_data_block(struct archive *,
+ const void **, size_t *, int64_t *);
+static int _archive_read_next_header(struct archive *,
+ struct archive_entry **);
+static int _archive_read_next_header2(struct archive *,
+ struct archive_entry *);
+static const char *trivial_lookup_gname(void *, int64_t gid);
+static const char *trivial_lookup_uname(void *, int64_t uid);
+static int setup_sparse(struct archive_read_disk *, struct archive_entry *);
+static int close_and_restore_time(HANDLE, struct tree *,
+ struct restore_time *);
+static int setup_sparse_from_disk(struct archive_read_disk *,
+ struct archive_entry *, HANDLE);
+static int la_linkname_from_handle(HANDLE, wchar_t **, int *);
+static int la_linkname_from_pathw(const wchar_t *, wchar_t **, int *);
+static void entry_symlink_from_pathw(struct archive_entry *,
+ const wchar_t *path);
+
+typedef struct _REPARSE_DATA_BUFFER {
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union {
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[1];
+ } SymbolicLinkReparseBuffer;
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ } DUMMYUNIONNAME;
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
+/*
+ * Reads the target of a symbolic link
+ *
+ * Returns 0 on success and -1 on failure
+ * outbuf is allocated in the function
+ */
+static int
+la_linkname_from_handle(HANDLE h, wchar_t **linkname, int *linktype)
+{
+ DWORD inbytes;
+ REPARSE_DATA_BUFFER *buf;
+ BY_HANDLE_FILE_INFORMATION st;
+ size_t len;
+ BOOL ret;
+ BYTE *indata;
+ wchar_t *tbuf;
+
+ ret = GetFileInformationByHandle(h, &st);
+ if (ret == 0 ||
+ (st.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
+ return (-1);
+ }
+
+ indata = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+ ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, indata,
+ 1024, &inbytes, NULL);
+ if (ret == 0) {
+ la_dosmaperr(GetLastError());
+ free(indata);
+ return (-1);
+ }
+
+ buf = (REPARSE_DATA_BUFFER *) indata;
+ if (buf->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
+ free(indata);
+ /* File is not a symbolic link */
+ errno = EINVAL;
+ return (-1);
+ }
+
+ len = buf->SymbolicLinkReparseBuffer.SubstituteNameLength;
+ if (len <= 0) {
+ free(indata);
+ return (-1);
+ }
+
+ tbuf = malloc(len + 1 * sizeof(wchar_t));
+ if (tbuf == NULL) {
+ free(indata);
+ return (-1);
+ }
+
+ memcpy(tbuf, &((BYTE *)buf->SymbolicLinkReparseBuffer.PathBuffer)
+ [buf->SymbolicLinkReparseBuffer.SubstituteNameOffset], len);
+ free(indata);
+
+ tbuf[len / sizeof(wchar_t)] = L'\0';
+
+ *linkname = tbuf;
+
+ /*
+ * Translate backslashes to slashes for libarchive internal use
+ */
+ while(*tbuf != L'\0') {
+ if (*tbuf == L'\\')
+ *tbuf = L'/';
+ tbuf++;
+ }
+
+ if ((st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ *linktype = AE_SYMLINK_TYPE_FILE;
+ else
+ *linktype = AE_SYMLINK_TYPE_DIRECTORY;
+
+ return (0);
+}
+
+/*
+ * Returns AE_SYMLINK_TYPE_FILE, AE_SYMLINK_TYPE_DIRECTORY or -1 on error
+ */
+static int
+la_linkname_from_pathw(const wchar_t *path, wchar_t **outbuf, int *linktype)
+{
+ HANDLE h;
+ const DWORD flag = FILE_FLAG_BACKUP_SEMANTICS |
+ FILE_FLAG_OPEN_REPARSE_POINT;
+ int ret;
+
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = flag;
+ h = CreateFile2(path, 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(path, 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, flag, NULL);
+#endif
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+
+ ret = la_linkname_from_handle(h, outbuf, linktype);
+ CloseHandle(h);
+
+ return (ret);
+}
+
+static void
+entry_symlink_from_pathw(struct archive_entry *entry, const wchar_t *path)
+{
+ wchar_t *linkname = NULL;
+ int ret, linktype;
+
+ ret = la_linkname_from_pathw(path, &linkname, &linktype);
+ if (ret != 0)
+ return;
+ if (linktype >= 0) {
+ archive_entry_copy_symlink_w(entry, linkname);
+ archive_entry_set_symlink_type(entry, linktype);
+ }
+ free(linkname);
+
+ return;
+}
+
+static const struct archive_vtable
+archive_read_disk_vtable = {
+ .archive_free = _archive_read_free,
+ .archive_close = _archive_read_close,
+ .archive_read_data_block = _archive_read_data_block,
+ .archive_read_next_header = _archive_read_next_header,
+ .archive_read_next_header2 = _archive_read_next_header2,
+};
+
+const char *
+archive_read_disk_gname(struct archive *_a, la_int64_t gid)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_gname"))
+ return (NULL);
+ if (a->lookup_gname == NULL)
+ return (NULL);
+ return ((*a->lookup_gname)(a->lookup_gname_data, gid));
+}
+
+const char *
+archive_read_disk_uname(struct archive *_a, la_int64_t uid)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_uname"))
+ return (NULL);
+ if (a->lookup_uname == NULL)
+ return (NULL);
+ return ((*a->lookup_uname)(a->lookup_uname_data, uid));
+}
+
+int
+archive_read_disk_set_gname_lookup(struct archive *_a,
+ void *private_data,
+ const char * (*lookup_gname)(void *private, la_int64_t gid),
+ void (*cleanup_gname)(void *private))
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup");
+
+ if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
+ (a->cleanup_gname)(a->lookup_gname_data);
+
+ a->lookup_gname = lookup_gname;
+ a->cleanup_gname = cleanup_gname;
+ a->lookup_gname_data = private_data;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_uname_lookup(struct archive *_a,
+ void *private_data,
+ const char * (*lookup_uname)(void *private, int64_t uid),
+ void (*cleanup_uname)(void *private))
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup");
+
+ if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
+ (a->cleanup_uname)(a->lookup_uname_data);
+
+ a->lookup_uname = lookup_uname;
+ a->cleanup_uname = cleanup_uname;
+ a->lookup_uname_data = private_data;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Create a new archive_read_disk object and initialize it with global state.
+ */
+struct archive *
+archive_read_disk_new(void)
+{
+ struct archive_read_disk *a;
+
+ a = (struct archive_read_disk *)calloc(1, sizeof(*a));
+ if (a == NULL)
+ return (NULL);
+ a->archive.magic = ARCHIVE_READ_DISK_MAGIC;
+ a->archive.state = ARCHIVE_STATE_NEW;
+ a->archive.vtable = &archive_read_disk_vtable;
+ a->entry = archive_entry_new2(&a->archive);
+ a->lookup_uname = trivial_lookup_uname;
+ a->lookup_gname = trivial_lookup_gname;
+ a->flags = ARCHIVE_READDISK_MAC_COPYFILE;
+ return (&a->archive);
+}
+
+static int
+_archive_read_free(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ int r;
+
+ if (_a == NULL)
+ return (ARCHIVE_OK);
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free");
+
+ if (a->archive.state != ARCHIVE_STATE_CLOSED)
+ r = _archive_read_close(&a->archive);
+ else
+ r = ARCHIVE_OK;
+
+ tree_free(a->tree);
+ if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
+ (a->cleanup_gname)(a->lookup_gname_data);
+ if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
+ (a->cleanup_uname)(a->lookup_uname_data);
+ archive_string_free(&a->archive.error_string);
+ archive_entry_free(a->entry);
+ a->archive.magic = 0;
+ free(a);
+ return (r);
+}
+
+static int
+_archive_read_close(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close");
+
+ if (a->archive.state != ARCHIVE_STATE_FATAL)
+ a->archive.state = ARCHIVE_STATE_CLOSED;
+
+ tree_close(a->tree);
+
+ return (ARCHIVE_OK);
+}
+
+static void
+setup_symlink_mode(struct archive_read_disk *a, char symlink_mode,
+ int follow_symlinks)
+{
+ a->symlink_mode = symlink_mode;
+ a->follow_symlinks = follow_symlinks;
+ if (a->tree != NULL) {
+ a->tree->initial_symlink_mode = a->symlink_mode;
+ a->tree->symlink_mode = a->symlink_mode;
+ }
+}
+
+int
+archive_read_disk_set_symlink_logical(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_logical");
+ setup_symlink_mode(a, 'L', 1);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_symlink_physical(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_physical");
+ setup_symlink_mode(a, 'P', 0);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_symlink_hybrid(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_hybrid");
+ setup_symlink_mode(a, 'H', 1);/* Follow symlinks initially. */
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_atime_restored(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_restore_atime");
+ a->flags |= ARCHIVE_READDISK_RESTORE_ATIME;
+ if (a->tree != NULL)
+ a->tree->flags |= needsRestoreTimes;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_behavior(struct archive *_a, int flags)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ int r = ARCHIVE_OK;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump");
+
+ a->flags = flags;
+
+ if (flags & ARCHIVE_READDISK_RESTORE_ATIME)
+ r = archive_read_disk_set_atime_restored(_a);
+ else {
+ if (a->tree != NULL)
+ a->tree->flags &= ~needsRestoreTimes;
+ }
+ return (r);
+}
+
+/*
+ * Trivial implementations of gname/uname lookup functions.
+ * These are normally overridden by the client, but these stub
+ * versions ensure that we always have something that works.
+ */
+static const char *
+trivial_lookup_gname(void *private_data, int64_t gid)
+{
+ (void)private_data; /* UNUSED */
+ (void)gid; /* UNUSED */
+ return (NULL);
+}
+
+static const char *
+trivial_lookup_uname(void *private_data, int64_t uid)
+{
+ (void)private_data; /* UNUSED */
+ (void)uid; /* UNUSED */
+ return (NULL);
+}
+
+static int64_t
+align_num_per_sector(struct tree *t, int64_t size)
+{
+ int64_t surplus;
+
+ size += t->current_filesystem->bytesPerSector -1;
+ surplus = size % t->current_filesystem->bytesPerSector;
+ size -= surplus;
+ return (size);
+}
+
+static int
+start_next_async_read(struct archive_read_disk *a, struct tree *t)
+{
+ struct la_overlapped *olp;
+ DWORD buffbytes, rbytes;
+
+ if (t->ol_remaining_bytes == 0)
+ return (ARCHIVE_EOF);
+
+ olp = &(t->ol[t->ol_idx_doing]);
+ t->ol_idx_doing = (t->ol_idx_doing + 1) % MAX_OVERLAPPED;
+
+ /* Allocate read buffer. */
+ if (olp->buff == NULL) {
+ void *p;
+ size_t s = (size_t)align_num_per_sector(t, READ_BUFFER_SIZE);
+ p = VirtualAlloc(NULL, s, MEM_COMMIT, PAGE_READWRITE);
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ olp->buff = p;
+ olp->buff_size = s;
+ olp->_a = &a->archive;
+ olp->ol.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
+ if (olp->ol.hEvent == NULL) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "CreateEvent failed");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ } else
+ ResetEvent(olp->ol.hEvent);
+
+ buffbytes = (DWORD)olp->buff_size;
+ if (buffbytes > t->current_sparse->length)
+ buffbytes = (DWORD)t->current_sparse->length;
+
+ /* Skip hole. */
+ if (t->current_sparse->offset > t->ol_total) {
+ t->ol_remaining_bytes -=
+ t->current_sparse->offset - t->ol_total;
+ }
+
+ olp->offset = t->current_sparse->offset;
+ olp->ol.Offset = (DWORD)(olp->offset & 0xffffffff);
+ olp->ol.OffsetHigh = (DWORD)(olp->offset >> 32);
+
+ if (t->ol_remaining_bytes > buffbytes) {
+ olp->bytes_expected = buffbytes;
+ t->ol_remaining_bytes -= buffbytes;
+ } else {
+ olp->bytes_expected = (size_t)t->ol_remaining_bytes;
+ t->ol_remaining_bytes = 0;
+ }
+ olp->bytes_transferred = 0;
+ t->current_sparse->offset += buffbytes;
+ t->current_sparse->length -= buffbytes;
+ t->ol_total = t->current_sparse->offset;
+ if (t->current_sparse->length == 0 && t->ol_remaining_bytes > 0)
+ t->current_sparse++;
+
+ if (!ReadFile(t->entry_fh, olp->buff, buffbytes, &rbytes, &(olp->ol))) {
+ DWORD lasterr;
+
+ lasterr = GetLastError();
+ if (lasterr == ERROR_HANDLE_EOF) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Reading file truncated");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ } else if (lasterr != ERROR_IO_PENDING) {
+ if (lasterr == ERROR_NO_DATA)
+ errno = EAGAIN;
+ else if (lasterr == ERROR_ACCESS_DENIED)
+ errno = EBADF;
+ else
+ la_dosmaperr(lasterr);
+ archive_set_error(&a->archive, errno, "Read error");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ } else
+ olp->bytes_transferred = rbytes;
+ t->ol_num_doing++;
+
+ return (t->ol_remaining_bytes == 0)? ARCHIVE_EOF: ARCHIVE_OK;
+}
+
+static void
+cancel_async(struct tree *t)
+{
+ if (t->ol_num_doing != t->ol_num_done) {
+ CancelIo(t->entry_fh);
+ t->ol_num_doing = t->ol_num_done = 0;
+ }
+}
+
+static int
+_archive_read_data_block(struct archive *_a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct tree *t = a->tree;
+ struct la_overlapped *olp;
+ DWORD bytes_transferred;
+ int r = ARCHIVE_FATAL;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_data_block");
+
+ if (t->entry_eof || t->entry_remaining_bytes <= 0) {
+ r = ARCHIVE_EOF;
+ goto abort_read_data;
+ }
+
+ /*
+ * Make a request to read the file in asynchronous.
+ */
+ if (t->ol_num_doing == 0) {
+ do {
+ r = start_next_async_read(a, t);
+ if (r == ARCHIVE_FATAL)
+ goto abort_read_data;
+ if (!t->async_io)
+ break;
+ } while (r == ARCHIVE_OK && t->ol_num_doing < MAX_OVERLAPPED);
+ } else {
+ if ((r = start_next_async_read(a, t)) == ARCHIVE_FATAL)
+ goto abort_read_data;
+ }
+
+ olp = &(t->ol[t->ol_idx_done]);
+ t->ol_idx_done = (t->ol_idx_done + 1) % MAX_OVERLAPPED;
+ if (olp->bytes_transferred)
+ bytes_transferred = (DWORD)olp->bytes_transferred;
+ else if (!GetOverlappedResult(t->entry_fh, &(olp->ol),
+ &bytes_transferred, TRUE)) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "GetOverlappedResult failed");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ r = ARCHIVE_FATAL;
+ goto abort_read_data;
+ }
+ t->ol_num_done++;
+
+ if (bytes_transferred == 0 ||
+ olp->bytes_expected != bytes_transferred) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Reading file truncated");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ r = ARCHIVE_FATAL;
+ goto abort_read_data;
+ }
+
+ *buff = olp->buff;
+ *size = bytes_transferred;
+ *offset = olp->offset;
+ if (olp->offset > t->entry_total)
+ t->entry_remaining_bytes -= olp->offset - t->entry_total;
+ t->entry_total = olp->offset + *size;
+ t->entry_remaining_bytes -= *size;
+ if (t->entry_remaining_bytes == 0) {
+ /* Close the current file descriptor */
+ close_and_restore_time(t->entry_fh, t, &t->restore_time);
+ t->entry_fh = INVALID_HANDLE_VALUE;
+ t->entry_eof = 1;
+ }
+ return (ARCHIVE_OK);
+
+abort_read_data:
+ *buff = NULL;
+ *size = 0;
+ *offset = t->entry_total;
+ if (t->entry_fh != INVALID_HANDLE_VALUE) {
+ cancel_async(t);
+ /* Close the current file descriptor */
+ close_and_restore_time(t->entry_fh, t, &t->restore_time);
+ t->entry_fh = INVALID_HANDLE_VALUE;
+ }
+ return (r);
+}
+
+static int
+next_entry(struct archive_read_disk *a, struct tree *t,
+ struct archive_entry *entry)
+{
+ const BY_HANDLE_FILE_INFORMATION *st;
+ const BY_HANDLE_FILE_INFORMATION *lst;
+ const char*name;
+ int descend, r;
+
+ st = NULL;
+ lst = NULL;
+ t->descend = 0;
+ do {
+ switch (tree_next(t)) {
+ case TREE_ERROR_FATAL:
+ archive_set_error(&a->archive, t->tree_errno,
+ "%ls: Unable to continue traversing directory tree",
+ tree_current_path(t));
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ case TREE_ERROR_DIR:
+ archive_set_error(&a->archive, t->tree_errno,
+ "%ls: Couldn't visit directory",
+ tree_current_path(t));
+ return (ARCHIVE_FAILED);
+ case 0:
+ return (ARCHIVE_EOF);
+ case TREE_POSTDESCENT:
+ case TREE_POSTASCENT:
+ break;
+ case TREE_REGULAR:
+ lst = tree_current_lstat(t);
+ if (lst == NULL) {
+ archive_set_error(&a->archive, t->tree_errno,
+ "%ls: Cannot stat",
+ tree_current_path(t));
+ return (ARCHIVE_FAILED);
+ }
+ break;
+ }
+ } while (lst == NULL);
+
+ archive_entry_copy_pathname_w(entry, tree_current_path(t));
+
+ /*
+ * Perform path matching.
+ */
+ if (a->matching) {
+ r = archive_match_path_excluded(a->matching, entry);
+ if (r < 0) {
+ archive_set_error(&(a->archive), errno,
+ "Failed : %s", archive_error_string(a->matching));
+ return (r);
+ }
+ if (r) {
+ if (a->excluded_cb_func)
+ a->excluded_cb_func(&(a->archive),
+ a->excluded_cb_data, entry);
+ return (ARCHIVE_RETRY);
+ }
+ }
+
+ /*
+ * Distinguish 'L'/'P'/'H' symlink following.
+ */
+ switch(t->symlink_mode) {
+ case 'H':
+ /* 'H': After the first item, rest like 'P'. */
+ t->symlink_mode = 'P';
+ /* 'H': First item (from command line) like 'L'. */
+ /* FALLTHROUGH */
+ case 'L':
+ /* 'L': Do descend through a symlink to dir. */
+ descend = tree_current_is_dir(t);
+ /* 'L': Follow symlinks to files. */
+ a->symlink_mode = 'L';
+ a->follow_symlinks = 1;
+ /* 'L': Archive symlinks as targets, if we can. */
+ st = tree_current_stat(t);
+ if (st != NULL && !tree_target_is_same_as_parent(t, st))
+ break;
+ /* If stat fails, we have a broken symlink;
+ * in that case, don't follow the link. */
+ /* FALLTHROUGH */
+ default:
+ /* 'P': Don't descend through a symlink to dir. */
+ descend = tree_current_is_physical_dir(t);
+ /* 'P': Don't follow symlinks to files. */
+ a->symlink_mode = 'P';
+ a->follow_symlinks = 0;
+ /* 'P': Archive symlinks as symlinks. */
+ st = lst;
+ break;
+ }
+
+ if (update_current_filesystem(a, bhfi_dev(st)) != ARCHIVE_OK) {
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ if (t->initial_filesystem_id == -1)
+ t->initial_filesystem_id = t->current_filesystem_id;
+ if (a->flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) {
+ if (t->initial_filesystem_id != t->current_filesystem_id)
+ return (ARCHIVE_RETRY);
+ }
+ t->descend = descend;
+
+ tree_archive_entry_copy_bhfi(entry, t, st);
+
+ /* Save the times to be restored. This must be in before
+ * calling archive_read_disk_descend() or any chance of it,
+ * especially, invoking a callback. */
+ t->restore_time.lastWriteTime = st->ftLastWriteTime;
+ t->restore_time.lastAccessTime = st->ftLastAccessTime;
+ t->restore_time.filetype = archive_entry_filetype(entry);
+
+ /*
+ * Perform time matching.
+ */
+ if (a->matching) {
+ r = archive_match_time_excluded(a->matching, entry);
+ if (r < 0) {
+ archive_set_error(&(a->archive), errno,
+ "Failed : %s", archive_error_string(a->matching));
+ return (r);
+ }
+ if (r) {
+ if (a->excluded_cb_func)
+ a->excluded_cb_func(&(a->archive),
+ a->excluded_cb_data, entry);
+ return (ARCHIVE_RETRY);
+ }
+ }
+
+ /* Lookup uname/gname */
+ name = archive_read_disk_uname(&(a->archive), archive_entry_uid(entry));
+ if (name != NULL)
+ archive_entry_copy_uname(entry, name);
+ name = archive_read_disk_gname(&(a->archive), archive_entry_gid(entry));
+ if (name != NULL)
+ archive_entry_copy_gname(entry, name);
+
+ /*
+ * Perform owner matching.
+ */
+ if (a->matching) {
+ r = archive_match_owner_excluded(a->matching, entry);
+ if (r < 0) {
+ archive_set_error(&(a->archive), errno,
+ "Failed : %s", archive_error_string(a->matching));
+ return (r);
+ }
+ if (r) {
+ if (a->excluded_cb_func)
+ a->excluded_cb_func(&(a->archive),
+ a->excluded_cb_data, entry);
+ return (ARCHIVE_RETRY);
+ }
+ }
+
+ /*
+ * File attributes
+ */
+ if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0) {
+ const int supported_attrs =
+ FILE_ATTRIBUTE_READONLY |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_SYSTEM;
+ DWORD file_attrs = st->dwFileAttributes & supported_attrs;
+ if (file_attrs != 0)
+ archive_entry_set_fflags(entry, file_attrs, 0);
+ }
+
+ /*
+ * Invoke a meta data filter callback.
+ */
+ if (a->metadata_filter_func) {
+ if (!a->metadata_filter_func(&(a->archive),
+ a->metadata_filter_data, entry))
+ return (ARCHIVE_RETRY);
+ }
+
+ archive_entry_copy_sourcepath_w(entry, tree_current_access_path(t));
+
+ r = ARCHIVE_OK;
+ if (archive_entry_filetype(entry) == AE_IFREG &&
+ archive_entry_size(entry) > 0) {
+ DWORD flags = FILE_FLAG_BACKUP_SEMANTICS;
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+ if (t->async_io)
+ flags |= FILE_FLAG_OVERLAPPED;
+ if (t->direct_io)
+ flags |= FILE_FLAG_NO_BUFFERING;
+ else
+ flags |= FILE_FLAG_SEQUENTIAL_SCAN;
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = flags;
+ t->entry_fh = CreateFile2(tree_current_access_path(t),
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ OPEN_EXISTING, &createExParams);
+#else
+ t->entry_fh = CreateFileW(tree_current_access_path(t),
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING, flags, NULL);
+#endif
+ if (t->entry_fh == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Couldn't open %ls", tree_current_path(a->tree));
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Find sparse data from the disk. */
+ if ((a->flags & ARCHIVE_READDISK_NO_SPARSE) == 0) {
+ if (archive_entry_hardlink(entry) == NULL &&
+ (st->dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) != 0)
+ r = setup_sparse_from_disk(a, entry, t->entry_fh);
+ }
+ }
+ return (r);
+}
+
+static int
+_archive_read_next_header(struct archive *_a, struct archive_entry **entryp)
+{
+ int ret;
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ *entryp = NULL;
+ ret = _archive_read_next_header2(_a, a->entry);
+ *entryp = a->entry;
+ return ret;
+}
+
+static int
+_archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct tree *t;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_read_next_header2");
+
+ t = a->tree;
+ if (t->entry_fh != INVALID_HANDLE_VALUE) {
+ cancel_async(t);
+ close_and_restore_time(t->entry_fh, t, &t->restore_time);
+ t->entry_fh = INVALID_HANDLE_VALUE;
+ }
+
+ archive_entry_clear(entry);
+
+ while ((r = next_entry(a, t, entry)) == ARCHIVE_RETRY)
+ archive_entry_clear(entry);
+
+ /*
+ * EOF and FATAL are persistent at this layer. By
+ * modifying the state, we guarantee that future calls to
+ * read a header or read data will fail.
+ */
+ switch (r) {
+ case ARCHIVE_EOF:
+ a->archive.state = ARCHIVE_STATE_EOF;
+ break;
+ case ARCHIVE_OK:
+ case ARCHIVE_WARN:
+ t->entry_total = 0;
+ if (archive_entry_filetype(entry) == AE_IFREG) {
+ t->entry_remaining_bytes = archive_entry_size(entry);
+ t->entry_eof = (t->entry_remaining_bytes == 0)? 1: 0;
+ if (!t->entry_eof &&
+ setup_sparse(a, entry) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } else {
+ t->entry_remaining_bytes = 0;
+ t->entry_eof = 1;
+ }
+ t->ol_idx_doing = t->ol_idx_done = 0;
+ t->ol_num_doing = t->ol_num_done = 0;
+ t->ol_remaining_bytes = t->entry_remaining_bytes;
+ t->ol_total = 0;
+ a->archive.state = ARCHIVE_STATE_DATA;
+ break;
+ case ARCHIVE_RETRY:
+ break;
+ case ARCHIVE_FATAL:
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ break;
+ }
+
+ __archive_reset_read_data(&a->archive);
+ return (r);
+}
+
+static int
+setup_sparse(struct archive_read_disk *a, struct archive_entry *entry)
+{
+ struct tree *t = a->tree;
+ int64_t aligned, length, offset;
+ int i;
+
+ t->sparse_count = archive_entry_sparse_reset(entry);
+ if (t->sparse_count+1 > t->sparse_list_size) {
+ free(t->sparse_list);
+ t->sparse_list_size = t->sparse_count + 1;
+ t->sparse_list = malloc(sizeof(t->sparse_list[0]) *
+ t->sparse_list_size);
+ if (t->sparse_list == NULL) {
+ t->sparse_list_size = 0;
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate data");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ }
+ /*
+ * Get sparse list and make sure those offsets and lengths are
+ * aligned by a sector size.
+ */
+ for (i = 0; i < t->sparse_count; i++) {
+ archive_entry_sparse_next(entry, &offset, &length);
+ aligned = align_num_per_sector(t, offset);
+ if (aligned != offset) {
+ aligned -= t->current_filesystem->bytesPerSector;
+ length += offset - aligned;
+ }
+ t->sparse_list[i].offset = aligned;
+ aligned = align_num_per_sector(t, length);
+ t->sparse_list[i].length = aligned;
+ }
+
+ aligned = align_num_per_sector(t, archive_entry_size(entry));
+ if (i == 0) {
+ t->sparse_list[i].offset = 0;
+ t->sparse_list[i].length = aligned;
+ } else {
+ int j, last = i;
+
+ t->sparse_list[i].offset = aligned;
+ t->sparse_list[i].length = 0;
+ for (i = 0; i < last; i++) {
+ if ((t->sparse_list[i].offset +
+ t->sparse_list[i].length) <=
+ t->sparse_list[i+1].offset)
+ continue;
+ /*
+ * Now sparse_list[i+1] is overlapped by sparse_list[i].
+ * Merge those two.
+ */
+ length = t->sparse_list[i+1].offset -
+ t->sparse_list[i].offset;
+ t->sparse_list[i+1].offset = t->sparse_list[i].offset;
+ t->sparse_list[i+1].length += length;
+ /* Remove sparse_list[i]. */
+ for (j = i; j < last; j++) {
+ t->sparse_list[j].offset =
+ t->sparse_list[j+1].offset;
+ t->sparse_list[j].length =
+ t->sparse_list[j+1].length;
+ }
+ last--;
+ }
+ }
+ t->current_sparse = t->sparse_list;
+
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_matching(struct archive *_a, struct archive *_ma,
+ void (*_excluded_func)(struct archive *, void *, struct archive_entry *),
+ void *_client_data)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_matching");
+ a->matching = _ma;
+ a->excluded_cb_func = _excluded_func;
+ a->excluded_cb_data = _client_data;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_metadata_filter_callback(struct archive *_a,
+ int (*_metadata_filter_func)(struct archive *, void *,
+ struct archive_entry *), void *_client_data)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY,
+ "archive_read_disk_set_metadata_filter_callback");
+
+ a->metadata_filter_func = _metadata_filter_func;
+ a->metadata_filter_data = _client_data;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_can_descend(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct tree *t = a->tree;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_read_disk_can_descend");
+
+ return (t->visit_type == TREE_REGULAR && t->descend);
+}
+
+/*
+ * Called by the client to mark the directory just returned from
+ * tree_next() as needing to be visited.
+ */
+int
+archive_read_disk_descend(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct tree *t = a->tree;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_read_disk_descend");
+
+ if (!archive_read_disk_can_descend(_a))
+ return (ARCHIVE_OK);
+
+ if (tree_current_is_physical_dir(t)) {
+ tree_push(t, t->basename, t->full_path.s,
+ t->current_filesystem_id,
+ bhfi_dev(&(t->lst)), bhfi_ino(&(t->lst)),
+ &t->restore_time);
+ t->stack->flags |= isDir;
+ } else if (tree_current_is_dir(t)) {
+ tree_push(t, t->basename, t->full_path.s,
+ t->current_filesystem_id,
+ bhfi_dev(&(t->st)), bhfi_ino(&(t->st)),
+ &t->restore_time);
+ t->stack->flags |= isDirLink;
+ }
+ t->descend = 0;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_open(struct archive *_a, const char *pathname)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct archive_wstring wpath;
+ int ret;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED,
+ "archive_read_disk_open");
+ archive_clear_error(&a->archive);
+
+ /* Make a wchar_t string from a char string. */
+ archive_string_init(&wpath);
+ if (archive_wstring_append_from_mbs(&wpath, pathname,
+ strlen(pathname)) != 0) {
+ if (errno == ENOMEM)
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't convert a path to a wchar_t string");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ ret = ARCHIVE_FATAL;
+ } else
+ ret = _archive_read_disk_open_w(_a, wpath.s);
+
+ archive_wstring_free(&wpath);
+ return (ret);
+}
+
+int
+archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED,
+ "archive_read_disk_open_w");
+ archive_clear_error(&a->archive);
+
+ return (_archive_read_disk_open_w(_a, pathname));
+}
+
+static int
+_archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ if (a->tree != NULL)
+ a->tree = tree_reopen(a->tree, pathname,
+ a->flags & ARCHIVE_READDISK_RESTORE_ATIME);
+ else
+ a->tree = tree_open(pathname, a->symlink_mode,
+ a->flags & ARCHIVE_READDISK_RESTORE_ATIME);
+ if (a->tree == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate directory traversal data");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ a->archive.state = ARCHIVE_STATE_HEADER;
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Return a current filesystem ID which is index of the filesystem entry
+ * you've visited through archive_read_disk.
+ */
+int
+archive_read_disk_current_filesystem(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_disk_current_filesystem");
+
+ return (a->tree->current_filesystem_id);
+}
+
+static int
+update_current_filesystem(struct archive_read_disk *a, int64_t dev)
+{
+ struct tree *t = a->tree;
+ int i, fid;
+
+ if (t->current_filesystem != NULL &&
+ t->current_filesystem->dev == dev)
+ return (ARCHIVE_OK);
+
+ for (i = 0; i < t->max_filesystem_id; i++) {
+ if (t->filesystem_table[i].dev == dev) {
+ /* There is the filesystem ID we've already generated. */
+ t->current_filesystem_id = i;
+ t->current_filesystem = &(t->filesystem_table[i]);
+ return (ARCHIVE_OK);
+ }
+ }
+
+ /*
+ * There is a new filesystem, we generate a new ID for.
+ */
+ fid = t->max_filesystem_id++;
+ if (t->max_filesystem_id > t->allocated_filesystem) {
+ size_t s;
+ void *p;
+
+ s = t->max_filesystem_id * 2;
+ p = realloc(t->filesystem_table,
+ s * sizeof(*t->filesystem_table));
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate tar data");
+ return (ARCHIVE_FATAL);
+ }
+ t->filesystem_table = (struct filesystem *)p;
+ t->allocated_filesystem = (int)s;
+ }
+ t->current_filesystem_id = fid;
+ t->current_filesystem = &(t->filesystem_table[fid]);
+ t->current_filesystem->dev = dev;
+
+ return (setup_current_filesystem(a));
+}
+
+/*
+ * Returns 1 if current filesystem is generated filesystem, 0 if it is not
+ * or -1 if it is unknown.
+ */
+int
+archive_read_disk_current_filesystem_is_synthetic(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_disk_current_filesystem");
+
+ return (a->tree->current_filesystem->synthetic);
+}
+
+/*
+ * Returns 1 if current filesystem is remote filesystem, 0 if it is not
+ * or -1 if it is unknown.
+ */
+int
+archive_read_disk_current_filesystem_is_remote(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_disk_current_filesystem");
+
+ return (a->tree->current_filesystem->remote);
+}
+
+/*
+ * If symlink is broken, statfs or statvfs will fail.
+ * Use its directory path instead.
+ */
+static wchar_t *
+safe_path_for_statfs(struct tree *t)
+{
+ const wchar_t *path;
+ wchar_t *cp, *p = NULL;
+
+ path = tree_current_access_path(t);
+ if (tree_current_stat(t) == NULL) {
+ p = _wcsdup(path);
+ cp = wcsrchr(p, '/');
+ if (cp != NULL && wcslen(cp) >= 2) {
+ cp[1] = '.';
+ cp[2] = '\0';
+ path = p;
+ }
+ } else
+ p = _wcsdup(path);
+ return (p);
+}
+
+/*
+ * Get conditions of synthetic and remote on Windows
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+ struct tree *t = a->tree;
+ wchar_t vol[256];
+ wchar_t *path;
+
+ t->current_filesystem->synthetic = -1;/* Not supported */
+ path = safe_path_for_statfs(t);
+ if (!GetVolumePathNameW(path, vol, sizeof(vol)/sizeof(vol[0]))) {
+ free(path);
+ t->current_filesystem->remote = -1;
+ t->current_filesystem->bytesPerSector = 0;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "GetVolumePathName failed: %d", (int)GetLastError());
+ return (ARCHIVE_FAILED);
+ }
+ free(path);
+ switch (GetDriveTypeW(vol)) {
+ case DRIVE_UNKNOWN:
+ case DRIVE_NO_ROOT_DIR:
+ t->current_filesystem->remote = -1;
+ break;
+ case DRIVE_REMOTE:
+ t->current_filesystem->remote = 1;
+ break;
+ default:
+ t->current_filesystem->remote = 0;
+ break;
+ }
+
+ if (!GetDiskFreeSpaceW(vol, NULL,
+ &(t->current_filesystem->bytesPerSector), NULL, NULL)) {
+ t->current_filesystem->bytesPerSector = 0;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "GetDiskFreeSpace failed: %d", (int)GetLastError());
+ return (ARCHIVE_FAILED);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int
+close_and_restore_time(HANDLE h, struct tree *t, struct restore_time *rt)
+{
+ HANDLE handle;
+ int r = 0;
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+ if (h == INVALID_HANDLE_VALUE && AE_IFLNK == rt->filetype)
+ return (0);
+
+ /* Close a file descriptor.
+ * It will not be used for SetFileTime() because it has been opened
+ * by a read only mode.
+ */
+ if (h != INVALID_HANDLE_VALUE)
+ CloseHandle(h);
+ if ((t->flags & needsRestoreTimes) == 0)
+ return (r);
+
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+ handle = CreateFile2(rt->full_path, FILE_WRITE_ATTRIBUTES,
+ 0, OPEN_EXISTING, &createExParams);
+#else
+ handle = CreateFileW(rt->full_path, FILE_WRITE_ATTRIBUTES,
+ 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+#endif
+ if (handle == INVALID_HANDLE_VALUE) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (SetFileTime(handle, NULL, &rt->lastAccessTime,
+ &rt->lastWriteTime) == 0) {
+ errno = EINVAL;
+ r = -1;
+ } else
+ r = 0;
+ CloseHandle(handle);
+ return (r);
+}
+
+/*
+ * Add a directory path to the current stack.
+ */
+static void
+tree_push(struct tree *t, const wchar_t *path, const wchar_t *full_path,
+ int filesystem_id, int64_t dev, int64_t ino, struct restore_time *rt)
+{
+ struct tree_entry *te;
+
+ te = calloc(1, sizeof(*te));
+ te->next = t->stack;
+ te->parent = t->current;
+ if (te->parent)
+ te->depth = te->parent->depth + 1;
+ t->stack = te;
+ archive_string_init(&te->name);
+ archive_wstrcpy(&te->name, path);
+ archive_string_init(&te->full_path);
+ archive_wstrcpy(&te->full_path, full_path);
+ te->flags = needsDescent | needsOpen | needsAscent;
+ te->filesystem_id = filesystem_id;
+ te->dev = dev;
+ te->ino = ino;
+ te->dirname_length = t->dirname_length;
+ te->full_path_dir_length = t->full_path_dir_length;
+ te->restore_time.full_path = te->full_path.s;
+ if (rt != NULL) {
+ te->restore_time.lastWriteTime = rt->lastWriteTime;
+ te->restore_time.lastAccessTime = rt->lastAccessTime;
+ te->restore_time.filetype = rt->filetype;
+ }
+}
+
+/*
+ * Append a name to the current dir path.
+ */
+static void
+tree_append(struct tree *t, const wchar_t *name, size_t name_length)
+{
+ size_t size_needed;
+
+ t->path.s[t->dirname_length] = L'\0';
+ t->path.length = t->dirname_length;
+ /* Strip trailing '/' from name, unless entire name is "/". */
+ while (name_length > 1 && name[name_length - 1] == L'/')
+ name_length--;
+
+ /* Resize pathname buffer as needed. */
+ size_needed = name_length + t->dirname_length + 2;
+ archive_wstring_ensure(&t->path, size_needed);
+ /* Add a separating '/' if it's needed. */
+ if (t->dirname_length > 0 &&
+ t->path.s[archive_strlen(&t->path)-1] != L'/')
+ archive_wstrappend_wchar(&t->path, L'/');
+ t->basename = t->path.s + archive_strlen(&t->path);
+ archive_wstrncat(&t->path, name, name_length);
+ t->restore_time.full_path = t->basename;
+ if (t->full_path_dir_length > 0) {
+ t->full_path.s[t->full_path_dir_length] = L'\0';
+ t->full_path.length = t->full_path_dir_length;
+ size_needed = name_length + t->full_path_dir_length + 2;
+ archive_wstring_ensure(&t->full_path, size_needed);
+ /* Add a separating '\' if it's needed. */
+ if (t->full_path.s[archive_strlen(&t->full_path)-1] != L'\\')
+ archive_wstrappend_wchar(&t->full_path, L'\\');
+ archive_wstrncat(&t->full_path, name, name_length);
+ t->restore_time.full_path = t->full_path.s;
+ }
+}
+
+/*
+ * Open a directory tree for traversal.
+ */
+static struct tree *
+tree_open(const wchar_t *path, int symlink_mode, int restore_time)
+{
+ struct tree *t;
+
+ t = calloc(1, sizeof(*t));
+ archive_string_init(&(t->full_path));
+ archive_string_init(&t->path);
+ archive_wstring_ensure(&t->path, 15);
+ t->initial_symlink_mode = symlink_mode;
+ return (tree_reopen(t, path, restore_time));
+}
+
+static struct tree *
+tree_reopen(struct tree *t, const wchar_t *path, int restore_time)
+{
+ struct archive_wstring ws;
+ wchar_t *pathname, *p, *base;
+
+ t->flags = (restore_time != 0)?needsRestoreTimes:0;
+ t->visit_type = 0;
+ t->tree_errno = 0;
+ t->full_path_dir_length = 0;
+ t->dirname_length = 0;
+ t->depth = 0;
+ t->descend = 0;
+ t->current = NULL;
+ t->d = INVALID_HANDLE_VALUE;
+ t->symlink_mode = t->initial_symlink_mode;
+ archive_string_empty(&(t->full_path));
+ archive_string_empty(&t->path);
+ t->entry_fh = INVALID_HANDLE_VALUE;
+ t->entry_eof = 0;
+ t->entry_remaining_bytes = 0;
+ t->initial_filesystem_id = -1;
+
+ /* Get wchar_t strings from char strings. */
+ archive_string_init(&ws);
+ archive_wstrcpy(&ws, path);
+ pathname = ws.s;
+ /* Get a full-path-name. */
+ p = __la_win_permissive_name_w(pathname);
+ if (p == NULL)
+ goto failed;
+ archive_wstrcpy(&(t->full_path), p);
+ free(p);
+
+ /* Convert path separators from '\' to '/' */
+ for (p = pathname; *p != L'\0'; ++p) {
+ if (*p == L'\\')
+ *p = L'/';
+ }
+ base = pathname;
+
+ /* First item is set up a lot like a symlink traversal. */
+ /* printf("Looking for wildcard in %s\n", path); */
+ if ((base[0] == L'/' && base[1] == L'/' &&
+ base[2] == L'?' && base[3] == L'/' &&
+ (wcschr(base+4, L'*') || wcschr(base+4, L'?'))) ||
+ (!(base[0] == L'/' && base[1] == L'/' &&
+ base[2] == L'?' && base[3] == L'/') &&
+ (wcschr(base, L'*') || wcschr(base, L'?')))) {
+ // It has a wildcard in it...
+ // Separate the last element.
+ p = wcsrchr(base, L'/');
+ if (p != NULL) {
+ *p = L'\0';
+ tree_append(t, base, p - base);
+ t->dirname_length = archive_strlen(&t->path);
+ base = p + 1;
+ }
+ p = wcsrchr(t->full_path.s, L'\\');
+ if (p != NULL) {
+ *p = L'\0';
+ t->full_path.length = wcslen(t->full_path.s);
+ t->full_path_dir_length = archive_strlen(&t->full_path);
+ }
+ }
+ tree_push(t, base, t->full_path.s, 0, 0, 0, NULL);
+ archive_wstring_free(&ws);
+ t->stack->flags = needsFirstVisit;
+ /*
+ * Debug flag for Direct IO(No buffering) or Async IO.
+ * Those dependent on environment variable switches
+ * will be removed until next release.
+ */
+ {
+ const char *e;
+ if ((e = getenv("LIBARCHIVE_DIRECT_IO")) != NULL) {
+ if (e[0] == '0')
+ t->direct_io = 0;
+ else
+ t->direct_io = 1;
+ fprintf(stderr, "LIBARCHIVE_DIRECT_IO=%s\n",
+ (t->direct_io)?"Enabled":"Disabled");
+ } else
+ t->direct_io = DIRECT_IO;
+ if ((e = getenv("LIBARCHIVE_ASYNC_IO")) != NULL) {
+ if (e[0] == '0')
+ t->async_io = 0;
+ else
+ t->async_io = 1;
+ fprintf(stderr, "LIBARCHIVE_ASYNC_IO=%s\n",
+ (t->async_io)?"Enabled":"Disabled");
+ } else
+ t->async_io = ASYNC_IO;
+ }
+ return (t);
+failed:
+ archive_wstring_free(&ws);
+ tree_free(t);
+ return (NULL);
+}
+
+static int
+tree_descent(struct tree *t)
+{
+ t->dirname_length = archive_strlen(&t->path);
+ t->full_path_dir_length = archive_strlen(&t->full_path);
+ t->depth++;
+ return (0);
+}
+
+/*
+ * We've finished a directory; ascend back to the parent.
+ */
+static int
+tree_ascend(struct tree *t)
+{
+ struct tree_entry *te;
+
+ te = t->stack;
+ t->depth--;
+ close_and_restore_time(INVALID_HANDLE_VALUE, t, &te->restore_time);
+ return (0);
+}
+
+/*
+ * Pop the working stack.
+ */
+static void
+tree_pop(struct tree *t)
+{
+ struct tree_entry *te;
+
+ t->full_path.s[t->full_path_dir_length] = L'\0';
+ t->full_path.length = t->full_path_dir_length;
+ t->path.s[t->dirname_length] = L'\0';
+ t->path.length = t->dirname_length;
+ if (t->stack == t->current && t->current != NULL)
+ t->current = t->current->parent;
+ te = t->stack;
+ t->stack = te->next;
+ t->dirname_length = te->dirname_length;
+ t->basename = t->path.s + t->dirname_length;
+ t->full_path_dir_length = te->full_path_dir_length;
+ while (t->basename[0] == L'/')
+ t->basename++;
+ archive_wstring_free(&te->name);
+ archive_wstring_free(&te->full_path);
+ free(te);
+}
+
+/*
+ * Get the next item in the tree traversal.
+ */
+static int
+tree_next(struct tree *t)
+{
+ int r;
+
+ while (t->stack != NULL) {
+ /* If there's an open dir, get the next entry from there. */
+ if (t->d != INVALID_HANDLE_VALUE) {
+ r = tree_dir_next_windows(t, NULL);
+ if (r == 0)
+ continue;
+ return (r);
+ }
+
+ if (t->stack->flags & needsFirstVisit) {
+ wchar_t *d = t->stack->name.s;
+ t->stack->flags &= ~needsFirstVisit;
+ if (!(d[0] == L'/' && d[1] == L'/' &&
+ d[2] == L'?' && d[3] == L'/') &&
+ (wcschr(d, L'*') || wcschr(d, L'?'))) {
+ r = tree_dir_next_windows(t, d);
+ if (r == 0)
+ continue;
+ return (r);
+ } else {
+ HANDLE h = FindFirstFileW(t->stack->full_path.s, &t->_findData);
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ t->tree_errno = errno;
+ t->visit_type = TREE_ERROR_DIR;
+ return (t->visit_type);
+ }
+ t->findData = &t->_findData;
+ FindClose(h);
+ }
+ /* Top stack item needs a regular visit. */
+ t->current = t->stack;
+ tree_append(t, t->stack->name.s,
+ archive_strlen(&(t->stack->name)));
+ //t->dirname_length = t->path_length;
+ //tree_pop(t);
+ t->stack->flags &= ~needsFirstVisit;
+ return (t->visit_type = TREE_REGULAR);
+ } else if (t->stack->flags & needsDescent) {
+ /* Top stack item is dir to descend into. */
+ t->current = t->stack;
+ tree_append(t, t->stack->name.s,
+ archive_strlen(&(t->stack->name)));
+ t->stack->flags &= ~needsDescent;
+ r = tree_descent(t);
+ if (r != 0) {
+ tree_pop(t);
+ t->visit_type = r;
+ } else
+ t->visit_type = TREE_POSTDESCENT;
+ return (t->visit_type);
+ } else if (t->stack->flags & needsOpen) {
+ t->stack->flags &= ~needsOpen;
+ r = tree_dir_next_windows(t, L"*");
+ if (r == 0)
+ continue;
+ return (r);
+ } else if (t->stack->flags & needsAscent) {
+ /* Top stack item is dir and we're done with it. */
+ r = tree_ascend(t);
+ tree_pop(t);
+ t->visit_type = r != 0 ? r : TREE_POSTASCENT;
+ return (t->visit_type);
+ } else {
+ /* Top item on stack is dead. */
+ tree_pop(t);
+ t->flags &= ~hasLstat;
+ t->flags &= ~hasStat;
+ }
+ }
+ return (t->visit_type = 0);
+}
+
+static int
+tree_dir_next_windows(struct tree *t, const wchar_t *pattern)
+{
+ const wchar_t *name;
+ size_t namelen;
+ int r;
+
+ for (;;) {
+ if (pattern != NULL) {
+ struct archive_wstring pt;
+
+ archive_string_init(&pt);
+ archive_wstring_ensure(&pt,
+ archive_strlen(&(t->full_path))
+ + 2 + wcslen(pattern));
+ archive_wstring_copy(&pt, &(t->full_path));
+ archive_wstrappend_wchar(&pt, L'\\');
+ archive_wstrcat(&pt, pattern);
+ t->d = FindFirstFileW(pt.s, &t->_findData);
+ archive_wstring_free(&pt);
+ if (t->d == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ t->tree_errno = errno;
+ r = tree_ascend(t); /* Undo "chdir" */
+ tree_pop(t);
+ t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
+ return (t->visit_type);
+ }
+ t->findData = &t->_findData;
+ pattern = NULL;
+ } else if (!FindNextFileW(t->d, &t->_findData)) {
+ FindClose(t->d);
+ t->d = INVALID_HANDLE_VALUE;
+ t->findData = NULL;
+ return (0);
+ }
+ name = t->findData->cFileName;
+ namelen = wcslen(name);
+ t->flags &= ~hasLstat;
+ t->flags &= ~hasStat;
+ if (name[0] == L'.' && name[1] == L'\0')
+ continue;
+ if (name[0] == L'.' && name[1] == L'.' && name[2] == L'\0')
+ continue;
+ tree_append(t, name, namelen);
+ return (t->visit_type = TREE_REGULAR);
+ }
+}
+
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+static void
+fileTimeToUtc(const FILETIME *filetime, time_t *t, long *ns)
+{
+ ULARGE_INTEGER utc;
+
+ utc.HighPart = filetime->dwHighDateTime;
+ utc.LowPart = filetime->dwLowDateTime;
+ if (utc.QuadPart >= EPOC_TIME) {
+ utc.QuadPart -= EPOC_TIME;
+ /* milli seconds base */
+ *t = (time_t)(utc.QuadPart / 10000000);
+ /* nano seconds base */
+ *ns = (long)(utc.QuadPart % 10000000) * 100;
+ } else {
+ *t = 0;
+ *ns = 0;
+ }
+}
+
+static void
+entry_copy_bhfi(struct archive_entry *entry, const wchar_t *path,
+ const WIN32_FIND_DATAW *findData,
+ const BY_HANDLE_FILE_INFORMATION *bhfi)
+{
+ time_t secs;
+ long nsecs;
+ mode_t mode;
+
+ fileTimeToUtc(&bhfi->ftLastAccessTime, &secs, &nsecs);
+ archive_entry_set_atime(entry, secs, nsecs);
+ fileTimeToUtc(&bhfi->ftLastWriteTime, &secs, &nsecs);
+ archive_entry_set_mtime(entry, secs, nsecs);
+ fileTimeToUtc(&bhfi->ftCreationTime, &secs, &nsecs);
+ archive_entry_set_birthtime(entry, secs, nsecs);
+ archive_entry_set_ctime(entry, secs, nsecs);
+ archive_entry_set_dev(entry, bhfi_dev(bhfi));
+ archive_entry_set_ino64(entry, bhfi_ino(bhfi));
+ if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ archive_entry_set_nlink(entry, bhfi->nNumberOfLinks + 1);
+ else
+ archive_entry_set_nlink(entry, bhfi->nNumberOfLinks);
+ archive_entry_set_size(entry,
+ (((int64_t)bhfi->nFileSizeHigh) << 32)
+ + bhfi->nFileSizeLow);
+ archive_entry_set_uid(entry, 0);
+ archive_entry_set_gid(entry, 0);
+ archive_entry_set_rdev(entry, 0);
+
+ mode = S_IRUSR | S_IRGRP | S_IROTH;
+ if ((bhfi->dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
+ mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+ if ((bhfi->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
+ findData != NULL &&
+ findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK) {
+ mode |= S_IFLNK;
+ entry_symlink_from_pathw(entry, path);
+ } else if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ else {
+ const wchar_t *p;
+
+ mode |= S_IFREG;
+ p = wcsrchr(path, L'.');
+ if (p != NULL && wcslen(p) == 4) {
+ switch (p[1]) {
+ case L'B': case L'b':
+ if ((p[2] == L'A' || p[2] == L'a' ) &&
+ (p[3] == L'T' || p[3] == L't' ))
+ mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ case L'C': case L'c':
+ if (((p[2] == L'M' || p[2] == L'm' ) &&
+ (p[3] == L'D' || p[3] == L'd' )))
+ mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ case L'E': case L'e':
+ if ((p[2] == L'X' || p[2] == L'x' ) &&
+ (p[3] == L'E' || p[3] == L'e' ))
+ mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ archive_entry_set_mode(entry, mode);
+}
+
+static void
+tree_archive_entry_copy_bhfi(struct archive_entry *entry, struct tree *t,
+ const BY_HANDLE_FILE_INFORMATION *bhfi)
+{
+ entry_copy_bhfi(entry, tree_current_path(t), t->findData, bhfi);
+}
+
+static int
+tree_current_file_information(struct tree *t, BY_HANDLE_FILE_INFORMATION *st,
+ int sim_lstat)
+{
+ HANDLE h;
+ int r;
+ DWORD flag = FILE_FLAG_BACKUP_SEMANTICS;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+ if (sim_lstat && tree_current_is_physical_link(t))
+ flag |= FILE_FLAG_OPEN_REPARSE_POINT;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = flag;
+ h = CreateFile2(tree_current_access_path(t), 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(tree_current_access_path(t), 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, flag, NULL);
+#endif
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ t->tree_errno = errno;
+ return (0);
+ }
+ r = GetFileInformationByHandle(h, st);
+ CloseHandle(h);
+ return (r);
+}
+
+/*
+ * Get the stat() data for the entry just returned from tree_next().
+ */
+static const BY_HANDLE_FILE_INFORMATION *
+tree_current_stat(struct tree *t)
+{
+ if (!(t->flags & hasStat)) {
+ if (!tree_current_file_information(t, &t->st, 0))
+ return NULL;
+ t->flags |= hasStat;
+ }
+ return (&t->st);
+}
+
+/*
+ * Get the lstat() data for the entry just returned from tree_next().
+ */
+static const BY_HANDLE_FILE_INFORMATION *
+tree_current_lstat(struct tree *t)
+{
+ if (!(t->flags & hasLstat)) {
+ if (!tree_current_file_information(t, &t->lst, 1))
+ return NULL;
+ t->flags |= hasLstat;
+ }
+ return (&t->lst);
+}
+
+/*
+ * Test whether current entry is a dir or link to a dir.
+ */
+static int
+tree_current_is_dir(struct tree *t)
+{
+ if (t->findData)
+ return (t->findData->dwFileAttributes
+ & FILE_ATTRIBUTE_DIRECTORY);
+ return (0);
+}
+
+/*
+ * Test whether current entry is a physical directory. Usually, we
+ * already have at least one of stat() or lstat() in memory, so we
+ * use tricks to try to avoid an extra trip to the disk.
+ */
+static int
+tree_current_is_physical_dir(struct tree *t)
+{
+ if (tree_current_is_physical_link(t))
+ return (0);
+ return (tree_current_is_dir(t));
+}
+
+/*
+ * Test whether current entry is a symbolic link.
+ */
+static int
+tree_current_is_physical_link(struct tree *t)
+{
+ if (t->findData)
+ return ((t->findData->dwFileAttributes
+ & FILE_ATTRIBUTE_REPARSE_POINT) &&
+ (t->findData->dwReserved0
+ == IO_REPARSE_TAG_SYMLINK));
+ return (0);
+}
+
+/*
+ * Test whether the same file has been in the tree as its parent.
+ */
+static int
+tree_target_is_same_as_parent(struct tree *t,
+ const BY_HANDLE_FILE_INFORMATION *st)
+{
+ struct tree_entry *te;
+ int64_t dev = bhfi_dev(st);
+ int64_t ino = bhfi_ino(st);
+
+ for (te = t->current->parent; te != NULL; te = te->parent) {
+ if (te->dev == dev && te->ino == ino)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Return the access path for the entry just returned from tree_next().
+ */
+static const wchar_t *
+tree_current_access_path(struct tree *t)
+{
+ return (t->full_path.s);
+}
+
+/*
+ * Return the full path for the entry just returned from tree_next().
+ */
+static const wchar_t *
+tree_current_path(struct tree *t)
+{
+ return (t->path.s);
+}
+
+/*
+ * Terminate the traversal.
+ */
+static void
+tree_close(struct tree *t)
+{
+
+ if (t == NULL)
+ return;
+ if (t->entry_fh != INVALID_HANDLE_VALUE) {
+ cancel_async(t);
+ close_and_restore_time(t->entry_fh, t, &t->restore_time);
+ t->entry_fh = INVALID_HANDLE_VALUE;
+ }
+ /* Close the handle of FindFirstFileW */
+ if (t->d != INVALID_HANDLE_VALUE) {
+ FindClose(t->d);
+ t->d = INVALID_HANDLE_VALUE;
+ t->findData = NULL;
+ }
+ /* Release anything remaining in the stack. */
+ while (t->stack != NULL)
+ tree_pop(t);
+}
+
+/*
+ * Release any resources.
+ */
+static void
+tree_free(struct tree *t)
+{
+ int i;
+
+ if (t == NULL)
+ return;
+ archive_wstring_free(&t->path);
+ archive_wstring_free(&t->full_path);
+ free(t->sparse_list);
+ free(t->filesystem_table);
+ for (i = 0; i < MAX_OVERLAPPED; i++) {
+ if (t->ol[i].buff)
+ VirtualFree(t->ol[i].buff, 0, MEM_RELEASE);
+ CloseHandle(t->ol[i].ol.hEvent);
+ }
+ free(t);
+}
+
+
+/*
+ * Populate the archive_entry with metadata from the disk.
+ */
+int
+archive_read_disk_entry_from_file(struct archive *_a,
+ struct archive_entry *entry, int fd, const struct stat *st)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ const wchar_t *path;
+ const wchar_t *wname;
+ const char *name;
+ HANDLE h;
+ BY_HANDLE_FILE_INFORMATION bhfi;
+ DWORD fileAttributes = 0;
+ int r;
+
+ archive_clear_error(_a);
+ wname = archive_entry_sourcepath_w(entry);
+ if (wname == NULL)
+ wname = archive_entry_pathname_w(entry);
+ if (wname == NULL) {
+ archive_set_error(&a->archive, EINVAL,
+ "Can't get a wide character version of the path");
+ return (ARCHIVE_FAILED);
+ }
+ path = __la_win_permissive_name_w(wname);
+
+ if (st == NULL) {
+ /*
+ * Get metadata through GetFileInformationByHandle().
+ */
+ if (fd >= 0) {
+ h = (HANDLE)_get_osfhandle(fd);
+ r = GetFileInformationByHandle(h, &bhfi);
+ if (r == 0) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Can't GetFileInformationByHandle");
+ return (ARCHIVE_FAILED);
+ }
+ entry_copy_bhfi(entry, path, NULL, &bhfi);
+ } else {
+ WIN32_FIND_DATAW findData;
+ DWORD flag, desiredAccess;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+ h = FindFirstFileW(path, &findData);
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Can't FindFirstFileW");
+ return (ARCHIVE_FAILED);
+ }
+ FindClose(h);
+
+ flag = FILE_FLAG_BACKUP_SEMANTICS;
+ if (!a->follow_symlinks &&
+ (findData.dwFileAttributes
+ & FILE_ATTRIBUTE_REPARSE_POINT) &&
+ (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
+ flag |= FILE_FLAG_OPEN_REPARSE_POINT;
+ desiredAccess = 0;
+ } else if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ desiredAccess = 0;
+ } else
+ desiredAccess = GENERIC_READ;
+
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = flag;
+ h = CreateFile2(path, desiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(path, desiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, flag, NULL);
+#endif
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Can't CreateFileW");
+ return (ARCHIVE_FAILED);
+ }
+ r = GetFileInformationByHandle(h, &bhfi);
+ if (r == 0) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Can't GetFileInformationByHandle");
+ CloseHandle(h);
+ return (ARCHIVE_FAILED);
+ }
+ entry_copy_bhfi(entry, path, &findData, &bhfi);
+ }
+ fileAttributes = bhfi.dwFileAttributes;
+ } else {
+ archive_entry_copy_stat(entry, st);
+ if (st->st_mode & S_IFLNK)
+ entry_symlink_from_pathw(entry, path);
+ h = INVALID_HANDLE_VALUE;
+ }
+
+ /* Lookup uname/gname */
+ name = archive_read_disk_uname(_a, archive_entry_uid(entry));
+ if (name != NULL)
+ archive_entry_copy_uname(entry, name);
+ name = archive_read_disk_gname(_a, archive_entry_gid(entry));
+ if (name != NULL)
+ archive_entry_copy_gname(entry, name);
+
+ /*
+ * File attributes
+ */
+ if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0) {
+ const int supported_attrs =
+ FILE_ATTRIBUTE_READONLY |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_SYSTEM;
+ DWORD file_attrs = fileAttributes & supported_attrs;
+ if (file_attrs != 0)
+ archive_entry_set_fflags(entry, file_attrs, 0);
+ }
+
+ /*
+ * Can this file be sparse file ?
+ */
+ if (archive_entry_filetype(entry) != AE_IFREG
+ || archive_entry_size(entry) <= 0
+ || archive_entry_hardlink(entry) != NULL) {
+ if (h != INVALID_HANDLE_VALUE && fd < 0)
+ CloseHandle(h);
+ return (ARCHIVE_OK);
+ }
+
+ if (h == INVALID_HANDLE_VALUE) {
+ if (fd >= 0) {
+ h = (HANDLE)_get_osfhandle(fd);
+ } else {
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+ h = CreateFile2(path, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(path, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+#endif
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Can't CreateFileW");
+ return (ARCHIVE_FAILED);
+ }
+ }
+ r = GetFileInformationByHandle(h, &bhfi);
+ if (r == 0) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Can't GetFileInformationByHandle");
+ if (h != INVALID_HANDLE_VALUE && fd < 0)
+ CloseHandle(h);
+ return (ARCHIVE_FAILED);
+ }
+ fileAttributes = bhfi.dwFileAttributes;
+ }
+
+ /* Sparse file must be set a mark, FILE_ATTRIBUTE_SPARSE_FILE */
+ if ((fileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) == 0) {
+ if (fd < 0)
+ CloseHandle(h);
+ return (ARCHIVE_OK);
+ }
+
+ if ((a->flags & ARCHIVE_READDISK_NO_SPARSE) == 0) {
+ r = setup_sparse_from_disk(a, entry, h);
+ if (fd < 0)
+ CloseHandle(h);
+ }
+
+ return (r);
+}
+
+/*
+ * Windows sparse interface.
+ */
+#if defined(__MINGW32__) && !defined(FSCTL_QUERY_ALLOCATED_RANGES)
+#define FSCTL_QUERY_ALLOCATED_RANGES 0x940CF
+typedef struct {
+ LARGE_INTEGER FileOffset;
+ LARGE_INTEGER Length;
+} FILE_ALLOCATED_RANGE_BUFFER;
+#endif
+
+static int
+setup_sparse_from_disk(struct archive_read_disk *a,
+ struct archive_entry *entry, HANDLE handle)
+{
+ FILE_ALLOCATED_RANGE_BUFFER range, *outranges = NULL;
+ size_t outranges_size;
+ int64_t entry_size = archive_entry_size(entry);
+ int exit_sts = ARCHIVE_OK;
+
+ range.FileOffset.QuadPart = 0;
+ range.Length.QuadPart = entry_size;
+ outranges_size = 2048;
+ outranges = (FILE_ALLOCATED_RANGE_BUFFER *)malloc(outranges_size);
+ if (outranges == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory");
+ exit_sts = ARCHIVE_FATAL;
+ goto exit_setup_sparse;
+ }
+
+ for (;;) {
+ DWORD retbytes;
+ BOOL ret;
+
+ for (;;) {
+ ret = DeviceIoControl(handle,
+ FSCTL_QUERY_ALLOCATED_RANGES,
+ &range, sizeof(range), outranges,
+ (DWORD)outranges_size, &retbytes, NULL);
+ if (ret == 0 && GetLastError() == ERROR_MORE_DATA) {
+ free(outranges);
+ outranges_size *= 2;
+ outranges = (FILE_ALLOCATED_RANGE_BUFFER *)
+ malloc(outranges_size);
+ if (outranges == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory");
+ exit_sts = ARCHIVE_FATAL;
+ goto exit_setup_sparse;
+ }
+ continue;
+ } else
+ break;
+ }
+ if (ret != 0) {
+ if (retbytes > 0) {
+ DWORD i, n;
+
+ n = retbytes / sizeof(outranges[0]);
+ if (n == 1 &&
+ outranges[0].FileOffset.QuadPart == 0 &&
+ outranges[0].Length.QuadPart == entry_size)
+ break;/* This is not sparse. */
+ for (i = 0; i < n; i++)
+ archive_entry_sparse_add_entry(entry,
+ outranges[i].FileOffset.QuadPart,
+ outranges[i].Length.QuadPart);
+ range.FileOffset.QuadPart =
+ outranges[n-1].FileOffset.QuadPart
+ + outranges[n-1].Length.QuadPart;
+ range.Length.QuadPart =
+ entry_size - range.FileOffset.QuadPart;
+ if (range.Length.QuadPart > 0)
+ continue;
+ } else {
+ /* The entire file is a hole. Add one data block of size 0 at the end. */
+ archive_entry_sparse_add_entry(entry,
+ entry_size,
+ 0);
+ }
+ break;
+ } else {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "DeviceIoControl Failed: %lu", GetLastError());
+ exit_sts = ARCHIVE_FAILED;
+ goto exit_setup_sparse;
+ }
+ }
+exit_setup_sparse:
+ free(outranges);
+
+ return (exit_sts);
+}
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_read_extract.c b/contrib/libs/libarchive/libarchive/archive_read_extract.c
new file mode 100644
index 00000000000..b7973fa8e00
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_extract.c
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.61 2008/05/26 17:00:22 kientzle Exp $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+int
+archive_read_extract(struct archive *_a, struct archive_entry *entry, int flags)
+{
+ struct archive_read_extract *extract;
+ struct archive_read * a = (struct archive_read *)_a;
+
+ extract = __archive_read_get_extract(a);
+ if (extract == NULL)
+ return (ARCHIVE_FATAL);
+
+ /* If we haven't initialized the archive_write_disk object, do it now. */
+ if (extract->ad == NULL) {
+ extract->ad = archive_write_disk_new();
+ if (extract->ad == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't extract");
+ return (ARCHIVE_FATAL);
+ }
+ archive_write_disk_set_standard_lookup(extract->ad);
+ }
+
+ archive_write_disk_set_options(extract->ad, flags);
+ return (archive_read_extract2(&a->archive, entry, extract->ad));
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_extract2.c b/contrib/libs/libarchive/libarchive/archive_read_extract2.c
new file mode 100644
index 00000000000..4febd8ce056
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_extract2.c
@@ -0,0 +1,155 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.61 2008/05/26 17:00:22 kientzle Exp $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+static int copy_data(struct archive *ar, struct archive *aw);
+static int archive_read_extract_cleanup(struct archive_read *);
+
+
+/* Retrieve an extract object without initialising the associated
+ * archive_write_disk object.
+ */
+struct archive_read_extract *
+__archive_read_get_extract(struct archive_read *a)
+{
+ if (a->extract == NULL) {
+ a->extract = (struct archive_read_extract *)calloc(1, sizeof(*a->extract));
+ if (a->extract == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't extract");
+ return (NULL);
+ }
+ a->cleanup_archive_extract = archive_read_extract_cleanup;
+ }
+ return (a->extract);
+}
+
+/*
+ * Cleanup function for archive_extract.
+ */
+static int
+archive_read_extract_cleanup(struct archive_read *a)
+{
+ int ret = ARCHIVE_OK;
+
+ if (a->extract->ad != NULL) {
+ ret = archive_write_free(a->extract->ad);
+ }
+ free(a->extract);
+ a->extract = NULL;
+ return (ret);
+}
+
+int
+archive_read_extract2(struct archive *_a, struct archive_entry *entry,
+ struct archive *ad)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ int r, r2;
+
+ /* Set up for this particular entry. */
+ if (a->skip_file_set)
+ archive_write_disk_set_skip_file(ad,
+ a->skip_file_dev, a->skip_file_ino);
+ r = archive_write_header(ad, entry);
+ if (r < ARCHIVE_WARN)
+ r = ARCHIVE_WARN;
+ if (r != ARCHIVE_OK)
+ /* If _write_header failed, copy the error. */
+ archive_copy_error(&a->archive, ad);
+ else if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) > 0)
+ /* Otherwise, pour data into the entry. */
+ r = copy_data(_a, ad);
+ r2 = archive_write_finish_entry(ad);
+ if (r2 < ARCHIVE_WARN)
+ r2 = ARCHIVE_WARN;
+ /* Use the first message. */
+ if (r2 != ARCHIVE_OK && r == ARCHIVE_OK)
+ archive_copy_error(&a->archive, ad);
+ /* Use the worst error return. */
+ if (r2 < r)
+ r = r2;
+ return (r);
+}
+
+void
+archive_read_extract_set_progress_callback(struct archive *_a,
+ void (*progress_func)(void *), void *user_data)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct archive_read_extract *extract = __archive_read_get_extract(a);
+ if (extract != NULL) {
+ extract->extract_progress = progress_func;
+ extract->extract_progress_user_data = user_data;
+ }
+}
+
+static int
+copy_data(struct archive *ar, struct archive *aw)
+{
+ int64_t offset;
+ const void *buff;
+ struct archive_read_extract *extract;
+ size_t size;
+ int r;
+
+ extract = __archive_read_get_extract((struct archive_read *)ar);
+ if (extract == NULL)
+ return (ARCHIVE_FATAL);
+ for (;;) {
+ r = archive_read_data_block(ar, &buff, &size, &offset);
+ if (r == ARCHIVE_EOF)
+ return (ARCHIVE_OK);
+ if (r != ARCHIVE_OK)
+ return (r);
+ r = (int)archive_write_data_block(aw, buff, size, offset);
+ if (r < ARCHIVE_WARN)
+ r = ARCHIVE_WARN;
+ if (r < ARCHIVE_OK) {
+ archive_set_error(ar, archive_errno(aw),
+ "%s", archive_error_string(aw));
+ return (r);
+ }
+ if (extract->extract_progress)
+ (extract->extract_progress)
+ (extract->extract_progress_user_data);
+ }
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_open_fd.c b/contrib/libs/libarchive/libarchive/archive_read_open_fd.c
new file mode 100644
index 00000000000..f59cd07fe6c
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_open_fd.c
@@ -0,0 +1,211 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_open_fd.c 201103 2009-12-28 03:13:49Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+
+struct read_fd_data {
+ int fd;
+ size_t block_size;
+ char use_lseek;
+ void *buffer;
+};
+
+static int file_close(struct archive *, void *);
+static ssize_t file_read(struct archive *, void *, const void **buff);
+static int64_t file_seek(struct archive *, void *, int64_t request, int);
+static int64_t file_skip(struct archive *, void *, int64_t request);
+
+int
+archive_read_open_fd(struct archive *a, int fd, size_t block_size)
+{
+ struct stat st;
+ struct read_fd_data *mine;
+ void *b;
+
+ archive_clear_error(a);
+ if (fstat(fd, &st) != 0) {
+ archive_set_error(a, errno, "Can't stat fd %d", fd);
+ return (ARCHIVE_FATAL);
+ }
+
+ mine = (struct read_fd_data *)calloc(1, sizeof(*mine));
+ b = malloc(block_size);
+ if (mine == NULL || b == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ free(mine);
+ free(b);
+ return (ARCHIVE_FATAL);
+ }
+ mine->block_size = block_size;
+ mine->buffer = b;
+ mine->fd = fd;
+ /*
+ * Skip support is a performance optimization for anything
+ * that supports lseek(). On FreeBSD, only regular files and
+ * raw disk devices support lseek() and there's no portable
+ * way to determine if a device is a raw disk device, so we
+ * only enable this optimization for regular files.
+ */
+ if (S_ISREG(st.st_mode)) {
+ archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+ mine->use_lseek = 1;
+ }
+#if defined(__CYGWIN__) || defined(_WIN32)
+ setmode(mine->fd, O_BINARY);
+#endif
+
+ archive_read_set_read_callback(a, file_read);
+ archive_read_set_skip_callback(a, file_skip);
+ archive_read_set_seek_callback(a, file_seek);
+ archive_read_set_close_callback(a, file_close);
+ archive_read_set_callback_data(a, mine);
+ return (archive_read_open1(a));
+}
+
+static ssize_t
+file_read(struct archive *a, void *client_data, const void **buff)
+{
+ struct read_fd_data *mine = (struct read_fd_data *)client_data;
+ ssize_t bytes_read;
+
+ *buff = mine->buffer;
+ for (;;) {
+ bytes_read = read(mine->fd, mine->buffer, mine->block_size);
+ if (bytes_read < 0) {
+ if (errno == EINTR)
+ continue;
+ archive_set_error(a, errno, "Error reading fd %d",
+ mine->fd);
+ }
+ return (bytes_read);
+ }
+}
+
+static int64_t
+file_skip(struct archive *a, void *client_data, int64_t request)
+{
+ struct read_fd_data *mine = (struct read_fd_data *)client_data;
+ int64_t skip = request;
+ int64_t old_offset, new_offset;
+ int skip_bits = sizeof(skip) * 8 - 1; /* off_t is a signed type. */
+
+ if (!mine->use_lseek)
+ return (0);
+
+ /* Reduce a request that would overflow the 'skip' variable. */
+ if (sizeof(request) > sizeof(skip)) {
+ int64_t max_skip =
+ (((int64_t)1 << (skip_bits - 1)) - 1) * 2 + 1;
+ if (request > max_skip)
+ skip = max_skip;
+ }
+
+ /* Reduce request to the next smallest multiple of block_size */
+ request = (request / mine->block_size) * mine->block_size;
+ if (request == 0)
+ return (0);
+
+ if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0) &&
+ ((new_offset = lseek(mine->fd, skip, SEEK_CUR)) >= 0))
+ return (new_offset - old_offset);
+
+ /* If seek failed once, it will probably fail again. */
+ mine->use_lseek = 0;
+
+ /* Let libarchive recover with read+discard. */
+ if (errno == ESPIPE)
+ return (0);
+
+ /*
+ * There's been an error other than ESPIPE. This is most
+ * likely caused by a programmer error (too large request)
+ * or a corrupted archive file.
+ */
+ archive_set_error(a, errno, "Error seeking");
+ return (-1);
+}
+
+/*
+ * TODO: Store the offset and use it in the read callback.
+ */
+static int64_t
+file_seek(struct archive *a, void *client_data, int64_t request, int whence)
+{
+ struct read_fd_data *mine = (struct read_fd_data *)client_data;
+ int64_t r;
+
+ /* We use off_t here because lseek() is declared that way. */
+ /* See above for notes about when off_t is less than 64 bits. */
+ r = lseek(mine->fd, request, whence);
+ if (r >= 0)
+ return r;
+
+ if (errno == ESPIPE) {
+ archive_set_error(a, errno,
+ "A file descriptor(%d) is not seekable(PIPE)", mine->fd);
+ return (ARCHIVE_FAILED);
+ } else {
+ /* If the input is corrupted or truncated, fail. */
+ archive_set_error(a, errno,
+ "Error seeking in a file descriptor(%d)", mine->fd);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+ struct read_fd_data *mine = (struct read_fd_data *)client_data;
+
+ (void)a; /* UNUSED */
+ free(mine->buffer);
+ free(mine);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_open_file.c b/contrib/libs/libarchive/libarchive/archive_read_open_file.c
new file mode 100644
index 00000000000..03719e8bff0
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_open_file.c
@@ -0,0 +1,180 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_open_file.c 201093 2009-12-28 02:28:44Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+
+struct read_FILE_data {
+ FILE *f;
+ size_t block_size;
+ void *buffer;
+ char can_skip;
+};
+
+static int file_close(struct archive *, void *);
+static ssize_t file_read(struct archive *, void *, const void **buff);
+static int64_t file_skip(struct archive *, void *, int64_t request);
+
+int
+archive_read_open_FILE(struct archive *a, FILE *f)
+{
+ struct stat st;
+ struct read_FILE_data *mine;
+ size_t block_size = 128 * 1024;
+ void *b;
+
+ archive_clear_error(a);
+ mine = (struct read_FILE_data *)malloc(sizeof(*mine));
+ b = malloc(block_size);
+ if (mine == NULL || b == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ free(mine);
+ free(b);
+ return (ARCHIVE_FATAL);
+ }
+ mine->block_size = block_size;
+ mine->buffer = b;
+ mine->f = f;
+ /*
+ * If we can't fstat() the file, it may just be that it's not
+ * a file. (On some platforms, FILE * objects can wrap I/O
+ * streams that don't support fileno()). As a result, fileno()
+ * should be used cautiously.)
+ */
+ if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode)) {
+ archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+ /* Enable the seek optimization only for regular files. */
+ mine->can_skip = 1;
+ } else
+ mine->can_skip = 0;
+
+#if defined(__CYGWIN__) || defined(_WIN32)
+ setmode(fileno(mine->f), O_BINARY);
+#endif
+
+ archive_read_set_read_callback(a, file_read);
+ archive_read_set_skip_callback(a, file_skip);
+ archive_read_set_close_callback(a, file_close);
+ archive_read_set_callback_data(a, mine);
+ return (archive_read_open1(a));
+}
+
+static ssize_t
+file_read(struct archive *a, void *client_data, const void **buff)
+{
+ struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
+ size_t bytes_read;
+
+ *buff = mine->buffer;
+ bytes_read = fread(mine->buffer, 1, mine->block_size, mine->f);
+ if (bytes_read < mine->block_size && ferror(mine->f)) {
+ archive_set_error(a, errno, "Error reading file");
+ }
+ return (bytes_read);
+}
+
+static int64_t
+file_skip(struct archive *a, void *client_data, int64_t request)
+{
+ struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
+#if HAVE_FSEEKO
+ off_t skip = (off_t)request;
+#elif HAVE__FSEEKI64
+ int64_t skip = request;
+#else
+ long skip = (long)request;
+#endif
+ int skip_bits = sizeof(skip) * 8 - 1;
+
+ (void)a; /* UNUSED */
+
+ /*
+ * If we can't skip, return 0 as the amount we did step and
+ * the caller will work around by reading and discarding.
+ */
+ if (!mine->can_skip)
+ return (0);
+ if (request == 0)
+ return (0);
+
+ /* If request is too big for a long or an off_t, reduce it. */
+ if (sizeof(request) > sizeof(skip)) {
+ int64_t max_skip =
+ (((int64_t)1 << (skip_bits - 1)) - 1) * 2 + 1;
+ if (request > max_skip)
+ skip = max_skip;
+ }
+
+#ifdef __ANDROID__
+ /* fileno() isn't safe on all platforms ... see above. */
+ if (lseek(fileno(mine->f), skip, SEEK_CUR) < 0)
+#elif HAVE__FSEEKI64
+ if (_fseeki64(mine->f, skip, SEEK_CUR) != 0)
+#elif HAVE_FSEEKO
+ if (fseeko(mine->f, skip, SEEK_CUR) != 0)
+#else
+ if (fseek(mine->f, skip, SEEK_CUR) != 0)
+#endif
+ {
+ mine->can_skip = 0;
+ return (0);
+ }
+ return (request);
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+ struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
+
+ (void)a; /* UNUSED */
+ free(mine->buffer);
+ free(mine);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_open_filename.c b/contrib/libs/libarchive/libarchive/archive_read_open_filename.c
new file mode 100644
index 00000000000..f3b8d09f708
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_open_filename.c
@@ -0,0 +1,586 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_open_filename.c 201093 2009-12-28 02:28:44Z kientzle $");
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <sys/disk.h>
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/disklabel.h>
+#error #include <sys/dkio.h>
+#elif defined(__DragonFly__)
+#error #include <sys/diskslice.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+struct read_file_data {
+ int fd;
+ size_t block_size;
+ void *buffer;
+ mode_t st_mode; /* Mode bits for opened file. */
+ char use_lseek;
+ enum fnt_e { FNT_STDIN, FNT_MBS, FNT_WCS } filename_type;
+ union {
+ char m[1];/* MBS filename. */
+ wchar_t w[1];/* WCS filename. */
+ } filename; /* Must be last! */
+};
+
+static int file_open(struct archive *, void *);
+static int file_close(struct archive *, void *);
+static int file_close2(struct archive *, void *);
+static int file_switch(struct archive *, void *, void *);
+static ssize_t file_read(struct archive *, void *, const void **buff);
+static int64_t file_seek(struct archive *, void *, int64_t request, int);
+static int64_t file_skip(struct archive *, void *, int64_t request);
+static int64_t file_skip_lseek(struct archive *, void *, int64_t request);
+
+int
+archive_read_open_file(struct archive *a, const char *filename,
+ size_t block_size)
+{
+ return (archive_read_open_filename(a, filename, block_size));
+}
+
+int
+archive_read_open_filename(struct archive *a, const char *filename,
+ size_t block_size)
+{
+ const char *filenames[2];
+ filenames[0] = filename;
+ filenames[1] = NULL;
+ return archive_read_open_filenames(a, filenames, block_size);
+}
+
+int
+archive_read_open_filenames(struct archive *a, const char **filenames,
+ size_t block_size)
+{
+ struct read_file_data *mine;
+ const char *filename = NULL;
+ if (filenames)
+ filename = *(filenames++);
+
+ archive_clear_error(a);
+ do
+ {
+ if (filename == NULL)
+ filename = "";
+ mine = (struct read_file_data *)calloc(1,
+ sizeof(*mine) + strlen(filename));
+ if (mine == NULL)
+ goto no_memory;
+ strcpy(mine->filename.m, filename);
+ mine->block_size = block_size;
+ mine->fd = -1;
+ mine->buffer = NULL;
+ mine->st_mode = mine->use_lseek = 0;
+ if (filename == NULL || filename[0] == '\0') {
+ mine->filename_type = FNT_STDIN;
+ } else
+ mine->filename_type = FNT_MBS;
+ if (archive_read_append_callback_data(a, mine) != (ARCHIVE_OK))
+ return (ARCHIVE_FATAL);
+ if (filenames == NULL)
+ break;
+ filename = *(filenames++);
+ } while (filename != NULL && filename[0] != '\0');
+ archive_read_set_open_callback(a, file_open);
+ archive_read_set_read_callback(a, file_read);
+ archive_read_set_skip_callback(a, file_skip);
+ archive_read_set_close_callback(a, file_close);
+ archive_read_set_switch_callback(a, file_switch);
+ archive_read_set_seek_callback(a, file_seek);
+
+ return (archive_read_open1(a));
+no_memory:
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+}
+
+int
+archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename,
+ size_t block_size)
+{
+ struct read_file_data *mine = (struct read_file_data *)calloc(1,
+ sizeof(*mine) + wcslen(wfilename) * sizeof(wchar_t));
+ if (!mine)
+ {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ mine->fd = -1;
+ mine->block_size = block_size;
+
+ if (wfilename == NULL || wfilename[0] == L'\0') {
+ mine->filename_type = FNT_STDIN;
+ } else {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ mine->filename_type = FNT_WCS;
+ wcscpy(mine->filename.w, wfilename);
+#else
+ /*
+ * POSIX system does not support a wchar_t interface for
+ * open() system call, so we have to translate a wchar_t
+ * filename to multi-byte one and use it.
+ */
+ struct archive_string fn;
+
+ archive_string_init(&fn);
+ if (archive_string_append_from_wcs(&fn, wfilename,
+ wcslen(wfilename)) != 0) {
+ if (errno == ENOMEM)
+ archive_set_error(a, errno,
+ "Can't allocate memory");
+ else
+ archive_set_error(a, EINVAL,
+ "Failed to convert a wide-character"
+ " filename to a multi-byte filename");
+ archive_string_free(&fn);
+ free(mine);
+ return (ARCHIVE_FATAL);
+ }
+ mine->filename_type = FNT_MBS;
+ strcpy(mine->filename.m, fn.s);
+ archive_string_free(&fn);
+#endif
+ }
+ if (archive_read_append_callback_data(a, mine) != (ARCHIVE_OK))
+ return (ARCHIVE_FATAL);
+ archive_read_set_open_callback(a, file_open);
+ archive_read_set_read_callback(a, file_read);
+ archive_read_set_skip_callback(a, file_skip);
+ archive_read_set_close_callback(a, file_close);
+ archive_read_set_switch_callback(a, file_switch);
+ archive_read_set_seek_callback(a, file_seek);
+
+ return (archive_read_open1(a));
+}
+
+static int
+file_open(struct archive *a, void *client_data)
+{
+ struct stat st;
+ struct read_file_data *mine = (struct read_file_data *)client_data;
+ void *buffer;
+ const char *filename = NULL;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ const wchar_t *wfilename = NULL;
+#endif
+ int fd = -1;
+ int is_disk_like = 0;
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ off_t mediasize = 0; /* FreeBSD-specific, so off_t okay here. */
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+ struct disklabel dl;
+#elif defined(__DragonFly__)
+ struct partinfo pi;
+#endif
+
+ archive_clear_error(a);
+ if (mine->filename_type == FNT_STDIN) {
+ /* We used to delegate stdin support by
+ * directly calling archive_read_open_fd(a,0,block_size)
+ * here, but that doesn't (and shouldn't) handle the
+ * end-of-file flush when reading stdout from a pipe.
+ * Basically, read_open_fd() is intended for folks who
+ * are willing to handle such details themselves. This
+ * API is intended to be a little smarter for folks who
+ * want easy handling of the common case.
+ */
+ fd = 0;
+#if defined(__CYGWIN__) || defined(_WIN32)
+ setmode(0, O_BINARY);
+#endif
+ filename = "";
+ } else if (mine->filename_type == FNT_MBS) {
+ filename = mine->filename.m;
+ fd = open(filename, O_RDONLY | O_BINARY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
+ if (fd < 0) {
+ archive_set_error(a, errno,
+ "Failed to open '%s'", filename);
+ return (ARCHIVE_FATAL);
+ }
+ } else {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ wfilename = mine->filename.w;
+ fd = _wopen(wfilename, O_RDONLY | O_BINARY);
+ if (fd < 0 && errno == ENOENT) {
+ wchar_t *fullpath;
+ fullpath = __la_win_permissive_name_w(wfilename);
+ if (fullpath != NULL) {
+ fd = _wopen(fullpath, O_RDONLY | O_BINARY);
+ free(fullpath);
+ }
+ }
+ if (fd < 0) {
+ archive_set_error(a, errno,
+ "Failed to open '%S'", wfilename);
+ return (ARCHIVE_FATAL);
+ }
+#else
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Unexpedted operation in archive_read_open_filename");
+ goto fail;
+#endif
+ }
+ if (fstat(fd, &st) != 0) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (mine->filename_type == FNT_WCS)
+ archive_set_error(a, errno, "Can't stat '%S'",
+ wfilename);
+ else
+#endif
+ archive_set_error(a, errno, "Can't stat '%s'",
+ filename);
+ goto fail;
+ }
+
+ /*
+ * Determine whether the input looks like a disk device or a
+ * tape device. The results are used below to select an I/O
+ * strategy:
+ * = "disk-like" devices support arbitrary lseek() and will
+ * support I/O requests of any size. So we get easy skipping
+ * and can cheat on block sizes to get better performance.
+ * = "tape-like" devices require strict blocking and use
+ * specialized ioctls for seeking.
+ * = "socket-like" devices cannot seek at all but can improve
+ * performance by using nonblocking I/O to read "whatever is
+ * available right now".
+ *
+ * Right now, we only specially recognize disk-like devices,
+ * but it should be straightforward to add probes and strategy
+ * here for tape-like and socket-like devices.
+ */
+ if (S_ISREG(st.st_mode)) {
+ /* Safety: Tell the extractor not to overwrite the input. */
+ archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+ /* Regular files act like disks. */
+ is_disk_like = 1;
+ }
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ /* FreeBSD: if it supports DIOCGMEDIASIZE ioctl, it's disk-like. */
+ else if (S_ISCHR(st.st_mode) &&
+ ioctl(fd, DIOCGMEDIASIZE, &mediasize) == 0 &&
+ mediasize > 0) {
+ is_disk_like = 1;
+ }
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+ /* Net/OpenBSD: if it supports DIOCGDINFO ioctl, it's disk-like. */
+ else if ((S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) &&
+ ioctl(fd, DIOCGDINFO, &dl) == 0 &&
+ dl.d_partitions[DISKPART(st.st_rdev)].p_size > 0) {
+ is_disk_like = 1;
+ }
+#elif defined(__DragonFly__)
+ /* DragonFly BSD: if it supports DIOCGPART ioctl, it's disk-like. */
+ else if (S_ISCHR(st.st_mode) &&
+ ioctl(fd, DIOCGPART, &pi) == 0 &&
+ pi.media_size > 0) {
+ is_disk_like = 1;
+ }
+#elif defined(__linux__)
+ /* Linux: All block devices are disk-like. */
+ else if (S_ISBLK(st.st_mode) &&
+ lseek(fd, 0, SEEK_CUR) == 0 &&
+ lseek(fd, 0, SEEK_SET) == 0 &&
+ lseek(fd, 0, SEEK_END) > 0 &&
+ lseek(fd, 0, SEEK_SET) == 0) {
+ is_disk_like = 1;
+ }
+#endif
+ /* TODO: Add an "is_tape_like" variable and appropriate tests. */
+
+ /* Disk-like devices prefer power-of-two block sizes. */
+ /* Use provided block_size as a guide so users have some control. */
+ if (is_disk_like) {
+ size_t new_block_size = 64 * 1024;
+ while (new_block_size < mine->block_size
+ && new_block_size < 64 * 1024 * 1024)
+ new_block_size *= 2;
+ mine->block_size = new_block_size;
+ }
+ buffer = malloc(mine->block_size);
+ if (buffer == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ goto fail;
+ }
+ mine->buffer = buffer;
+ mine->fd = fd;
+ /* Remember mode so close can decide whether to flush. */
+ mine->st_mode = st.st_mode;
+
+ /* Disk-like inputs can use lseek(). */
+ if (is_disk_like)
+ mine->use_lseek = 1;
+
+ return (ARCHIVE_OK);
+fail:
+ /*
+ * Don't close file descriptors not opened or ones pointing referring
+ * to `FNT_STDIN`.
+ */
+ if (fd != -1 && fd != 0)
+ close(fd);
+ return (ARCHIVE_FATAL);
+}
+
+static ssize_t
+file_read(struct archive *a, void *client_data, const void **buff)
+{
+ struct read_file_data *mine = (struct read_file_data *)client_data;
+ ssize_t bytes_read;
+
+ /* TODO: If a recent lseek() operation has left us
+ * mis-aligned, read and return a short block to try to get
+ * us back in alignment. */
+
+ /* TODO: Someday, try mmap() here; if that succeeds, give
+ * the entire file to libarchive as a single block. That
+ * could be a lot faster than block-by-block manual I/O. */
+
+ /* TODO: We might be able to improve performance on pipes and
+ * sockets by setting non-blocking I/O and just accepting
+ * whatever we get here instead of waiting for a full block
+ * worth of data. */
+
+ *buff = mine->buffer;
+ for (;;) {
+ bytes_read = read(mine->fd, mine->buffer, mine->block_size);
+ if (bytes_read < 0) {
+ if (errno == EINTR)
+ continue;
+ else if (mine->filename_type == FNT_STDIN)
+ archive_set_error(a, errno,
+ "Error reading stdin");
+ else if (mine->filename_type == FNT_MBS)
+ archive_set_error(a, errno,
+ "Error reading '%s'", mine->filename.m);
+ else
+ archive_set_error(a, errno,
+ "Error reading '%S'", mine->filename.w);
+ }
+ return (bytes_read);
+ }
+}
+
+/*
+ * Regular files and disk-like block devices can use simple lseek
+ * without needing to round the request to the block size.
+ *
+ * TODO: This can leave future reads mis-aligned. Since we know the
+ * offset here, we should store it and use it in file_read() above
+ * to determine whether we should perform a short read to get back
+ * into alignment. Long series of mis-aligned reads can negatively
+ * impact disk throughput. (Of course, the performance impact should
+ * be carefully tested; extra code complexity is only worthwhile if
+ * it does provide measurable improvement.)
+ *
+ * TODO: Be lazy about the actual seek. There are a few pathological
+ * cases where libarchive makes a bunch of seek requests in a row
+ * without any intervening reads. This isn't a huge performance
+ * problem, since the kernel handles seeks lazily already, but
+ * it would be very slightly faster if we simply remembered the
+ * seek request here and then actually performed the seek at the
+ * top of the read callback above.
+ */
+static int64_t
+file_skip_lseek(struct archive *a, void *client_data, int64_t request)
+{
+ struct read_file_data *mine = (struct read_file_data *)client_data;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* We use _lseeki64() on Windows. */
+ int64_t old_offset, new_offset;
+#else
+ off_t old_offset, new_offset;
+#endif
+
+ /* We use off_t here because lseek() is declared that way. */
+
+ /* TODO: Deal with case where off_t isn't 64 bits.
+ * This shouldn't be a problem on Linux or other POSIX
+ * systems, since the configuration logic for libarchive
+ * tries to obtain a 64-bit off_t.
+ */
+ if ((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0 &&
+ (new_offset = lseek(mine->fd, request, SEEK_CUR)) >= 0)
+ return (new_offset - old_offset);
+
+ /* If lseek() fails, don't bother trying again. */
+ mine->use_lseek = 0;
+
+ /* Let libarchive recover with read+discard */
+ if (errno == ESPIPE)
+ return (0);
+
+ /* If the input is corrupted or truncated, fail. */
+ if (mine->filename_type == FNT_STDIN)
+ archive_set_error(a, errno, "Error seeking in stdin");
+ else if (mine->filename_type == FNT_MBS)
+ archive_set_error(a, errno, "Error seeking in '%s'",
+ mine->filename.m);
+ else
+ archive_set_error(a, errno, "Error seeking in '%S'",
+ mine->filename.w);
+ return (-1);
+}
+
+
+/*
+ * TODO: Implement another file_skip_XXXX that uses MTIO ioctls to
+ * accelerate operation on tape drives.
+ */
+
+static int64_t
+file_skip(struct archive *a, void *client_data, int64_t request)
+{
+ struct read_file_data *mine = (struct read_file_data *)client_data;
+
+ /* Delegate skip requests. */
+ if (mine->use_lseek)
+ return (file_skip_lseek(a, client_data, request));
+
+ /* If we can't skip, return 0; libarchive will read+discard instead. */
+ return (0);
+}
+
+/*
+ * TODO: Store the offset and use it in the read callback.
+ */
+static int64_t
+file_seek(struct archive *a, void *client_data, int64_t request, int whence)
+{
+ struct read_file_data *mine = (struct read_file_data *)client_data;
+ int64_t r;
+
+ /* We use off_t here because lseek() is declared that way. */
+ /* See above for notes about when off_t is less than 64 bits. */
+ r = lseek(mine->fd, request, whence);
+ if (r >= 0)
+ return r;
+
+ /* If the input is corrupted or truncated, fail. */
+ if (mine->filename_type == FNT_STDIN)
+ archive_set_error(a, errno, "Error seeking in stdin");
+ else if (mine->filename_type == FNT_MBS)
+ archive_set_error(a, errno, "Error seeking in '%s'",
+ mine->filename.m);
+ else
+ archive_set_error(a, errno, "Error seeking in '%S'",
+ mine->filename.w);
+ return (ARCHIVE_FATAL);
+}
+
+static int
+file_close2(struct archive *a, void *client_data)
+{
+ struct read_file_data *mine = (struct read_file_data *)client_data;
+
+ (void)a; /* UNUSED */
+
+ /* Only flush and close if open succeeded. */
+ if (mine->fd >= 0) {
+ /*
+ * Sometimes, we should flush the input before closing.
+ * Regular files: faster to just close without flush.
+ * Disk-like devices: Ditto.
+ * Tapes: must not flush (user might need to
+ * read the "next" item on a non-rewind device).
+ * Pipes and sockets: must flush (otherwise, the
+ * program feeding the pipe or socket may complain).
+ * Here, I flush everything except for regular files and
+ * device nodes.
+ */
+ if (!S_ISREG(mine->st_mode)
+ && !S_ISCHR(mine->st_mode)
+ && !S_ISBLK(mine->st_mode)) {
+ ssize_t bytesRead;
+ do {
+ bytesRead = read(mine->fd, mine->buffer,
+ mine->block_size);
+ } while (bytesRead > 0);
+ }
+ /* If a named file was opened, then it needs to be closed. */
+ if (mine->filename_type != FNT_STDIN)
+ close(mine->fd);
+ }
+ free(mine->buffer);
+ mine->buffer = NULL;
+ mine->fd = -1;
+ return (ARCHIVE_OK);
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+ struct read_file_data *mine = (struct read_file_data *)client_data;
+ file_close2(a, client_data);
+ free(mine);
+ return (ARCHIVE_OK);
+}
+
+static int
+file_switch(struct archive *a, void *client_data1, void *client_data2)
+{
+ file_close2(a, client_data1);
+ return file_open(a, client_data2);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_open_memory.c b/contrib/libs/libarchive/libarchive/archive_read_open_memory.c
new file mode 100644
index 00000000000..311be47046a
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_open_memory.c
@@ -0,0 +1,186 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_memory.c,v 1.6 2007/07/06 15:51:59 kientzle Exp $");
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "archive.h"
+
+/*
+ * Glue to read an archive from a block of memory.
+ *
+ * This is mostly a huge help in building test harnesses;
+ * test programs can build archives in memory and read them
+ * back again without having to mess with files on disk.
+ */
+
+struct read_memory_data {
+ const unsigned char *start;
+ const unsigned char *p;
+ const unsigned char *end;
+ ssize_t read_size;
+};
+
+static int memory_read_close(struct archive *, void *);
+static int memory_read_open(struct archive *, void *);
+static int64_t memory_read_seek(struct archive *, void *, int64_t offset, int whence);
+static int64_t memory_read_skip(struct archive *, void *, int64_t request);
+static ssize_t memory_read(struct archive *, void *, const void **buff);
+
+int
+archive_read_open_memory(struct archive *a, const void *buff, size_t size)
+{
+ return archive_read_open_memory2(a, buff, size, size);
+}
+
+/*
+ * Don't use _open_memory2() in production code; the archive_read_open_memory()
+ * version is the one you really want. This is just here so that
+ * test harnesses can exercise block operations inside the library.
+ */
+int
+archive_read_open_memory2(struct archive *a, const void *buff,
+ size_t size, size_t read_size)
+{
+ struct read_memory_data *mine;
+
+ mine = (struct read_memory_data *)calloc(1, sizeof(*mine));
+ if (mine == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ mine->start = mine->p = (const unsigned char *)buff;
+ mine->end = mine->start + size;
+ mine->read_size = read_size;
+ archive_read_set_open_callback(a, memory_read_open);
+ archive_read_set_read_callback(a, memory_read);
+ archive_read_set_seek_callback(a, memory_read_seek);
+ archive_read_set_skip_callback(a, memory_read_skip);
+ archive_read_set_close_callback(a, memory_read_close);
+ archive_read_set_callback_data(a, mine);
+ return (archive_read_open1(a));
+}
+
+/*
+ * There's nothing to open.
+ */
+static int
+memory_read_open(struct archive *a, void *client_data)
+{
+ (void)a; /* UNUSED */
+ (void)client_data; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+/*
+ * This is scary simple: Just advance a pointer. Limiting
+ * to read_size is not technically necessary, but it exercises
+ * more of the internal logic when used with a small block size
+ * in a test harness. Production use should not specify a block
+ * size; then this is much faster.
+ */
+static ssize_t
+memory_read(struct archive *a, void *client_data, const void **buff)
+{
+ struct read_memory_data *mine = (struct read_memory_data *)client_data;
+ ssize_t size;
+
+ (void)a; /* UNUSED */
+ *buff = mine->p;
+ size = mine->end - mine->p;
+ if (size > mine->read_size)
+ size = mine->read_size;
+ mine->p += size;
+ return (size);
+}
+
+/*
+ * Advancing is just as simple. Again, this is doing more than
+ * necessary in order to better exercise internal code when used
+ * as a test harness.
+ */
+static int64_t
+memory_read_skip(struct archive *a, void *client_data, int64_t skip)
+{
+ struct read_memory_data *mine = (struct read_memory_data *)client_data;
+
+ (void)a; /* UNUSED */
+ if ((int64_t)skip > (int64_t)(mine->end - mine->p))
+ skip = mine->end - mine->p;
+ /* Round down to block size. */
+ skip /= mine->read_size;
+ skip *= mine->read_size;
+ mine->p += skip;
+ return (skip);
+}
+
+/*
+ * Seeking.
+ */
+static int64_t
+memory_read_seek(struct archive *a, void *client_data, int64_t offset, int whence)
+{
+ struct read_memory_data *mine = (struct read_memory_data *)client_data;
+
+ (void)a; /* UNUSED */
+ switch (whence) {
+ case SEEK_SET:
+ mine->p = mine->start + offset;
+ break;
+ case SEEK_CUR:
+ mine->p += offset;
+ break;
+ case SEEK_END:
+ mine->p = mine->end + offset;
+ break;
+ default:
+ return ARCHIVE_FATAL;
+ }
+ if (mine->p < mine->start) {
+ mine->p = mine->start;
+ return ARCHIVE_FAILED;
+ }
+ if (mine->p > mine->end) {
+ mine->p = mine->end;
+ return ARCHIVE_FAILED;
+ }
+ return (mine->p - mine->start);
+}
+
+/*
+ * Close is just cleaning up our one small bit of data.
+ */
+static int
+memory_read_close(struct archive *a, void *client_data)
+{
+ struct read_memory_data *mine = (struct read_memory_data *)client_data;
+ (void)a; /* UNUSED */
+ free(mine);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_private.h b/contrib/libs/libarchive/libarchive/archive_read_private.h
new file mode 100644
index 00000000000..383405d5290
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_private.h
@@ -0,0 +1,267 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_read_private.h 201088 2009-12-28 02:18:55Z kientzle $
+ */
+
+#ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED
+#define ARCHIVE_READ_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_private.h"
+
+struct archive_read;
+struct archive_read_filter_bidder;
+struct archive_read_filter;
+
+struct archive_read_filter_bidder_vtable {
+ /* Taste the upstream filter to see if we handle this. */
+ int (*bid)(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+ /* Initialize a newly-created filter. */
+ int (*init)(struct archive_read_filter *);
+ /* Release the bidder's configuration data. */
+ void (*free)(struct archive_read_filter_bidder *);
+};
+
+/*
+ * How bidding works for filters:
+ * * The bid manager initializes the client-provided reader as the
+ * first filter.
+ * * It invokes the bidder for each registered filter with the
+ * current head filter.
+ * * The bidders can use archive_read_filter_ahead() to peek ahead
+ * at the incoming data to compose their bids.
+ * * The bid manager creates a new filter structure for the winning
+ * bidder and gives the winning bidder a chance to initialize it.
+ * * The new filter becomes the new top filter and we repeat the
+ * process.
+ * This ends only when no bidder provides a non-zero bid. Then
+ * we perform a similar dance with the registered format handlers.
+ */
+struct archive_read_filter_bidder {
+ /* Configuration data for the bidder. */
+ void *data;
+ /* Name of the filter */
+ const char *name;
+ const struct archive_read_filter_bidder_vtable *vtable;
+};
+
+struct archive_read_filter_vtable {
+ /* Return next block. */
+ ssize_t (*read)(struct archive_read_filter *, const void **);
+ /* Close (just this filter) and free(self). */
+ int (*close)(struct archive_read_filter *self);
+ /* Read any header metadata if available. */
+ int (*read_header)(struct archive_read_filter *self, struct archive_entry *entry);
+};
+
+/*
+ * This structure is allocated within the archive_read core
+ * and initialized by archive_read and the init() method of the
+ * corresponding bidder above.
+ */
+struct archive_read_filter {
+ int64_t position;
+ /* Essentially all filters will need these values, so
+ * just declare them here. */
+ struct archive_read_filter_bidder *bidder; /* My bidder. */
+ struct archive_read_filter *upstream; /* Who I read from. */
+ struct archive_read *archive; /* Associated archive. */
+ const struct archive_read_filter_vtable *vtable;
+ /* My private data. */
+ void *data;
+
+ const char *name;
+ int code;
+ int can_skip;
+ int can_seek;
+
+ /* Used by reblocking logic. */
+ char *buffer;
+ size_t buffer_size;
+ char *next; /* Current read location. */
+ size_t avail; /* Bytes in my buffer. */
+ const void *client_buff; /* Client buffer information. */
+ size_t client_total;
+ const char *client_next;
+ size_t client_avail;
+ char end_of_file;
+ char closed;
+ char fatal;
+};
+
+/*
+ * The client looks a lot like a filter, so we just wrap it here.
+ *
+ * TODO: Make archive_read_filter and archive_read_client identical so
+ * that users of the library can easily register their own
+ * transformation filters. This will probably break the API/ABI and
+ * so should be deferred at least until libarchive 3.0.
+ */
+struct archive_read_data_node {
+ int64_t begin_position;
+ int64_t total_size;
+ void *data;
+};
+struct archive_read_client {
+ archive_open_callback *opener;
+ archive_read_callback *reader;
+ archive_skip_callback *skipper;
+ archive_seek_callback *seeker;
+ archive_close_callback *closer;
+ archive_switch_callback *switcher;
+ unsigned int nodes;
+ unsigned int cursor;
+ int64_t position;
+ struct archive_read_data_node *dataset;
+};
+struct archive_read_passphrase {
+ char *passphrase;
+ struct archive_read_passphrase *next;
+};
+
+struct archive_read_extract {
+ struct archive *ad; /* archive_write_disk object */
+
+ /* Progress function invoked during extract. */
+ void (*extract_progress)(void *);
+ void *extract_progress_user_data;
+};
+
+struct archive_read {
+ struct archive archive;
+
+ struct archive_entry *entry;
+
+ /* Dev/ino of the archive being read/written. */
+ int skip_file_set;
+ int64_t skip_file_dev;
+ int64_t skip_file_ino;
+
+ /* Callbacks to open/read/write/close client archive streams. */
+ struct archive_read_client client;
+
+ /* Registered filter bidders. */
+ struct archive_read_filter_bidder bidders[16];
+
+ /* Last filter in chain */
+ struct archive_read_filter *filter;
+
+ /* Whether to bypass filter bidding process */
+ int bypass_filter_bidding;
+
+ /* File offset of beginning of most recently-read header. */
+ int64_t header_position;
+
+ /* Nodes and offsets of compressed data block */
+ unsigned int data_start_node;
+ unsigned int data_end_node;
+
+ /*
+ * Format detection is mostly the same as compression
+ * detection, with one significant difference: The bidders
+ * use the read_ahead calls above to examine the stream rather
+ * than having the supervisor hand them a block of data to
+ * examine.
+ */
+
+ struct archive_format_descriptor {
+ void *data;
+ const char *name;
+ int (*bid)(struct archive_read *, int best_bid);
+ int (*options)(struct archive_read *, const char *key,
+ const char *value);
+ int (*read_header)(struct archive_read *, struct archive_entry *);
+ int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *);
+ int (*read_data_skip)(struct archive_read *);
+ int64_t (*seek_data)(struct archive_read *, int64_t, int);
+ int (*cleanup)(struct archive_read *);
+ int (*format_capabilties)(struct archive_read *);
+ int (*has_encrypted_entries)(struct archive_read *);
+ } formats[16];
+ struct archive_format_descriptor *format; /* Active format. */
+
+ /*
+ * Various information needed by archive_extract.
+ */
+ struct archive_read_extract *extract;
+ int (*cleanup_archive_extract)(struct archive_read *);
+
+ /*
+ * Decryption passphrase.
+ */
+ struct {
+ struct archive_read_passphrase *first;
+ struct archive_read_passphrase **last;
+ int candidate;
+ archive_passphrase_callback *callback;
+ void *client_data;
+ } passphrases;
+};
+
+int __archive_read_register_format(struct archive_read *a,
+ void *format_data,
+ const char *name,
+ int (*bid)(struct archive_read *, int),
+ int (*options)(struct archive_read *, const char *, const char *),
+ int (*read_header)(struct archive_read *, struct archive_entry *),
+ int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *),
+ int (*read_data_skip)(struct archive_read *),
+ int64_t (*seek_data)(struct archive_read *, int64_t, int),
+ int (*cleanup)(struct archive_read *),
+ int (*format_capabilities)(struct archive_read *),
+ int (*has_encrypted_entries)(struct archive_read *));
+
+int __archive_read_register_bidder(struct archive_read *a,
+ void *bidder_data,
+ const char *name,
+ const struct archive_read_filter_bidder_vtable *vtable);
+
+const void *__archive_read_ahead(struct archive_read *, size_t, ssize_t *);
+const void *__archive_read_filter_ahead(struct archive_read_filter *,
+ size_t, ssize_t *);
+int64_t __archive_read_seek(struct archive_read*, int64_t, int);
+int64_t __archive_read_filter_seek(struct archive_read_filter *, int64_t, int);
+int64_t __archive_read_consume(struct archive_read *, int64_t);
+int64_t __archive_read_filter_consume(struct archive_read_filter *, int64_t);
+int __archive_read_header(struct archive_read *, struct archive_entry *);
+int __archive_read_program(struct archive_read_filter *, const char *);
+void __archive_read_free_filters(struct archive_read *);
+struct archive_read_extract *__archive_read_get_extract(struct archive_read *);
+
+
+/*
+ * Get a decryption passphrase.
+ */
+void __archive_read_reset_passphrase(struct archive_read *a);
+const char * __archive_read_next_passphrase(struct archive_read *a);
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_read_set_format.c b/contrib/libs/libarchive/libarchive/archive_read_set_format.c
new file mode 100644
index 00000000000..796dcdcced1
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_set_format.c
@@ -0,0 +1,117 @@
+/*-
+ * Copyright (c) 2003-2012 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+int
+archive_read_set_format(struct archive *_a, int code)
+{
+ int r1, r2, slots, i;
+ char str[10];
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if ((r1 = archive_read_support_format_by_code(_a, code)) < (ARCHIVE_OK))
+ return r1;
+
+ r1 = r2 = (ARCHIVE_OK);
+ if (a->format)
+ r2 = (ARCHIVE_WARN);
+ switch (code & ARCHIVE_FORMAT_BASE_MASK)
+ {
+ case ARCHIVE_FORMAT_7ZIP:
+ strcpy(str, "7zip");
+ break;
+ case ARCHIVE_FORMAT_AR:
+ strcpy(str, "ar");
+ break;
+ case ARCHIVE_FORMAT_CAB:
+ strcpy(str, "cab");
+ break;
+ case ARCHIVE_FORMAT_CPIO:
+ strcpy(str, "cpio");
+ break;
+ case ARCHIVE_FORMAT_EMPTY:
+ strcpy(str, "empty");
+ break;
+ case ARCHIVE_FORMAT_ISO9660:
+ strcpy(str, "iso9660");
+ break;
+ case ARCHIVE_FORMAT_LHA:
+ strcpy(str, "lha");
+ break;
+ case ARCHIVE_FORMAT_MTREE:
+ strcpy(str, "mtree");
+ break;
+ case ARCHIVE_FORMAT_RAR:
+ strcpy(str, "rar");
+ break;
+ case ARCHIVE_FORMAT_RAR_V5:
+ strcpy(str, "rar5");
+ break;
+ case ARCHIVE_FORMAT_RAW:
+ strcpy(str, "raw");
+ break;
+ case ARCHIVE_FORMAT_TAR:
+ strcpy(str, "tar");
+ break;
+ case ARCHIVE_FORMAT_WARC:
+ strcpy(str, "warc");
+ break;
+ case ARCHIVE_FORMAT_XAR:
+ strcpy(str, "xar");
+ break;
+ case ARCHIVE_FORMAT_ZIP:
+ strcpy(str, "zip");
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Invalid format code specified");
+ return (ARCHIVE_FATAL);
+ }
+
+ slots = sizeof(a->formats) / sizeof(a->formats[0]);
+ a->format = &(a->formats[0]);
+ for (i = 0; i < slots; i++, a->format++) {
+ if (!a->format->name || !strcmp(a->format->name, str))
+ break;
+ }
+ if (!a->format->name || strcmp(a->format->name, str))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal error: Unable to set format");
+ r1 = (ARCHIVE_FATAL);
+ }
+
+ return (r1 < r2) ? r1 : r2;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_set_options.c b/contrib/libs/libarchive/libarchive/archive_read_set_options.c
new file mode 100644
index 00000000000..2bd9b811eaf
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_set_options.c
@@ -0,0 +1,133 @@
+/*-
+ * Copyright (c) 2011 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive_read_private.h"
+#include "archive_options_private.h"
+
+static int archive_set_format_option(struct archive *a,
+ const char *m, const char *o, const char *v);
+static int archive_set_filter_option(struct archive *a,
+ const char *m, const char *o, const char *v);
+static int archive_set_option(struct archive *a,
+ const char *m, const char *o, const char *v);
+
+int
+archive_read_set_format_option(struct archive *a, const char *m, const char *o,
+ const char *v)
+{
+ return _archive_set_option(a, m, o, v,
+ ARCHIVE_READ_MAGIC, "archive_read_set_format_option",
+ archive_set_format_option);
+}
+
+int
+archive_read_set_filter_option(struct archive *a, const char *m, const char *o,
+ const char *v)
+{
+ return _archive_set_option(a, m, o, v,
+ ARCHIVE_READ_MAGIC, "archive_read_set_filter_option",
+ archive_set_filter_option);
+}
+
+int
+archive_read_set_option(struct archive *a, const char *m, const char *o,
+ const char *v)
+{
+ return _archive_set_option(a, m, o, v,
+ ARCHIVE_READ_MAGIC, "archive_read_set_option",
+ archive_set_option);
+}
+
+int
+archive_read_set_options(struct archive *a, const char *options)
+{
+ return _archive_set_options(a, options,
+ ARCHIVE_READ_MAGIC, "archive_read_set_options",
+ archive_set_option);
+}
+
+static int
+archive_set_format_option(struct archive *_a, const char *m, const char *o,
+ const char *v)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ size_t i;
+ int r, rv = ARCHIVE_WARN, matched_modules = 0;
+
+ for (i = 0; i < sizeof(a->formats)/sizeof(a->formats[0]); i++) {
+ struct archive_format_descriptor *format = &a->formats[i];
+
+ if (format->options == NULL || format->name == NULL)
+ /* This format does not support option. */
+ continue;
+ if (m != NULL) {
+ if (strcmp(format->name, m) != 0)
+ continue;
+ ++matched_modules;
+ }
+
+ a->format = format;
+ r = format->options(a, o, v);
+ a->format = NULL;
+
+ if (r == ARCHIVE_FATAL)
+ return (ARCHIVE_FATAL);
+
+ if (r == ARCHIVE_OK)
+ rv = ARCHIVE_OK;
+ }
+ /* If the format name didn't match, return a special code for
+ * _archive_set_option[s]. */
+ if (m != NULL && matched_modules == 0)
+ return ARCHIVE_WARN - 1;
+ return (rv);
+}
+
+static int
+archive_set_filter_option(struct archive *_a, const char *m, const char *o,
+ const char *v)
+{
+ (void)_a; /* UNUSED */
+ (void)o; /* UNUSED */
+ (void)v; /* UNUSED */
+
+ /* If the filter name didn't match, return a special code for
+ * _archive_set_option[s]. */
+ if (m != NULL)
+ return ARCHIVE_WARN - 1;
+ return ARCHIVE_WARN;
+}
+
+static int
+archive_set_option(struct archive *a, const char *m, const char *o,
+ const char *v)
+{
+ return _archive_set_either_option(a, m, o, v,
+ archive_set_format_option,
+ archive_set_filter_option);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_all.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_all.c
new file mode 100644
index 00000000000..edb508c1dfd
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_all.c
@@ -0,0 +1,85 @@
+/*-
+ * Copyright (c) 2003-2011 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive.h"
+#include "archive_private.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_all(struct archive *a)
+{
+ return archive_read_support_filter_all(a);
+}
+#endif
+
+int
+archive_read_support_filter_all(struct archive *a)
+{
+ archive_check_magic(a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_filter_all");
+
+ /* Bzip falls back to "bunzip2" command-line */
+ archive_read_support_filter_bzip2(a);
+ /* The decompress code doesn't use an outside library. */
+ archive_read_support_filter_compress(a);
+ /* Gzip decompress falls back to "gzip -d" command-line. */
+ archive_read_support_filter_gzip(a);
+ /* Lzip falls back to "unlzip" command-line program. */
+ archive_read_support_filter_lzip(a);
+ /* The LZMA file format has a very weak signature, so it
+ * may not be feasible to keep this here, but we'll try.
+ * This will come back out if there are problems. */
+ /* Lzma falls back to "unlzma" command-line program. */
+ archive_read_support_filter_lzma(a);
+ /* Xz falls back to "unxz" command-line program. */
+ archive_read_support_filter_xz(a);
+ /* The decode code doesn't use an outside library. */
+ archive_read_support_filter_uu(a);
+ /* The decode code doesn't use an outside library. */
+ archive_read_support_filter_rpm(a);
+ /* The decode code always uses "lrzip -q -d" command-line. */
+ archive_read_support_filter_lrzip(a);
+ /* Lzop decompress falls back to "lzop -d" command-line. */
+ archive_read_support_filter_lzop(a);
+ /* The decode code always uses "grzip -d" command-line. */
+ archive_read_support_filter_grzip(a);
+ /* Lz4 falls back to "lz4 -d" command-line program. */
+ archive_read_support_filter_lz4(a);
+ /* Zstd falls back to "zstd -d" command-line program. */
+ archive_read_support_filter_zstd(a);
+
+ /* Note: We always return ARCHIVE_OK here, even if some of the
+ * above return ARCHIVE_WARN. The intent here is to enable
+ * "as much as possible." Clients who need specific
+ * compression should enable those individually so they can
+ * verify the level of support. */
+ /* Clear any warning messages set by the above functions. */
+ archive_clear_error(a);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_by_code.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_by_code.c
new file mode 100644
index 00000000000..94c4af695f4
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_by_code.c
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2020 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive.h"
+#include "archive_private.h"
+
+int
+archive_read_support_filter_by_code(struct archive *a, int filter_code)
+{
+ archive_check_magic(a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_filter_by_code");
+
+ switch (filter_code) {
+ case ARCHIVE_FILTER_NONE:
+ return archive_read_support_filter_none(a);
+ break;
+ case ARCHIVE_FILTER_GZIP:
+ return archive_read_support_filter_gzip(a);
+ break;
+ case ARCHIVE_FILTER_BZIP2:
+ return archive_read_support_filter_bzip2(a);
+ break;
+ case ARCHIVE_FILTER_COMPRESS:
+ return archive_read_support_filter_compress(a);
+ break;
+ case ARCHIVE_FILTER_LZMA:
+ return archive_read_support_filter_lzma(a);
+ break;
+ case ARCHIVE_FILTER_XZ:
+ return archive_read_support_filter_xz(a);
+ break;
+ case ARCHIVE_FILTER_UU:
+ return archive_read_support_filter_uu(a);
+ break;
+ case ARCHIVE_FILTER_RPM:
+ return archive_read_support_filter_rpm(a);
+ break;
+ case ARCHIVE_FILTER_LZIP:
+ return archive_read_support_filter_lzip(a);
+ break;
+ case ARCHIVE_FILTER_LRZIP:
+ return archive_read_support_filter_lrzip(a);
+ break;
+ case ARCHIVE_FILTER_LZOP:
+ return archive_read_support_filter_lzop(a);
+ break;
+ case ARCHIVE_FILTER_GRZIP:
+ return archive_read_support_filter_grzip(a);
+ break;
+ case ARCHIVE_FILTER_LZ4:
+ return archive_read_support_filter_lz4(a);
+ break;
+ case ARCHIVE_FILTER_ZSTD:
+ return archive_read_support_filter_zstd(a);
+ break;
+ }
+ return (ARCHIVE_FATAL);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_bzip2.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_bzip2.c
new file mode 100644
index 00000000000..9158e668eb4
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_bzip2.c
@@ -0,0 +1,365 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+struct private_data {
+ bz_stream stream;
+ char *out_block;
+ size_t out_block_size;
+ char valid; /* True = decompressor is initialized */
+ char eof; /* True = found end of compressed data. */
+};
+
+/* Bzip2 filter */
+static ssize_t bzip2_filter_read(struct archive_read_filter *, const void **);
+static int bzip2_filter_close(struct archive_read_filter *);
+#endif
+
+/*
+ * Note that we can detect bzip2 archives even if we can't decompress
+ * them. (In fact, we like detecting them because we can give better
+ * error messages.) So the bid framework here gets compiled even
+ * if bzlib is unavailable.
+ */
+static int bzip2_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);
+static int bzip2_reader_init(struct archive_read_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_bzip2(struct archive *a)
+{
+ return archive_read_support_filter_bzip2(a);
+}
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+bzip2_bidder_vtable = {
+ .bid = bzip2_reader_bid,
+ .init = bzip2_reader_init,
+};
+
+int
+archive_read_support_filter_bzip2(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, "bzip2",
+ &bzip2_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ return (ARCHIVE_OK);
+#else
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external bzip2 program");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Test whether we can handle this data.
+ *
+ * This logic returns zero if any part of the signature fails. It
+ * also tries to Do The Right Thing if a very short buffer prevents us
+ * from verifying as much as we would like.
+ */
+static int
+bzip2_reader_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter)
+{
+ const unsigned char *buffer;
+ ssize_t avail;
+ int bits_checked;
+
+ (void)self; /* UNUSED */
+
+ /* Minimal bzip2 archive is 14 bytes. */
+ buffer = __archive_read_filter_ahead(filter, 14, &avail);
+ if (buffer == NULL)
+ return (0);
+
+ /* First three bytes must be "BZh" */
+ bits_checked = 0;
+ if (memcmp(buffer, "BZh", 3) != 0)
+ return (0);
+ bits_checked += 24;
+
+ /* Next follows a compression flag which must be an ASCII digit. */
+ if (buffer[3] < '1' || buffer[3] > '9')
+ return (0);
+ bits_checked += 5;
+
+ /* After BZh[1-9], there must be either a data block
+ * which begins with 0x314159265359 or an end-of-data
+ * marker of 0x177245385090. */
+ if (memcmp(buffer + 4, "\x31\x41\x59\x26\x53\x59", 6) == 0)
+ bits_checked += 48;
+ else if (memcmp(buffer + 4, "\x17\x72\x45\x38\x50\x90", 6) == 0)
+ bits_checked += 48;
+ else
+ return (0);
+
+ return (bits_checked);
+}
+
+#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR)
+
+/*
+ * If we don't have the library on this system, we can't actually do the
+ * decompression. We can, however, still detect compressed archives
+ * and emit a useful message.
+ */
+static int
+bzip2_reader_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "bzip2 -d");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_BZIP2;
+ self->name = "bzip2";
+ return (r);
+}
+
+
+#else
+
+static const struct archive_read_filter_vtable
+bzip2_reader_vtable = {
+ .read = bzip2_filter_read,
+ .close = bzip2_filter_close,
+};
+
+/*
+ * Setup the callbacks.
+ */
+static int
+bzip2_reader_init(struct archive_read_filter *self)
+{
+ static const size_t out_block_size = 64 * 1024;
+ void *out_block;
+ struct private_data *state;
+
+ self->code = ARCHIVE_FILTER_BZIP2;
+ self->name = "bzip2";
+
+ state = (struct private_data *)calloc(sizeof(*state), 1);
+ out_block = (unsigned char *)malloc(out_block_size);
+ if (state == NULL || out_block == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for bzip2 decompression");
+ free(out_block);
+ free(state);
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = state;
+ state->out_block_size = out_block_size;
+ state->out_block = out_block;
+ self->vtable = &bzip2_reader_vtable;
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Return the next block of decompressed data.
+ */
+static ssize_t
+bzip2_filter_read(struct archive_read_filter *self, const void **p)
+{
+ struct private_data *state;
+ size_t decompressed;
+ const char *read_buf;
+ ssize_t ret;
+
+ state = (struct private_data *)self->data;
+
+ if (state->eof) {
+ *p = NULL;
+ return (0);
+ }
+
+ /* Empty our output buffer. */
+ state->stream.next_out = state->out_block;
+ state->stream.avail_out = (uint32_t)state->out_block_size;
+
+ /* Try to fill the output buffer. */
+ for (;;) {
+ if (!state->valid) {
+ if (bzip2_reader_bid(self->bidder, self->upstream) == 0) {
+ state->eof = 1;
+ *p = state->out_block;
+ decompressed = state->stream.next_out
+ - state->out_block;
+ return (decompressed);
+ }
+ /* Initialize compression library. */
+ ret = BZ2_bzDecompressInit(&(state->stream),
+ 0 /* library verbosity */,
+ 0 /* don't use low-mem algorithm */);
+
+ /* If init fails, try low-memory algorithm instead. */
+ if (ret == BZ_MEM_ERROR)
+ ret = BZ2_bzDecompressInit(&(state->stream),
+ 0 /* library verbosity */,
+ 1 /* do use low-mem algo */);
+
+ if (ret != BZ_OK) {
+ const char *detail = NULL;
+ int err = ARCHIVE_ERRNO_MISC;
+ switch (ret) {
+ case BZ_PARAM_ERROR:
+ detail = "invalid setup parameter";
+ break;
+ case BZ_MEM_ERROR:
+ err = ENOMEM;
+ detail = "out of memory";
+ break;
+ case BZ_CONFIG_ERROR:
+ detail = "mis-compiled library";
+ break;
+ }
+ archive_set_error(&self->archive->archive, err,
+ "Internal error initializing decompressor%s%s",
+ detail == NULL ? "" : ": ",
+ detail);
+ return (ARCHIVE_FATAL);
+ }
+ state->valid = 1;
+ }
+
+ /* stream.next_in is really const, but bzlib
+ * doesn't declare it so. <sigh> */
+ read_buf =
+ __archive_read_filter_ahead(self->upstream, 1, &ret);
+ if (read_buf == NULL) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "truncated bzip2 input");
+ return (ARCHIVE_FATAL);
+ }
+ state->stream.next_in = (char *)(uintptr_t)read_buf;
+ state->stream.avail_in = (uint32_t)ret;
+ /* There is no more data, return whatever we have. */
+ if (ret == 0) {
+ state->eof = 1;
+ *p = state->out_block;
+ decompressed = state->stream.next_out
+ - state->out_block;
+ return (decompressed);
+ }
+
+ /* Decompress as much as we can in one pass. */
+ ret = BZ2_bzDecompress(&(state->stream));
+ __archive_read_filter_consume(self->upstream,
+ state->stream.next_in - read_buf);
+
+ switch (ret) {
+ case BZ_STREAM_END: /* Found end of stream. */
+ switch (BZ2_bzDecompressEnd(&(state->stream))) {
+ case BZ_OK:
+ break;
+ default:
+ archive_set_error(&(self->archive->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up decompressor");
+ return (ARCHIVE_FATAL);
+ }
+ state->valid = 0;
+ /* FALLTHROUGH */
+ case BZ_OK: /* Decompressor made some progress. */
+ /* If we filled our buffer, update stats and return. */
+ if (state->stream.avail_out == 0) {
+ *p = state->out_block;
+ decompressed = state->stream.next_out
+ - state->out_block;
+ return (decompressed);
+ }
+ break;
+ default: /* Return an error. */
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "bzip decompression failed");
+ return (ARCHIVE_FATAL);
+ }
+ }
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+bzip2_filter_close(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ int ret = ARCHIVE_OK;
+
+ state = (struct private_data *)self->data;
+
+ if (state->valid) {
+ switch (BZ2_bzDecompressEnd(&state->stream)) {
+ case BZ_OK:
+ break;
+ default:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up decompressor");
+ ret = ARCHIVE_FATAL;
+ }
+ state->valid = 0;
+ }
+
+ free(state->out_block);
+ free(state);
+ return (ret);
+}
+
+#endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_compress.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_compress.c
new file mode 100644
index 00000000000..05b80a576ac
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_compress.c
@@ -0,0 +1,452 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This code borrows heavily from "compress" source code, which is
+ * protected by the following copyright. (Clause 3 dropped by request
+ * of the Regents.)
+ */
+
+/*-
+ * Copyright (c) 1985, 1986, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis and James A. Woods, derived from original
+ * work by Spencer Thomas and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+/*
+ * Because LZW decompression is pretty simple, I've just implemented
+ * the whole decompressor here (cribbing from "compress" source code,
+ * of course), rather than relying on an external library. I have
+ * made an effort to clarify and simplify the algorithm, so the
+ * names and structure here don't exactly match those used by compress.
+ */
+
+struct private_data {
+ /* Input variables. */
+ const unsigned char *next_in;
+ size_t avail_in;
+ size_t consume_unnotified;
+ int bit_buffer;
+ int bits_avail;
+ size_t bytes_in_section;
+
+ /* Output variables. */
+ size_t out_block_size;
+ void *out_block;
+
+ /* Decompression status variables. */
+ int use_reset_code;
+ int end_of_stream; /* EOF status. */
+ int maxcode; /* Largest code. */
+ int maxcode_bits; /* Length of largest code. */
+ int section_end_code; /* When to increase bits. */
+ int bits; /* Current code length. */
+ int oldcode; /* Previous code. */
+ int finbyte; /* Last byte of prev code. */
+
+ /* Dictionary. */
+ int free_ent; /* Next dictionary entry. */
+ unsigned char suffix[65536];
+ uint16_t prefix[65536];
+
+ /*
+ * Scratch area for expanding dictionary entries. Note:
+ * "worst" case here comes from compressing /dev/zero: the
+ * last code in the dictionary will code a sequence of
+ * 65536-256 zero bytes. Thus, we need stack space to expand
+ * a 65280-byte dictionary entry. (Of course, 32640:1
+ * compression could also be considered the "best" case. ;-)
+ */
+ unsigned char *stackp;
+ unsigned char stack[65300];
+};
+
+static int compress_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);
+static int compress_bidder_init(struct archive_read_filter *);
+
+static ssize_t compress_filter_read(struct archive_read_filter *, const void **);
+static int compress_filter_close(struct archive_read_filter *);
+
+static int getbits(struct archive_read_filter *, int n);
+static int next_code(struct archive_read_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_compress(struct archive *a)
+{
+ return archive_read_support_filter_compress(a);
+}
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+compress_bidder_vtable = {
+ .bid = compress_bidder_bid,
+ .init = compress_bidder_init,
+};
+
+int
+archive_read_support_filter_compress(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ return __archive_read_register_bidder(a, NULL, "compress (.Z)",
+ &compress_bidder_vtable);
+}
+
+/*
+ * Test whether we can handle this data.
+ * This logic returns zero if any part of the signature fails.
+ */
+static int
+compress_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *buffer;
+ ssize_t avail;
+ int bits_checked;
+
+ (void)self; /* UNUSED */
+
+ /* Shortest valid compress file is 3 bytes. */
+ buffer = __archive_read_filter_ahead(filter, 3, &avail);
+
+ if (buffer == NULL)
+ return (0);
+
+ bits_checked = 0;
+ /* First two bytes are the magic value */
+ if (buffer[0] != 0x1F || buffer[1] != 0x9D)
+ return (0);
+ /* Third byte holds compression parameters. */
+ if (buffer[2] & 0x20) /* Reserved bit, must be zero. */
+ return (0);
+ if (buffer[2] & 0x40) /* Reserved bit, must be zero. */
+ return (0);
+ bits_checked += 18;
+
+ return (bits_checked);
+}
+
+static const struct archive_read_filter_vtable
+compress_reader_vtable = {
+ .read = compress_filter_read,
+ .close = compress_filter_close,
+};
+
+/*
+ * Setup the callbacks.
+ */
+static int
+compress_bidder_init(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ static const size_t out_block_size = 64 * 1024;
+ void *out_block;
+ int code;
+
+ self->code = ARCHIVE_FILTER_COMPRESS;
+ self->name = "compress (.Z)";
+
+ state = (struct private_data *)calloc(sizeof(*state), 1);
+ out_block = malloc(out_block_size);
+ if (state == NULL || out_block == NULL) {
+ free(out_block);
+ free(state);
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for %s decompression",
+ self->name);
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = state;
+ state->out_block_size = out_block_size;
+ state->out_block = out_block;
+ self->vtable = &compress_reader_vtable;
+
+ /* XXX MOVE THE FOLLOWING OUT OF INIT() XXX */
+
+ (void)getbits(self, 8); /* Skip first signature byte. */
+ (void)getbits(self, 8); /* Skip second signature byte. */
+
+ /* Get compression parameters. */
+ code = getbits(self, 8);
+ if ((code & 0x1f) > 16) {
+ archive_set_error(&self->archive->archive, -1,
+ "Invalid compressed data");
+ return (ARCHIVE_FATAL);
+ }
+ state->maxcode_bits = code & 0x1f;
+ state->maxcode = (1 << state->maxcode_bits);
+ state->use_reset_code = code & 0x80;
+
+ /* Initialize decompressor. */
+ state->free_ent = 256;
+ state->stackp = state->stack;
+ if (state->use_reset_code)
+ state->free_ent++;
+ state->bits = 9;
+ state->section_end_code = (1<<state->bits) - 1;
+ state->oldcode = -1;
+ for (code = 255; code >= 0; code--) {
+ state->prefix[code] = 0;
+ state->suffix[code] = code;
+ }
+ next_code(self);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Return a block of data from the decompression buffer. Decompress more
+ * as necessary.
+ */
+static ssize_t
+compress_filter_read(struct archive_read_filter *self, const void **pblock)
+{
+ struct private_data *state;
+ unsigned char *p, *start, *end;
+ int ret;
+
+ state = (struct private_data *)self->data;
+ if (state->end_of_stream) {
+ *pblock = NULL;
+ return (0);
+ }
+ p = start = (unsigned char *)state->out_block;
+ end = start + state->out_block_size;
+
+ while (p < end && !state->end_of_stream) {
+ if (state->stackp > state->stack) {
+ *p++ = *--state->stackp;
+ } else {
+ ret = next_code(self);
+ if (ret == -1)
+ state->end_of_stream = ret;
+ else if (ret != ARCHIVE_OK)
+ return (ret);
+ }
+ }
+
+ *pblock = start;
+ return (p - start);
+}
+
+/*
+ * Close and release the filter.
+ */
+static int
+compress_filter_close(struct archive_read_filter *self)
+{
+ struct private_data *state = (struct private_data *)self->data;
+
+ free(state->out_block);
+ free(state);
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Process the next code and fill the stack with the expansion
+ * of the code. Returns ARCHIVE_FATAL if there is a fatal I/O or
+ * format error, ARCHIVE_EOF if we hit end of data, ARCHIVE_OK otherwise.
+ */
+static int
+next_code(struct archive_read_filter *self)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ int code, newcode;
+
+ static int debug_buff[1024];
+ static unsigned debug_index;
+
+ code = newcode = getbits(self, state->bits);
+ if (code < 0)
+ return (code);
+
+ debug_buff[debug_index++] = code;
+ if (debug_index >= sizeof(debug_buff)/sizeof(debug_buff[0]))
+ debug_index = 0;
+
+ /* If it's a reset code, reset the dictionary. */
+ if ((code == 256) && state->use_reset_code) {
+ /*
+ * The original 'compress' implementation blocked its
+ * I/O in a manner that resulted in junk bytes being
+ * inserted after every reset. The next section skips
+ * this junk. (Yes, the number of *bytes* to skip is
+ * a function of the current *bit* length.)
+ */
+ int skip_bytes = state->bits -
+ (state->bytes_in_section % state->bits);
+ skip_bytes %= state->bits;
+ state->bits_avail = 0; /* Discard rest of this byte. */
+ while (skip_bytes-- > 0) {
+ code = getbits(self, 8);
+ if (code < 0)
+ return (code);
+ }
+ /* Now, actually do the reset. */
+ state->bytes_in_section = 0;
+ state->bits = 9;
+ state->section_end_code = (1 << state->bits) - 1;
+ state->free_ent = 257;
+ state->oldcode = -1;
+ return (next_code(self));
+ }
+
+ if (code > state->free_ent
+ || (code == state->free_ent && state->oldcode < 0)) {
+ /* An invalid code is a fatal error. */
+ archive_set_error(&(self->archive->archive), -1,
+ "Invalid compressed data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Special case for KwKwK string. */
+ if (code >= state->free_ent) {
+ *state->stackp++ = state->finbyte;
+ code = state->oldcode;
+ }
+
+ /* Generate output characters in reverse order. */
+ while (code >= 256) {
+ *state->stackp++ = state->suffix[code];
+ code = state->prefix[code];
+ }
+ *state->stackp++ = state->finbyte = code;
+
+ /* Generate the new entry. */
+ code = state->free_ent;
+ if (code < state->maxcode && state->oldcode >= 0) {
+ state->prefix[code] = state->oldcode;
+ state->suffix[code] = state->finbyte;
+ ++state->free_ent;
+ }
+ if (state->free_ent > state->section_end_code) {
+ state->bits++;
+ state->bytes_in_section = 0;
+ if (state->bits == state->maxcode_bits)
+ state->section_end_code = state->maxcode;
+ else
+ state->section_end_code = (1 << state->bits) - 1;
+ }
+
+ /* Remember previous code. */
+ state->oldcode = newcode;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Return next 'n' bits from stream.
+ *
+ * -1 indicates end of available data.
+ */
+static int
+getbits(struct archive_read_filter *self, int n)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ int code;
+ ssize_t ret;
+ static const int mask[] = {
+ 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff,
+ 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+ };
+
+ while (state->bits_avail < n) {
+ if (state->avail_in <= 0) {
+ if (state->consume_unnotified) {
+ __archive_read_filter_consume(self->upstream,
+ state->consume_unnotified);
+ state->consume_unnotified = 0;
+ }
+ state->next_in
+ = __archive_read_filter_ahead(self->upstream,
+ 1, &ret);
+ if (ret == 0)
+ return (-1);
+ if (ret < 0 || state->next_in == NULL)
+ return (ARCHIVE_FATAL);
+ state->consume_unnotified = state->avail_in = ret;
+ }
+ state->bit_buffer |= *state->next_in++ << state->bits_avail;
+ state->avail_in--;
+ state->bits_avail += 8;
+ state->bytes_in_section++;
+ }
+
+ code = state->bit_buffer;
+ state->bit_buffer >>= n;
+ state->bits_avail -= n;
+
+ return (code & mask[n]);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_grzip.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_grzip.c
new file mode 100644
index 00000000000..d4d1737cd97
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_grzip.c
@@ -0,0 +1,112 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+static const unsigned char grzip_magic[] = {
+ 0x47, 0x52, 0x5a, 0x69, 0x70, 0x49, 0x49, 0x00,
+ 0x02, 0x04, 0x3a, 0x29 };
+
+static int grzip_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int grzip_bidder_init(struct archive_read_filter *);
+
+
+static const struct archive_read_filter_bidder_vtable
+grzip_bidder_vtable = {
+ .bid = grzip_bidder_bid,
+ .init = grzip_bidder_init,
+};
+
+int
+archive_read_support_filter_grzip(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, NULL,
+ &grzip_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* This filter always uses an external program. */
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external grzip program for grzip decompression");
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Bidder just verifies the header and returns the number of verified bits.
+ */
+static int
+grzip_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *p;
+ ssize_t avail;
+
+ (void)self; /* UNUSED */
+
+ p = __archive_read_filter_ahead(filter, sizeof(grzip_magic), &avail);
+ if (p == NULL || avail == 0)
+ return (0);
+
+ if (memcmp(p, grzip_magic, sizeof(grzip_magic)))
+ return (0);
+
+ return (sizeof(grzip_magic) * 8);
+}
+
+static int
+grzip_bidder_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "grzip -d");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_GRZIP;
+ self->name = "grzip";
+ return (r);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_gzip.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_gzip.c
new file mode 100644
index 00000000000..4135a636180
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_gzip.c
@@ -0,0 +1,536 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#ifdef HAVE_ZLIB_H
+struct private_data {
+ z_stream stream;
+ char in_stream;
+ unsigned char *out_block;
+ size_t out_block_size;
+ int64_t total_out;
+ unsigned long crc;
+ uint32_t mtime;
+ char *name;
+ char eof; /* True = found end of compressed data. */
+};
+
+/* Gzip Filter. */
+static ssize_t gzip_filter_read(struct archive_read_filter *, const void **);
+static int gzip_filter_close(struct archive_read_filter *);
+#endif
+
+/*
+ * Note that we can detect gzip archives even if we can't decompress
+ * them. (In fact, we like detecting them because we can give better
+ * error messages.) So the bid framework here gets compiled even
+ * if zlib is unavailable.
+ *
+ * TODO: If zlib is unavailable, gzip_bidder_init() should
+ * use the compress_program framework to try to fire up an external
+ * gzip program.
+ */
+static int gzip_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int gzip_bidder_init(struct archive_read_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_gzip(struct archive *a)
+{
+ return archive_read_support_filter_gzip(a);
+}
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+gzip_bidder_vtable = {
+ .bid = gzip_bidder_bid,
+ .init = gzip_bidder_init,
+};
+
+int
+archive_read_support_filter_gzip(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, "gzip",
+ &gzip_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Signal the extent of gzip support with the return value here. */
+#if HAVE_ZLIB_H
+ return (ARCHIVE_OK);
+#else
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external gzip program");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Read and verify the header.
+ *
+ * Returns zero if the header couldn't be validated, else returns
+ * number of bytes in header. If pbits is non-NULL, it receives a
+ * count of bits verified, suitable for use by bidder.
+ */
+static ssize_t
+peek_at_header(struct archive_read_filter *filter, int *pbits,
+#ifdef HAVE_ZLIB_H
+ struct private_data *state
+#else
+ void *state
+#endif
+ )
+{
+ const unsigned char *p;
+ ssize_t avail, len;
+ int bits = 0;
+ int header_flags;
+#ifndef HAVE_ZLIB_H
+ (void)state; /* UNUSED */
+#endif
+
+ /* Start by looking at the first ten bytes of the header, which
+ * is all fixed layout. */
+ len = 10;
+ p = __archive_read_filter_ahead(filter, len, &avail);
+ if (p == NULL || avail == 0)
+ return (0);
+ /* We only support deflation- third byte must be 0x08. */
+ if (memcmp(p, "\x1F\x8B\x08", 3) != 0)
+ return (0);
+ bits += 24;
+ if ((p[3] & 0xE0)!= 0) /* No reserved flags set. */
+ return (0);
+ bits += 3;
+ header_flags = p[3];
+ /* Bytes 4-7 are mod time in little endian. */
+#ifdef HAVE_ZLIB_H
+ if (state)
+ state->mtime = archive_le32dec(p + 4);
+#endif
+ /* Byte 8 is deflate flags. */
+ /* XXXX TODO: return deflate flags back to consume_header for use
+ in initializing the decompressor. */
+ /* Byte 9 is OS. */
+
+ /* Optional extra data: 2 byte length plus variable body. */
+ if (header_flags & 4) {
+ p = __archive_read_filter_ahead(filter, len + 2, &avail);
+ if (p == NULL)
+ return (0);
+ len += ((int)p[len + 1] << 8) | (int)p[len];
+ len += 2;
+ }
+
+ /* Null-terminated optional filename. */
+ if (header_flags & 8) {
+#ifdef HAVE_ZLIB_H
+ ssize_t file_start = len;
+#endif
+ do {
+ ++len;
+ if (avail < len)
+ p = __archive_read_filter_ahead(filter,
+ len, &avail);
+ if (p == NULL)
+ return (0);
+ } while (p[len - 1] != 0);
+
+#ifdef HAVE_ZLIB_H
+ if (state) {
+ /* Reset the name in case of repeat header reads. */
+ free(state->name);
+ state->name = strdup((const char *)&p[file_start]);
+ }
+#endif
+ }
+
+ /* Null-terminated optional comment. */
+ if (header_flags & 16) {
+ do {
+ ++len;
+ if (avail < len)
+ p = __archive_read_filter_ahead(filter,
+ len, &avail);
+ if (p == NULL)
+ return (0);
+ } while (p[len - 1] != 0);
+ }
+
+ /* Optional header CRC */
+ if ((header_flags & 2)) {
+ p = __archive_read_filter_ahead(filter, len + 2, &avail);
+ if (p == NULL)
+ return (0);
+#if 0
+ int hcrc = ((int)p[len + 1] << 8) | (int)p[len];
+ int crc = /* XXX TODO: Compute header CRC. */;
+ if (crc != hcrc)
+ return (0);
+ bits += 16;
+#endif
+ len += 2;
+ }
+
+ if (pbits != NULL)
+ *pbits = bits;
+ return (len);
+}
+
+/*
+ * Bidder just verifies the header and returns the number of verified bits.
+ */
+static int
+gzip_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ int bits_checked;
+
+ (void)self; /* UNUSED */
+
+ if (peek_at_header(filter, &bits_checked, NULL))
+ return (bits_checked);
+ return (0);
+}
+
+#ifndef HAVE_ZLIB_H
+
+/*
+ * If we don't have the library on this system, we can't do the
+ * decompression directly. We can, however, try to run "gzip -d"
+ * in case that's available.
+ */
+static int
+gzip_bidder_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "gzip -d");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_GZIP;
+ self->name = "gzip";
+ return (r);
+}
+
+#else
+
+static int
+gzip_read_header(struct archive_read_filter *self, struct archive_entry *entry)
+{
+ struct private_data *state;
+
+ state = (struct private_data *)self->data;
+
+ /* A mtime of 0 is considered invalid/missing. */
+ if (state->mtime != 0)
+ archive_entry_set_mtime(entry, state->mtime, 0);
+
+ /* If the name is available, extract it. */
+ if (state->name)
+ archive_entry_set_pathname(entry, state->name);
+
+ return (ARCHIVE_OK);
+}
+
+static const struct archive_read_filter_vtable
+gzip_reader_vtable = {
+ .read = gzip_filter_read,
+ .close = gzip_filter_close,
+#ifdef HAVE_ZLIB_H
+ .read_header = gzip_read_header,
+#endif
+};
+
+/*
+ * Initialize the filter object.
+ */
+static int
+gzip_bidder_init(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ static const size_t out_block_size = 64 * 1024;
+ void *out_block;
+
+ self->code = ARCHIVE_FILTER_GZIP;
+ self->name = "gzip";
+
+ state = (struct private_data *)calloc(sizeof(*state), 1);
+ out_block = (unsigned char *)malloc(out_block_size);
+ if (state == NULL || out_block == NULL) {
+ free(out_block);
+ free(state);
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for gzip decompression");
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = state;
+ state->out_block_size = out_block_size;
+ state->out_block = out_block;
+ self->vtable = &gzip_reader_vtable;
+
+ state->in_stream = 0; /* We're not actually within a stream yet. */
+
+ return (ARCHIVE_OK);
+}
+
+static int
+consume_header(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ ssize_t avail;
+ size_t len;
+ int ret;
+
+ state = (struct private_data *)self->data;
+
+ /* If this is a real header, consume it. */
+ len = peek_at_header(self->upstream, NULL, state);
+ if (len == 0)
+ return (ARCHIVE_EOF);
+ __archive_read_filter_consume(self->upstream, len);
+
+ /* Initialize CRC accumulator. */
+ state->crc = crc32(0L, NULL, 0);
+
+ /* Initialize compression library. */
+ state->stream.next_in = (unsigned char *)(uintptr_t)
+ __archive_read_filter_ahead(self->upstream, 1, &avail);
+ state->stream.avail_in = (uInt)avail;
+ ret = inflateInit2(&(state->stream),
+ -15 /* Don't check for zlib header */);
+
+ /* Decipher the error code. */
+ switch (ret) {
+ case Z_OK:
+ state->in_stream = 1;
+ return (ARCHIVE_OK);
+ case Z_STREAM_ERROR:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ "invalid setup parameter");
+ break;
+ case Z_MEM_ERROR:
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Internal error initializing compression library: "
+ "out of memory");
+ break;
+ case Z_VERSION_ERROR:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ "invalid library version");
+ break;
+ default:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ " Zlib error %d", ret);
+ break;
+ }
+ return (ARCHIVE_FATAL);
+}
+
+static int
+consume_trailer(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ const unsigned char *p;
+ ssize_t avail;
+
+ state = (struct private_data *)self->data;
+
+ state->in_stream = 0;
+ switch (inflateEnd(&(state->stream))) {
+ case Z_OK:
+ break;
+ default:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up gzip decompressor");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* GZip trailer is a fixed 8 byte structure. */
+ p = __archive_read_filter_ahead(self->upstream, 8, &avail);
+ if (p == NULL || avail == 0)
+ return (ARCHIVE_FATAL);
+
+ /* XXX TODO: Verify the length and CRC. */
+
+ /* We've verified the trailer, so consume it now. */
+ __archive_read_filter_consume(self->upstream, 8);
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+gzip_filter_read(struct archive_read_filter *self, const void **p)
+{
+ struct private_data *state;
+ size_t decompressed;
+ ssize_t avail_in, max_in;
+ int ret;
+
+ state = (struct private_data *)self->data;
+
+ /* Empty our output buffer. */
+ state->stream.next_out = state->out_block;
+ state->stream.avail_out = (uInt)state->out_block_size;
+
+ /* Try to fill the output buffer. */
+ while (state->stream.avail_out > 0 && !state->eof) {
+ /* If we're not in a stream, read a header
+ * and initialize the decompression library. */
+ if (!state->in_stream) {
+ ret = consume_header(self);
+ if (ret == ARCHIVE_EOF) {
+ state->eof = 1;
+ break;
+ }
+ if (ret < ARCHIVE_OK)
+ return (ret);
+ }
+
+ /* Peek at the next available data. */
+ /* ZLib treats stream.next_in as const but doesn't declare
+ * it so, hence this ugly cast. */
+ state->stream.next_in = (unsigned char *)(uintptr_t)
+ __archive_read_filter_ahead(self->upstream, 1, &avail_in);
+ if (state->stream.next_in == NULL) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "truncated gzip input");
+ return (ARCHIVE_FATAL);
+ }
+ if (UINT_MAX >= SSIZE_MAX)
+ max_in = SSIZE_MAX;
+ else
+ max_in = UINT_MAX;
+ if (avail_in > max_in)
+ avail_in = max_in;
+ state->stream.avail_in = (uInt)avail_in;
+
+ /* Decompress and consume some of that data. */
+ ret = inflate(&(state->stream), 0);
+ switch (ret) {
+ case Z_OK: /* Decompressor made some progress. */
+ __archive_read_filter_consume(self->upstream,
+ avail_in - state->stream.avail_in);
+ break;
+ case Z_STREAM_END: /* Found end of stream. */
+ __archive_read_filter_consume(self->upstream,
+ avail_in - state->stream.avail_in);
+ /* Consume the stream trailer; release the
+ * decompression library. */
+ ret = consume_trailer(self);
+ if (ret < ARCHIVE_OK)
+ return (ret);
+ break;
+ default:
+ /* Return an error. */
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "gzip decompression failed");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ /* We've read as much as we can. */
+ decompressed = state->stream.next_out - state->out_block;
+ state->total_out += decompressed;
+ if (decompressed == 0)
+ *p = NULL;
+ else
+ *p = state->out_block;
+ return (decompressed);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+gzip_filter_close(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ int ret;
+
+ state = (struct private_data *)self->data;
+ ret = ARCHIVE_OK;
+
+ if (state->in_stream) {
+ switch (inflateEnd(&(state->stream))) {
+ case Z_OK:
+ break;
+ default:
+ archive_set_error(&(self->archive->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up gzip compressor");
+ ret = ARCHIVE_FATAL;
+ }
+ }
+
+ free(state->name);
+ free(state->out_block);
+ free(state);
+ return (ret);
+}
+
+#endif /* HAVE_ZLIB_H */
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_lrzip.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_lrzip.c
new file mode 100644
index 00000000000..a2389894f1d
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_lrzip.c
@@ -0,0 +1,122 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#define LRZIP_HEADER_MAGIC "LRZI"
+#define LRZIP_HEADER_MAGIC_LEN 4
+
+static int lrzip_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int lrzip_bidder_init(struct archive_read_filter *);
+
+
+static const struct archive_read_filter_bidder_vtable
+lrzip_bidder_vtable = {
+ .bid = lrzip_bidder_bid,
+ .init = lrzip_bidder_init,
+};
+
+int
+archive_read_support_filter_lrzip(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, "lrzip",
+ &lrzip_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* This filter always uses an external program. */
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external lrzip program for lrzip decompression");
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Bidder just verifies the header and returns the number of verified bits.
+ */
+static int
+lrzip_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *p;
+ ssize_t avail, len;
+ int i;
+
+ (void)self; /* UNUSED */
+ /* Start by looking at the first six bytes of the header, which
+ * is all fixed layout. */
+ len = 6;
+ p = __archive_read_filter_ahead(filter, len, &avail);
+ if (p == NULL || avail == 0)
+ return (0);
+
+ if (memcmp(p, LRZIP_HEADER_MAGIC, LRZIP_HEADER_MAGIC_LEN))
+ return (0);
+
+ /* current major version is always 0, verify this */
+ if (p[LRZIP_HEADER_MAGIC_LEN])
+ return 0;
+ /* support only v0.6+ lrzip for sanity */
+ i = p[LRZIP_HEADER_MAGIC_LEN + 1];
+ if ((i < 6) || (i > 10))
+ return 0;
+
+ return (int)len;
+}
+
+static int
+lrzip_bidder_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "lrzip -d -q");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_LRZIP;
+ self->name = "lrzip";
+ return (r);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_lz4.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_lz4.c
new file mode 100644
index 00000000000..d0fc1a83e46
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_lz4.c
@@ -0,0 +1,742 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_LZ4_H
+#include <lz4.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_xxhash.h"
+
+#define LZ4_MAGICNUMBER 0x184d2204
+#define LZ4_SKIPPABLED 0x184d2a50
+#define LZ4_LEGACY 0x184c2102
+
+#if defined(HAVE_LIBLZ4)
+struct private_data {
+ enum { SELECT_STREAM,
+ READ_DEFAULT_STREAM,
+ READ_DEFAULT_BLOCK,
+ READ_LEGACY_STREAM,
+ READ_LEGACY_BLOCK,
+ } stage;
+ struct {
+ unsigned block_independence:1;
+ unsigned block_checksum:3;
+ unsigned stream_size:1;
+ unsigned stream_checksum:1;
+ unsigned preset_dictionary:1;
+ int block_maximum_size;
+ } flags;
+ int64_t stream_size;
+ uint32_t dict_id;
+ char *out_block;
+ size_t out_block_size;
+
+ /* Bytes read but not yet consumed via __archive_read_consume() */
+ size_t unconsumed;
+ size_t decoded_size;
+ void *xxh32_state;
+
+ char valid; /* True = decompressor is initialized */
+ char eof; /* True = found end of compressed data. */
+};
+
+#define LEGACY_BLOCK_SIZE (8 * 1024 * 1024)
+
+/* Lz4 filter */
+static ssize_t lz4_filter_read(struct archive_read_filter *, const void **);
+static int lz4_filter_close(struct archive_read_filter *);
+#endif
+
+/*
+ * Note that we can detect lz4 archives even if we can't decompress
+ * them. (In fact, we like detecting them because we can give better
+ * error messages.) So the bid framework here gets compiled even
+ * if liblz4 is unavailable.
+ */
+static int lz4_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);
+static int lz4_reader_init(struct archive_read_filter *);
+#if defined(HAVE_LIBLZ4)
+static ssize_t lz4_filter_read_default_stream(struct archive_read_filter *,
+ const void **);
+static ssize_t lz4_filter_read_legacy_stream(struct archive_read_filter *,
+ const void **);
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+lz4_bidder_vtable = {
+ .bid = lz4_reader_bid,
+ .init = lz4_reader_init,
+};
+
+int
+archive_read_support_filter_lz4(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, "lz4",
+ &lz4_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+#if defined(HAVE_LIBLZ4)
+ return (ARCHIVE_OK);
+#else
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external lz4 program");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Test whether we can handle this data.
+ *
+ * This logic returns zero if any part of the signature fails. It
+ * also tries to Do The Right Thing if a very short buffer prevents us
+ * from verifying as much as we would like.
+ */
+static int
+lz4_reader_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *buffer;
+ ssize_t avail;
+ int bits_checked;
+ uint32_t number;
+
+ (void)self; /* UNUSED */
+
+ /* Minimal lz4 archive is 11 bytes. */
+ buffer = __archive_read_filter_ahead(filter, 11, &avail);
+ if (buffer == NULL)
+ return (0);
+
+ /* First four bytes must be LZ4 magic numbers. */
+ bits_checked = 0;
+ if ((number = archive_le32dec(buffer)) == LZ4_MAGICNUMBER) {
+ unsigned char flag, BD;
+
+ bits_checked += 32;
+ /* Next follows a stream descriptor. */
+ /* Descriptor Flags. */
+ flag = buffer[4];
+ /* A version number must be "01". */
+ if (((flag & 0xc0) >> 6) != 1)
+ return (0);
+ /* A reserved bit must be "0". */
+ if (flag & 2)
+ return (0);
+ bits_checked += 8;
+ BD = buffer[5];
+ /* A block maximum size should be more than 3. */
+ if (((BD & 0x70) >> 4) < 4)
+ return (0);
+ /* Reserved bits must be "0". */
+ if (BD & ~0x70)
+ return (0);
+ bits_checked += 8;
+ } else if (number == LZ4_LEGACY) {
+ bits_checked += 32;
+ }
+
+ return (bits_checked);
+}
+
+#if !defined(HAVE_LIBLZ4)
+
+/*
+ * If we don't have the library on this system, we can't actually do the
+ * decompression. We can, however, still detect compressed archives
+ * and emit a useful message.
+ */
+static int
+lz4_reader_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "lz4 -d -q");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_LZ4;
+ self->name = "lz4";
+ return (r);
+}
+
+
+#else
+
+static const struct archive_read_filter_vtable
+lz4_reader_vtable = {
+ .read = lz4_filter_read,
+ .close = lz4_filter_close,
+};
+
+/*
+ * Setup the callbacks.
+ */
+static int
+lz4_reader_init(struct archive_read_filter *self)
+{
+ struct private_data *state;
+
+ self->code = ARCHIVE_FILTER_LZ4;
+ self->name = "lz4";
+
+ state = (struct private_data *)calloc(sizeof(*state), 1);
+ if (state == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for lz4 decompression");
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = state;
+ state->stage = SELECT_STREAM;
+ self->vtable = &lz4_reader_vtable;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+lz4_allocate_out_block(struct archive_read_filter *self)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ size_t out_block_size = state->flags.block_maximum_size;
+ void *out_block;
+
+ if (!state->flags.block_independence)
+ out_block_size += 64 * 1024;
+ if (state->out_block_size < out_block_size) {
+ free(state->out_block);
+ out_block = (unsigned char *)malloc(out_block_size);
+ state->out_block_size = out_block_size;
+ if (out_block == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for lz4 decompression");
+ return (ARCHIVE_FATAL);
+ }
+ state->out_block = out_block;
+ }
+ if (!state->flags.block_independence)
+ memset(state->out_block, 0, 64 * 1024);
+ return (ARCHIVE_OK);
+}
+
+static int
+lz4_allocate_out_block_for_legacy(struct archive_read_filter *self)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ size_t out_block_size = LEGACY_BLOCK_SIZE;
+ void *out_block;
+
+ if (state->out_block_size < out_block_size) {
+ free(state->out_block);
+ out_block = (unsigned char *)malloc(out_block_size);
+ state->out_block_size = out_block_size;
+ if (out_block == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for lz4 decompression");
+ return (ARCHIVE_FATAL);
+ }
+ state->out_block = out_block;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Return the next block of decompressed data.
+ */
+static ssize_t
+lz4_filter_read(struct archive_read_filter *self, const void **p)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ ssize_t ret;
+
+ if (state->eof) {
+ *p = NULL;
+ return (0);
+ }
+
+ __archive_read_filter_consume(self->upstream, state->unconsumed);
+ state->unconsumed = 0;
+
+ switch (state->stage) {
+ case SELECT_STREAM:
+ break;
+ case READ_DEFAULT_STREAM:
+ case READ_LEGACY_STREAM:
+ /* Reading a lz4 stream already failed. */
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "Invalid sequence.");
+ return (ARCHIVE_FATAL);
+ case READ_DEFAULT_BLOCK:
+ ret = lz4_filter_read_default_stream(self, p);
+ if (ret != 0 || state->stage != SELECT_STREAM)
+ return ret;
+ break;
+ case READ_LEGACY_BLOCK:
+ ret = lz4_filter_read_legacy_stream(self, p);
+ if (ret != 0 || state->stage != SELECT_STREAM)
+ return ret;
+ break;
+ default:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "Program error.");
+ return (ARCHIVE_FATAL);
+ break;
+ }
+
+ while (state->stage == SELECT_STREAM) {
+ const char *read_buf;
+
+ /* Read a magic number. */
+ read_buf = __archive_read_filter_ahead(self->upstream, 4,
+ NULL);
+ if (read_buf == NULL) {
+ state->eof = 1;
+ *p = NULL;
+ return (0);
+ }
+ uint32_t number = archive_le32dec(read_buf);
+ __archive_read_filter_consume(self->upstream, 4);
+ if (number == LZ4_MAGICNUMBER)
+ return lz4_filter_read_default_stream(self, p);
+ else if (number == LZ4_LEGACY)
+ return lz4_filter_read_legacy_stream(self, p);
+ else if ((number & ~0xF) == LZ4_SKIPPABLED) {
+ read_buf = __archive_read_filter_ahead(
+ self->upstream, 4, NULL);
+ if (read_buf == NULL) {
+ archive_set_error(
+ &self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Malformed lz4 data");
+ return (ARCHIVE_FATAL);
+ }
+ uint32_t skip_bytes = archive_le32dec(read_buf);
+ __archive_read_filter_consume(self->upstream,
+ 4 + skip_bytes);
+ } else {
+ /* Ignore following unrecognized data. */
+ state->eof = 1;
+ *p = NULL;
+ return (0);
+ }
+ }
+ state->eof = 1;
+ *p = NULL;
+ return (0);
+}
+
+static int
+lz4_filter_read_descriptor(struct archive_read_filter *self)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ const char *read_buf;
+ ssize_t bytes_remaining;
+ ssize_t descriptor_bytes;
+ unsigned char flag, bd;
+ unsigned int chsum, chsum_verifier;
+
+ /* Make sure we have 2 bytes for flags. */
+ read_buf = __archive_read_filter_ahead(self->upstream, 2,
+ &bytes_remaining);
+ if (read_buf == NULL) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "truncated lz4 input");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ Parse flags.
+ */
+ flag = (unsigned char)read_buf[0];
+ /* Verify version number. */
+ if ((flag & 0xc0) != 1<<6)
+ goto malformed_error;
+ /* A reserved bit must be zero. */
+ if (flag & 0x02)
+ goto malformed_error;
+ state->flags.block_independence = (flag & 0x20) != 0;
+ state->flags.block_checksum = (flag & 0x10)?4:0;
+ state->flags.stream_size = (flag & 0x08) != 0;
+ state->flags.stream_checksum = (flag & 0x04) != 0;
+ state->flags.preset_dictionary = (flag & 0x01) != 0;
+
+ /* BD */
+ bd = (unsigned char)read_buf[1];
+ /* Reserved bits must be zero. */
+ if (bd & 0x8f)
+ goto malformed_error;
+ /* Get a maximum block size. */
+ switch (read_buf[1] >> 4) {
+ case 4: /* 64 KB */
+ state->flags.block_maximum_size = 64 * 1024;
+ break;
+ case 5: /* 256 KB */
+ state->flags.block_maximum_size = 256 * 1024;
+ break;
+ case 6: /* 1 MB */
+ state->flags.block_maximum_size = 1024 * 1024;
+ break;
+ case 7: /* 4 MB */
+ state->flags.block_maximum_size = 4 * 1024 * 1024;
+ break;
+ default:
+ goto malformed_error;
+ }
+
+ /* Read the whole descriptor in a stream block. */
+ descriptor_bytes = 3;
+ if (state->flags.stream_size)
+ descriptor_bytes += 8;
+ if (state->flags.preset_dictionary)
+ descriptor_bytes += 4;
+ if (bytes_remaining < descriptor_bytes) {
+ read_buf = __archive_read_filter_ahead(self->upstream,
+ descriptor_bytes, &bytes_remaining);
+ if (read_buf == NULL) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "truncated lz4 input");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ /* Check if a descriptor is corrupted */
+ chsum = __archive_xxhash.XXH32(read_buf, (int)descriptor_bytes -1, 0);
+ chsum = (chsum >> 8) & 0xff;
+ chsum_verifier = read_buf[descriptor_bytes-1] & 0xff;
+ if (chsum != chsum_verifier)
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ goto malformed_error;
+#endif
+
+ __archive_read_filter_consume(self->upstream, descriptor_bytes);
+
+ /* Make sure we have a large enough buffer for uncompressed data. */
+ if (lz4_allocate_out_block(self) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ if (state->flags.stream_checksum)
+ state->xxh32_state = __archive_xxhash.XXH32_init(0);
+
+ state->decoded_size = 0;
+ /* Success */
+ return (ARCHIVE_OK);
+malformed_error:
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "malformed lz4 data");
+ return (ARCHIVE_FATAL);
+}
+
+static ssize_t
+lz4_filter_read_data_block(struct archive_read_filter *self, const void **p)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ ssize_t compressed_size;
+ const char *read_buf;
+ ssize_t bytes_remaining;
+ int checksum_size;
+ ssize_t uncompressed_size;
+ size_t prefix64k;
+
+ *p = NULL;
+
+ /* Make sure we have 4 bytes for a block size. */
+ read_buf = __archive_read_filter_ahead(self->upstream, 4,
+ &bytes_remaining);
+ if (read_buf == NULL)
+ goto truncated_error;
+ compressed_size = archive_le32dec(read_buf);
+ if ((compressed_size & 0x7fffffff) > state->flags.block_maximum_size)
+ goto malformed_error;
+ /* A compressed size == 0 means the end of stream blocks. */
+ if (compressed_size == 0) {
+ __archive_read_filter_consume(self->upstream, 4);
+ return 0;
+ }
+
+ checksum_size = state->flags.block_checksum;
+ /* Check if the block is uncompressed. */
+ if (compressed_size & 0x80000000U) {
+ compressed_size &= 0x7fffffff;
+ uncompressed_size = compressed_size;
+ } else
+ uncompressed_size = 0;/* Unknown yet. */
+
+ /*
+ Unfortunately, lz4 decompression API requires a whole block
+ for its decompression speed, so we read a whole block and allocate
+ a huge buffer used for decoded data.
+ */
+ read_buf = __archive_read_filter_ahead(self->upstream,
+ 4 + compressed_size + checksum_size, &bytes_remaining);
+ if (read_buf == NULL)
+ goto truncated_error;
+
+ /* Optional processing, checking a block sum. */
+ if (checksum_size) {
+ unsigned int chsum = __archive_xxhash.XXH32(
+ read_buf + 4, (int)compressed_size, 0);
+ unsigned int chsum_block =
+ archive_le32dec(read_buf + 4 + compressed_size);
+ if (chsum != chsum_block)
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ goto malformed_error;
+#endif
+ }
+
+
+ /* If the block is uncompressed, there is nothing to do. */
+ if (uncompressed_size) {
+ /* Prepare a prefix 64k block for next block. */
+ if (!state->flags.block_independence) {
+ prefix64k = 64 * 1024;
+ if (uncompressed_size < (ssize_t)prefix64k) {
+ memcpy(state->out_block
+ + prefix64k - uncompressed_size,
+ read_buf + 4,
+ uncompressed_size);
+ memset(state->out_block, 0,
+ prefix64k - uncompressed_size);
+ } else {
+ memcpy(state->out_block,
+ read_buf + 4
+ + uncompressed_size - prefix64k,
+ prefix64k);
+ }
+ state->decoded_size = 0;
+ }
+ state->unconsumed = 4 + uncompressed_size + checksum_size;
+ *p = read_buf + 4;
+ return uncompressed_size;
+ }
+
+ /*
+ Decompress a block data.
+ */
+ if (state->flags.block_independence) {
+ prefix64k = 0;
+ uncompressed_size = LZ4_decompress_safe(read_buf + 4,
+ state->out_block, (int)compressed_size,
+ state->flags.block_maximum_size);
+ } else {
+ prefix64k = 64 * 1024;
+ if (state->decoded_size) {
+ if (state->decoded_size < prefix64k) {
+ memmove(state->out_block
+ + prefix64k - state->decoded_size,
+ state->out_block + prefix64k,
+ state->decoded_size);
+ memset(state->out_block, 0,
+ prefix64k - state->decoded_size);
+ } else {
+ memmove(state->out_block,
+ state->out_block + state->decoded_size,
+ prefix64k);
+ }
+ }
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+ uncompressed_size = LZ4_decompress_safe_usingDict(
+ read_buf + 4,
+ state->out_block + prefix64k, (int)compressed_size,
+ state->flags.block_maximum_size,
+ state->out_block,
+ (int)prefix64k);
+#else
+ uncompressed_size = LZ4_decompress_safe_withPrefix64k(
+ read_buf + 4,
+ state->out_block + prefix64k, (int)compressed_size,
+ state->flags.block_maximum_size);
+#endif
+ }
+
+ /* Check if an error occurred in the decompression process. */
+ if (uncompressed_size < 0) {
+ archive_set_error(&(self->archive->archive),
+ ARCHIVE_ERRNO_MISC, "lz4 decompression failed");
+ return (ARCHIVE_FATAL);
+ }
+
+ state->unconsumed = 4 + compressed_size + checksum_size;
+ *p = state->out_block + prefix64k;
+ state->decoded_size = uncompressed_size;
+ return uncompressed_size;
+
+malformed_error:
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "malformed lz4 data");
+ return (ARCHIVE_FATAL);
+truncated_error:
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "truncated lz4 input");
+ return (ARCHIVE_FATAL);
+}
+
+static ssize_t
+lz4_filter_read_default_stream(struct archive_read_filter *self, const void **p)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ const char *read_buf;
+ ssize_t bytes_remaining;
+ ssize_t ret;
+
+ if (state->stage == SELECT_STREAM) {
+ state->stage = READ_DEFAULT_STREAM;
+ /* First, read a descriptor. */
+ if((ret = lz4_filter_read_descriptor(self)) != ARCHIVE_OK)
+ return (ret);
+ state->stage = READ_DEFAULT_BLOCK;
+ }
+ /* Decompress a block. */
+ ret = lz4_filter_read_data_block(self, p);
+
+ /* If the end of block is detected, change the filter status
+ to read next stream. */
+ if (ret == 0 && *p == NULL)
+ state->stage = SELECT_STREAM;
+
+ /* Optional processing, checking a stream sum. */
+ if (state->flags.stream_checksum) {
+ if (state->stage == SELECT_STREAM) {
+ unsigned int checksum;
+ unsigned int checksum_stream;
+ read_buf = __archive_read_filter_ahead(self->upstream,
+ 4, &bytes_remaining);
+ if (read_buf == NULL) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "truncated lz4 input");
+ return (ARCHIVE_FATAL);
+ }
+ checksum = archive_le32dec(read_buf);
+ __archive_read_filter_consume(self->upstream, 4);
+ checksum_stream = __archive_xxhash.XXH32_digest(
+ state->xxh32_state);
+ state->xxh32_state = NULL;
+ if (checksum != checksum_stream) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "lz4 stream checksum error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ } else if (ret > 0)
+ __archive_xxhash.XXH32_update(state->xxh32_state,
+ *p, (int)ret);
+ }
+ return (ret);
+}
+
+static ssize_t
+lz4_filter_read_legacy_stream(struct archive_read_filter *self, const void **p)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ uint32_t compressed;
+ const char *read_buf;
+ ssize_t ret;
+
+ *p = NULL;
+ ret = lz4_allocate_out_block_for_legacy(self);
+ if (ret != ARCHIVE_OK)
+ return ret;
+
+ /* Make sure we have 4 bytes for a block size. */
+ read_buf = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ if (read_buf == NULL) {
+ if (state->stage == SELECT_STREAM) {
+ state->stage = READ_LEGACY_STREAM;
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "truncated lz4 input");
+ return (ARCHIVE_FATAL);
+ }
+ state->stage = SELECT_STREAM;
+ return 0;
+ }
+ state->stage = READ_LEGACY_BLOCK;
+ compressed = archive_le32dec(read_buf);
+ if (compressed > LZ4_COMPRESSBOUND(LEGACY_BLOCK_SIZE)) {
+ state->stage = SELECT_STREAM;
+ return 0;
+ }
+
+ /* Make sure we have a whole block. */
+ read_buf = __archive_read_filter_ahead(self->upstream,
+ 4 + compressed, NULL);
+ if (read_buf == NULL) {
+ archive_set_error(&(self->archive->archive),
+ ARCHIVE_ERRNO_MISC, "truncated lz4 input");
+ return (ARCHIVE_FATAL);
+ }
+ ret = LZ4_decompress_safe(read_buf + 4, state->out_block,
+ compressed, (int)state->out_block_size);
+ if (ret < 0) {
+ archive_set_error(&(self->archive->archive),
+ ARCHIVE_ERRNO_MISC, "lz4 decompression failed");
+ return (ARCHIVE_FATAL);
+ }
+ *p = state->out_block;
+ state->unconsumed = 4 + compressed;
+ return ret;
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+lz4_filter_close(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ int ret = ARCHIVE_OK;
+
+ state = (struct private_data *)self->data;
+ free(state->xxh32_state);
+ free(state->out_block);
+ free(state);
+ return (ret);
+}
+
+#endif /* HAVE_LIBLZ4 */
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_lzop.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_lzop.c
new file mode 100644
index 00000000000..54e6e198c9a
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_lzop.c
@@ -0,0 +1,499 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_LZO_LZOCONF_H
+#error #include <lzo/lzoconf.h>
+#endif
+#ifdef HAVE_LZO_LZO1X_H
+#error #include <lzo/lzo1x.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h> /* for crc32 and adler32 */
+#endif
+
+#include "archive.h"
+#if !defined(HAVE_ZLIB_H) &&\
+ defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+#error #include "archive_crc32.h"
+#endif
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#ifndef HAVE_ZLIB_H
+#define adler32 lzo_adler32
+#endif
+
+#define LZOP_HEADER_MAGIC "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a"
+#define LZOP_HEADER_MAGIC_LEN 9
+
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+struct read_lzop {
+ unsigned char *out_block;
+ size_t out_block_size;
+ int64_t total_out;
+ int flags;
+ uint32_t compressed_cksum;
+ uint32_t uncompressed_cksum;
+ size_t compressed_size;
+ size_t uncompressed_size;
+ size_t unconsumed_bytes;
+ char in_stream;
+ char eof; /* True = found end of compressed data. */
+};
+
+#define FILTER 0x0800
+#define CRC32_HEADER 0x1000
+#define EXTRA_FIELD 0x0040
+#define ADLER32_UNCOMPRESSED 0x0001
+#define ADLER32_COMPRESSED 0x0002
+#define CRC32_UNCOMPRESSED 0x0100
+#define CRC32_COMPRESSED 0x0200
+#define MAX_BLOCK_SIZE (64 * 1024 * 1024)
+
+static ssize_t lzop_filter_read(struct archive_read_filter *, const void **);
+static int lzop_filter_close(struct archive_read_filter *);
+#endif
+
+static int lzop_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int lzop_bidder_init(struct archive_read_filter *);
+
+static const struct archive_read_filter_bidder_vtable
+lzop_bidder_vtable = {
+ .bid = lzop_bidder_bid,
+ .init = lzop_bidder_init,
+};
+
+int
+archive_read_support_filter_lzop(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, NULL,
+ &lzop_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Signal the extent of lzop support with the return value here. */
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+ return (ARCHIVE_OK);
+#else
+ /* Return ARCHIVE_WARN since this always uses an external program. */
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external lzop program for lzop decompression");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Bidder just verifies the header and returns the number of verified bits.
+ */
+static int
+lzop_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *p;
+ ssize_t avail;
+
+ (void)self; /* UNUSED */
+
+ p = __archive_read_filter_ahead(filter, LZOP_HEADER_MAGIC_LEN, &avail);
+ if (p == NULL || avail == 0)
+ return (0);
+
+ if (memcmp(p, LZOP_HEADER_MAGIC, LZOP_HEADER_MAGIC_LEN))
+ return (0);
+
+ return (LZOP_HEADER_MAGIC_LEN * 8);
+}
+
+#if !defined(HAVE_LZO_LZOCONF_H) || !defined(HAVE_LZO_LZO1X_H)
+/*
+ * If we don't have the library on this system, we can't do the
+ * decompression directly. We can, however, try to run "lzop -d"
+ * in case that's available.
+ */
+static int
+lzop_bidder_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "lzop -d");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_LZOP;
+ self->name = "lzop";
+ return (r);
+}
+#else
+
+static const struct archive_read_filter_vtable
+lzop_reader_vtable = {
+ .read = lzop_filter_read,
+ .close = lzop_filter_close
+};
+
+/*
+ * Initialize the filter object.
+ */
+static int
+lzop_bidder_init(struct archive_read_filter *self)
+{
+ struct read_lzop *state;
+
+ self->code = ARCHIVE_FILTER_LZOP;
+ self->name = "lzop";
+
+ state = (struct read_lzop *)calloc(sizeof(*state), 1);
+ if (state == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for lzop decompression");
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = state;
+ self->vtable = &lzop_reader_vtable;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+consume_header(struct archive_read_filter *self)
+{
+ struct read_lzop *state = (struct read_lzop *)self->data;
+ const unsigned char *p, *_p;
+ unsigned checksum, flags, len, method, version;
+
+ /*
+ * Check LZOP magic code.
+ */
+ p = __archive_read_filter_ahead(self->upstream,
+ LZOP_HEADER_MAGIC_LEN, NULL);
+ if (p == NULL)
+ return (ARCHIVE_EOF);
+
+ if (memcmp(p, LZOP_HEADER_MAGIC, LZOP_HEADER_MAGIC_LEN))
+ return (ARCHIVE_EOF);
+ __archive_read_filter_consume(self->upstream,
+ LZOP_HEADER_MAGIC_LEN);
+
+ p = __archive_read_filter_ahead(self->upstream, 29, NULL);
+ if (p == NULL)
+ goto truncated;
+ _p = p;
+ version = archive_be16dec(p);
+ p += 4;/* version(2 bytes) + library version(2 bytes) */
+
+ if (version >= 0x940) {
+ unsigned reqversion = archive_be16dec(p); p += 2;
+ if (reqversion < 0x900) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "Invalid required version");
+ return (ARCHIVE_FAILED);
+ }
+ }
+
+ method = *p++;
+ if (method < 1 || method > 3) {
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "Unsupported method");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (version >= 0x940) {
+ unsigned level = *p++;
+#if 0
+ unsigned default_level[] = {0, 3, 1, 9};
+#endif
+ if (level == 0)
+ /* Method is 1..3 here due to check above. */
+#if 0 /* Avoid an error Clang Static Analyzer claims
+ "Value stored to 'level' is never read". */
+ level = default_level[method];
+#else
+ ;/* NOP */
+#endif
+ else if (level > 9) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "Invalid level");
+ return (ARCHIVE_FAILED);
+ }
+ }
+
+ flags = archive_be32dec(p); p += 4;
+
+ if (flags & FILTER)
+ p += 4; /* Skip filter */
+ p += 4; /* Skip mode */
+ if (version >= 0x940)
+ p += 8; /* Skip mtime */
+ else
+ p += 4; /* Skip mtime */
+ len = *p++; /* Read filename length */
+ len += p - _p;
+ /* Make sure we have all bytes we need to calculate checksum. */
+ p = __archive_read_filter_ahead(self->upstream, len + 4, NULL);
+ if (p == NULL)
+ goto truncated;
+ if (flags & CRC32_HEADER)
+ checksum = crc32(crc32(0, NULL, 0), p, len);
+ else
+ checksum = adler32(adler32(0, NULL, 0), p, len);
+ if (archive_be32dec(p + len) != checksum)
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ goto corrupted;
+#endif
+ __archive_read_filter_consume(self->upstream, len + 4);
+ if (flags & EXTRA_FIELD) {
+ /* Skip extra field */
+ p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ if (p == NULL)
+ goto truncated;
+ len = archive_be32dec(p);
+ __archive_read_filter_consume(self->upstream, len + 4 + 4);
+ }
+ state->flags = flags;
+ state->in_stream = 1;
+ return (ARCHIVE_OK);
+truncated:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");
+ return (ARCHIVE_FAILED);
+corrupted:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted lzop header");
+ return (ARCHIVE_FAILED);
+}
+
+static int
+consume_block_info(struct archive_read_filter *self)
+{
+ struct read_lzop *state = (struct read_lzop *)self->data;
+ const unsigned char *p;
+ unsigned flags = state->flags;
+
+ p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ if (p == NULL)
+ goto truncated;
+ state->uncompressed_size = archive_be32dec(p);
+ __archive_read_filter_consume(self->upstream, 4);
+ if (state->uncompressed_size == 0)
+ return (ARCHIVE_EOF);
+ if (state->uncompressed_size > MAX_BLOCK_SIZE)
+ goto corrupted;
+
+ p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ if (p == NULL)
+ goto truncated;
+ state->compressed_size = archive_be32dec(p);
+ __archive_read_filter_consume(self->upstream, 4);
+ if (state->compressed_size > state->uncompressed_size)
+ goto corrupted;
+
+ if (flags & (CRC32_UNCOMPRESSED | ADLER32_UNCOMPRESSED)) {
+ p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ if (p == NULL)
+ goto truncated;
+ state->compressed_cksum = state->uncompressed_cksum =
+ archive_be32dec(p);
+ __archive_read_filter_consume(self->upstream, 4);
+ }
+ if ((flags & (CRC32_COMPRESSED | ADLER32_COMPRESSED)) &&
+ state->compressed_size < state->uncompressed_size) {
+ p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ if (p == NULL)
+ goto truncated;
+ state->compressed_cksum = archive_be32dec(p);
+ __archive_read_filter_consume(self->upstream, 4);
+ }
+ return (ARCHIVE_OK);
+truncated:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");
+ return (ARCHIVE_FAILED);
+corrupted:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted lzop header");
+ return (ARCHIVE_FAILED);
+}
+
+static ssize_t
+lzop_filter_read(struct archive_read_filter *self, const void **p)
+{
+ struct read_lzop *state = (struct read_lzop *)self->data;
+ const void *b;
+ lzo_uint out_size;
+ uint32_t cksum;
+ int ret, r;
+
+ if (state->unconsumed_bytes) {
+ __archive_read_filter_consume(self->upstream,
+ state->unconsumed_bytes);
+ state->unconsumed_bytes = 0;
+ }
+ if (state->eof)
+ return (0);
+
+ for (;;) {
+ if (!state->in_stream) {
+ ret = consume_header(self);
+ if (ret < ARCHIVE_OK)
+ return (ret);
+ if (ret == ARCHIVE_EOF) {
+ state->eof = 1;
+ return (0);
+ }
+ }
+ ret = consume_block_info(self);
+ if (ret < ARCHIVE_OK)
+ return (ret);
+ if (ret == ARCHIVE_EOF)
+ state->in_stream = 0;
+ else
+ break;
+ }
+
+ if (state->out_block == NULL ||
+ state->out_block_size < state->uncompressed_size) {
+ void *new_block;
+
+ new_block = realloc(state->out_block, state->uncompressed_size);
+ if (new_block == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for lzop decompression");
+ return (ARCHIVE_FATAL);
+ }
+ state->out_block = new_block;
+ state->out_block_size = state->uncompressed_size;
+ }
+
+ b = __archive_read_filter_ahead(self->upstream,
+ state->compressed_size, NULL);
+ if (b == NULL) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");
+ return (ARCHIVE_FATAL);
+ }
+ if (state->flags & CRC32_COMPRESSED)
+ cksum = crc32(crc32(0, NULL, 0), b, state->compressed_size);
+ else if (state->flags & ADLER32_COMPRESSED)
+ cksum = adler32(adler32(0, NULL, 0), b, state->compressed_size);
+ else
+ cksum = state->compressed_cksum;
+ if (cksum != state->compressed_cksum) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "Corrupted data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * If the both uncompressed size and compressed size are the same,
+ * we do not decompress this block.
+ */
+ if (state->uncompressed_size == state->compressed_size) {
+ *p = b;
+ state->total_out += state->compressed_size;
+ state->unconsumed_bytes = state->compressed_size;
+ return ((ssize_t)state->uncompressed_size);
+ }
+
+ /*
+ * Drive lzo uncompression.
+ */
+ out_size = (lzo_uint)state->uncompressed_size;
+ r = lzo1x_decompress_safe(b, (lzo_uint)state->compressed_size,
+ state->out_block, &out_size, NULL);
+ switch (r) {
+ case LZO_E_OK:
+ if (out_size == state->uncompressed_size)
+ break;
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "Corrupted data");
+ return (ARCHIVE_FATAL);
+ case LZO_E_OUT_OF_MEMORY:
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "lzop decompression failed: out of memory");
+ return (ARCHIVE_FATAL);
+ default:
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "lzop decompression failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+
+ if (state->flags & CRC32_UNCOMPRESSED)
+ cksum = crc32(crc32(0, NULL, 0), state->out_block,
+ state->uncompressed_size);
+ else if (state->flags & ADLER32_UNCOMPRESSED)
+ cksum = adler32(adler32(0, NULL, 0), state->out_block,
+ state->uncompressed_size);
+ else
+ cksum = state->uncompressed_cksum;
+ if (cksum != state->uncompressed_cksum) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "Corrupted data");
+ return (ARCHIVE_FATAL);
+ }
+
+ __archive_read_filter_consume(self->upstream, state->compressed_size);
+ *p = state->out_block;
+ state->total_out += out_size;
+ return ((ssize_t)out_size);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+lzop_filter_close(struct archive_read_filter *self)
+{
+ struct read_lzop *state = (struct read_lzop *)self->data;
+
+ free(state->out_block);
+ free(state);
+ return (ARCHIVE_OK);
+}
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_none.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_none.c
new file mode 100644
index 00000000000..95e5cfdb15d
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_none.c
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive.h"
+#include "archive_private.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_none(struct archive *a)
+{
+ return archive_read_support_filter_none(a);
+}
+#endif
+
+/*
+ * Uncompressed streams are handled implicitly by the read core,
+ * so this is now a no-op.
+ */
+int
+archive_read_support_filter_none(struct archive *a)
+{
+ archive_check_magic(a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_filter_none");
+
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_program.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_program.c
new file mode 100644
index 00000000000..885b2c2056e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_program.c
@@ -0,0 +1,496 @@
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifdef HAVE_ERRNO_H
+# include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+# include <signal.h>
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_read_private.h"
+#include "filter_fork.h"
+
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_program(struct archive *a, const char *cmd)
+{
+ return archive_read_support_filter_program(a, cmd);
+}
+
+int
+archive_read_support_compression_program_signature(struct archive *a,
+ const char *cmd, const void *signature, size_t signature_len)
+{
+ return archive_read_support_filter_program_signature(a,
+ cmd, signature, signature_len);
+}
+#endif
+
+int
+archive_read_support_filter_program(struct archive *a, const char *cmd)
+{
+ return (archive_read_support_filter_program_signature(a, cmd, NULL, 0));
+}
+
+/*
+ * The bidder object stores the command and the signature to watch for.
+ * The 'inhibit' entry here is used to ensure that unchecked filters never
+ * bid twice in the same pipeline.
+ */
+struct program_bidder {
+ char *description;
+ char *cmd;
+ void *signature;
+ size_t signature_len;
+ int inhibit;
+};
+
+static int program_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *upstream);
+static int program_bidder_init(struct archive_read_filter *);
+static void program_bidder_free(struct archive_read_filter_bidder *);
+
+/*
+ * The actual filter needs to track input and output data.
+ */
+struct program_filter {
+ struct archive_string description;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ HANDLE child;
+#else
+ pid_t child;
+#endif
+ int exit_status;
+ int waitpid_return;
+ int child_stdin, child_stdout;
+
+ char *out_buf;
+ size_t out_buf_len;
+};
+
+static ssize_t program_filter_read(struct archive_read_filter *,
+ const void **);
+static int program_filter_close(struct archive_read_filter *);
+static void free_state(struct program_bidder *);
+
+static const struct archive_read_filter_bidder_vtable
+program_bidder_vtable = {
+ .bid = program_bidder_bid,
+ .init = program_bidder_init,
+ .free = program_bidder_free,
+};
+
+int
+archive_read_support_filter_program_signature(struct archive *_a,
+ const char *cmd, const void *signature, size_t signature_len)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct program_bidder *state;
+
+ /*
+ * Allocate our private state.
+ */
+ state = (struct program_bidder *)calloc(1, sizeof (*state));
+ if (state == NULL)
+ goto memerr;
+ state->cmd = strdup(cmd);
+ if (state->cmd == NULL)
+ goto memerr;
+
+ if (signature != NULL && signature_len > 0) {
+ state->signature_len = signature_len;
+ state->signature = malloc(signature_len);
+ memcpy(state->signature, signature, signature_len);
+ }
+
+ if (__archive_read_register_bidder(a, state, NULL,
+ &program_bidder_vtable) != ARCHIVE_OK) {
+ free_state(state);
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+
+memerr:
+ free_state(state);
+ archive_set_error(_a, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+}
+
+static void
+program_bidder_free(struct archive_read_filter_bidder *self)
+{
+ struct program_bidder *state = (struct program_bidder *)self->data;
+
+ free_state(state);
+}
+
+static void
+free_state(struct program_bidder *state)
+{
+
+ if (state) {
+ free(state->cmd);
+ free(state->signature);
+ free(state);
+ }
+}
+
+/*
+ * If we do have a signature, bid only if that matches.
+ *
+ * If there's no signature, we bid INT_MAX the first time
+ * we're called, then never bid again.
+ */
+static int
+program_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *upstream)
+{
+ struct program_bidder *state = self->data;
+ const char *p;
+
+ /* If we have a signature, use that to match. */
+ if (state->signature_len > 0) {
+ p = __archive_read_filter_ahead(upstream,
+ state->signature_len, NULL);
+ if (p == NULL)
+ return (0);
+ /* No match, so don't bid. */
+ if (memcmp(p, state->signature, state->signature_len) != 0)
+ return (0);
+ return ((int)state->signature_len * 8);
+ }
+
+ /* Otherwise, bid once and then never bid again. */
+ if (state->inhibit)
+ return (0);
+ state->inhibit = 1;
+ return (INT_MAX);
+}
+
+/*
+ * Shut down the child, return ARCHIVE_OK if it exited normally.
+ *
+ * Note that the return value is sticky; if we're called again,
+ * we won't reap the child again, but we will return the same status
+ * (including error message if the child came to a bad end).
+ */
+static int
+child_stop(struct archive_read_filter *self, struct program_filter *state)
+{
+ /* Close our side of the I/O with the child. */
+ if (state->child_stdin != -1) {
+ close(state->child_stdin);
+ state->child_stdin = -1;
+ }
+ if (state->child_stdout != -1) {
+ close(state->child_stdout);
+ state->child_stdout = -1;
+ }
+
+ if (state->child != 0) {
+ /* Reap the child. */
+ do {
+ state->waitpid_return
+ = waitpid(state->child, &state->exit_status, 0);
+ } while (state->waitpid_return == -1 && errno == EINTR);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ CloseHandle(state->child);
+#endif
+ state->child = 0;
+ }
+
+ if (state->waitpid_return < 0) {
+ /* waitpid() failed? This is ugly. */
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "Child process exited badly");
+ return (ARCHIVE_WARN);
+ }
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+ if (WIFSIGNALED(state->exit_status)) {
+#ifdef SIGPIPE
+ /* If the child died because we stopped reading before
+ * it was done, that's okay. Some archive formats
+ * have padding at the end that we routinely ignore. */
+ /* The alternative to this would be to add a step
+ * before close(child_stdout) above to read from the
+ * child until the child has no more to write. */
+ if (WTERMSIG(state->exit_status) == SIGPIPE)
+ return (ARCHIVE_OK);
+#endif
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "Child process exited with signal %d",
+ WTERMSIG(state->exit_status));
+ return (ARCHIVE_WARN);
+ }
+#endif /* !_WIN32 || __CYGWIN__ */
+
+ if (WIFEXITED(state->exit_status)) {
+ if (WEXITSTATUS(state->exit_status) == 0)
+ return (ARCHIVE_OK);
+
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Child process exited with status %d",
+ WEXITSTATUS(state->exit_status));
+ return (ARCHIVE_WARN);
+ }
+
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Use select() to decide whether the child is ready for read or write.
+ */
+static ssize_t
+child_read(struct archive_read_filter *self, char *buf, size_t buf_len)
+{
+ struct program_filter *state = self->data;
+ ssize_t ret, requested, avail;
+ const char *p;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ HANDLE handle = (HANDLE)_get_osfhandle(state->child_stdout);
+#endif
+
+ requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len;
+
+ for (;;) {
+ do {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Avoid infinity wait.
+ * Note: If there is no data in the pipe, ReadFile()
+ * called in read() never returns and so we won't
+ * write remaining encoded data to the pipe.
+ * Note: This way may cause performance problem.
+ * we are looking forward to great code to resolve
+ * this. */
+ DWORD pipe_avail = -1;
+ int cnt = 2;
+
+ while (PeekNamedPipe(handle, NULL, 0, NULL,
+ &pipe_avail, NULL) != 0 && pipe_avail == 0 &&
+ cnt--)
+ Sleep(5);
+ if (pipe_avail == 0) {
+ ret = -1;
+ errno = EAGAIN;
+ break;
+ }
+#endif
+ ret = read(state->child_stdout, buf, requested);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret > 0)
+ return (ret);
+ if (ret == 0 || (ret == -1 && errno == EPIPE))
+ /* Child has closed its output; reap the child
+ * and return the status. */
+ return (child_stop(self, state));
+ if (ret == -1 && errno != EAGAIN)
+ return (-1);
+
+ if (state->child_stdin == -1) {
+ /* Block until child has some I/O ready. */
+ __archive_check_child(state->child_stdin,
+ state->child_stdout);
+ continue;
+ }
+
+ /* Get some more data from upstream. */
+ p = __archive_read_filter_ahead(self->upstream, 1, &avail);
+ if (p == NULL) {
+ close(state->child_stdin);
+ state->child_stdin = -1;
+ fcntl(state->child_stdout, F_SETFL, 0);
+ if (avail < 0)
+ return (avail);
+ continue;
+ }
+
+ do {
+ ret = write(state->child_stdin, p, avail);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret > 0) {
+ /* Consume whatever we managed to write. */
+ __archive_read_filter_consume(self->upstream, ret);
+ } else if (ret == -1 && errno == EAGAIN) {
+ /* Block until child has some I/O ready. */
+ __archive_check_child(state->child_stdin,
+ state->child_stdout);
+ } else {
+ /* Write failed. */
+ close(state->child_stdin);
+ state->child_stdin = -1;
+ fcntl(state->child_stdout, F_SETFL, 0);
+ /* If it was a bad error, we're done; otherwise
+ * it was EPIPE or EOF, and we can still read
+ * from the child. */
+ if (ret == -1 && errno != EPIPE)
+ return (-1);
+ }
+ }
+}
+
+static const struct archive_read_filter_vtable
+program_reader_vtable = {
+ .read = program_filter_read,
+ .close = program_filter_close,
+};
+
+int
+__archive_read_program(struct archive_read_filter *self, const char *cmd)
+{
+ struct program_filter *state;
+ static const size_t out_buf_len = 65536;
+ char *out_buf;
+ const char *prefix = "Program: ";
+ int ret;
+ size_t l;
+
+ l = strlen(prefix) + strlen(cmd) + 1;
+ state = (struct program_filter *)calloc(1, sizeof(*state));
+ out_buf = (char *)malloc(out_buf_len);
+ if (state == NULL || out_buf == NULL ||
+ archive_string_ensure(&state->description, l) == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate input data");
+ if (state != NULL) {
+ archive_string_free(&state->description);
+ free(state);
+ }
+ free(out_buf);
+ return (ARCHIVE_FATAL);
+ }
+ archive_strcpy(&state->description, prefix);
+ archive_strcat(&state->description, cmd);
+
+ self->code = ARCHIVE_FILTER_PROGRAM;
+ self->name = state->description.s;
+
+ state->out_buf = out_buf;
+ state->out_buf_len = out_buf_len;
+
+ ret = __archive_create_child(cmd, &state->child_stdin,
+ &state->child_stdout, &state->child);
+ if (ret != ARCHIVE_OK) {
+ free(state->out_buf);
+ archive_string_free(&state->description);
+ free(state);
+ archive_set_error(&self->archive->archive, EINVAL,
+ "Can't initialize filter; unable to run program \"%s\"",
+ cmd);
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = state;
+ self->vtable = &program_reader_vtable;
+
+ /* XXX Check that we can read at least one byte? */
+ return (ARCHIVE_OK);
+}
+
+static int
+program_bidder_init(struct archive_read_filter *self)
+{
+ struct program_bidder *bidder_state;
+
+ bidder_state = (struct program_bidder *)self->bidder->data;
+ return (__archive_read_program(self, bidder_state->cmd));
+}
+
+static ssize_t
+program_filter_read(struct archive_read_filter *self, const void **buff)
+{
+ struct program_filter *state;
+ ssize_t bytes;
+ size_t total;
+ char *p;
+
+ state = (struct program_filter *)self->data;
+
+ total = 0;
+ p = state->out_buf;
+ while (state->child_stdout != -1 && total < state->out_buf_len) {
+ bytes = child_read(self, p, state->out_buf_len - total);
+ if (bytes < 0)
+ /* No recovery is possible if we can no longer
+ * read from the child. */
+ return (ARCHIVE_FATAL);
+ if (bytes == 0)
+ /* We got EOF from the child. */
+ break;
+ total += bytes;
+ p += bytes;
+ }
+
+ *buff = state->out_buf;
+ return (total);
+}
+
+static int
+program_filter_close(struct archive_read_filter *self)
+{
+ struct program_filter *state;
+ int e;
+
+ state = (struct program_filter *)self->data;
+ e = child_stop(self, state);
+
+ /* Release our private data. */
+ free(state->out_buf);
+ archive_string_free(&state->description);
+ free(state);
+
+ return (e);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_rpm.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_rpm.c
new file mode 100644
index 00000000000..67a979cd78f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_rpm.c
@@ -0,0 +1,287 @@
+/*-
+ * Copyright (c) 2009 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+struct rpm {
+ int64_t total_in;
+ size_t hpos;
+ size_t hlen;
+ unsigned char header[16];
+ enum {
+ ST_LEAD, /* Skipping 'Lead' section. */
+ ST_HEADER, /* Reading 'Header' section;
+ * first 16 bytes. */
+ ST_HEADER_DATA, /* Skipping 'Header' section. */
+ ST_PADDING, /* Skipping padding data after the
+ * 'Header' section. */
+ ST_ARCHIVE /* Reading 'Archive' section. */
+ } state;
+ int first_header;
+};
+#define RPM_LEAD_SIZE 96 /* Size of 'Lead' section. */
+
+static int rpm_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int rpm_bidder_init(struct archive_read_filter *);
+
+static ssize_t rpm_filter_read(struct archive_read_filter *,
+ const void **);
+static int rpm_filter_close(struct archive_read_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_rpm(struct archive *a)
+{
+ return archive_read_support_filter_rpm(a);
+}
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+rpm_bidder_vtable = {
+ .bid = rpm_bidder_bid,
+ .init = rpm_bidder_init,
+};
+
+int
+archive_read_support_filter_rpm(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ return __archive_read_register_bidder(a, NULL, "rpm",
+ &rpm_bidder_vtable);
+}
+
+static int
+rpm_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *b;
+ ssize_t avail;
+ int bits_checked;
+
+ (void)self; /* UNUSED */
+
+ b = __archive_read_filter_ahead(filter, 8, &avail);
+ if (b == NULL)
+ return (0);
+
+ bits_checked = 0;
+ /*
+ * Verify Header Magic Bytes : 0XED 0XAB 0XEE 0XDB
+ */
+ if (memcmp(b, "\xED\xAB\xEE\xDB", 4) != 0)
+ return (0);
+ bits_checked += 32;
+ /*
+ * Check major version.
+ */
+ if (b[4] != 3 && b[4] != 4)
+ return (0);
+ bits_checked += 8;
+ /*
+ * Check package type; binary or source.
+ */
+ if (b[6] != 0)
+ return (0);
+ bits_checked += 8;
+ if (b[7] != 0 && b[7] != 1)
+ return (0);
+ bits_checked += 8;
+
+ return (bits_checked);
+}
+
+static const struct archive_read_filter_vtable
+rpm_reader_vtable = {
+ .read = rpm_filter_read,
+ .close = rpm_filter_close,
+};
+
+static int
+rpm_bidder_init(struct archive_read_filter *self)
+{
+ struct rpm *rpm;
+
+ self->code = ARCHIVE_FILTER_RPM;
+ self->name = "rpm";
+
+ rpm = (struct rpm *)calloc(sizeof(*rpm), 1);
+ if (rpm == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for rpm");
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = rpm;
+ rpm->state = ST_LEAD;
+ self->vtable = &rpm_reader_vtable;
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+rpm_filter_read(struct archive_read_filter *self, const void **buff)
+{
+ struct rpm *rpm;
+ const unsigned char *b;
+ ssize_t avail_in, total;
+ size_t used, n;
+ uint32_t section;
+ uint32_t bytes;
+
+ rpm = (struct rpm *)self->data;
+ *buff = NULL;
+ total = avail_in = 0;
+ b = NULL;
+ used = 0;
+ do {
+ if (b == NULL) {
+ b = __archive_read_filter_ahead(self->upstream, 1,
+ &avail_in);
+ if (b == NULL) {
+ if (avail_in < 0)
+ return (ARCHIVE_FATAL);
+ else
+ break;
+ }
+ }
+
+ switch (rpm->state) {
+ case ST_LEAD:
+ if (rpm->total_in + avail_in < RPM_LEAD_SIZE)
+ used += avail_in;
+ else {
+ n = (size_t)(RPM_LEAD_SIZE - rpm->total_in);
+ used += n;
+ b += n;
+ rpm->state = ST_HEADER;
+ rpm->hpos = 0;
+ rpm->hlen = 0;
+ rpm->first_header = 1;
+ }
+ break;
+ case ST_HEADER:
+ n = 16 - rpm->hpos;
+ if (n > avail_in - used)
+ n = avail_in - used;
+ memcpy(rpm->header+rpm->hpos, b, n);
+ b += n;
+ used += n;
+ rpm->hpos += n;
+
+ if (rpm->hpos == 16) {
+ if (rpm->header[0] != 0x8e ||
+ rpm->header[1] != 0xad ||
+ rpm->header[2] != 0xe8 ||
+ rpm->header[3] != 0x01) {
+ if (rpm->first_header) {
+ archive_set_error(
+ &self->archive->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unrecognized rpm header");
+ return (ARCHIVE_FATAL);
+ }
+ rpm->state = ST_ARCHIVE;
+ *buff = rpm->header;
+ total = rpm->hpos;
+ break;
+ }
+ /* Calculate 'Header' length. */
+ section = archive_be32dec(rpm->header+8);
+ bytes = archive_be32dec(rpm->header+12);
+ rpm->hlen = 16 + section * 16 + bytes;
+ rpm->state = ST_HEADER_DATA;
+ rpm->first_header = 0;
+ }
+ break;
+ case ST_HEADER_DATA:
+ n = rpm->hlen - rpm->hpos;
+ if (n > avail_in - used)
+ n = avail_in - used;
+ b += n;
+ used += n;
+ rpm->hpos += n;
+ if (rpm->hpos == rpm->hlen)
+ rpm->state = ST_PADDING;
+ break;
+ case ST_PADDING:
+ while (used < (size_t)avail_in) {
+ if (*b != 0) {
+ /* Read next header. */
+ rpm->state = ST_HEADER;
+ rpm->hpos = 0;
+ rpm->hlen = 0;
+ break;
+ }
+ b++;
+ used++;
+ }
+ break;
+ case ST_ARCHIVE:
+ *buff = b;
+ total = avail_in;
+ used = avail_in;
+ break;
+ }
+ if (used == (size_t)avail_in) {
+ rpm->total_in += used;
+ __archive_read_filter_consume(self->upstream, used);
+ b = NULL;
+ used = 0;
+ }
+ } while (total == 0 && avail_in > 0);
+
+ if (used > 0 && b != NULL) {
+ rpm->total_in += used;
+ __archive_read_filter_consume(self->upstream, used);
+ }
+ return (total);
+}
+
+static int
+rpm_filter_close(struct archive_read_filter *self)
+{
+ struct rpm *rpm;
+
+ rpm = (struct rpm *)self->data;
+ free(rpm);
+
+ return (ARCHIVE_OK);
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_uu.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_uu.c
new file mode 100644
index 00000000000..209b2a1593a
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_uu.c
@@ -0,0 +1,683 @@
+/*-
+ * Copyright (c) 2009-2011 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+/* Maximum lookahead during bid phase */
+#define UUENCODE_BID_MAX_READ 128*1024 /* in bytes */
+
+struct uudecode {
+ int64_t total;
+ unsigned char *in_buff;
+#define IN_BUFF_SIZE (1024)
+ int in_cnt;
+ size_t in_allocated;
+ unsigned char *out_buff;
+#define OUT_BUFF_SIZE (64 * 1024)
+ int state;
+#define ST_FIND_HEAD 0
+#define ST_READ_UU 1
+#define ST_UUEND 2
+#define ST_READ_BASE64 3
+#define ST_IGNORE 4
+};
+
+static int uudecode_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *filter);
+static int uudecode_bidder_init(struct archive_read_filter *);
+
+static ssize_t uudecode_filter_read(struct archive_read_filter *,
+ const void **);
+static int uudecode_filter_close(struct archive_read_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_uu(struct archive *a)
+{
+ return archive_read_support_filter_uu(a);
+}
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+uudecode_bidder_vtable = {
+ .bid = uudecode_bidder_bid,
+ .init = uudecode_bidder_init,
+};
+
+int
+archive_read_support_filter_uu(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ return __archive_read_register_bidder(a, NULL, "uu",
+ &uudecode_bidder_vtable);
+}
+
+static const unsigned char ascii[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\n', 0, 0, '\r', 0, 0, /* 00 - 0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
+};
+
+static const unsigned char uuchar[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
+};
+
+static const unsigned char base64[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, /* 20 - 2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, /* 30 - 3F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 50 - 5F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 70 - 7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
+};
+
+static const int base64num[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 62, 0, 0, 0, 63, /* 20 - 2F */
+ 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 0, 0, 0, 0, 0, 0, /* 30 - 3F */
+ 0, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, /* 40 - 4F */
+ 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 0, 0, 0, 0, 0, /* 50 - 5F */
+ 0, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */
+ 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 0, 0, 0, 0, 0, /* 70 - 7F */
+};
+
+static ssize_t
+get_line(const unsigned char *b, ssize_t avail, ssize_t *nlsize)
+{
+ ssize_t len;
+
+ len = 0;
+ while (len < avail) {
+ switch (ascii[*b]) {
+ case 0: /* Non-ascii character or control character. */
+ if (nlsize != NULL)
+ *nlsize = 0;
+ return (-1);
+ case '\r':
+ if (avail-len > 1 && b[1] == '\n') {
+ if (nlsize != NULL)
+ *nlsize = 2;
+ return (len+2);
+ }
+ /* FALL THROUGH */
+ case '\n':
+ if (nlsize != NULL)
+ *nlsize = 1;
+ return (len+1);
+ case 1:
+ b++;
+ len++;
+ break;
+ }
+ }
+ if (nlsize != NULL)
+ *nlsize = 0;
+ return (avail);
+}
+
+static ssize_t
+bid_get_line(struct archive_read_filter *filter,
+ const unsigned char **b, ssize_t *avail, ssize_t *ravail,
+ ssize_t *nl, size_t* nbytes_read)
+{
+ ssize_t len;
+ int quit;
+
+ quit = 0;
+ if (*avail == 0) {
+ *nl = 0;
+ len = 0;
+ } else
+ len = get_line(*b, *avail, nl);
+
+ /*
+ * Read bytes more while it does not reach the end of line.
+ */
+ while (*nl == 0 && len == *avail && !quit &&
+ *nbytes_read < UUENCODE_BID_MAX_READ) {
+ ssize_t diff = *ravail - *avail;
+ size_t nbytes_req = (*ravail+1023) & ~1023U;
+ ssize_t tested;
+
+ /* Increase reading bytes if it is not enough to at least
+ * new two lines. */
+ if (nbytes_req < (size_t)*ravail + 160)
+ nbytes_req <<= 1;
+
+ *b = __archive_read_filter_ahead(filter, nbytes_req, avail);
+ if (*b == NULL) {
+ if (*ravail >= *avail)
+ return (0);
+ /* Reading bytes reaches the end of a stream. */
+ *b = __archive_read_filter_ahead(filter, *avail, avail);
+ quit = 1;
+ }
+ *nbytes_read = *avail;
+ *ravail = *avail;
+ *b += diff;
+ *avail -= diff;
+ tested = len;/* Skip some bytes we already determined. */
+ len = get_line(*b + tested, *avail - tested, nl);
+ if (len >= 0)
+ len += tested;
+ }
+ return (len);
+}
+
+#define UUDECODE(c) (((c) - 0x20) & 0x3f)
+
+static int
+uudecode_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *b;
+ ssize_t avail, ravail;
+ ssize_t len, nl;
+ int l;
+ int firstline;
+ size_t nbytes_read;
+
+ (void)self; /* UNUSED */
+
+ b = __archive_read_filter_ahead(filter, 1, &avail);
+ if (b == NULL)
+ return (0);
+
+ firstline = 20;
+ ravail = avail;
+ nbytes_read = avail;
+ for (;;) {
+ len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read);
+ if (len < 0 || nl == 0)
+ return (0); /* No match found. */
+ if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
+ l = 6;
+ else if (len -nl >= 18 && memcmp(b, "begin-base64 ", 13) == 0)
+ l = 13;
+ else
+ l = 0;
+
+ if (l > 0 && (b[l] < '0' || b[l] > '7' ||
+ b[l+1] < '0' || b[l+1] > '7' ||
+ b[l+2] < '0' || b[l+2] > '7' || b[l+3] != ' '))
+ l = 0;
+
+ b += len;
+ avail -= len;
+ if (l)
+ break;
+ firstline = 0;
+
+ /* Do not read more than UUENCODE_BID_MAX_READ bytes */
+ if (nbytes_read >= UUENCODE_BID_MAX_READ)
+ return (0);
+ }
+ if (!avail)
+ return (0);
+ len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read);
+ if (len < 0 || nl == 0)
+ return (0);/* There are non-ascii characters. */
+ avail -= len;
+
+ if (l == 6) {
+ /* "begin " */
+ if (!uuchar[*b])
+ return (0);
+ /* Get a length of decoded bytes. */
+ l = UUDECODE(*b++); len--;
+ if (l > 45)
+ /* Normally, maximum length is 45(character 'M'). */
+ return (0);
+ if (l > len - nl)
+ return (0); /* Line too short. */
+ while (l) {
+ if (!uuchar[*b++])
+ return (0);
+ --len;
+ --l;
+ }
+ if (len-nl == 1 &&
+ (uuchar[*b] || /* Check sum. */
+ (*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */
+ ++b;
+ --len;
+ }
+ b += nl;
+ if (avail && uuchar[*b])
+ return (firstline+30);
+ } else if (l == 13) {
+ /* "begin-base64 " */
+ while (len-nl > 0) {
+ if (!base64[*b++])
+ return (0);
+ --len;
+ }
+ b += nl;
+
+ if (avail >= 5 && memcmp(b, "====\n", 5) == 0)
+ return (firstline+40);
+ if (avail >= 6 && memcmp(b, "====\r\n", 6) == 0)
+ return (firstline+40);
+ if (avail > 0 && base64[*b])
+ return (firstline+30);
+ }
+
+ return (0);
+}
+
+static const struct archive_read_filter_vtable
+uudecode_reader_vtable = {
+ .read = uudecode_filter_read,
+ .close = uudecode_filter_close,
+};
+
+static int
+uudecode_bidder_init(struct archive_read_filter *self)
+{
+ struct uudecode *uudecode;
+ void *out_buff;
+ void *in_buff;
+
+ self->code = ARCHIVE_FILTER_UU;
+ self->name = "uu";
+
+ uudecode = (struct uudecode *)calloc(sizeof(*uudecode), 1);
+ out_buff = malloc(OUT_BUFF_SIZE);
+ in_buff = malloc(IN_BUFF_SIZE);
+ if (uudecode == NULL || out_buff == NULL || in_buff == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for uudecode");
+ free(uudecode);
+ free(out_buff);
+ free(in_buff);
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = uudecode;
+ uudecode->in_buff = in_buff;
+ uudecode->in_cnt = 0;
+ uudecode->in_allocated = IN_BUFF_SIZE;
+ uudecode->out_buff = out_buff;
+ uudecode->state = ST_FIND_HEAD;
+ self->vtable = &uudecode_reader_vtable;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+ensure_in_buff_size(struct archive_read_filter *self,
+ struct uudecode *uudecode, size_t size)
+{
+
+ if (size > uudecode->in_allocated) {
+ unsigned char *ptr;
+ size_t newsize;
+
+ /*
+ * Calculate a new buffer size for in_buff.
+ * Increase its value until it has enough size we need.
+ */
+ newsize = uudecode->in_allocated;
+ do {
+ if (newsize < IN_BUFF_SIZE*32)
+ newsize <<= 1;
+ else
+ newsize += IN_BUFF_SIZE;
+ } while (size > newsize);
+ /* Allocate the new buffer. */
+ ptr = malloc(newsize);
+ if (ptr == NULL) {
+ free(ptr);
+ archive_set_error(&self->archive->archive,
+ ENOMEM,
+ "Can't allocate data for uudecode");
+ return (ARCHIVE_FATAL);
+ }
+ /* Move the remaining data in in_buff into the new buffer. */
+ if (uudecode->in_cnt)
+ memmove(ptr, uudecode->in_buff, uudecode->in_cnt);
+ /* Replace in_buff with the new buffer. */
+ free(uudecode->in_buff);
+ uudecode->in_buff = ptr;
+ uudecode->in_allocated = newsize;
+ }
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+uudecode_filter_read(struct archive_read_filter *self, const void **buff)
+{
+ struct uudecode *uudecode;
+ const unsigned char *b, *d;
+ unsigned char *out;
+ ssize_t avail_in, ravail;
+ ssize_t used;
+ ssize_t total;
+ ssize_t len, llen, nl;
+
+ uudecode = (struct uudecode *)self->data;
+
+read_more:
+ d = __archive_read_filter_ahead(self->upstream, 1, &avail_in);
+ if (d == NULL && avail_in < 0)
+ return (ARCHIVE_FATAL);
+ /* Quiet a code analyzer; make sure avail_in must be zero
+ * when d is NULL. */
+ if (d == NULL)
+ avail_in = 0;
+ used = 0;
+ total = 0;
+ out = uudecode->out_buff;
+ ravail = avail_in;
+ if (uudecode->state == ST_IGNORE) {
+ used = avail_in;
+ goto finish;
+ }
+ if (uudecode->in_cnt) {
+ /*
+ * If there is remaining data which is saved by
+ * previous calling, use it first.
+ */
+ if (ensure_in_buff_size(self, uudecode,
+ avail_in + uudecode->in_cnt) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ memcpy(uudecode->in_buff + uudecode->in_cnt,
+ d, avail_in);
+ d = uudecode->in_buff;
+ avail_in += uudecode->in_cnt;
+ uudecode->in_cnt = 0;
+ }
+ for (;used < avail_in; d += llen, used += llen) {
+ int64_t l, body;
+
+ b = d;
+ len = get_line(b, avail_in - used, &nl);
+ if (len < 0) {
+ /* Non-ascii character is found. */
+ if (uudecode->state == ST_FIND_HEAD &&
+ (uudecode->total > 0 || total > 0)) {
+ uudecode->state = ST_IGNORE;
+ used = avail_in;
+ goto finish;
+ }
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Insufficient compressed data");
+ return (ARCHIVE_FATAL);
+ }
+ llen = len;
+ if ((nl == 0) && (uudecode->state != ST_UUEND)) {
+ if (total == 0 && ravail <= 0) {
+ /* There is nothing more to read, fail */
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Missing format data");
+ return (ARCHIVE_FATAL);
+ }
+ /*
+ * Save remaining data which does not contain
+ * NL('\n','\r').
+ */
+ if (ensure_in_buff_size(self, uudecode, len)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ if (uudecode->in_buff != b)
+ memmove(uudecode->in_buff, b, len);
+ uudecode->in_cnt = (int)len;
+ if (total == 0) {
+ /* Do not return 0; it means end-of-file.
+ * We should try to read bytes more. */
+ __archive_read_filter_consume(
+ self->upstream, ravail);
+ goto read_more;
+ }
+ used += len;
+ break;
+ }
+ switch (uudecode->state) {
+ default:
+ case ST_FIND_HEAD:
+ /* Do not read more than UUENCODE_BID_MAX_READ bytes */
+ if (total + len >= UUENCODE_BID_MAX_READ) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid format data");
+ return (ARCHIVE_FATAL);
+ }
+ if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
+ l = 6;
+ else if (len - nl >= 18 &&
+ memcmp(b, "begin-base64 ", 13) == 0)
+ l = 13;
+ else
+ l = 0;
+ if (l != 0 && b[l] >= '0' && b[l] <= '7' &&
+ b[l+1] >= '0' && b[l+1] <= '7' &&
+ b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') {
+ if (l == 6)
+ uudecode->state = ST_READ_UU;
+ else
+ uudecode->state = ST_READ_BASE64;
+ }
+ break;
+ case ST_READ_UU:
+ if (total + len * 2 > OUT_BUFF_SIZE)
+ goto finish;
+ body = len - nl;
+ if (!uuchar[*b] || body <= 0) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Insufficient compressed data");
+ return (ARCHIVE_FATAL);
+ }
+ /* Get length of undecoded bytes of current line. */
+ l = UUDECODE(*b++);
+ body--;
+ if (l > body) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Insufficient compressed data");
+ return (ARCHIVE_FATAL);
+ }
+ if (l == 0) {
+ uudecode->state = ST_UUEND;
+ break;
+ }
+ while (l > 0) {
+ int n = 0;
+
+ if (!uuchar[b[0]] || !uuchar[b[1]])
+ break;
+ n = UUDECODE(*b++) << 18;
+ n |= UUDECODE(*b++) << 12;
+ *out++ = n >> 16; total++;
+ --l;
+
+ if (l > 0) {
+ if (!uuchar[b[0]])
+ break;
+ n |= UUDECODE(*b++) << 6;
+ *out++ = (n >> 8) & 0xFF; total++;
+ --l;
+ }
+ if (l > 0) {
+ if (!uuchar[b[0]])
+ break;
+ n |= UUDECODE(*b++);
+ *out++ = n & 0xFF; total++;
+ --l;
+ }
+ }
+ if (l) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Insufficient compressed data");
+ return (ARCHIVE_FATAL);
+ }
+ break;
+ case ST_UUEND:
+ if (len - nl == 3 && memcmp(b, "end ", 3) == 0)
+ uudecode->state = ST_FIND_HEAD;
+ else {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Insufficient compressed data");
+ return (ARCHIVE_FATAL);
+ }
+ break;
+ case ST_READ_BASE64:
+ if (total + len * 2 > OUT_BUFF_SIZE)
+ goto finish;
+ l = len - nl;
+ if (l >= 3 && b[0] == '=' && b[1] == '=' &&
+ b[2] == '=') {
+ uudecode->state = ST_FIND_HEAD;
+ break;
+ }
+ while (l > 0) {
+ int n = 0;
+
+ if (!base64[b[0]] || !base64[b[1]])
+ break;
+ n = base64num[*b++] << 18;
+ n |= base64num[*b++] << 12;
+ *out++ = n >> 16; total++;
+ l -= 2;
+
+ if (l > 0) {
+ if (*b == '=')
+ break;
+ if (!base64[*b])
+ break;
+ n |= base64num[*b++] << 6;
+ *out++ = (n >> 8) & 0xFF; total++;
+ --l;
+ }
+ if (l > 0) {
+ if (*b == '=')
+ break;
+ if (!base64[*b])
+ break;
+ n |= base64num[*b++];
+ *out++ = n & 0xFF; total++;
+ --l;
+ }
+ }
+ if (l && *b != '=') {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Insufficient compressed data");
+ return (ARCHIVE_FATAL);
+ }
+ break;
+ }
+ }
+finish:
+ if (ravail < avail_in)
+ used -= avail_in - ravail;
+ __archive_read_filter_consume(self->upstream, used);
+
+ *buff = uudecode->out_buff;
+ uudecode->total += total;
+ return (total);
+}
+
+static int
+uudecode_filter_close(struct archive_read_filter *self)
+{
+ struct uudecode *uudecode;
+
+ uudecode = (struct uudecode *)self->data;
+ free(uudecode->in_buff);
+ free(uudecode->out_buff);
+ free(uudecode);
+
+ return (ARCHIVE_OK);
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_xz.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_xz.c
new file mode 100644
index 00000000000..13d4ebd7e3f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_xz.c
@@ -0,0 +1,793 @@
+/*-
+ * Copyright (c) 2009-2011 Michihiro NAKAJIMA
+ * Copyright (c) 2003-2008 Tim Kientzle and Miklos Vajna
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+
+struct private_data {
+ lzma_stream stream;
+ unsigned char *out_block;
+ size_t out_block_size;
+ int64_t total_out;
+ char eof; /* True = found end of compressed data. */
+ char in_stream;
+
+ /* Following variables are used for lzip only. */
+ char lzip_ver;
+ uint32_t crc32;
+ int64_t member_in;
+ int64_t member_out;
+};
+
+#if LZMA_VERSION_MAJOR >= 5
+/* Effectively disable the limiter. */
+#define LZMA_MEMLIMIT UINT64_MAX
+#else
+/* NOTE: This needs to check memory size which running system has. */
+#define LZMA_MEMLIMIT (1U << 30)
+#endif
+
+/* Combined lzip/lzma/xz filter */
+static ssize_t xz_filter_read(struct archive_read_filter *, const void **);
+static int xz_filter_close(struct archive_read_filter *);
+static int xz_lzma_bidder_init(struct archive_read_filter *);
+
+#endif
+
+/*
+ * Note that we can detect xz and lzma compressed files even if we
+ * can't decompress them. (In fact, we like detecting them because we
+ * can give better error messages.) So the bid framework here gets
+ * compiled even if no lzma library is available.
+ */
+static int xz_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int xz_bidder_init(struct archive_read_filter *);
+static int lzma_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int lzma_bidder_init(struct archive_read_filter *);
+static int lzip_has_member(struct archive_read_filter *);
+static int lzip_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int lzip_bidder_init(struct archive_read_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_xz(struct archive *a)
+{
+ return archive_read_support_filter_xz(a);
+}
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+xz_bidder_vtable = {
+ .bid = xz_bidder_bid,
+ .init = xz_bidder_init,
+};
+
+int
+archive_read_support_filter_xz(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, "xz",
+ &xz_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+ return (ARCHIVE_OK);
+#else
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external xz program for xz decompression");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_read_support_compression_lzma(struct archive *a)
+{
+ return archive_read_support_filter_lzma(a);
+}
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+lzma_bidder_vtable = {
+ .bid = lzma_bidder_bid,
+ .init = lzma_bidder_init,
+};
+
+int
+archive_read_support_filter_lzma(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, "lzma",
+ &lzma_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+ return (ARCHIVE_OK);
+#else
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external lzma program for lzma decompression");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_read_support_compression_lzip(struct archive *a)
+{
+ return archive_read_support_filter_lzip(a);
+}
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+lzip_bidder_vtable = {
+ .bid = lzip_bidder_bid,
+ .init = lzip_bidder_init,
+};
+
+int
+archive_read_support_filter_lzip(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, "lzip",
+ &lzip_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+ return (ARCHIVE_OK);
+#else
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external lzip program for lzip decompression");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Test whether we can handle this data.
+ */
+static int
+xz_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *buffer;
+ ssize_t avail;
+
+ (void)self; /* UNUSED */
+
+ buffer = __archive_read_filter_ahead(filter, 6, &avail);
+ if (buffer == NULL)
+ return (0);
+
+ /*
+ * Verify Header Magic Bytes : FD 37 7A 58 5A 00
+ */
+ if (memcmp(buffer, "\xFD\x37\x7A\x58\x5A\x00", 6) != 0)
+ return (0);
+
+ return (48);
+}
+
+/*
+ * Test whether we can handle this data.
+ *
+ * <sigh> LZMA has a rather poor file signature. Zeros do not
+ * make good signature bytes as a rule, and the only non-zero byte
+ * here is an ASCII character. For example, an uncompressed tar
+ * archive whose first file is ']' would satisfy this check. It may
+ * be necessary to exclude LZMA from compression_all() because of
+ * this. Clients of libarchive would then have to explicitly enable
+ * LZMA checking instead of (or in addition to) compression_all() when
+ * they have other evidence (file name, command-line option) to go on.
+ */
+static int
+lzma_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *buffer;
+ ssize_t avail;
+ uint32_t dicsize;
+ uint64_t uncompressed_size;
+ int bits_checked;
+
+ (void)self; /* UNUSED */
+
+ buffer = __archive_read_filter_ahead(filter, 14, &avail);
+ if (buffer == NULL)
+ return (0);
+
+ /* First byte of raw LZMA stream is commonly 0x5d.
+ * The first byte is a special number, which consists of
+ * three parameters of LZMA compression, a number of literal
+ * context bits(which is from 0 to 8, default is 3), a number
+ * of literal pos bits(which is from 0 to 4, default is 0),
+ * a number of pos bits(which is from 0 to 4, default is 2).
+ * The first byte is made by
+ * (pos bits * 5 + literal pos bit) * 9 + * literal contest bit,
+ * and so the default value in this field is
+ * (2 * 5 + 0) * 9 + 3 = 0x5d.
+ * lzma of LZMA SDK has options to change those parameters.
+ * It means a range of this field is from 0 to 224. And lzma of
+ * XZ Utils with option -e records 0x5e in this field. */
+ /* NOTE: If this checking of the first byte increases false
+ * recognition, we should allow only 0x5d and 0x5e for the first
+ * byte of LZMA stream. */
+ bits_checked = 0;
+ if (buffer[0] > (4 * 5 + 4) * 9 + 8)
+ return (0);
+ /* Most likely value in the first byte of LZMA stream. */
+ if (buffer[0] == 0x5d || buffer[0] == 0x5e)
+ bits_checked += 8;
+
+ /* Sixth through fourteenth bytes are uncompressed size,
+ * stored in little-endian order. `-1' means uncompressed
+ * size is unknown and lzma of XZ Utils always records `-1'
+ * in this field. */
+ uncompressed_size = archive_le64dec(buffer+5);
+ if (uncompressed_size == (uint64_t)ARCHIVE_LITERAL_LL(-1))
+ bits_checked += 64;
+
+ /* Second through fifth bytes are dictionary size, stored in
+ * little-endian order. The minimum dictionary size is
+ * 1 << 12(4KiB) which the lzma of LZMA SDK uses with option
+ * -d12 and the maximum dictionary size is 1 << 29(512MiB)
+ * which the one uses with option -d29.
+ * NOTE: A comment of LZMA SDK source code says this dictionary
+ * range is from 1 << 12 to 1 << 30. */
+ dicsize = archive_le32dec(buffer+1);
+ switch (dicsize) {
+ case 0x00001000:/* lzma of LZMA SDK option -d12. */
+ case 0x00002000:/* lzma of LZMA SDK option -d13. */
+ case 0x00004000:/* lzma of LZMA SDK option -d14. */
+ case 0x00008000:/* lzma of LZMA SDK option -d15. */
+ case 0x00010000:/* lzma of XZ Utils option -0 and -1.
+ * lzma of LZMA SDK option -d16. */
+ case 0x00020000:/* lzma of LZMA SDK option -d17. */
+ case 0x00040000:/* lzma of LZMA SDK option -d18. */
+ case 0x00080000:/* lzma of XZ Utils option -2.
+ * lzma of LZMA SDK option -d19. */
+ case 0x00100000:/* lzma of XZ Utils option -3.
+ * lzma of LZMA SDK option -d20. */
+ case 0x00200000:/* lzma of XZ Utils option -4.
+ * lzma of LZMA SDK option -d21. */
+ case 0x00400000:/* lzma of XZ Utils option -5.
+ * lzma of LZMA SDK option -d22. */
+ case 0x00800000:/* lzma of XZ Utils option -6.
+ * lzma of LZMA SDK option -d23. */
+ case 0x01000000:/* lzma of XZ Utils option -7.
+ * lzma of LZMA SDK option -d24. */
+ case 0x02000000:/* lzma of XZ Utils option -8.
+ * lzma of LZMA SDK option -d25. */
+ case 0x04000000:/* lzma of XZ Utils option -9.
+ * lzma of LZMA SDK option -d26. */
+ case 0x08000000:/* lzma of LZMA SDK option -d27. */
+ bits_checked += 32;
+ break;
+ default:
+ /* If a memory usage for encoding was not enough on
+ * the platform where LZMA stream was made, lzma of
+ * XZ Utils automatically decreased the dictionary
+ * size to enough memory for encoding by 1Mi bytes
+ * (1 << 20).*/
+ if (dicsize <= 0x03F00000 && dicsize >= 0x00300000 &&
+ (dicsize & ((1 << 20)-1)) == 0 &&
+ bits_checked == 8 + 64) {
+ bits_checked += 32;
+ break;
+ }
+ /* Otherwise dictionary size is unlikely. But it is
+ * possible that someone makes lzma stream with
+ * liblzma/LZMA SDK in one's dictionary size. */
+ return (0);
+ }
+
+ /* TODO: The above test is still very weak. It would be
+ * good to do better. */
+
+ return (bits_checked);
+}
+
+static int
+lzip_has_member(struct archive_read_filter *filter)
+{
+ const unsigned char *buffer;
+ ssize_t avail;
+ int bits_checked;
+ int log2dic;
+
+ buffer = __archive_read_filter_ahead(filter, 6, &avail);
+ if (buffer == NULL)
+ return (0);
+
+ /*
+ * Verify Header Magic Bytes : 4C 5A 49 50 (`LZIP')
+ */
+ bits_checked = 0;
+ if (memcmp(buffer, "LZIP", 4) != 0)
+ return (0);
+ bits_checked += 32;
+
+ /* A version number must be 0 or 1 */
+ if (buffer[4] != 0 && buffer[4] != 1)
+ return (0);
+ bits_checked += 8;
+
+ /* Dictionary size. */
+ log2dic = buffer[5] & 0x1f;
+ if (log2dic < 12 || log2dic > 29)
+ return (0);
+ bits_checked += 8;
+
+ return (bits_checked);
+}
+
+static int
+lzip_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+
+ (void)self; /* UNUSED */
+ return (lzip_has_member(filter));
+}
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+
+/*
+ * liblzma 4.999.7 and later support both lzma and xz streams.
+ */
+static int
+xz_bidder_init(struct archive_read_filter *self)
+{
+ self->code = ARCHIVE_FILTER_XZ;
+ self->name = "xz";
+ return (xz_lzma_bidder_init(self));
+}
+
+static int
+lzma_bidder_init(struct archive_read_filter *self)
+{
+ self->code = ARCHIVE_FILTER_LZMA;
+ self->name = "lzma";
+ return (xz_lzma_bidder_init(self));
+}
+
+static int
+lzip_bidder_init(struct archive_read_filter *self)
+{
+ self->code = ARCHIVE_FILTER_LZIP;
+ self->name = "lzip";
+ return (xz_lzma_bidder_init(self));
+}
+
+/*
+ * Set an error code and choose an error message
+ */
+static void
+set_error(struct archive_read_filter *self, int ret)
+{
+
+ switch (ret) {
+ case LZMA_STREAM_END: /* Found end of stream. */
+ case LZMA_OK: /* Decompressor made some progress. */
+ break;
+ case LZMA_MEM_ERROR:
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Lzma library error: Cannot allocate memory");
+ break;
+ case LZMA_MEMLIMIT_ERROR:
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Lzma library error: Out of memory");
+ break;
+ case LZMA_FORMAT_ERROR:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma library error: format not recognized");
+ break;
+ case LZMA_OPTIONS_ERROR:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma library error: Invalid options");
+ break;
+ case LZMA_DATA_ERROR:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma library error: Corrupted input data");
+ break;
+ case LZMA_BUF_ERROR:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma library error: No progress is possible");
+ break;
+ default:
+ /* Return an error. */
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma decompression failed: Unknown error");
+ break;
+ }
+}
+
+static const struct archive_read_filter_vtable
+xz_lzma_reader_vtable = {
+ .read = xz_filter_read,
+ .close = xz_filter_close,
+};
+
+/*
+ * Setup the callbacks.
+ */
+static int
+xz_lzma_bidder_init(struct archive_read_filter *self)
+{
+ static const size_t out_block_size = 64 * 1024;
+ void *out_block;
+ struct private_data *state;
+ int ret;
+
+ state = (struct private_data *)calloc(sizeof(*state), 1);
+ out_block = (unsigned char *)malloc(out_block_size);
+ if (state == NULL || out_block == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for xz decompression");
+ free(out_block);
+ free(state);
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = state;
+ state->out_block_size = out_block_size;
+ state->out_block = out_block;
+ self->vtable = &xz_lzma_reader_vtable;
+
+ state->stream.avail_in = 0;
+
+ state->stream.next_out = state->out_block;
+ state->stream.avail_out = state->out_block_size;
+
+ state->crc32 = 0;
+ if (self->code == ARCHIVE_FILTER_LZIP) {
+ /*
+ * We have to read a lzip header and use it to initialize
+ * compression library, thus we cannot initialize the
+ * library for lzip here.
+ */
+ state->in_stream = 0;
+ return (ARCHIVE_OK);
+ } else
+ state->in_stream = 1;
+
+ /* Initialize compression library. */
+ if (self->code == ARCHIVE_FILTER_XZ)
+ ret = lzma_stream_decoder(&(state->stream),
+ LZMA_MEMLIMIT,/* memlimit */
+ LZMA_CONCATENATED);
+ else
+ ret = lzma_alone_decoder(&(state->stream),
+ LZMA_MEMLIMIT);/* memlimit */
+
+ if (ret == LZMA_OK)
+ return (ARCHIVE_OK);
+
+ /* Library setup failed: Choose an error message and clean up. */
+ set_error(self, ret);
+
+ free(state->out_block);
+ free(state);
+ self->data = NULL;
+ return (ARCHIVE_FATAL);
+}
+
+static int
+lzip_init(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ const unsigned char *h;
+ lzma_filter filters[2];
+ unsigned char props[5];
+ ssize_t avail_in;
+ uint32_t dicsize;
+ int log2dic, ret;
+
+ state = (struct private_data *)self->data;
+ h = __archive_read_filter_ahead(self->upstream, 6, &avail_in);
+ if (h == NULL)
+ return (ARCHIVE_FATAL);
+
+ /* Get a version number. */
+ state->lzip_ver = h[4];
+
+ /*
+ * Setup lzma property.
+ */
+ props[0] = 0x5d;
+
+ /* Get dictionary size. */
+ log2dic = h[5] & 0x1f;
+ if (log2dic < 12 || log2dic > 29)
+ return (ARCHIVE_FATAL);
+ dicsize = 1U << log2dic;
+ if (log2dic > 12)
+ dicsize -= (dicsize / 16) * (h[5] >> 5);
+ archive_le32enc(props+1, dicsize);
+
+ /* Consume lzip header. */
+ __archive_read_filter_consume(self->upstream, 6);
+ state->member_in = 6;
+
+ filters[0].id = LZMA_FILTER_LZMA1;
+ filters[0].options = NULL;
+ filters[1].id = LZMA_VLI_UNKNOWN;
+ filters[1].options = NULL;
+
+ ret = lzma_properties_decode(&filters[0], NULL, props, sizeof(props));
+ if (ret != LZMA_OK) {
+ set_error(self, ret);
+ return (ARCHIVE_FATAL);
+ }
+ ret = lzma_raw_decoder(&(state->stream), filters);
+ free(filters[0].options);
+ if (ret != LZMA_OK) {
+ set_error(self, ret);
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+lzip_tail(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ const unsigned char *f;
+ ssize_t avail_in;
+ int tail;
+
+ state = (struct private_data *)self->data;
+ if (state->lzip_ver == 0)
+ tail = 12;
+ else
+ tail = 20;
+ f = __archive_read_filter_ahead(self->upstream, tail, &avail_in);
+ if (f == NULL && avail_in < 0)
+ return (ARCHIVE_FATAL);
+ if (f == NULL || avail_in < tail) {
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "Lzip: Remaining data is less bytes");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Check the crc32 value of the uncompressed data of the current
+ * member */
+ if (state->crc32 != archive_le32dec(f)) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "Lzip: CRC32 error");
+ return (ARCHIVE_FAILED);
+#endif
+ }
+
+ /* Check the uncompressed size of the current member */
+ if ((uint64_t)state->member_out != archive_le64dec(f + 4)) {
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "Lzip: Uncompressed size error");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Check the total size of the current member */
+ if (state->lzip_ver == 1 &&
+ (uint64_t)state->member_in + tail != archive_le64dec(f + 12)) {
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "Lzip: Member size error");
+ return (ARCHIVE_FAILED);
+ }
+ __archive_read_filter_consume(self->upstream, tail);
+
+ /* If current lzip data consists of multi member, try decompressing
+ * a next member. */
+ if (lzip_has_member(self->upstream) != 0) {
+ state->in_stream = 0;
+ state->crc32 = 0;
+ state->member_out = 0;
+ state->member_in = 0;
+ state->eof = 0;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Return the next block of decompressed data.
+ */
+static ssize_t
+xz_filter_read(struct archive_read_filter *self, const void **p)
+{
+ struct private_data *state;
+ size_t decompressed;
+ ssize_t avail_in;
+ int ret;
+
+ state = (struct private_data *)self->data;
+
+ /* Empty our output buffer. */
+ state->stream.next_out = state->out_block;
+ state->stream.avail_out = state->out_block_size;
+
+ /* Try to fill the output buffer. */
+ while (state->stream.avail_out > 0 && !state->eof) {
+ if (!state->in_stream) {
+ /*
+ * Initialize liblzma for lzip
+ */
+ ret = lzip_init(self);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ state->in_stream = 1;
+ }
+ state->stream.next_in =
+ __archive_read_filter_ahead(self->upstream, 1, &avail_in);
+ if (state->stream.next_in == NULL && avail_in < 0) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "truncated input");
+ return (ARCHIVE_FATAL);
+ }
+ state->stream.avail_in = avail_in;
+
+ /* Decompress as much as we can in one pass. */
+ ret = lzma_code(&(state->stream),
+ (state->stream.avail_in == 0)? LZMA_FINISH: LZMA_RUN);
+ switch (ret) {
+ case LZMA_STREAM_END: /* Found end of stream. */
+ state->eof = 1;
+ /* FALL THROUGH */
+ case LZMA_OK: /* Decompressor made some progress. */
+ __archive_read_filter_consume(self->upstream,
+ avail_in - state->stream.avail_in);
+ state->member_in +=
+ avail_in - state->stream.avail_in;
+ break;
+ default:
+ set_error(self, ret);
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ decompressed = state->stream.next_out - state->out_block;
+ state->total_out += decompressed;
+ state->member_out += decompressed;
+ if (decompressed == 0)
+ *p = NULL;
+ else {
+ *p = state->out_block;
+ if (self->code == ARCHIVE_FILTER_LZIP) {
+ state->crc32 = lzma_crc32(state->out_block,
+ decompressed, state->crc32);
+ if (state->eof) {
+ ret = lzip_tail(self);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ }
+ }
+ }
+ return (decompressed);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+xz_filter_close(struct archive_read_filter *self)
+{
+ struct private_data *state;
+
+ state = (struct private_data *)self->data;
+ lzma_end(&(state->stream));
+ free(state->out_block);
+ free(state);
+ return (ARCHIVE_OK);
+}
+
+#else
+
+/*
+ *
+ * If we have no suitable library on this system, we can't actually do
+ * the decompression. We can, however, still detect compressed
+ * archives and emit a useful message.
+ *
+ */
+static int
+lzma_bidder_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "lzma -d -qq");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_LZMA;
+ self->name = "lzma";
+ return (r);
+}
+
+static int
+xz_bidder_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "xz -d -qq");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_XZ;
+ self->name = "xz";
+ return (r);
+}
+
+static int
+lzip_bidder_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "lzip -d -q");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_LZIP;
+ self->name = "lzip";
+ return (r);
+}
+
+#endif /* HAVE_LZMA_H */
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_zstd.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_zstd.c
new file mode 100644
index 00000000000..1959b5ac399
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_zstd.c
@@ -0,0 +1,297 @@
+/*-
+ * Copyright (c) 2009-2011 Sean Purcell
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ZSTD_H
+#include <zstd.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+
+struct private_data {
+ ZSTD_DStream *dstream;
+ unsigned char *out_block;
+ size_t out_block_size;
+ int64_t total_out;
+ char in_frame; /* True = in the middle of a zstd frame. */
+ char eof; /* True = found end of compressed data. */
+};
+
+/* Zstd Filter. */
+static ssize_t zstd_filter_read(struct archive_read_filter *, const void**);
+static int zstd_filter_close(struct archive_read_filter *);
+#endif
+
+/*
+ * Note that we can detect zstd compressed files even if we can't decompress
+ * them. (In fact, we like detecting them because we can give better error
+ * messages.) So the bid framework here gets compiled even if no zstd library
+ * is available.
+ */
+static int zstd_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int zstd_bidder_init(struct archive_read_filter *);
+
+static const struct archive_read_filter_bidder_vtable
+zstd_bidder_vtable = {
+ .bid = zstd_bidder_bid,
+ .init = zstd_bidder_init,
+};
+
+int
+archive_read_support_filter_zstd(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, "zstd",
+ &zstd_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+ return (ARCHIVE_OK);
+#else
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external zstd program for zstd decompression");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Test whether we can handle this data.
+ */
+static int
+zstd_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *buffer;
+ ssize_t avail;
+ unsigned prefix;
+
+ /* Zstd frame magic values */
+ unsigned zstd_magic = 0xFD2FB528U;
+ unsigned zstd_magic_skippable_start = 0x184D2A50U;
+ unsigned zstd_magic_skippable_mask = 0xFFFFFFF0;
+
+ (void) self; /* UNUSED */
+
+ buffer = __archive_read_filter_ahead(filter, 4, &avail);
+ if (buffer == NULL)
+ return (0);
+
+ prefix = archive_le32dec(buffer);
+ if (prefix == zstd_magic)
+ return (32);
+ if ((prefix & zstd_magic_skippable_mask) == zstd_magic_skippable_start)
+ return (32);
+
+ return (0);
+}
+
+#if !(HAVE_ZSTD_H && HAVE_LIBZSTD)
+
+/*
+ * If we don't have the library on this system, we can't do the
+ * decompression directly. We can, however, try to run "zstd -d"
+ * in case that's available.
+ */
+static int
+zstd_bidder_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "zstd -d -qq");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_ZSTD;
+ self->name = "zstd";
+ return (r);
+}
+
+#else
+
+static const struct archive_read_filter_vtable
+zstd_reader_vtable = {
+ .read = zstd_filter_read,
+ .close = zstd_filter_close,
+};
+
+/*
+ * Initialize the filter object
+ */
+static int
+zstd_bidder_init(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ size_t out_block_size = ZSTD_DStreamOutSize();
+ void *out_block;
+ ZSTD_DStream *dstream;
+
+ self->code = ARCHIVE_FILTER_ZSTD;
+ self->name = "zstd";
+
+ state = (struct private_data *)calloc(sizeof(*state), 1);
+ out_block = (unsigned char *)malloc(out_block_size);
+ dstream = ZSTD_createDStream();
+
+ if (state == NULL || out_block == NULL || dstream == NULL) {
+ free(out_block);
+ free(state);
+ ZSTD_freeDStream(dstream); /* supports free on NULL */
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for zstd decompression");
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = state;
+
+ state->out_block_size = out_block_size;
+ state->out_block = out_block;
+ state->dstream = dstream;
+ self->vtable = &zstd_reader_vtable;
+
+ state->eof = 0;
+ state->in_frame = 0;
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+zstd_filter_read(struct archive_read_filter *self, const void **p)
+{
+ struct private_data *state;
+ size_t decompressed;
+ ssize_t avail_in;
+ ZSTD_outBuffer out;
+ ZSTD_inBuffer in;
+ size_t ret;
+
+ state = (struct private_data *)self->data;
+
+ out = (ZSTD_outBuffer) { state->out_block, state->out_block_size, 0 };
+
+ /* Try to fill the output buffer. */
+ while (out.pos < out.size && !state->eof) {
+ if (!state->in_frame) {
+ ret = ZSTD_initDStream(state->dstream);
+ if (ZSTD_isError(ret)) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Error initializing zstd decompressor: %s",
+ ZSTD_getErrorName(ret));
+ return (ARCHIVE_FATAL);
+ }
+ }
+ in.src = __archive_read_filter_ahead(self->upstream, 1,
+ &avail_in);
+ if (avail_in < 0) {
+ return avail_in;
+ }
+ if (in.src == NULL && avail_in == 0) {
+ if (!state->in_frame) {
+ /* end of stream */
+ state->eof = 1;
+ break;
+ } else {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Truncated zstd input");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ in.size = avail_in;
+ in.pos = 0;
+
+ {
+ ret = ZSTD_decompressStream(state->dstream, &out, &in);
+
+ if (ZSTD_isError(ret)) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Zstd decompression failed: %s",
+ ZSTD_getErrorName(ret));
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Decompressor made some progress */
+ __archive_read_filter_consume(self->upstream, in.pos);
+
+ /* ret guaranteed to be > 0 if frame isn't done yet */
+ state->in_frame = (ret != 0);
+ }
+ }
+
+ decompressed = out.pos;
+ state->total_out += decompressed;
+ if (decompressed == 0)
+ *p = NULL;
+ else
+ *p = state->out_block;
+ return (decompressed);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+zstd_filter_close(struct archive_read_filter *self)
+{
+ struct private_data *state;
+
+ state = (struct private_data *)self->data;
+
+ ZSTD_freeDStream(state->dstream);
+ free(state->out_block);
+ free(state);
+
+ return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_ZLIB_H && HAVE_LIBZSTD */
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_7zip.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_7zip.c
new file mode 100644
index 00000000000..87dd540ba1b
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_7zip.c
@@ -0,0 +1,4074 @@
+/*-
+ * Copyright (c) 2011 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#ifdef HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+#ifdef HAVE_ZSTD_H
+#include <zstd.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_ppmd7_private.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_endian.h"
+
+#ifndef HAVE_ZLIB_H
+#error #include "archive_crc32.h"
+#endif
+
+#define _7ZIP_SIGNATURE "7z\xBC\xAF\x27\x1C"
+#define SFX_MIN_ADDR 0x27000
+#define SFX_MAX_ADDR 0x60000
+
+
+/*
+ * Codec ID
+ */
+#define _7Z_COPY 0
+#define _7Z_LZMA 0x030101
+#define _7Z_LZMA2 0x21
+#define _7Z_DEFLATE 0x040108
+#define _7Z_BZ2 0x040202
+#define _7Z_PPMD 0x030401
+#define _7Z_DELTA 0x03
+#define _7Z_CRYPTO_MAIN_ZIP 0x06F10101 /* Main Zip crypto algo */
+#define _7Z_CRYPTO_RAR_29 0x06F10303 /* Rar29 AES-128 + (modified SHA-1) */
+#define _7Z_CRYPTO_AES_256_SHA_256 0x06F10701 /* AES-256 + SHA-256 */
+
+
+#define _7Z_X86 0x03030103
+#define _7Z_X86_BCJ2 0x0303011B
+#define _7Z_POWERPC 0x03030205
+#define _7Z_IA64 0x03030401
+#define _7Z_ARM 0x03030501
+#define _7Z_ARMTHUMB 0x03030701
+#define _7Z_ARM64 0xa
+#define _7Z_SPARC 0x03030805
+
+#define _7Z_ZSTD 0x4F71101 /* Copied from https://github.com/mcmilk/7-Zip-zstd.git */
+
+/*
+ * 7-Zip header property IDs.
+ */
+#define kEnd 0x00
+#define kHeader 0x01
+#define kArchiveProperties 0x02
+#define kAdditionalStreamsInfo 0x03
+#define kMainStreamsInfo 0x04
+#define kFilesInfo 0x05
+#define kPackInfo 0x06
+#define kUnPackInfo 0x07
+#define kSubStreamsInfo 0x08
+#define kSize 0x09
+#define kCRC 0x0A
+#define kFolder 0x0B
+#define kCodersUnPackSize 0x0C
+#define kNumUnPackStream 0x0D
+#define kEmptyStream 0x0E
+#define kEmptyFile 0x0F
+#define kAnti 0x10
+#define kName 0x11
+#define kCTime 0x12
+#define kATime 0x13
+#define kMTime 0x14
+#define kAttributes 0x15
+#define kEncodedHeader 0x17
+#define kDummy 0x19
+
+struct _7z_digests {
+ unsigned char *defineds;
+ uint32_t *digests;
+};
+
+
+struct _7z_folder {
+ uint64_t numCoders;
+ struct _7z_coder {
+ unsigned long codec;
+ uint64_t numInStreams;
+ uint64_t numOutStreams;
+ uint64_t propertiesSize;
+ unsigned char *properties;
+ } *coders;
+ uint64_t numBindPairs;
+ struct {
+ uint64_t inIndex;
+ uint64_t outIndex;
+ } *bindPairs;
+ uint64_t numPackedStreams;
+ uint64_t *packedStreams;
+ uint64_t numInStreams;
+ uint64_t numOutStreams;
+ uint64_t *unPackSize;
+ unsigned char digest_defined;
+ uint32_t digest;
+ uint64_t numUnpackStreams;
+ uint32_t packIndex;
+ /* Unoperated bytes. */
+ uint64_t skipped_bytes;
+};
+
+struct _7z_coders_info {
+ uint64_t numFolders;
+ struct _7z_folder *folders;
+ uint64_t dataStreamIndex;
+};
+
+struct _7z_pack_info {
+ uint64_t pos;
+ uint64_t numPackStreams;
+ uint64_t *sizes;
+ struct _7z_digests digest;
+ /* Calculated from pos and numPackStreams. */
+ uint64_t *positions;
+};
+
+struct _7z_substream_info {
+ size_t unpack_streams;
+ uint64_t *unpackSizes;
+ unsigned char *digestsDefined;
+ uint32_t *digests;
+};
+
+struct _7z_stream_info {
+ struct _7z_pack_info pi;
+ struct _7z_coders_info ci;
+ struct _7z_substream_info ss;
+};
+
+struct _7z_header_info {
+ uint64_t dataIndex;
+
+ unsigned char *emptyStreamBools;
+ unsigned char *emptyFileBools;
+ unsigned char *antiBools;
+ unsigned char *attrBools;
+};
+
+struct _7zip_entry {
+ size_t name_len;
+ unsigned char *utf16name;
+#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
+ const wchar_t *wname;
+#endif
+ uint32_t folderIndex;
+ uint32_t ssIndex;
+ unsigned flg;
+#define MTIME_IS_SET (1<<0)
+#define ATIME_IS_SET (1<<1)
+#define CTIME_IS_SET (1<<2)
+#define CRC32_IS_SET (1<<3)
+#define HAS_STREAM (1<<4)
+
+ time_t mtime;
+ time_t atime;
+ time_t ctime;
+ long mtime_ns;
+ long atime_ns;
+ long ctime_ns;
+ uint32_t mode;
+ uint32_t attr;
+};
+
+struct _7zip {
+ /* Structural information about the archive. */
+ struct _7z_stream_info si;
+
+ int header_is_being_read;
+ int header_is_encoded;
+ uint64_t header_bytes_remaining;
+ unsigned long header_crc32;
+ /* Header offset to check that reading points of the file contents
+ * will not exceed the header. */
+ uint64_t header_offset;
+ /* Base offset of the archive file for a seek in case reading SFX. */
+ uint64_t seek_base;
+
+ /* List of entries */
+ size_t entries_remaining;
+ uint64_t numFiles;
+ struct _7zip_entry *entries;
+ struct _7zip_entry *entry;
+ unsigned char *entry_names;
+
+ /* entry_bytes_remaining is the number of bytes we expect. */
+ int64_t entry_offset;
+ uint64_t entry_bytes_remaining;
+
+ /* Running CRC32 of the decompressed data */
+ unsigned long entry_crc32;
+
+ /* Flags to mark progress of decompression. */
+ char end_of_entry;
+
+ /* Uncompressed buffer control. */
+#define UBUFF_SIZE (64 * 1024)
+ unsigned char *uncompressed_buffer;
+ unsigned char *uncompressed_buffer_pointer;
+ size_t uncompressed_buffer_size;
+ size_t uncompressed_buffer_bytes_remaining;
+
+ /* Offset of the compressed data. */
+ int64_t stream_offset;
+
+ /*
+ * Decompressing control data.
+ */
+ unsigned folder_index;
+ uint64_t folder_outbytes_remaining;
+ unsigned pack_stream_index;
+ unsigned pack_stream_remaining;
+ uint64_t pack_stream_inbytes_remaining;
+ size_t pack_stream_bytes_unconsumed;
+
+ /* The codec information of a folder. */
+ unsigned long codec;
+ unsigned long codec2;
+
+ /*
+ * Decompressor controllers.
+ */
+ /* Decoding LZMA1 and LZMA2 data. */
+#ifdef HAVE_LZMA_H
+ lzma_stream lzstream;
+ int lzstream_valid;
+#endif
+ /* Decoding bzip2 data. */
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ bz_stream bzstream;
+ int bzstream_valid;
+#endif
+ /* Decoding deflate data. */
+#ifdef HAVE_ZLIB_H
+ z_stream stream;
+ int stream_valid;
+#endif
+ /* Decoding Zstandard data. */
+#if HAVE_ZSTD_H
+ ZSTD_DStream *zstd_dstream;
+ int zstdstream_valid;
+#endif
+ /* Decoding PPMd data. */
+ int ppmd7_stat;
+ CPpmd7 ppmd7_context;
+ CPpmd7z_RangeDec range_dec;
+ IByteIn bytein;
+ struct {
+ const unsigned char *next_in;
+ int64_t avail_in;
+ int64_t total_in;
+ int64_t stream_in;
+ unsigned char *next_out;
+ int64_t avail_out;
+ int64_t total_out;
+ int overconsumed;
+ } ppstream;
+ int ppmd7_valid;
+
+ /* Decoding BCJ and BCJ2 data. */
+ uint32_t bcj_state;
+ size_t odd_bcj_size;
+ unsigned char odd_bcj[4];
+ /* Decoding BCJ data. */
+ size_t bcj_prevPosT;
+ uint32_t bcj_prevMask;
+ uint32_t bcj_ip;
+
+ /* Decoding BCJ2 data. */
+ size_t main_stream_bytes_remaining;
+ unsigned char *sub_stream_buff[3];
+ size_t sub_stream_size[3];
+ size_t sub_stream_bytes_remaining[3];
+ unsigned char *tmp_stream_buff;
+ size_t tmp_stream_buff_size;
+ size_t tmp_stream_bytes_avail;
+ size_t tmp_stream_bytes_remaining;
+#ifdef _LZMA_PROB32
+#define CProb uint32_t
+#else
+#define CProb uint16_t
+#endif
+ CProb bcj2_p[256 + 2];
+ uint8_t bcj2_prevByte;
+ uint32_t bcj2_range;
+ uint32_t bcj2_code;
+ uint64_t bcj2_outPos;
+
+ /* Filename character-set conversion data. */
+ struct archive_string_conv *sconv;
+
+ char format_name[64];
+
+ /* Custom value that is non-zero if this archive contains encrypted entries. */
+ int has_encrypted_entries;
+};
+
+/* Maximum entry size. This limitation prevents reading intentional
+ * corrupted 7-zip files on assuming there are not so many entries in
+ * the files. */
+#define UMAX_ENTRY ARCHIVE_LITERAL_ULL(100000000)
+
+static int archive_read_format_7zip_has_encrypted_entries(struct archive_read *);
+static int archive_read_support_format_7zip_capabilities(struct archive_read *a);
+static int archive_read_format_7zip_bid(struct archive_read *, int);
+static int archive_read_format_7zip_cleanup(struct archive_read *);
+static int archive_read_format_7zip_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_7zip_read_data_skip(struct archive_read *);
+static int archive_read_format_7zip_read_header(struct archive_read *,
+ struct archive_entry *);
+static int check_7zip_header_in_sfx(const char *);
+static unsigned long decode_codec_id(const unsigned char *, size_t);
+static int decode_encoded_header_info(struct archive_read *,
+ struct _7z_stream_info *);
+static int decompress(struct archive_read *, struct _7zip *,
+ void *, size_t *, const void *, size_t *);
+static ssize_t extract_pack_stream(struct archive_read *, size_t);
+static void fileTimeToUtc(uint64_t, time_t *, long *);
+static uint64_t folder_uncompressed_size(struct _7z_folder *);
+static void free_CodersInfo(struct _7z_coders_info *);
+static void free_Digest(struct _7z_digests *);
+static void free_Folder(struct _7z_folder *);
+static void free_Header(struct _7z_header_info *);
+static void free_PackInfo(struct _7z_pack_info *);
+static void free_StreamsInfo(struct _7z_stream_info *);
+static void free_SubStreamsInfo(struct _7z_substream_info *);
+static int free_decompression(struct archive_read *, struct _7zip *);
+static ssize_t get_uncompressed_data(struct archive_read *, const void **,
+ size_t, size_t);
+static const unsigned char * header_bytes(struct archive_read *, size_t);
+static int init_decompression(struct archive_read *, struct _7zip *,
+ const struct _7z_coder *, const struct _7z_coder *);
+static int parse_7zip_uint64(struct archive_read *, uint64_t *);
+static int read_Bools(struct archive_read *, unsigned char *, size_t);
+static int read_CodersInfo(struct archive_read *,
+ struct _7z_coders_info *);
+static int read_Digests(struct archive_read *, struct _7z_digests *,
+ size_t);
+static int read_Folder(struct archive_read *, struct _7z_folder *);
+static int read_Header(struct archive_read *, struct _7z_header_info *,
+ int);
+static int read_PackInfo(struct archive_read *, struct _7z_pack_info *);
+static int read_StreamsInfo(struct archive_read *,
+ struct _7z_stream_info *);
+static int read_SubStreamsInfo(struct archive_read *,
+ struct _7z_substream_info *, struct _7z_folder *, size_t);
+static int read_Times(struct archive_read *, struct _7z_header_info *,
+ int);
+static void read_consume(struct archive_read *);
+static ssize_t read_stream(struct archive_read *, const void **, size_t,
+ size_t);
+static int seek_pack(struct archive_read *);
+static int64_t skip_stream(struct archive_read *, size_t);
+static int skip_sfx(struct archive_read *, ssize_t);
+static int slurp_central_directory(struct archive_read *, struct _7zip *,
+ struct _7z_header_info *);
+static int setup_decode_folder(struct archive_read *, struct _7z_folder *,
+ int);
+static void x86_Init(struct _7zip *);
+static size_t x86_Convert(struct _7zip *, uint8_t *, size_t);
+static void arm_Init(struct _7zip *);
+static size_t arm_Convert(struct _7zip *, uint8_t *, size_t);
+static size_t arm64_Convert(struct _7zip *, uint8_t *, size_t);
+static ssize_t Bcj2_Decode(struct _7zip *, uint8_t *, size_t);
+
+
+int
+archive_read_support_format_7zip(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct _7zip *zip;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_7zip");
+
+ zip = calloc(1, sizeof(*zip));
+ if (zip == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate 7zip data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Until enough data has been read, we cannot tell about
+ * any encrypted entries yet.
+ */
+ zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+
+
+ r = __archive_read_register_format(a,
+ zip,
+ "7zip",
+ archive_read_format_7zip_bid,
+ NULL,
+ archive_read_format_7zip_read_header,
+ archive_read_format_7zip_read_data,
+ archive_read_format_7zip_read_data_skip,
+ NULL,
+ archive_read_format_7zip_cleanup,
+ archive_read_support_format_7zip_capabilities,
+ archive_read_format_7zip_has_encrypted_entries);
+
+ if (r != ARCHIVE_OK)
+ free(zip);
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_support_format_7zip_capabilities(struct archive_read * a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA |
+ ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
+}
+
+
+static int
+archive_read_format_7zip_has_encrypted_entries(struct archive_read *_a)
+{
+ if (_a && _a->format) {
+ struct _7zip * zip = (struct _7zip *)_a->format->data;
+ if (zip) {
+ return zip->has_encrypted_entries;
+ }
+ }
+ return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+}
+
+static int
+archive_read_format_7zip_bid(struct archive_read *a, int best_bid)
+{
+ const char *p;
+
+ /* If someone has already bid more than 32, then avoid
+ trashing the look-ahead buffers with a seek. */
+ if (best_bid > 32)
+ return (-1);
+
+ if ((p = __archive_read_ahead(a, 6, NULL)) == NULL)
+ return (0);
+
+ /* If first six bytes are the 7-Zip signature,
+ * return the bid right now. */
+ if (memcmp(p, _7ZIP_SIGNATURE, 6) == 0)
+ return (48);
+
+ /*
+ * It may a 7-Zip SFX archive file. If first two bytes are
+ * 'M' and 'Z' available on Windows or first four bytes are
+ * "\x7F\x45LF" available on posix like system, seek the 7-Zip
+ * signature. Although we will perform a seek when reading
+ * a header, what we do not use __archive_read_seek() here is
+ * due to a bidding performance.
+ */
+ if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
+ ssize_t offset = SFX_MIN_ADDR;
+ ssize_t window = 4096;
+ ssize_t bytes_avail;
+ while (offset + window <= (SFX_MAX_ADDR)) {
+ const char *buff = __archive_read_ahead(a,
+ offset + window, &bytes_avail);
+ if (buff == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < 0x40)
+ return (0);
+ continue;
+ }
+ p = buff + offset;
+ while (p + 32 < buff + bytes_avail) {
+ int step = check_7zip_header_in_sfx(p);
+ if (step == 0)
+ return (48);
+ p += step;
+ }
+ offset = p - buff;
+ }
+ }
+ return (0);
+}
+
+static int
+check_7zip_header_in_sfx(const char *p)
+{
+ switch ((unsigned char)p[5]) {
+ case 0x1C:
+ if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0)
+ return (6);
+ /*
+ * Test the CRC because its extraction code has 7-Zip
+ * Magic Code, so we should do this in order not to
+ * make a mis-detection.
+ */
+ if (crc32(0, (const unsigned char *)p + 12, 20)
+ != archive_le32dec(p + 8))
+ return (6);
+ /* Hit the header! */
+ return (0);
+ case 0x37: return (5);
+ case 0x7A: return (4);
+ case 0xBC: return (3);
+ case 0xAF: return (2);
+ case 0x27: return (1);
+ default: return (6);
+ }
+}
+
+static int
+skip_sfx(struct archive_read *a, ssize_t bytes_avail)
+{
+ const void *h;
+ const char *p, *q;
+ size_t skip, offset;
+ ssize_t bytes, window;
+
+ /*
+ * If bytes_avail > SFX_MIN_ADDR we do not have to call
+ * __archive_read_seek() at this time since we have
+ * already had enough data.
+ */
+ if (bytes_avail > SFX_MIN_ADDR)
+ __archive_read_consume(a, SFX_MIN_ADDR);
+ else if (__archive_read_seek(a, SFX_MIN_ADDR, SEEK_SET) < 0)
+ return (ARCHIVE_FATAL);
+
+ offset = 0;
+ window = 1;
+ while (offset + window <= SFX_MAX_ADDR - SFX_MIN_ADDR) {
+ h = __archive_read_ahead(a, window, &bytes);
+ if (h == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < 0x40)
+ goto fatal;
+ continue;
+ }
+ if (bytes < 6) {
+ /* This case might happen when window == 1. */
+ window = 4096;
+ continue;
+ }
+ p = (const char *)h;
+ q = p + bytes;
+
+ /*
+ * Scan ahead until we find something that looks
+ * like the 7-Zip header.
+ */
+ while (p + 32 < q) {
+ int step = check_7zip_header_in_sfx(p);
+ if (step == 0) {
+ struct _7zip *zip =
+ (struct _7zip *)a->format->data;
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ zip->seek_base = SFX_MIN_ADDR + offset + skip;
+ return (ARCHIVE_OK);
+ }
+ p += step;
+ }
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ offset += skip;
+ if (window == 1)
+ window = 4096;
+ }
+fatal:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Couldn't find out 7-Zip header");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+archive_read_format_7zip_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ struct _7zip_entry *zip_entry;
+ int r, ret = ARCHIVE_OK;
+ struct _7z_folder *folder = 0;
+ uint64_t fidx = 0;
+
+ /*
+ * It should be sufficient to call archive_read_next_header() for
+ * a reader to determine if an entry is encrypted or not. If the
+ * encryption of an entry is only detectable when calling
+ * archive_read_data(), so be it. We'll do the same check there
+ * as well.
+ */
+ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+ zip->has_encrypted_entries = 0;
+ }
+
+ a->archive.archive_format = ARCHIVE_FORMAT_7ZIP;
+ if (a->archive.archive_format_name == NULL)
+ a->archive.archive_format_name = "7-Zip";
+
+ if (zip->entries == NULL) {
+ struct _7z_header_info header;
+
+ memset(&header, 0, sizeof(header));
+ r = slurp_central_directory(a, zip, &header);
+ free_Header(&header);
+ if (r != ARCHIVE_OK)
+ return (r);
+ zip->entries_remaining = (size_t)zip->numFiles;
+ zip->entry = zip->entries;
+ } else {
+ ++zip->entry;
+ }
+ zip_entry = zip->entry;
+
+ if (zip->entries_remaining <= 0 || zip_entry == NULL)
+ return ARCHIVE_EOF;
+ --zip->entries_remaining;
+
+ zip->entry_offset = 0;
+ zip->end_of_entry = 0;
+ zip->entry_crc32 = crc32(0, NULL, 0);
+
+ /* Setup a string conversion for a filename. */
+ if (zip->sconv == NULL) {
+ zip->sconv = archive_string_conversion_from_charset(
+ &a->archive, "UTF-16LE", 1);
+ if (zip->sconv == NULL)
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Figure out if the entry is encrypted by looking at the folder
+ that is associated to the current 7zip entry. If the folder
+ has a coder with a _7Z_CRYPTO codec then the folder is encrypted.
+ Hence the entry must also be encrypted. */
+ if (zip_entry && zip_entry->folderIndex < zip->si.ci.numFolders) {
+ folder = &(zip->si.ci.folders[zip_entry->folderIndex]);
+ for (fidx=0; folder && fidx<folder->numCoders; fidx++) {
+ switch(folder->coders[fidx].codec) {
+ case _7Z_CRYPTO_MAIN_ZIP:
+ case _7Z_CRYPTO_RAR_29:
+ case _7Z_CRYPTO_AES_256_SHA_256: {
+ archive_entry_set_is_data_encrypted(entry, 1);
+ zip->has_encrypted_entries = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Now that we've checked for encryption, if there were still no
+ * encrypted entries found we can say for sure that there are none.
+ */
+ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+ zip->has_encrypted_entries = 0;
+ }
+
+ if (archive_entry_copy_pathname_l(entry,
+ (const char *)zip_entry->utf16name,
+ zip_entry->name_len, zip->sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname cannot be converted "
+ "from %s to current locale.",
+ archive_string_conversion_charset_name(zip->sconv));
+ ret = ARCHIVE_WARN;
+ }
+
+ /* Populate some additional entry fields: */
+ archive_entry_set_mode(entry, zip_entry->mode);
+ if (zip_entry->flg & MTIME_IS_SET)
+ archive_entry_set_mtime(entry, zip_entry->mtime,
+ zip_entry->mtime_ns);
+ if (zip_entry->flg & CTIME_IS_SET)
+ archive_entry_set_ctime(entry, zip_entry->ctime,
+ zip_entry->ctime_ns);
+ if (zip_entry->flg & ATIME_IS_SET)
+ archive_entry_set_atime(entry, zip_entry->atime,
+ zip_entry->atime_ns);
+ if (zip_entry->ssIndex != (uint32_t)-1) {
+ zip->entry_bytes_remaining =
+ zip->si.ss.unpackSizes[zip_entry->ssIndex];
+ archive_entry_set_size(entry, zip->entry_bytes_remaining);
+ } else {
+ zip->entry_bytes_remaining = 0;
+ archive_entry_set_size(entry, 0);
+ }
+
+ /* If there's no body, force read_data() to return EOF immediately. */
+ if (zip->entry_bytes_remaining < 1)
+ zip->end_of_entry = 1;
+
+ if ((zip_entry->mode & AE_IFMT) == AE_IFLNK) {
+ unsigned char *symname = NULL;
+ size_t symsize = 0;
+
+ /*
+ * Symbolic-name is recorded as its contents. We have to
+ * read the contents at this time.
+ */
+ while (zip->entry_bytes_remaining > 0) {
+ const void *buff;
+ unsigned char *mem;
+ size_t size;
+ int64_t offset;
+
+ r = archive_read_format_7zip_read_data(a, &buff,
+ &size, &offset);
+ if (r < ARCHIVE_WARN) {
+ free(symname);
+ return (r);
+ }
+ mem = realloc(symname, symsize + size + 1);
+ if (mem == NULL) {
+ free(symname);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Symname");
+ return (ARCHIVE_FATAL);
+ }
+ symname = mem;
+ memcpy(symname+symsize, buff, size);
+ symsize += size;
+ }
+ if (symsize == 0) {
+ /* If there is no symname, handle it as a regular
+ * file. */
+ zip_entry->mode &= ~AE_IFMT;
+ zip_entry->mode |= AE_IFREG;
+ archive_entry_set_mode(entry, zip_entry->mode);
+ } else {
+ symname[symsize] = '\0';
+ archive_entry_copy_symlink(entry,
+ (const char *)symname);
+ }
+ free(symname);
+ archive_entry_set_size(entry, 0);
+ }
+
+ /* Set up a more descriptive format name. */
+ snprintf(zip->format_name, sizeof(zip->format_name), "7-Zip");
+ a->archive.archive_format_name = zip->format_name;
+
+ return (ret);
+}
+
+static int
+archive_read_format_7zip_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ struct _7zip *zip;
+ ssize_t bytes;
+ int ret = ARCHIVE_OK;
+
+ zip = (struct _7zip *)(a->format->data);
+
+ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+ zip->has_encrypted_entries = 0;
+ }
+
+ if (zip->pack_stream_bytes_unconsumed)
+ read_consume(a);
+
+ *offset = zip->entry_offset;
+ *size = 0;
+ *buff = NULL;
+ /*
+ * If we hit end-of-entry last time, clean up and return
+ * ARCHIVE_EOF this time.
+ */
+ if (zip->end_of_entry)
+ return (ARCHIVE_EOF);
+
+ const uint64_t max_read_size = 16 * 1024 * 1024; // Don't try to read more than 16 MB at a time
+ size_t bytes_to_read = max_read_size;
+ if ((uint64_t)bytes_to_read > zip->entry_bytes_remaining) {
+ bytes_to_read = zip->entry_bytes_remaining;
+ }
+ bytes = read_stream(a, buff, bytes_to_read, 0);
+ if (bytes < 0)
+ return ((int)bytes);
+ if (bytes == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated 7-Zip file body");
+ return (ARCHIVE_FATAL);
+ }
+ zip->entry_bytes_remaining -= bytes;
+ if (zip->entry_bytes_remaining == 0)
+ zip->end_of_entry = 1;
+
+ /* Update checksum */
+ if ((zip->entry->flg & CRC32_IS_SET) && bytes)
+ zip->entry_crc32 = crc32(zip->entry_crc32, *buff,
+ (unsigned)bytes);
+
+ /* If we hit the end, swallow any end-of-data marker. */
+ if (zip->end_of_entry) {
+ /* Check computed CRC against file contents. */
+ if ((zip->entry->flg & CRC32_IS_SET) &&
+ zip->si.ss.digests[zip->entry->ssIndex] !=
+ zip->entry_crc32) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "7-Zip bad CRC: 0x%lx should be 0x%lx",
+ (unsigned long)zip->entry_crc32,
+ (unsigned long)zip->si.ss.digests[
+ zip->entry->ssIndex]);
+ ret = ARCHIVE_WARN;
+ }
+ }
+
+ *size = bytes;
+ *offset = zip->entry_offset;
+ zip->entry_offset += bytes;
+
+ return (ret);
+}
+
+static int
+archive_read_format_7zip_read_data_skip(struct archive_read *a)
+{
+ struct _7zip *zip;
+ int64_t bytes_skipped;
+
+ zip = (struct _7zip *)(a->format->data);
+
+ if (zip->pack_stream_bytes_unconsumed)
+ read_consume(a);
+
+ /* If we've already read to end of data, we're done. */
+ if (zip->end_of_entry)
+ return (ARCHIVE_OK);
+
+ /*
+ * If the length is at the beginning, we can skip the
+ * compressed data much more quickly.
+ */
+ bytes_skipped = skip_stream(a, (size_t)zip->entry_bytes_remaining);
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+ zip->entry_bytes_remaining = 0;
+
+ /* This entry is finished and done. */
+ zip->end_of_entry = 1;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_7zip_cleanup(struct archive_read *a)
+{
+ struct _7zip *zip;
+
+ zip = (struct _7zip *)(a->format->data);
+ free_StreamsInfo(&(zip->si));
+ free(zip->entries);
+ free(zip->entry_names);
+ free_decompression(a, zip);
+ free(zip->uncompressed_buffer);
+ free(zip->sub_stream_buff[0]);
+ free(zip->sub_stream_buff[1]);
+ free(zip->sub_stream_buff[2]);
+ free(zip->tmp_stream_buff);
+ free(zip);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+static void
+read_consume(struct archive_read *a)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+
+ if (zip->pack_stream_bytes_unconsumed) {
+ __archive_read_consume(a, zip->pack_stream_bytes_unconsumed);
+ zip->stream_offset += zip->pack_stream_bytes_unconsumed;
+ zip->pack_stream_bytes_unconsumed = 0;
+ }
+}
+
+#ifdef HAVE_LZMA_H
+
+/*
+ * Set an error code and choose an error message for liblzma.
+ */
+static void
+set_error(struct archive_read *a, int ret)
+{
+
+ switch (ret) {
+ case LZMA_STREAM_END: /* Found end of stream. */
+ case LZMA_OK: /* Decompressor made some progress. */
+ break;
+ case LZMA_MEM_ERROR:
+ archive_set_error(&a->archive, ENOMEM,
+ "Lzma library error: Cannot allocate memory");
+ break;
+ case LZMA_MEMLIMIT_ERROR:
+ archive_set_error(&a->archive, ENOMEM,
+ "Lzma library error: Out of memory");
+ break;
+ case LZMA_FORMAT_ERROR:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma library error: format not recognized");
+ break;
+ case LZMA_OPTIONS_ERROR:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma library error: Invalid options");
+ break;
+ case LZMA_DATA_ERROR:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma library error: Corrupted input data");
+ break;
+ case LZMA_BUF_ERROR:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma library error: No progress is possible");
+ break;
+ default:
+ /* Return an error. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma decompression failed: Unknown error");
+ break;
+ }
+}
+
+#endif
+
+static unsigned long
+decode_codec_id(const unsigned char *codecId, size_t id_size)
+{
+ unsigned i;
+ unsigned long id = 0;
+
+ for (i = 0; i < id_size; i++) {
+ id <<= 8;
+ id += codecId[i];
+ }
+ return (id);
+}
+
+static Byte
+ppmd_read(void *p)
+{
+ struct archive_read *a = ((IByteIn*)p)->a;
+ struct _7zip *zip = (struct _7zip *)(a->format->data);
+ Byte b;
+
+ if (zip->ppstream.avail_in <= 0) {
+ /*
+ * Ppmd7_DecodeSymbol might require reading multiple bytes
+ * and we are on boundary;
+ * last resort to read using __archive_read_ahead.
+ */
+ ssize_t bytes_avail = 0;
+ const uint8_t* data = __archive_read_ahead(a,
+ zip->ppstream.stream_in+1, &bytes_avail);
+ if(bytes_avail < zip->ppstream.stream_in+1) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated 7z file data");
+ zip->ppstream.overconsumed = 1;
+ return (0);
+ }
+ zip->ppstream.next_in++;
+ b = data[zip->ppstream.stream_in];
+ } else {
+ b = *zip->ppstream.next_in++;
+ }
+ zip->ppstream.avail_in--;
+ zip->ppstream.total_in++;
+ zip->ppstream.stream_in++;
+ return (b);
+}
+
+static int
+init_decompression(struct archive_read *a, struct _7zip *zip,
+ const struct _7z_coder *coder1, const struct _7z_coder *coder2)
+{
+ int r;
+
+ zip->codec = coder1->codec;
+ zip->codec2 = -1;
+
+ switch (zip->codec) {
+ case _7Z_COPY:
+ case _7Z_BZ2:
+ case _7Z_DEFLATE:
+ case _7Z_ZSTD:
+ case _7Z_PPMD:
+ if (coder2 != NULL) {
+ if (coder2->codec != _7Z_X86 &&
+ coder2->codec != _7Z_X86_BCJ2 &&
+ coder2->codec != _7Z_ARM &&
+ coder2->codec != _7Z_ARM64) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Unsupported filter %lx for %lx",
+ coder2->codec, coder1->codec);
+ return (ARCHIVE_FAILED);
+ }
+ zip->codec2 = coder2->codec;
+ zip->bcj_state = 0;
+ if (coder2->codec == _7Z_X86)
+ x86_Init(zip);
+ else if (coder2->codec == _7Z_ARM)
+ arm_Init(zip);
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (zip->codec) {
+ case _7Z_COPY:
+ break;
+
+ case _7Z_LZMA: case _7Z_LZMA2:
+#ifdef HAVE_LZMA_H
+#if LZMA_VERSION_MAJOR >= 5
+/* Effectively disable the limiter. */
+#define LZMA_MEMLIMIT UINT64_MAX
+#else
+/* NOTE: This needs to check memory size which running system has. */
+#define LZMA_MEMLIMIT (1U << 30)
+#endif
+ {
+ lzma_options_delta delta_opt;
+ lzma_filter filters[LZMA_FILTERS_MAX], *ff;
+ int fi = 0;
+
+ if (zip->lzstream_valid) {
+ lzma_end(&(zip->lzstream));
+ zip->lzstream_valid = 0;
+ }
+
+ /*
+ * NOTE: liblzma incompletely handle the BCJ+LZMA compressed
+ * data made by 7-Zip because 7-Zip does not add End-Of-
+ * Payload Marker(EOPM) at the end of LZMA compressed data,
+ * and so liblzma cannot know the end of the compressed data
+ * without EOPM. So consequently liblzma will not return last
+ * three or four bytes of uncompressed data because
+ * LZMA_FILTER_X86 filter does not handle input data if its
+ * data size is less than five bytes. If liblzma detect EOPM
+ * or know the uncompressed data size, liblzma will flush out
+ * the remaining that three or four bytes of uncompressed
+ * data. That is why we have to use our converting program
+ * for BCJ+LZMA. If we were able to tell the uncompressed
+ * size to liblzma when using lzma_raw_decoder() liblzma
+ * could correctly deal with BCJ+LZMA. But unfortunately
+ * there is no way to do that.
+ * Discussion about this can be found at XZ Utils forum.
+ */
+ if (coder2 != NULL) {
+ zip->codec2 = coder2->codec;
+
+ filters[fi].options = NULL;
+ switch (zip->codec2) {
+ case _7Z_X86:
+ if (zip->codec == _7Z_LZMA2) {
+ filters[fi].id = LZMA_FILTER_X86;
+ fi++;
+ } else
+ /* Use our filter. */
+ x86_Init(zip);
+ break;
+ case _7Z_X86_BCJ2:
+ /* Use our filter. */
+ zip->bcj_state = 0;
+ break;
+ case _7Z_DELTA:
+ if (coder2->propertiesSize != 1) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid Delta parameter");
+ return (ARCHIVE_FAILED);
+ }
+ filters[fi].id = LZMA_FILTER_DELTA;
+ memset(&delta_opt, 0, sizeof(delta_opt));
+ delta_opt.type = LZMA_DELTA_TYPE_BYTE;
+ delta_opt.dist =
+ (uint32_t)coder2->properties[0] + 1;
+ filters[fi].options = &delta_opt;
+ fi++;
+ break;
+ /* Following filters have not been tested yet. */
+ case _7Z_POWERPC:
+ filters[fi].id = LZMA_FILTER_POWERPC;
+ fi++;
+ break;
+ case _7Z_IA64:
+ filters[fi].id = LZMA_FILTER_IA64;
+ fi++;
+ break;
+ case _7Z_ARM:
+ filters[fi].id = LZMA_FILTER_ARM;
+ fi++;
+ break;
+ case _7Z_ARMTHUMB:
+ filters[fi].id = LZMA_FILTER_ARMTHUMB;
+ fi++;
+ break;
+#ifdef LZMA_FILTER_ARM64
+ case _7Z_ARM64:
+ filters[fi].id = LZMA_FILTER_ARM64;
+ fi++;
+ break;
+#endif
+ case _7Z_SPARC:
+ filters[fi].id = LZMA_FILTER_SPARC;
+ fi++;
+ break;
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Unexpected codec ID: %lX", zip->codec2);
+ return (ARCHIVE_FAILED);
+ }
+ }
+
+ if (zip->codec == _7Z_LZMA2)
+ filters[fi].id = LZMA_FILTER_LZMA2;
+ else
+ filters[fi].id = LZMA_FILTER_LZMA1;
+ filters[fi].options = NULL;
+ ff = &filters[fi];
+ r = lzma_properties_decode(&filters[fi], NULL,
+ coder1->properties, (size_t)coder1->propertiesSize);
+ if (r != LZMA_OK) {
+ set_error(a, r);
+ return (ARCHIVE_FAILED);
+ }
+ fi++;
+
+ filters[fi].id = LZMA_VLI_UNKNOWN;
+ filters[fi].options = NULL;
+ r = lzma_raw_decoder(&(zip->lzstream), filters);
+ free(ff->options);
+ if (r != LZMA_OK) {
+ set_error(a, r);
+ return (ARCHIVE_FAILED);
+ }
+ zip->lzstream_valid = 1;
+ zip->lzstream.total_in = 0;
+ zip->lzstream.total_out = 0;
+ break;
+ }
+#else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "LZMA codec is unsupported");
+ return (ARCHIVE_FAILED);
+#endif
+ case _7Z_BZ2:
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ if (zip->bzstream_valid) {
+ BZ2_bzDecompressEnd(&(zip->bzstream));
+ zip->bzstream_valid = 0;
+ }
+ r = BZ2_bzDecompressInit(&(zip->bzstream), 0, 0);
+ if (r == BZ_MEM_ERROR)
+ r = BZ2_bzDecompressInit(&(zip->bzstream), 0, 1);
+ if (r != BZ_OK) {
+ int err = ARCHIVE_ERRNO_MISC;
+ const char *detail = NULL;
+ switch (r) {
+ case BZ_PARAM_ERROR:
+ detail = "invalid setup parameter";
+ break;
+ case BZ_MEM_ERROR:
+ err = ENOMEM;
+ detail = "out of memory";
+ break;
+ case BZ_CONFIG_ERROR:
+ detail = "mis-compiled library";
+ break;
+ }
+ archive_set_error(&a->archive, err,
+ "Internal error initializing decompressor: %s",
+ detail != NULL ? detail : "??");
+ zip->bzstream_valid = 0;
+ return (ARCHIVE_FAILED);
+ }
+ zip->bzstream_valid = 1;
+ zip->bzstream.total_in_lo32 = 0;
+ zip->bzstream.total_in_hi32 = 0;
+ zip->bzstream.total_out_lo32 = 0;
+ zip->bzstream.total_out_hi32 = 0;
+ break;
+#else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "BZ2 codec is unsupported");
+ return (ARCHIVE_FAILED);
+#endif
+ case _7Z_ZSTD:
+ {
+#if defined(HAVE_ZSTD_H)
+ if (zip->zstdstream_valid) {
+ ZSTD_freeDStream(zip->zstd_dstream);
+ zip->zstdstream_valid = 0;
+ }
+ zip->zstd_dstream = ZSTD_createDStream();
+ zip->zstdstream_valid = 1;
+ break;
+#else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "ZSTD codec is unsupported");
+ return (ARCHIVE_FAILED);
+#endif
+ }
+ case _7Z_DEFLATE:
+#ifdef HAVE_ZLIB_H
+ if (zip->stream_valid)
+ r = inflateReset(&(zip->stream));
+ else
+ r = inflateInit2(&(zip->stream),
+ -15 /* Don't check for zlib header */);
+ if (r != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Couldn't initialize zlib stream.");
+ return (ARCHIVE_FAILED);
+ }
+ zip->stream_valid = 1;
+ zip->stream.total_in = 0;
+ zip->stream.total_out = 0;
+ break;
+#else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "DEFLATE codec is unsupported");
+ return (ARCHIVE_FAILED);
+#endif
+ case _7Z_PPMD:
+ {
+ unsigned order;
+ uint32_t msize;
+
+ if (zip->ppmd7_valid) {
+ __archive_ppmd7_functions.Ppmd7_Free(
+ &zip->ppmd7_context);
+ zip->ppmd7_valid = 0;
+ }
+
+ if (coder1->propertiesSize < 5) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Malformed PPMd parameter");
+ return (ARCHIVE_FAILED);
+ }
+ order = coder1->properties[0];
+ msize = archive_le32dec(&(coder1->properties[1]));
+ if (order < PPMD7_MIN_ORDER || order > PPMD7_MAX_ORDER ||
+ msize < PPMD7_MIN_MEM_SIZE || msize > PPMD7_MAX_MEM_SIZE) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Malformed PPMd parameter");
+ return (ARCHIVE_FAILED);
+ }
+ __archive_ppmd7_functions.Ppmd7_Construct(&zip->ppmd7_context);
+ r = __archive_ppmd7_functions.Ppmd7_Alloc(
+ &zip->ppmd7_context, msize);
+ if (r == 0) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Coludn't allocate memory for PPMd");
+ return (ARCHIVE_FATAL);
+ }
+ __archive_ppmd7_functions.Ppmd7_Init(
+ &zip->ppmd7_context, order);
+ __archive_ppmd7_functions.Ppmd7z_RangeDec_CreateVTable(
+ &zip->range_dec);
+ zip->ppmd7_valid = 1;
+ zip->ppmd7_stat = 0;
+ zip->ppstream.overconsumed = 0;
+ zip->ppstream.total_in = 0;
+ zip->ppstream.total_out = 0;
+ break;
+ }
+ case _7Z_X86:
+ case _7Z_X86_BCJ2:
+ case _7Z_POWERPC:
+ case _7Z_IA64:
+ case _7Z_ARM:
+ case _7Z_ARMTHUMB:
+ case _7Z_SPARC:
+ case _7Z_DELTA:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Unexpected codec ID: %lX", zip->codec);
+ return (ARCHIVE_FAILED);
+ case _7Z_CRYPTO_MAIN_ZIP:
+ case _7Z_CRYPTO_RAR_29:
+ case _7Z_CRYPTO_AES_256_SHA_256:
+ if (a->entry) {
+ archive_entry_set_is_metadata_encrypted(a->entry, 1);
+ archive_entry_set_is_data_encrypted(a->entry, 1);
+ zip->has_encrypted_entries = 1;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Crypto codec not supported yet (ID: 0x%lX)", zip->codec);
+ return (ARCHIVE_FAILED);
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Unknown codec ID: %lX", zip->codec);
+ return (ARCHIVE_FAILED);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int
+decompress(struct archive_read *a, struct _7zip *zip,
+ void *buff, size_t *outbytes, const void *b, size_t *used)
+{
+ const uint8_t *t_next_in;
+ uint8_t *t_next_out;
+ size_t o_avail_in, o_avail_out;
+ size_t t_avail_in, t_avail_out;
+ uint8_t *bcj2_next_out;
+ size_t bcj2_avail_out;
+ int r, ret = ARCHIVE_OK;
+
+ t_avail_in = o_avail_in = *used;
+ t_avail_out = o_avail_out = *outbytes;
+ t_next_in = b;
+ t_next_out = buff;
+
+ if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) {
+ int i;
+
+ /* Do not copy out the BCJ remaining bytes when the output
+ * buffer size is less than five bytes. */
+ if (o_avail_in != 0 && t_avail_out < 5 && zip->odd_bcj_size) {
+ *used = 0;
+ *outbytes = 0;
+ return (ret);
+ }
+ for (i = 0; zip->odd_bcj_size > 0 && t_avail_out; i++) {
+ *t_next_out++ = zip->odd_bcj[i];
+ t_avail_out--;
+ zip->odd_bcj_size--;
+ }
+ if (o_avail_in == 0 || t_avail_out == 0) {
+ *used = o_avail_in - t_avail_in;
+ *outbytes = o_avail_out - t_avail_out;
+ if (o_avail_in == 0)
+ ret = ARCHIVE_EOF;
+ return (ret);
+ }
+ }
+
+ bcj2_next_out = t_next_out;
+ bcj2_avail_out = t_avail_out;
+ if (zip->codec2 == _7Z_X86_BCJ2) {
+ /*
+ * Decord a remaining decompressed main stream for BCJ2.
+ */
+ if (zip->tmp_stream_bytes_remaining) {
+ ssize_t bytes;
+ size_t remaining = zip->tmp_stream_bytes_remaining;
+ bytes = Bcj2_Decode(zip, t_next_out, t_avail_out);
+ if (bytes < 0) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "BCJ2 conversion Failed");
+ return (ARCHIVE_FAILED);
+ }
+ zip->main_stream_bytes_remaining -=
+ remaining - zip->tmp_stream_bytes_remaining;
+ t_avail_out -= bytes;
+ if (o_avail_in == 0 || t_avail_out == 0) {
+ *used = 0;
+ *outbytes = o_avail_out - t_avail_out;
+ if (o_avail_in == 0 &&
+ zip->tmp_stream_bytes_remaining)
+ ret = ARCHIVE_EOF;
+ return (ret);
+ }
+ t_next_out += bytes;
+ bcj2_next_out = t_next_out;
+ bcj2_avail_out = t_avail_out;
+ }
+ t_next_out = zip->tmp_stream_buff;
+ t_avail_out = zip->tmp_stream_buff_size;
+ }
+
+ switch (zip->codec) {
+ case _7Z_COPY:
+ {
+ size_t bytes =
+ (t_avail_in > t_avail_out)?t_avail_out:t_avail_in;
+
+ memcpy(t_next_out, t_next_in, bytes);
+ t_avail_in -= bytes;
+ t_avail_out -= bytes;
+ if (o_avail_in == 0)
+ ret = ARCHIVE_EOF;
+ break;
+ }
+#ifdef HAVE_LZMA_H
+ case _7Z_LZMA: case _7Z_LZMA2:
+ zip->lzstream.next_in = t_next_in;
+ zip->lzstream.avail_in = t_avail_in;
+ zip->lzstream.next_out = t_next_out;
+ zip->lzstream.avail_out = t_avail_out;
+
+ r = lzma_code(&(zip->lzstream), LZMA_RUN);
+ switch (r) {
+ case LZMA_STREAM_END: /* Found end of stream. */
+ lzma_end(&(zip->lzstream));
+ zip->lzstream_valid = 0;
+ ret = ARCHIVE_EOF;
+ break;
+ case LZMA_OK: /* Decompressor made some progress. */
+ break;
+ default:
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Decompression failed(%d)",
+ r);
+ return (ARCHIVE_FAILED);
+ }
+ t_avail_in = zip->lzstream.avail_in;
+ t_avail_out = zip->lzstream.avail_out;
+ break;
+#endif
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ case _7Z_BZ2:
+ zip->bzstream.next_in = (char *)(uintptr_t)t_next_in;
+ zip->bzstream.avail_in = (uint32_t)t_avail_in;
+ zip->bzstream.next_out = (char *)(uintptr_t)t_next_out;
+ zip->bzstream.avail_out = (uint32_t)t_avail_out;
+ r = BZ2_bzDecompress(&(zip->bzstream));
+ switch (r) {
+ case BZ_STREAM_END: /* Found end of stream. */
+ switch (BZ2_bzDecompressEnd(&(zip->bzstream))) {
+ case BZ_OK:
+ break;
+ default:
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up decompressor");
+ return (ARCHIVE_FAILED);
+ }
+ zip->bzstream_valid = 0;
+ ret = ARCHIVE_EOF;
+ break;
+ case BZ_OK: /* Decompressor made some progress. */
+ break;
+ default:
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "bzip decompression failed");
+ return (ARCHIVE_FAILED);
+ }
+ t_avail_in = zip->bzstream.avail_in;
+ t_avail_out = zip->bzstream.avail_out;
+ break;
+#endif
+#ifdef HAVE_ZLIB_H
+ case _7Z_DEFLATE:
+ zip->stream.next_in = (Bytef *)(uintptr_t)t_next_in;
+ zip->stream.avail_in = (uInt)t_avail_in;
+ zip->stream.next_out = t_next_out;
+ zip->stream.avail_out = (uInt)t_avail_out;
+ r = inflate(&(zip->stream), 0);
+ switch (r) {
+ case Z_STREAM_END: /* Found end of stream. */
+ ret = ARCHIVE_EOF;
+ break;
+ case Z_OK: /* Decompressor made some progress.*/
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "File decompression failed (%d)", r);
+ return (ARCHIVE_FAILED);
+ }
+ t_avail_in = zip->stream.avail_in;
+ t_avail_out = zip->stream.avail_out;
+ break;
+#endif
+#ifdef HAVE_ZSTD_H
+ case _7Z_ZSTD:
+ {
+ ZSTD_inBuffer input = { t_next_in, t_avail_in, 0 }; // src, size, pos
+ ZSTD_outBuffer output = { t_next_out, t_avail_out, 0 }; // dst, size, pos
+
+ size_t const zret = ZSTD_decompressStream(zip->zstd_dstream, &output, &input);
+ if (ZSTD_isError(zret)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Zstd decompression failed: %s", ZSTD_getErrorName(zret));
+ return ARCHIVE_FAILED;
+ }
+ t_avail_in -= input.pos;
+ t_avail_out -= output.pos;
+ break;
+ }
+#endif
+ case _7Z_PPMD:
+ {
+ uint64_t flush_bytes;
+
+ if (!zip->ppmd7_valid || zip->ppmd7_stat < 0 ||
+ t_avail_out <= 0) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Decompression internal error");
+ return (ARCHIVE_FAILED);
+ }
+ zip->ppstream.next_in = t_next_in;
+ zip->ppstream.avail_in = t_avail_in;
+ zip->ppstream.stream_in = 0;
+ zip->ppstream.next_out = t_next_out;
+ zip->ppstream.avail_out = t_avail_out;
+ if (zip->ppmd7_stat == 0) {
+ zip->bytein.a = a;
+ zip->bytein.Read = &ppmd_read;
+ zip->range_dec.Stream = &zip->bytein;
+ r = __archive_ppmd7_functions.Ppmd7z_RangeDec_Init(
+ &(zip->range_dec));
+ if (r == 0) {
+ zip->ppmd7_stat = -1;
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to initialize PPMd range decoder");
+ return (ARCHIVE_FAILED);
+ }
+ if (zip->ppstream.overconsumed) {
+ zip->ppmd7_stat = -1;
+ return (ARCHIVE_FAILED);
+ }
+ zip->ppmd7_stat = 1;
+ }
+
+ if (t_avail_in == 0)
+ /* XXX Flush out remaining decoded data XXX */
+ flush_bytes = zip->folder_outbytes_remaining;
+ else
+ flush_bytes = 0;
+
+ do {
+ int sym;
+
+ sym = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+ &(zip->ppmd7_context), &(zip->range_dec.p));
+ if (sym < 0) {
+ zip->ppmd7_stat = -1;
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Failed to decode PPMd");
+ return (ARCHIVE_FAILED);
+ }
+ if (zip->ppstream.overconsumed) {
+ zip->ppmd7_stat = -1;
+ return (ARCHIVE_FAILED);
+ }
+ *zip->ppstream.next_out++ = (unsigned char)sym;
+ zip->ppstream.avail_out--;
+ zip->ppstream.total_out++;
+ if (flush_bytes)
+ flush_bytes--;
+ } while (zip->ppstream.avail_out &&
+ (zip->ppstream.avail_in || flush_bytes));
+
+ t_avail_in = (size_t)zip->ppstream.avail_in;
+ t_avail_out = (size_t)zip->ppstream.avail_out;
+ break;
+ }
+ default:
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "Decompression internal error");
+ return (ARCHIVE_FAILED);
+ }
+ if (ret != ARCHIVE_OK && ret != ARCHIVE_EOF)
+ return (ret);
+
+ *used = o_avail_in - t_avail_in;
+ *outbytes = o_avail_out - t_avail_out;
+
+ /*
+ * Decord BCJ.
+ */
+ if (zip->codec != _7Z_LZMA2) {
+ if (zip->codec2 == _7Z_X86) {
+ size_t l = x86_Convert(zip, buff, *outbytes);
+
+ zip->odd_bcj_size = *outbytes - l;
+ if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 &&
+ o_avail_in && ret != ARCHIVE_EOF) {
+ memcpy(zip->odd_bcj, ((unsigned char *)buff) + l,
+ zip->odd_bcj_size);
+ *outbytes = l;
+ } else
+ zip->odd_bcj_size = 0;
+ } else if (zip->codec2 == _7Z_ARM) {
+ *outbytes = arm_Convert(zip, buff, *outbytes);
+ } else if (zip->codec2 == _7Z_ARM64) {
+ *outbytes = arm64_Convert(zip, buff, *outbytes);
+ }
+ }
+
+ /*
+ * Decord BCJ2 with a decompressed main stream.
+ */
+ if (zip->codec2 == _7Z_X86_BCJ2) {
+ ssize_t bytes;
+
+ zip->tmp_stream_bytes_avail =
+ zip->tmp_stream_buff_size - t_avail_out;
+ if (zip->tmp_stream_bytes_avail >
+ zip->main_stream_bytes_remaining)
+ zip->tmp_stream_bytes_avail =
+ zip->main_stream_bytes_remaining;
+ zip->tmp_stream_bytes_remaining = zip->tmp_stream_bytes_avail;
+ bytes = Bcj2_Decode(zip, bcj2_next_out, bcj2_avail_out);
+ if (bytes < 0) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC, "BCJ2 conversion Failed");
+ return (ARCHIVE_FAILED);
+ }
+ zip->main_stream_bytes_remaining -=
+ zip->tmp_stream_bytes_avail
+ - zip->tmp_stream_bytes_remaining;
+ bcj2_avail_out -= bytes;
+ *outbytes = o_avail_out - bcj2_avail_out;
+ }
+
+ return (ret);
+}
+
+static int
+free_decompression(struct archive_read *a, struct _7zip *zip)
+{
+ int r = ARCHIVE_OK;
+
+#if !defined(HAVE_ZLIB_H) &&\
+ !(defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR))
+ (void)a;/* UNUSED */
+#endif
+#ifdef HAVE_LZMA_H
+ if (zip->lzstream_valid)
+ lzma_end(&(zip->lzstream));
+#endif
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ if (zip->bzstream_valid) {
+ if (BZ2_bzDecompressEnd(&(zip->bzstream)) != BZ_OK) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up bzip2 decompressor");
+ r = ARCHIVE_FATAL;
+ }
+ zip->bzstream_valid = 0;
+ }
+#endif
+#ifdef HAVE_ZLIB_H
+ if (zip->stream_valid) {
+ if (inflateEnd(&(zip->stream)) != Z_OK) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up zlib decompressor");
+ r = ARCHIVE_FATAL;
+ }
+ zip->stream_valid = 0;
+ }
+#endif
+ if (zip->ppmd7_valid) {
+ __archive_ppmd7_functions.Ppmd7_Free(
+ &zip->ppmd7_context);
+ zip->ppmd7_valid = 0;
+ }
+ return (r);
+}
+
+static int
+parse_7zip_uint64(struct archive_read *a, uint64_t *val)
+{
+ const unsigned char *p;
+ unsigned char avail, mask;
+ int i;
+
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ avail = *p;
+ mask = 0x80;
+ *val = 0;
+ for (i = 0; i < 8; i++) {
+ if (avail & mask) {
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ *val |= ((uint64_t)*p) << (8 * i);
+ mask >>= 1;
+ continue;
+ }
+ *val += ((uint64_t)(avail & (mask -1))) << (8 * i);
+ break;
+ }
+ return (0);
+}
+
+static int
+read_Bools(struct archive_read *a, unsigned char *data, size_t num)
+{
+ const unsigned char *p;
+ unsigned i, mask = 0, avail = 0;
+
+ for (i = 0; i < num; i++) {
+ if (mask == 0) {
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ avail = *p;
+ mask = 0x80;
+ }
+ data[i] = (avail & mask)?1:0;
+ mask >>= 1;
+ }
+ return (0);
+}
+
+static void
+free_Digest(struct _7z_digests *d)
+{
+ free(d->defineds);
+ free(d->digests);
+}
+
+static int
+read_Digests(struct archive_read *a, struct _7z_digests *d, size_t num)
+{
+ const unsigned char *p;
+ unsigned i;
+
+ if (num == 0)
+ return (-1);
+ memset(d, 0, sizeof(*d));
+
+ d->defineds = malloc(num);
+ if (d->defineds == NULL)
+ return (-1);
+ /*
+ * Read Bools.
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ if (*p == 0) {
+ if (read_Bools(a, d->defineds, num) < 0)
+ return (-1);
+ } else
+ /* All are defined */
+ memset(d->defineds, 1, num);
+
+ d->digests = calloc(num, sizeof(*d->digests));
+ if (d->digests == NULL)
+ return (-1);
+ for (i = 0; i < num; i++) {
+ if (d->defineds[i]) {
+ if ((p = header_bytes(a, 4)) == NULL)
+ return (-1);
+ d->digests[i] = archive_le32dec(p);
+ }
+ }
+
+ return (0);
+}
+
+static void
+free_PackInfo(struct _7z_pack_info *pi)
+{
+ free(pi->sizes);
+ free(pi->positions);
+ free_Digest(&(pi->digest));
+}
+
+static int
+read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi)
+{
+ const unsigned char *p;
+ unsigned i;
+
+ memset(pi, 0, sizeof(*pi));
+
+ /*
+ * Read PackPos.
+ */
+ if (parse_7zip_uint64(a, &(pi->pos)) < 0)
+ return (-1);
+
+ /*
+ * Read NumPackStreams.
+ */
+ if (parse_7zip_uint64(a, &(pi->numPackStreams)) < 0)
+ return (-1);
+ if (pi->numPackStreams == 0)
+ return (-1);
+ if (UMAX_ENTRY < pi->numPackStreams)
+ return (-1);
+
+ /*
+ * Read PackSizes[num]
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ if (*p == kEnd)
+ /* PackSizes[num] are not present. */
+ return (0);
+ if (*p != kSize)
+ return (-1);
+ pi->sizes = calloc((size_t)pi->numPackStreams, sizeof(uint64_t));
+ pi->positions = calloc((size_t)pi->numPackStreams, sizeof(uint64_t));
+ if (pi->sizes == NULL || pi->positions == NULL)
+ return (-1);
+
+ for (i = 0; i < pi->numPackStreams; i++) {
+ if (parse_7zip_uint64(a, &(pi->sizes[i])) < 0)
+ return (-1);
+ }
+
+ /*
+ * Read PackStreamDigests[num]
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ if (*p == kEnd) {
+ /* PackStreamDigests[num] are not present. */
+ pi->digest.defineds =
+ calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.defineds));
+ pi->digest.digests =
+ calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.digests));
+ if (pi->digest.defineds == NULL || pi->digest.digests == NULL)
+ return (-1);
+ return (0);
+ }
+
+ if (*p != kCRC)
+ return (-1);
+
+ if (read_Digests(a, &(pi->digest), (size_t)pi->numPackStreams) < 0)
+ return (-1);
+
+ /*
+ * Must be marked by kEnd.
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ if (*p != kEnd)
+ return (-1);
+ return (0);
+}
+
+static void
+free_Folder(struct _7z_folder *f)
+{
+ unsigned i;
+
+ if (f->coders) {
+ for (i = 0; i< f->numCoders; i++) {
+ free(f->coders[i].properties);
+ }
+ free(f->coders);
+ }
+ free(f->bindPairs);
+ free(f->packedStreams);
+ free(f->unPackSize);
+}
+
+static int
+read_Folder(struct archive_read *a, struct _7z_folder *f)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ const unsigned char *p;
+ uint64_t numInStreamsTotal = 0;
+ uint64_t numOutStreamsTotal = 0;
+ unsigned i;
+
+ memset(f, 0, sizeof(*f));
+
+ /*
+ * Read NumCoders.
+ */
+ if (parse_7zip_uint64(a, &(f->numCoders)) < 0)
+ return (-1);
+ if (f->numCoders > 4)
+ /* Too many coders. */
+ return (-1);
+
+ f->coders = calloc((size_t)f->numCoders, sizeof(*f->coders));
+ if (f->coders == NULL)
+ return (-1);
+ for (i = 0; i< f->numCoders; i++) {
+ size_t codec_size;
+ int simple, attr;
+
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ /*
+ * 0:3 CodecIdSize
+ * 4: 0 - IsSimple
+ * 1 - Is not Simple
+ * 5: 0 - No Attributes
+ * 1 - There are Attributes;
+ * 7: Must be zero.
+ */
+ codec_size = *p & 0xf;
+ simple = (*p & 0x10)?0:1;
+ attr = *p & 0x20;
+ if (*p & 0x80)
+ return (-1);/* Not supported. */
+
+ /*
+ * Read Decompression Method IDs.
+ */
+ if ((p = header_bytes(a, codec_size)) == NULL)
+ return (-1);
+
+ f->coders[i].codec = decode_codec_id(p, codec_size);
+
+ if (simple) {
+ f->coders[i].numInStreams = 1;
+ f->coders[i].numOutStreams = 1;
+ } else {
+ if (parse_7zip_uint64(
+ a, &(f->coders[i].numInStreams)) < 0)
+ return (-1);
+ if (UMAX_ENTRY < f->coders[i].numInStreams)
+ return (-1);
+ if (parse_7zip_uint64(
+ a, &(f->coders[i].numOutStreams)) < 0)
+ return (-1);
+ if (UMAX_ENTRY < f->coders[i].numOutStreams)
+ return (-1);
+ }
+
+ if (attr) {
+ if (parse_7zip_uint64(
+ a, &(f->coders[i].propertiesSize)) < 0)
+ return (-1);
+ if ((p = header_bytes(
+ a, (size_t)f->coders[i].propertiesSize)) == NULL)
+ return (-1);
+ f->coders[i].properties =
+ malloc((size_t)f->coders[i].propertiesSize);
+ if (f->coders[i].properties == NULL)
+ return (-1);
+ memcpy(f->coders[i].properties, p,
+ (size_t)f->coders[i].propertiesSize);
+ }
+
+ numInStreamsTotal += f->coders[i].numInStreams;
+ numOutStreamsTotal += f->coders[i].numOutStreams;
+ }
+
+ if (numOutStreamsTotal == 0 ||
+ numInStreamsTotal < numOutStreamsTotal-1)
+ return (-1);
+
+ f->numBindPairs = numOutStreamsTotal - 1;
+ if (zip->header_bytes_remaining < f->numBindPairs)
+ return (-1);
+ if (f->numBindPairs > 0) {
+ f->bindPairs =
+ calloc((size_t)f->numBindPairs, sizeof(*f->bindPairs));
+ if (f->bindPairs == NULL)
+ return (-1);
+ } else
+ f->bindPairs = NULL;
+ for (i = 0; i < f->numBindPairs; i++) {
+ if (parse_7zip_uint64(a, &(f->bindPairs[i].inIndex)) < 0)
+ return (-1);
+ if (UMAX_ENTRY < f->bindPairs[i].inIndex)
+ return (-1);
+ if (parse_7zip_uint64(a, &(f->bindPairs[i].outIndex)) < 0)
+ return (-1);
+ if (UMAX_ENTRY < f->bindPairs[i].outIndex)
+ return (-1);
+ }
+
+ f->numPackedStreams = numInStreamsTotal - f->numBindPairs;
+ f->packedStreams =
+ calloc((size_t)f->numPackedStreams, sizeof(*f->packedStreams));
+ if (f->packedStreams == NULL)
+ return (-1);
+ if (f->numPackedStreams == 1) {
+ for (i = 0; i < numInStreamsTotal; i++) {
+ unsigned j;
+ for (j = 0; j < f->numBindPairs; j++) {
+ if (f->bindPairs[j].inIndex == i)
+ break;
+ }
+ if (j == f->numBindPairs)
+ break;
+ }
+ if (i == numInStreamsTotal)
+ return (-1);
+ f->packedStreams[0] = i;
+ } else {
+ for (i = 0; i < f->numPackedStreams; i++) {
+ if (parse_7zip_uint64(a, &(f->packedStreams[i])) < 0)
+ return (-1);
+ if (UMAX_ENTRY < f->packedStreams[i])
+ return (-1);
+ }
+ }
+ f->numInStreams = numInStreamsTotal;
+ f->numOutStreams = numOutStreamsTotal;
+
+ return (0);
+}
+
+static void
+free_CodersInfo(struct _7z_coders_info *ci)
+{
+ unsigned i;
+
+ if (ci->folders) {
+ for (i = 0; i < ci->numFolders; i++)
+ free_Folder(&(ci->folders[i]));
+ free(ci->folders);
+ }
+}
+
+static int
+read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci)
+{
+ const unsigned char *p;
+ struct _7z_digests digest;
+ unsigned i;
+
+ memset(ci, 0, sizeof(*ci));
+ memset(&digest, 0, sizeof(digest));
+
+ if ((p = header_bytes(a, 1)) == NULL)
+ goto failed;
+ if (*p != kFolder)
+ goto failed;
+
+ /*
+ * Read NumFolders.
+ */
+ if (parse_7zip_uint64(a, &(ci->numFolders)) < 0)
+ goto failed;
+ if (UMAX_ENTRY < ci->numFolders)
+ return (-1);
+
+ /*
+ * Read External.
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ goto failed;
+ switch (*p) {
+ case 0:
+ ci->folders =
+ calloc((size_t)ci->numFolders, sizeof(*ci->folders));
+ if (ci->folders == NULL)
+ return (-1);
+ for (i = 0; i < ci->numFolders; i++) {
+ if (read_Folder(a, &(ci->folders[i])) < 0)
+ goto failed;
+ }
+ break;
+ case 1:
+ if (parse_7zip_uint64(a, &(ci->dataStreamIndex)) < 0)
+ return (-1);
+ if (UMAX_ENTRY < ci->dataStreamIndex)
+ return (-1);
+ if (ci->numFolders > 0) {
+ archive_set_error(&a->archive, -1,
+ "Malformed 7-Zip archive");
+ goto failed;
+ }
+ break;
+ default:
+ archive_set_error(&a->archive, -1,
+ "Malformed 7-Zip archive");
+ goto failed;
+ }
+
+ if ((p = header_bytes(a, 1)) == NULL)
+ goto failed;
+ if (*p != kCodersUnPackSize)
+ goto failed;
+
+ for (i = 0; i < ci->numFolders; i++) {
+ struct _7z_folder *folder = &(ci->folders[i]);
+ unsigned j;
+
+ folder->unPackSize =
+ calloc((size_t)folder->numOutStreams, sizeof(*folder->unPackSize));
+ if (folder->unPackSize == NULL)
+ goto failed;
+ for (j = 0; j < folder->numOutStreams; j++) {
+ if (parse_7zip_uint64(a, &(folder->unPackSize[j])) < 0)
+ goto failed;
+ }
+ }
+
+ /*
+ * Read CRCs.
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ goto failed;
+ if (*p == kEnd)
+ return (0);
+ if (*p != kCRC)
+ goto failed;
+ if (read_Digests(a, &digest, (size_t)ci->numFolders) < 0)
+ goto failed;
+ for (i = 0; i < ci->numFolders; i++) {
+ ci->folders[i].digest_defined = digest.defineds[i];
+ ci->folders[i].digest = digest.digests[i];
+ }
+
+ /*
+ * Must be kEnd.
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ goto failed;
+ if (*p != kEnd)
+ goto failed;
+ free_Digest(&digest);
+ return (0);
+failed:
+ free_Digest(&digest);
+ return (-1);
+}
+
+static uint64_t
+folder_uncompressed_size(struct _7z_folder *f)
+{
+ int n = (int)f->numOutStreams;
+ unsigned pairs = (unsigned)f->numBindPairs;
+
+ while (--n >= 0) {
+ unsigned i;
+ for (i = 0; i < pairs; i++) {
+ if (f->bindPairs[i].outIndex == (uint64_t)n)
+ break;
+ }
+ if (i >= pairs)
+ return (f->unPackSize[n]);
+ }
+ return (0);
+}
+
+static void
+free_SubStreamsInfo(struct _7z_substream_info *ss)
+{
+ free(ss->unpackSizes);
+ free(ss->digestsDefined);
+ free(ss->digests);
+}
+
+static int
+read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss,
+ struct _7z_folder *f, size_t numFolders)
+{
+ const unsigned char *p;
+ uint64_t *usizes;
+ size_t unpack_streams;
+ int type;
+ unsigned i;
+ uint32_t numDigests;
+
+ memset(ss, 0, sizeof(*ss));
+
+ for (i = 0; i < numFolders; i++)
+ f[i].numUnpackStreams = 1;
+
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ type = *p;
+
+ if (type == kNumUnPackStream) {
+ unpack_streams = 0;
+ for (i = 0; i < numFolders; i++) {
+ if (parse_7zip_uint64(a, &(f[i].numUnpackStreams)) < 0)
+ return (-1);
+ if (UMAX_ENTRY < f[i].numUnpackStreams)
+ return (-1);
+ if (unpack_streams > SIZE_MAX - UMAX_ENTRY) {
+ return (-1);
+ }
+ unpack_streams += (size_t)f[i].numUnpackStreams;
+ }
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ type = *p;
+ } else
+ unpack_streams = numFolders;
+
+ ss->unpack_streams = unpack_streams;
+ if (unpack_streams) {
+ ss->unpackSizes = calloc(unpack_streams,
+ sizeof(*ss->unpackSizes));
+ ss->digestsDefined = calloc(unpack_streams,
+ sizeof(*ss->digestsDefined));
+ ss->digests = calloc(unpack_streams,
+ sizeof(*ss->digests));
+ if (ss->unpackSizes == NULL || ss->digestsDefined == NULL ||
+ ss->digests == NULL)
+ return (-1);
+ }
+
+ usizes = ss->unpackSizes;
+ for (i = 0; i < numFolders; i++) {
+ unsigned pack;
+ uint64_t sum;
+
+ if (f[i].numUnpackStreams == 0)
+ continue;
+
+ sum = 0;
+ if (type == kSize) {
+ for (pack = 1; pack < f[i].numUnpackStreams; pack++) {
+ if (parse_7zip_uint64(a, usizes) < 0)
+ return (-1);
+ sum += *usizes++;
+ }
+ }
+ *usizes++ = folder_uncompressed_size(&f[i]) - sum;
+ }
+
+ if (type == kSize) {
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ type = *p;
+ }
+
+ for (i = 0; i < unpack_streams; i++) {
+ ss->digestsDefined[i] = 0;
+ ss->digests[i] = 0;
+ }
+
+ numDigests = 0;
+ for (i = 0; i < numFolders; i++) {
+ if (f[i].numUnpackStreams != 1 || !f[i].digest_defined)
+ numDigests += (uint32_t)f[i].numUnpackStreams;
+ }
+
+ if (type == kCRC) {
+ struct _7z_digests tmpDigests;
+ unsigned char *digestsDefined = ss->digestsDefined;
+ uint32_t * digests = ss->digests;
+ int di = 0;
+
+ memset(&tmpDigests, 0, sizeof(tmpDigests));
+ if (read_Digests(a, &(tmpDigests), numDigests) < 0) {
+ free_Digest(&tmpDigests);
+ return (-1);
+ }
+ for (i = 0; i < numFolders; i++) {
+ if (f[i].numUnpackStreams == 1 && f[i].digest_defined) {
+ *digestsDefined++ = 1;
+ *digests++ = f[i].digest;
+ } else {
+ unsigned j;
+
+ for (j = 0; j < f[i].numUnpackStreams;
+ j++, di++) {
+ *digestsDefined++ =
+ tmpDigests.defineds[di];
+ *digests++ =
+ tmpDigests.digests[di];
+ }
+ }
+ }
+ free_Digest(&tmpDigests);
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ type = *p;
+ }
+
+ /*
+ * Must be kEnd.
+ */
+ if (type != kEnd)
+ return (-1);
+ return (0);
+}
+
+static void
+free_StreamsInfo(struct _7z_stream_info *si)
+{
+ free_PackInfo(&(si->pi));
+ free_CodersInfo(&(si->ci));
+ free_SubStreamsInfo(&(si->ss));
+}
+
+static int
+read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ const unsigned char *p;
+ unsigned i;
+
+ memset(si, 0, sizeof(*si));
+
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ if (*p == kPackInfo) {
+ uint64_t packPos;
+
+ if (read_PackInfo(a, &(si->pi)) < 0)
+ return (-1);
+
+ if (si->pi.positions == NULL || si->pi.sizes == NULL)
+ return (-1);
+ /*
+ * Calculate packed stream positions.
+ */
+ packPos = si->pi.pos;
+ for (i = 0; i < si->pi.numPackStreams; i++) {
+ si->pi.positions[i] = packPos;
+ packPos += si->pi.sizes[i];
+ if (packPos > zip->header_offset)
+ return (-1);
+ }
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ }
+ if (*p == kUnPackInfo) {
+ uint32_t packIndex;
+ struct _7z_folder *f;
+
+ if (read_CodersInfo(a, &(si->ci)) < 0)
+ return (-1);
+
+ /*
+ * Calculate packed stream indexes.
+ */
+ packIndex = 0;
+ f = si->ci.folders;
+ for (i = 0; i < si->ci.numFolders; i++) {
+ f[i].packIndex = packIndex;
+ packIndex += (uint32_t)f[i].numPackedStreams;
+ if (packIndex > si->pi.numPackStreams)
+ return (-1);
+ }
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ }
+
+ if (*p == kSubStreamsInfo) {
+ if (read_SubStreamsInfo(a, &(si->ss),
+ si->ci.folders, (size_t)si->ci.numFolders) < 0)
+ return (-1);
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ }
+
+ /*
+ * Must be kEnd.
+ */
+ if (*p != kEnd)
+ return (-1);
+ return (0);
+}
+
+static void
+free_Header(struct _7z_header_info *h)
+{
+ free(h->emptyStreamBools);
+ free(h->emptyFileBools);
+ free(h->antiBools);
+ free(h->attrBools);
+}
+
+static int
+read_Header(struct archive_read *a, struct _7z_header_info *h,
+ int check_header_id)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ const unsigned char *p;
+ struct _7z_folder *folders;
+ struct _7z_stream_info *si = &(zip->si);
+ struct _7zip_entry *entries;
+ uint32_t folderIndex, indexInFolder;
+ unsigned i;
+ int eindex, empty_streams, sindex;
+
+ if (check_header_id) {
+ /*
+ * Read Header.
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ if (*p != kHeader)
+ return (-1);
+ }
+
+ /*
+ * Read ArchiveProperties.
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ if (*p == kArchiveProperties) {
+ for (;;) {
+ uint64_t size;
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ if (*p == 0)
+ break;
+ if (parse_7zip_uint64(a, &size) < 0)
+ return (-1);
+ }
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ }
+
+ /*
+ * Read MainStreamsInfo.
+ */
+ if (*p == kMainStreamsInfo) {
+ if (read_StreamsInfo(a, &(zip->si)) < 0)
+ return (-1);
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ }
+ if (*p == kEnd)
+ return (0);
+
+ /*
+ * Read FilesInfo.
+ */
+ if (*p != kFilesInfo)
+ return (-1);
+
+ if (parse_7zip_uint64(a, &(zip->numFiles)) < 0)
+ return (-1);
+ if (UMAX_ENTRY < zip->numFiles)
+ return (-1);
+
+ zip->entries = calloc((size_t)zip->numFiles, sizeof(*zip->entries));
+ if (zip->entries == NULL)
+ return (-1);
+ entries = zip->entries;
+
+ empty_streams = 0;
+ for (;;) {
+ int type;
+ uint64_t size;
+ size_t ll;
+
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ type = *p;
+ if (type == kEnd)
+ break;
+
+ if (parse_7zip_uint64(a, &size) < 0)
+ return (-1);
+ if (zip->header_bytes_remaining < size)
+ return (-1);
+ ll = (size_t)size;
+
+ switch (type) {
+ case kEmptyStream:
+ if (h->emptyStreamBools != NULL)
+ return (-1);
+ h->emptyStreamBools = calloc((size_t)zip->numFiles,
+ sizeof(*h->emptyStreamBools));
+ if (h->emptyStreamBools == NULL)
+ return (-1);
+ if (read_Bools(
+ a, h->emptyStreamBools, (size_t)zip->numFiles) < 0)
+ return (-1);
+ empty_streams = 0;
+ for (i = 0; i < zip->numFiles; i++) {
+ if (h->emptyStreamBools[i])
+ empty_streams++;
+ }
+ break;
+ case kEmptyFile:
+ if (empty_streams <= 0) {
+ /* Unexcepted sequence. Skip this. */
+ if (header_bytes(a, ll) == NULL)
+ return (-1);
+ break;
+ }
+ if (h->emptyFileBools != NULL)
+ return (-1);
+ h->emptyFileBools = calloc(empty_streams,
+ sizeof(*h->emptyFileBools));
+ if (h->emptyFileBools == NULL)
+ return (-1);
+ if (read_Bools(a, h->emptyFileBools, empty_streams) < 0)
+ return (-1);
+ break;
+ case kAnti:
+ if (empty_streams <= 0) {
+ /* Unexcepted sequence. Skip this. */
+ if (header_bytes(a, ll) == NULL)
+ return (-1);
+ break;
+ }
+ if (h->antiBools != NULL)
+ return (-1);
+ h->antiBools = calloc(empty_streams,
+ sizeof(*h->antiBools));
+ if (h->antiBools == NULL)
+ return (-1);
+ if (read_Bools(a, h->antiBools, empty_streams) < 0)
+ return (-1);
+ break;
+ case kCTime:
+ case kATime:
+ case kMTime:
+ if (read_Times(a, h, type) < 0)
+ return (-1);
+ break;
+ case kName:
+ {
+ unsigned char *np;
+ size_t nl, nb;
+
+ /* Skip one byte. */
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ ll--;
+
+ if ((ll & 1) || ll < zip->numFiles * 4)
+ return (-1);
+
+ if (zip->entry_names != NULL)
+ return (-1);
+ zip->entry_names = malloc(ll);
+ if (zip->entry_names == NULL)
+ return (-1);
+ np = zip->entry_names;
+ nb = ll;
+ /*
+ * Copy whole file names.
+ * NOTE: This loop prevents from expanding
+ * the uncompressed buffer in order not to
+ * use extra memory resource.
+ */
+ while (nb) {
+ size_t b;
+ if (nb > UBUFF_SIZE)
+ b = UBUFF_SIZE;
+ else
+ b = nb;
+ if ((p = header_bytes(a, b)) == NULL)
+ return (-1);
+ memcpy(np, p, b);
+ np += b;
+ nb -= b;
+ }
+ np = zip->entry_names;
+ nl = ll;
+
+ for (i = 0; i < zip->numFiles; i++) {
+ entries[i].utf16name = np;
+#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
+ entries[i].wname = (wchar_t *)np;
+#endif
+
+ /* Find a terminator. */
+ while (nl >= 2 && (np[0] || np[1])) {
+ np += 2;
+ nl -= 2;
+ }
+ if (nl < 2)
+ return (-1);/* Terminator not found */
+ entries[i].name_len = np - entries[i].utf16name;
+ np += 2;
+ nl -= 2;
+ }
+ break;
+ }
+ case kAttributes:
+ {
+ int allAreDefined;
+
+ if ((p = header_bytes(a, 2)) == NULL)
+ return (-1);
+ allAreDefined = *p;
+ if (h->attrBools != NULL)
+ return (-1);
+ h->attrBools = calloc((size_t)zip->numFiles,
+ sizeof(*h->attrBools));
+ if (h->attrBools == NULL)
+ return (-1);
+ if (allAreDefined)
+ memset(h->attrBools, 1, (size_t)zip->numFiles);
+ else {
+ if (read_Bools(a, h->attrBools,
+ (size_t)zip->numFiles) < 0)
+ return (-1);
+ }
+ for (i = 0; i < zip->numFiles; i++) {
+ if (h->attrBools[i]) {
+ if ((p = header_bytes(a, 4)) == NULL)
+ return (-1);
+ entries[i].attr = archive_le32dec(p);
+ }
+ }
+ break;
+ }
+ case kDummy:
+ if (ll == 0)
+ break;
+ __LA_FALLTHROUGH;
+ default:
+ if (header_bytes(a, ll) == NULL)
+ return (-1);
+ break;
+ }
+ }
+
+ /*
+ * Set up entry's attributes.
+ */
+ folders = si->ci.folders;
+ eindex = sindex = 0;
+ folderIndex = indexInFolder = 0;
+ for (i = 0; i < zip->numFiles; i++) {
+ if (h->emptyStreamBools == NULL || h->emptyStreamBools[i] == 0)
+ entries[i].flg |= HAS_STREAM;
+ /* The high 16 bits of attributes is a posix file mode. */
+ entries[i].mode = entries[i].attr >> 16;
+ if (entries[i].flg & HAS_STREAM) {
+ if ((size_t)sindex >= si->ss.unpack_streams)
+ return (-1);
+ if (entries[i].mode == 0)
+ entries[i].mode = AE_IFREG | 0666;
+ if (si->ss.digestsDefined[sindex])
+ entries[i].flg |= CRC32_IS_SET;
+ entries[i].ssIndex = sindex;
+ sindex++;
+ } else {
+ int dir;
+ if (h->emptyFileBools == NULL)
+ dir = 1;
+ else {
+ if (h->emptyFileBools[eindex])
+ dir = 0;
+ else
+ dir = 1;
+ eindex++;
+ }
+ if (entries[i].mode == 0) {
+ if (dir)
+ entries[i].mode = AE_IFDIR | 0777;
+ else
+ entries[i].mode = AE_IFREG | 0666;
+ } else if (dir &&
+ (entries[i].mode & AE_IFMT) != AE_IFDIR) {
+ entries[i].mode &= ~AE_IFMT;
+ entries[i].mode |= AE_IFDIR;
+ }
+ if ((entries[i].mode & AE_IFMT) == AE_IFDIR &&
+ entries[i].name_len >= 2 &&
+ (entries[i].utf16name[entries[i].name_len-2] != '/' ||
+ entries[i].utf16name[entries[i].name_len-1] != 0)) {
+ entries[i].utf16name[entries[i].name_len] = '/';
+ entries[i].utf16name[entries[i].name_len+1] = 0;
+ entries[i].name_len += 2;
+ }
+ entries[i].ssIndex = -1;
+ }
+ if (entries[i].attr & 0x01)
+ entries[i].mode &= ~0222;/* Read only. */
+
+ if ((entries[i].flg & HAS_STREAM) == 0 && indexInFolder == 0) {
+ /*
+ * The entry is an empty file or a directory file,
+ * those both have no contents.
+ */
+ entries[i].folderIndex = -1;
+ continue;
+ }
+ if (indexInFolder == 0) {
+ for (;;) {
+ if (folderIndex >= si->ci.numFolders)
+ return (-1);
+ if (folders[folderIndex].numUnpackStreams)
+ break;
+ folderIndex++;
+ }
+ }
+ entries[i].folderIndex = folderIndex;
+ if ((entries[i].flg & HAS_STREAM) == 0)
+ continue;
+ indexInFolder++;
+ if (indexInFolder >= folders[folderIndex].numUnpackStreams) {
+ folderIndex++;
+ indexInFolder = 0;
+ }
+ }
+
+ return (0);
+}
+
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+static void
+fileTimeToUtc(uint64_t fileTime, time_t *timep, long *ns)
+{
+
+ if (fileTime >= EPOC_TIME) {
+ fileTime -= EPOC_TIME;
+ /* milli seconds base */
+ *timep = (time_t)(fileTime / 10000000);
+ /* nano seconds base */
+ *ns = (long)(fileTime % 10000000) * 100;
+ } else {
+ *timep = 0;
+ *ns = 0;
+ }
+}
+
+static int
+read_Times(struct archive_read *a, struct _7z_header_info *h, int type)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ const unsigned char *p;
+ struct _7zip_entry *entries = zip->entries;
+ unsigned char *timeBools;
+ int allAreDefined;
+ unsigned i;
+
+ timeBools = calloc((size_t)zip->numFiles, sizeof(*timeBools));
+ if (timeBools == NULL)
+ return (-1);
+
+ /* Read allAreDefined. */
+ if ((p = header_bytes(a, 1)) == NULL)
+ goto failed;
+ allAreDefined = *p;
+ if (allAreDefined)
+ memset(timeBools, 1, (size_t)zip->numFiles);
+ else {
+ if (read_Bools(a, timeBools, (size_t)zip->numFiles) < 0)
+ goto failed;
+ }
+
+ /* Read external. */
+ if ((p = header_bytes(a, 1)) == NULL)
+ goto failed;
+ if (*p) {
+ if (parse_7zip_uint64(a, &(h->dataIndex)) < 0)
+ goto failed;
+ if (UMAX_ENTRY < h->dataIndex)
+ goto failed;
+ }
+
+ for (i = 0; i < zip->numFiles; i++) {
+ if (!timeBools[i])
+ continue;
+ if ((p = header_bytes(a, 8)) == NULL)
+ goto failed;
+ switch (type) {
+ case kCTime:
+ fileTimeToUtc(archive_le64dec(p),
+ &(entries[i].ctime),
+ &(entries[i].ctime_ns));
+ entries[i].flg |= CTIME_IS_SET;
+ break;
+ case kATime:
+ fileTimeToUtc(archive_le64dec(p),
+ &(entries[i].atime),
+ &(entries[i].atime_ns));
+ entries[i].flg |= ATIME_IS_SET;
+ break;
+ case kMTime:
+ fileTimeToUtc(archive_le64dec(p),
+ &(entries[i].mtime),
+ &(entries[i].mtime_ns));
+ entries[i].flg |= MTIME_IS_SET;
+ break;
+ }
+ }
+
+ free(timeBools);
+ return (0);
+failed:
+ free(timeBools);
+ return (-1);
+}
+
+static int
+decode_encoded_header_info(struct archive_read *a, struct _7z_stream_info *si)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+
+ errno = 0;
+ if (read_StreamsInfo(a, si) < 0) {
+ if (errno == ENOMEM)
+ archive_set_error(&a->archive, -1,
+ "Couldn't allocate memory");
+ else
+ archive_set_error(&a->archive, -1,
+ "Malformed 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (si->pi.numPackStreams == 0 || si->ci.numFolders == 0) {
+ archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (zip->header_offset < si->pi.pos + si->pi.sizes[0] ||
+ (int64_t)(si->pi.pos + si->pi.sizes[0]) < 0 ||
+ si->pi.sizes[0] == 0 || (int64_t)si->pi.pos < 0) {
+ archive_set_error(&a->archive, -1, "Malformed Header offset");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static const unsigned char *
+header_bytes(struct archive_read *a, size_t rbytes)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ const unsigned char *p;
+
+ if (zip->header_bytes_remaining < rbytes)
+ return (NULL);
+ if (zip->pack_stream_bytes_unconsumed)
+ read_consume(a);
+
+ if (zip->header_is_encoded == 0) {
+ p = __archive_read_ahead(a, rbytes, NULL);
+ if (p == NULL)
+ return (NULL);
+ zip->header_bytes_remaining -= rbytes;
+ zip->pack_stream_bytes_unconsumed = rbytes;
+ } else {
+ const void *buff;
+ ssize_t bytes;
+
+ bytes = read_stream(a, &buff, rbytes, rbytes);
+ if (bytes <= 0)
+ return (NULL);
+ zip->header_bytes_remaining -= bytes;
+ p = buff;
+ }
+
+ /* Update checksum */
+ zip->header_crc32 = crc32(zip->header_crc32, p, (unsigned)rbytes);
+ return (p);
+}
+
+static int
+slurp_central_directory(struct archive_read *a, struct _7zip *zip,
+ struct _7z_header_info *header)
+{
+ const unsigned char *p;
+ uint64_t next_header_offset;
+ uint64_t next_header_size;
+ uint32_t next_header_crc;
+ ssize_t bytes_avail;
+ int check_header_crc, r;
+
+ if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL)
+ return (ARCHIVE_FATAL);
+
+ if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
+ /* This is an executable ? Must be self-extracting... */
+ r = skip_sfx(a, bytes_avail);
+ if (r < ARCHIVE_WARN)
+ return (r);
+ if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ zip->seek_base += 32;
+
+ if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) {
+ archive_set_error(&a->archive, -1, "Not 7-Zip archive file");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* CRC check. */
+ if (crc32(0, (const unsigned char *)p + 12, 20)
+ != archive_le32dec(p + 8)) {
+#ifdef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, -1, "Header CRC error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+
+ next_header_offset = archive_le64dec(p + 12);
+ next_header_size = archive_le64dec(p + 20);
+ next_header_crc = archive_le32dec(p + 28);
+
+ if (next_header_size == 0)
+ /* There is no entry in an archive file. */
+ return (ARCHIVE_EOF);
+
+ if (((int64_t)next_header_offset) < 0) {
+ archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+ __archive_read_consume(a, 32);
+ if (next_header_offset != 0) {
+ if (bytes_avail >= (ssize_t)next_header_offset)
+ __archive_read_consume(a, next_header_offset);
+ else if (__archive_read_seek(a,
+ next_header_offset + zip->seek_base, SEEK_SET) < 0)
+ return (ARCHIVE_FATAL);
+ }
+ zip->stream_offset = next_header_offset;
+ zip->header_offset = next_header_offset;
+ zip->header_bytes_remaining = next_header_size;
+ zip->header_crc32 = 0;
+ zip->header_is_encoded = 0;
+ zip->header_is_being_read = 1;
+ zip->has_encrypted_entries = 0;
+ check_header_crc = 1;
+
+ if ((p = header_bytes(a, 1)) == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated 7-Zip file body");
+ return (ARCHIVE_FATAL);
+ }
+ /* Parse ArchiveProperties. */
+ switch (p[0]) {
+ case kEncodedHeader:
+ /*
+ * The archive has an encoded header and we have to decode it
+ * in order to parse the header correctly.
+ */
+ r = decode_encoded_header_info(a, &(zip->si));
+
+ /* Check the EncodedHeader CRC.*/
+ if (r == 0 && zip->header_crc32 != next_header_crc) {
+ archive_set_error(&a->archive, -1,
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ "Damaged 7-Zip archive");
+ r = -1;
+#endif
+ }
+ if (r == 0) {
+ if (zip->si.ci.folders[0].digest_defined)
+ next_header_crc = zip->si.ci.folders[0].digest;
+ else
+ check_header_crc = 0;
+ if (zip->pack_stream_bytes_unconsumed)
+ read_consume(a);
+ r = setup_decode_folder(a, zip->si.ci.folders, 1);
+ if (r == 0) {
+ zip->header_bytes_remaining =
+ zip->folder_outbytes_remaining;
+ r = seek_pack(a);
+ }
+ }
+ /* Clean up StreamsInfo. */
+ free_StreamsInfo(&(zip->si));
+ memset(&(zip->si), 0, sizeof(zip->si));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ zip->header_is_encoded = 1;
+ zip->header_crc32 = 0;
+ /* FALL THROUGH */
+ case kHeader:
+ /*
+ * Parse the header.
+ */
+ errno = 0;
+ r = read_Header(a, header, zip->header_is_encoded);
+ if (r < 0) {
+ if (errno == ENOMEM)
+ archive_set_error(&a->archive, -1,
+ "Couldn't allocate memory");
+ else
+ archive_set_error(&a->archive, -1,
+ "Damaged 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Must be kEnd.
+ */
+ if ((p = header_bytes(a, 1)) == NULL ||*p != kEnd) {
+ archive_set_error(&a->archive, -1,
+ "Malformed 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Check the Header CRC.*/
+ if (check_header_crc && zip->header_crc32 != next_header_crc) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, -1,
+ "Malformed 7-Zip archive");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ break;
+ default:
+ archive_set_error(&a->archive, -1,
+ "Unexpected Property ID = %X", p[0]);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Clean up variables be used for decoding the archive header */
+ zip->pack_stream_remaining = 0;
+ zip->pack_stream_index = 0;
+ zip->folder_outbytes_remaining = 0;
+ zip->uncompressed_buffer_bytes_remaining = 0;
+ zip->pack_stream_bytes_unconsumed = 0;
+ zip->header_is_being_read = 0;
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+get_uncompressed_data(struct archive_read *a, const void **buff, size_t size,
+ size_t minimum)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ ssize_t bytes_avail;
+
+ if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) {
+ /* Copy mode. */
+
+ *buff = __archive_read_ahead(a, minimum, &bytes_avail);
+ if (bytes_avail <= 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated 7-Zip file data");
+ return (ARCHIVE_FATAL);
+ }
+ if ((size_t)bytes_avail >
+ zip->uncompressed_buffer_bytes_remaining)
+ bytes_avail = (ssize_t)
+ zip->uncompressed_buffer_bytes_remaining;
+ if ((size_t)bytes_avail > size)
+ bytes_avail = (ssize_t)size;
+
+ zip->pack_stream_bytes_unconsumed = bytes_avail;
+ } else if (zip->uncompressed_buffer_pointer == NULL) {
+ /* Decompression has failed. */
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ } else {
+ /* Packed mode. */
+ if (minimum > zip->uncompressed_buffer_bytes_remaining) {
+ /*
+ * If remaining uncompressed data size is less than
+ * the minimum size, fill the buffer up to the
+ * minimum size.
+ */
+ if (extract_pack_stream(a, minimum) < 0)
+ return (ARCHIVE_FATAL);
+ }
+ if (size > zip->uncompressed_buffer_bytes_remaining)
+ bytes_avail = (ssize_t)
+ zip->uncompressed_buffer_bytes_remaining;
+ else
+ bytes_avail = (ssize_t)size;
+ *buff = zip->uncompressed_buffer_pointer;
+ zip->uncompressed_buffer_pointer += bytes_avail;
+ }
+ zip->uncompressed_buffer_bytes_remaining -= bytes_avail;
+ return (bytes_avail);
+}
+
+static ssize_t
+extract_pack_stream(struct archive_read *a, size_t minimum)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ ssize_t bytes_avail;
+ int r;
+
+ if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) {
+ if (minimum == 0)
+ minimum = 1;
+ if (__archive_read_ahead(a, minimum, &bytes_avail) == NULL
+ || bytes_avail <= 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated 7-Zip file body");
+ return (ARCHIVE_FATAL);
+ }
+ if ((uint64_t)bytes_avail > zip->pack_stream_inbytes_remaining)
+ bytes_avail = (ssize_t)zip->pack_stream_inbytes_remaining;
+ zip->pack_stream_inbytes_remaining -= bytes_avail;
+ if ((uint64_t)bytes_avail > zip->folder_outbytes_remaining)
+ bytes_avail = (ssize_t)zip->folder_outbytes_remaining;
+ zip->folder_outbytes_remaining -= bytes_avail;
+ zip->uncompressed_buffer_bytes_remaining = bytes_avail;
+ return (ARCHIVE_OK);
+ }
+
+ /* If the buffer hasn't been allocated, allocate it now. */
+ if (zip->uncompressed_buffer == NULL) {
+ zip->uncompressed_buffer_size = UBUFF_SIZE;
+ if (zip->uncompressed_buffer_size < minimum) {
+ zip->uncompressed_buffer_size = minimum + 1023;
+ zip->uncompressed_buffer_size &= ~0x3ff;
+ }
+ zip->uncompressed_buffer =
+ malloc(zip->uncompressed_buffer_size);
+ if (zip->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for 7-Zip decompression");
+ return (ARCHIVE_FATAL);
+ }
+ zip->uncompressed_buffer_bytes_remaining = 0;
+ } else if (zip->uncompressed_buffer_size < minimum ||
+ zip->uncompressed_buffer_bytes_remaining < minimum) {
+ /*
+ * Make sure the uncompressed buffer can have bytes
+ * at least `minimum' bytes.
+ * NOTE: This case happen when reading the header.
+ */
+ size_t used;
+ if (zip->uncompressed_buffer_pointer != 0)
+ used = zip->uncompressed_buffer_pointer -
+ zip->uncompressed_buffer;
+ else
+ used = 0;
+ if (zip->uncompressed_buffer_size < minimum) {
+ /*
+ * Expand the uncompressed buffer up to
+ * the minimum size.
+ */
+ void *p;
+ size_t new_size;
+
+ new_size = minimum + 1023;
+ new_size &= ~0x3ff;
+ p = realloc(zip->uncompressed_buffer, new_size);
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for 7-Zip decompression");
+ return (ARCHIVE_FATAL);
+ }
+ zip->uncompressed_buffer = (unsigned char *)p;
+ zip->uncompressed_buffer_size = new_size;
+ }
+ /*
+ * Move unconsumed bytes to the head.
+ */
+ if (used) {
+ memmove(zip->uncompressed_buffer,
+ zip->uncompressed_buffer + used,
+ zip->uncompressed_buffer_bytes_remaining);
+ }
+ } else
+ zip->uncompressed_buffer_bytes_remaining = 0;
+ zip->uncompressed_buffer_pointer = NULL;
+ for (;;) {
+ size_t bytes_in, bytes_out;
+ const void *buff_in;
+ unsigned char *buff_out;
+ int end_of_data;
+
+ /*
+ * Note: '1' here is a performance optimization.
+ * Recall that the decompression layer returns a count of
+ * available bytes; asking for more than that forces the
+ * decompressor to combine reads by copying data.
+ */
+ buff_in = __archive_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail <= 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated 7-Zip file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ buff_out = zip->uncompressed_buffer
+ + zip->uncompressed_buffer_bytes_remaining;
+ bytes_out = zip->uncompressed_buffer_size
+ - zip->uncompressed_buffer_bytes_remaining;
+ bytes_in = bytes_avail;
+ if (bytes_in > zip->pack_stream_inbytes_remaining)
+ bytes_in = (size_t)zip->pack_stream_inbytes_remaining;
+ /* Drive decompression. */
+ r = decompress(a, zip, buff_out, &bytes_out,
+ buff_in, &bytes_in);
+ switch (r) {
+ case ARCHIVE_OK:
+ end_of_data = 0;
+ break;
+ case ARCHIVE_EOF:
+ end_of_data = 1;
+ break;
+ default:
+ return (ARCHIVE_FATAL);
+ }
+ zip->pack_stream_inbytes_remaining -= bytes_in;
+ if (bytes_out > zip->folder_outbytes_remaining)
+ bytes_out = (size_t)zip->folder_outbytes_remaining;
+ zip->folder_outbytes_remaining -= bytes_out;
+ zip->uncompressed_buffer_bytes_remaining += bytes_out;
+ zip->pack_stream_bytes_unconsumed = bytes_in;
+
+ /*
+ * Continue decompression until uncompressed_buffer is full.
+ */
+ if (zip->uncompressed_buffer_bytes_remaining ==
+ zip->uncompressed_buffer_size)
+ break;
+ if (zip->codec2 == _7Z_X86 && zip->odd_bcj_size &&
+ zip->uncompressed_buffer_bytes_remaining + 5 >
+ zip->uncompressed_buffer_size)
+ break;
+ if (zip->pack_stream_inbytes_remaining == 0 &&
+ zip->folder_outbytes_remaining == 0)
+ break;
+ if (end_of_data || (bytes_in == 0 && bytes_out == 0)) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+ read_consume(a);
+ }
+ if (zip->uncompressed_buffer_bytes_remaining < minimum) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+ zip->uncompressed_buffer_pointer = zip->uncompressed_buffer;
+ return (ARCHIVE_OK);
+}
+
+static int
+seek_pack(struct archive_read *a)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ int64_t pack_offset;
+
+ if (zip->pack_stream_remaining <= 0) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+ zip->pack_stream_inbytes_remaining =
+ zip->si.pi.sizes[zip->pack_stream_index];
+ pack_offset = zip->si.pi.positions[zip->pack_stream_index];
+ if (zip->stream_offset != pack_offset) {
+ if (0 > __archive_read_seek(a, pack_offset + zip->seek_base,
+ SEEK_SET))
+ return (ARCHIVE_FATAL);
+ zip->stream_offset = pack_offset;
+ }
+ zip->pack_stream_index++;
+ zip->pack_stream_remaining--;
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+read_stream(struct archive_read *a, const void **buff, size_t size,
+ size_t minimum)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ uint64_t skip_bytes = 0;
+ ssize_t r;
+
+ if (zip->uncompressed_buffer_bytes_remaining == 0) {
+ if (zip->pack_stream_inbytes_remaining > 0) {
+ r = extract_pack_stream(a, 0);
+ if (r < 0)
+ return (r);
+ return (get_uncompressed_data(a, buff, size, minimum));
+ } else if (zip->folder_outbytes_remaining > 0) {
+ /* Extract a remaining pack stream. */
+ r = extract_pack_stream(a, 0);
+ if (r < 0)
+ return (r);
+ return (get_uncompressed_data(a, buff, size, minimum));
+ }
+ } else
+ return (get_uncompressed_data(a, buff, size, minimum));
+
+ /*
+ * Current pack stream has been consumed.
+ */
+ if (zip->pack_stream_remaining == 0) {
+ if (zip->header_is_being_read) {
+ /* Invalid sequence. This might happen when
+ * reading a malformed archive. */
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC, "Malformed 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * All current folder's pack streams have been
+ * consumed. Switch to next folder.
+ */
+ if (zip->folder_index == 0 &&
+ (zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes
+ || zip->folder_index != zip->entry->folderIndex)) {
+ zip->folder_index = zip->entry->folderIndex;
+ skip_bytes =
+ zip->si.ci.folders[zip->folder_index].skipped_bytes;
+ }
+
+ if (zip->folder_index >= zip->si.ci.numFolders) {
+ /*
+ * We have consumed all folders and its pack streams.
+ */
+ *buff = NULL;
+ return (0);
+ }
+ r = setup_decode_folder(a,
+ &(zip->si.ci.folders[zip->folder_index]), 0);
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ zip->folder_index++;
+ }
+
+ /*
+ * Switch to next pack stream.
+ */
+ r = seek_pack(a);
+ if (r < 0)
+ return (r);
+
+ /* Extract a new pack stream. */
+ r = extract_pack_stream(a, 0);
+ if (r < 0)
+ return (r);
+
+ /*
+ * Skip the bytes we already has skipped in skip_stream().
+ */
+ while (skip_bytes) {
+ ssize_t skipped;
+
+ if (zip->uncompressed_buffer_bytes_remaining == 0) {
+ if (zip->pack_stream_inbytes_remaining > 0) {
+ r = extract_pack_stream(a, 0);
+ if (r < 0)
+ return (r);
+ } else if (zip->folder_outbytes_remaining > 0) {
+ /* Extract a remaining pack stream. */
+ r = extract_pack_stream(a, 0);
+ if (r < 0)
+ return (r);
+ } else {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated 7-Zip file body");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ skipped = get_uncompressed_data(
+ a, buff, (size_t)skip_bytes, 0);
+ if (skipped < 0)
+ return (skipped);
+ skip_bytes -= skipped;
+ if (zip->pack_stream_bytes_unconsumed)
+ read_consume(a);
+ }
+
+ return (get_uncompressed_data(a, buff, size, minimum));
+}
+
+static int
+setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
+ int header)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ const struct _7z_coder *coder1, *coder2;
+ const char *cname = (header)?"archive header":"file content";
+ unsigned i;
+ int r, found_bcj2 = 0;
+
+ /*
+ * Release the memory which the previous folder used for BCJ2.
+ */
+ for (i = 0; i < 3; i++) {
+ free(zip->sub_stream_buff[i]);
+ zip->sub_stream_buff[i] = NULL;
+ }
+
+ /*
+ * Initialize a stream reader.
+ */
+ zip->pack_stream_remaining = (unsigned)folder->numPackedStreams;
+ zip->pack_stream_index = (unsigned)folder->packIndex;
+ zip->folder_outbytes_remaining = folder_uncompressed_size(folder);
+ zip->uncompressed_buffer_bytes_remaining = 0;
+
+ /*
+ * Check coder types.
+ */
+ for (i = 0; i < folder->numCoders; i++) {
+ switch(folder->coders[i].codec) {
+ case _7Z_CRYPTO_MAIN_ZIP:
+ case _7Z_CRYPTO_RAR_29:
+ case _7Z_CRYPTO_AES_256_SHA_256: {
+ /* For entry that is associated with this folder, mark
+ it as encrypted (data+metadata). */
+ zip->has_encrypted_entries = 1;
+ if (a->entry) {
+ archive_entry_set_is_data_encrypted(a->entry, 1);
+ archive_entry_set_is_metadata_encrypted(a->entry, 1);
+ }
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "The %s is encrypted, "
+ "but currently not supported", cname);
+ return (ARCHIVE_FATAL);
+ }
+ case _7Z_X86_BCJ2: {
+ found_bcj2++;
+ break;
+ }
+ }
+ }
+ /* Now that we've checked for encryption, if there were still no
+ * encrypted entries found we can say for sure that there are none.
+ */
+ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+ zip->has_encrypted_entries = 0;
+ }
+
+ if ((folder->numCoders > 2 && !found_bcj2) || found_bcj2 > 1) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "The %s is encoded with many filters, "
+ "but currently not supported", cname);
+ return (ARCHIVE_FATAL);
+ }
+ coder1 = &(folder->coders[0]);
+ if (folder->numCoders == 2)
+ coder2 = &(folder->coders[1]);
+ else
+ coder2 = NULL;
+
+ if (found_bcj2) {
+ /*
+ * Preparation to decode BCJ2.
+ * Decoding BCJ2 requires four sources. Those are at least,
+ * as far as I know, two types of the storage form.
+ */
+ const struct _7z_coder *fc = folder->coders;
+ static const struct _7z_coder coder_copy = {0, 1, 1, 0, NULL};
+ const struct _7z_coder *scoder[3] =
+ {&coder_copy, &coder_copy, &coder_copy};
+ const void *buff;
+ ssize_t bytes;
+ unsigned char *b[3] = {NULL, NULL, NULL};
+ uint64_t sunpack[3] ={-1, -1, -1};
+ size_t s[3] = {0, 0, 0};
+ int idx[3] = {0, 1, 2};
+
+ if (folder->numCoders == 4 && fc[3].codec == _7Z_X86_BCJ2 &&
+ folder->numInStreams == 7 && folder->numOutStreams == 4 &&
+ zip->pack_stream_remaining == 4) {
+ /* Source type 1 made by 7zr or 7z with -m options. */
+ if (folder->bindPairs[0].inIndex == 5) {
+ /* The form made by 7zr */
+ idx[0] = 1; idx[1] = 2; idx[2] = 0;
+ scoder[1] = &(fc[1]);
+ scoder[2] = &(fc[0]);
+ sunpack[1] = folder->unPackSize[1];
+ sunpack[2] = folder->unPackSize[0];
+ coder1 = &(fc[2]);
+ } else {
+ /*
+ * NOTE: Some patterns do not work.
+ * work:
+ * 7z a -m0=BCJ2 -m1=COPY -m2=COPY
+ * -m3=(any)
+ * 7z a -m0=BCJ2 -m1=COPY -m2=(any)
+ * -m3=COPY
+ * 7z a -m0=BCJ2 -m1=(any) -m2=COPY
+ * -m3=COPY
+ * not work:
+ * other patterns.
+ *
+ * We have to handle this like `pipe' or
+ * our libarchive7s filter frame work,
+ * decoding the BCJ2 main stream sequentially,
+ * m3 -> m2 -> m1 -> BCJ2.
+ *
+ */
+ if (fc[0].codec == _7Z_COPY &&
+ fc[1].codec == _7Z_COPY)
+ coder1 = &(folder->coders[2]);
+ else if (fc[0].codec == _7Z_COPY &&
+ fc[2].codec == _7Z_COPY)
+ coder1 = &(folder->coders[1]);
+ else if (fc[1].codec == _7Z_COPY &&
+ fc[2].codec == _7Z_COPY)
+ coder1 = &(folder->coders[0]);
+ else {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Unsupported form of "
+ "BCJ2 streams");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ coder2 = &(fc[3]);
+ zip->main_stream_bytes_remaining =
+ (size_t)folder->unPackSize[2];
+ } else if (coder2 != NULL && coder2->codec == _7Z_X86_BCJ2 &&
+ zip->pack_stream_remaining == 4 &&
+ folder->numInStreams == 5 && folder->numOutStreams == 2) {
+ /* Source type 0 made by 7z */
+ zip->main_stream_bytes_remaining =
+ (size_t)folder->unPackSize[0];
+ } else {
+ /* We got an unexpected form. */
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Unsupported form of BCJ2 streams");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Skip the main stream at this time. */
+ if ((r = seek_pack(a)) < 0)
+ return (r);
+ zip->pack_stream_bytes_unconsumed =
+ (size_t)zip->pack_stream_inbytes_remaining;
+ read_consume(a);
+
+ /* Read following three sub streams. */
+ for (i = 0; i < 3; i++) {
+ const struct _7z_coder *coder = scoder[i];
+
+ if ((r = seek_pack(a)) < 0) {
+ free(b[0]); free(b[1]); free(b[2]);
+ return (r);
+ }
+
+ if (sunpack[i] == (uint64_t)-1)
+ zip->folder_outbytes_remaining =
+ zip->pack_stream_inbytes_remaining;
+ else
+ zip->folder_outbytes_remaining = sunpack[i];
+
+ r = init_decompression(a, zip, coder, NULL);
+ if (r != ARCHIVE_OK) {
+ free(b[0]); free(b[1]); free(b[2]);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Allocate memory for the decoded data of a sub
+ * stream. */
+ b[i] = malloc((size_t)zip->folder_outbytes_remaining);
+ if (b[i] == NULL) {
+ free(b[0]); free(b[1]); free(b[2]);
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for 7-Zip decompression");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Extract a sub stream. */
+ while (zip->pack_stream_inbytes_remaining > 0) {
+ r = (int)extract_pack_stream(a, 0);
+ if (r < 0) {
+ free(b[0]); free(b[1]); free(b[2]);
+ return (r);
+ }
+ bytes = get_uncompressed_data(a, &buff,
+ zip->uncompressed_buffer_bytes_remaining,
+ 0);
+ if (bytes < 0) {
+ free(b[0]); free(b[1]); free(b[2]);
+ return ((int)bytes);
+ }
+ memcpy(b[i]+s[i], buff, bytes);
+ s[i] += bytes;
+ if (zip->pack_stream_bytes_unconsumed)
+ read_consume(a);
+ }
+ }
+
+ /* Set the sub streams to the right place. */
+ for (i = 0; i < 3; i++) {
+ zip->sub_stream_buff[i] = b[idx[i]];
+ zip->sub_stream_size[i] = s[idx[i]];
+ zip->sub_stream_bytes_remaining[i] = s[idx[i]];
+ }
+
+ /* Allocate memory used for decoded main stream bytes. */
+ if (zip->tmp_stream_buff == NULL) {
+ zip->tmp_stream_buff_size = 32 * 1024;
+ zip->tmp_stream_buff =
+ malloc(zip->tmp_stream_buff_size);
+ if (zip->tmp_stream_buff == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for 7-Zip decompression");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ zip->tmp_stream_bytes_avail = 0;
+ zip->tmp_stream_bytes_remaining = 0;
+ zip->odd_bcj_size = 0;
+ zip->bcj2_outPos = 0;
+
+ /*
+ * Reset a stream reader in order to read the main stream
+ * of BCJ2.
+ */
+ zip->pack_stream_remaining = 1;
+ zip->pack_stream_index = (unsigned)folder->packIndex;
+ zip->folder_outbytes_remaining =
+ folder_uncompressed_size(folder);
+ zip->uncompressed_buffer_bytes_remaining = 0;
+ }
+
+ /*
+ * Initialize the decompressor for the new folder's pack streams.
+ */
+ r = init_decompression(a, zip, coder1, coder2);
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ return (ARCHIVE_OK);
+}
+
+static int64_t
+skip_stream(struct archive_read *a, size_t skip_bytes)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ const void *p;
+ int64_t skipped_bytes;
+ size_t bytes = skip_bytes;
+
+ if (zip->folder_index == 0) {
+ /*
+ * Optimization for a list mode.
+ * Avoid unnecessary decoding operations.
+ */
+ zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes
+ += skip_bytes;
+ return (skip_bytes);
+ }
+
+ while (bytes) {
+ skipped_bytes = read_stream(a, &p, bytes, 0);
+ if (skipped_bytes < 0)
+ return (skipped_bytes);
+ if (skipped_bytes == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated 7-Zip file body");
+ return (ARCHIVE_FATAL);
+ }
+ bytes -= (size_t)skipped_bytes;
+ if (zip->pack_stream_bytes_unconsumed)
+ read_consume(a);
+ }
+ return (skip_bytes);
+}
+
+/*
+ * Brought from LZMA SDK.
+ *
+ * Bra86.c -- Converter for x86 code (BCJ)
+ * 2008-10-04 : Igor Pavlov : Public domain
+ *
+ */
+
+#define Test86MSByte(b) ((b) == 0 || (b) == 0xFF)
+
+static void
+x86_Init(struct _7zip *zip)
+{
+ zip->bcj_state = 0;
+ zip->bcj_prevPosT = (size_t)0 - 1;
+ zip->bcj_prevMask = 0;
+ zip->bcj_ip = 5;
+}
+
+static size_t
+x86_Convert(struct _7zip *zip, uint8_t *data, size_t size)
+{
+ static const uint8_t kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0};
+ static const uint8_t kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3};
+ size_t bufferPos, prevPosT;
+ uint32_t ip, prevMask;
+
+ if (size < 5)
+ return 0;
+
+ bufferPos = 0;
+ prevPosT = zip->bcj_prevPosT;
+ prevMask = zip->bcj_prevMask;
+ ip = zip->bcj_ip;
+
+ for (;;) {
+ uint8_t *p = data + bufferPos;
+ uint8_t *limit = data + size - 4;
+
+ for (; p < limit; p++)
+ if ((*p & 0xFE) == 0xE8)
+ break;
+ bufferPos = (size_t)(p - data);
+ if (p >= limit)
+ break;
+ prevPosT = bufferPos - prevPosT;
+ if (prevPosT > 3)
+ prevMask = 0;
+ else {
+ prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7;
+ if (prevMask != 0) {
+ unsigned char b =
+ p[4 - kMaskToBitNumber[prevMask]];
+ if (!kMaskToAllowedStatus[prevMask] ||
+ Test86MSByte(b)) {
+ prevPosT = bufferPos;
+ prevMask = ((prevMask << 1) & 0x7) | 1;
+ bufferPos++;
+ continue;
+ }
+ }
+ }
+ prevPosT = bufferPos;
+
+ if (Test86MSByte(p[4])) {
+ uint32_t src = ((uint32_t)p[4] << 24) |
+ ((uint32_t)p[3] << 16) | ((uint32_t)p[2] << 8) |
+ ((uint32_t)p[1]);
+ uint32_t dest;
+ for (;;) {
+ uint8_t b;
+ int b_index;
+
+ dest = src - (ip + (uint32_t)bufferPos);
+ if (prevMask == 0)
+ break;
+ b_index = kMaskToBitNumber[prevMask] * 8;
+ b = (uint8_t)(dest >> (24 - b_index));
+ if (!Test86MSByte(b))
+ break;
+ src = dest ^ ((1 << (32 - b_index)) - 1);
+ }
+ p[4] = (uint8_t)(~(((dest >> 24) & 1) - 1));
+ p[3] = (uint8_t)(dest >> 16);
+ p[2] = (uint8_t)(dest >> 8);
+ p[1] = (uint8_t)dest;
+ bufferPos += 5;
+ } else {
+ prevMask = ((prevMask << 1) & 0x7) | 1;
+ bufferPos++;
+ }
+ }
+ zip->bcj_prevPosT = prevPosT;
+ zip->bcj_prevMask = prevMask;
+ zip->bcj_ip += (uint32_t)bufferPos;
+ return (bufferPos);
+}
+
+static void
+arm_Init(struct _7zip *zip)
+{
+ zip->bcj_ip = 8;
+}
+
+static size_t
+arm_Convert(struct _7zip *zip, uint8_t *buf, size_t size)
+{
+ // This function was adapted from
+ // static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+ // in https://git.tukaani.org/xz-embedded.git
+
+ /*
+ * Branch/Call/Jump (BCJ) filter decoders
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <https://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+ size_t i;
+ uint32_t addr;
+
+ for (i = 0; i + 4 <= size; i += 4) {
+ if (buf[i + 3] == 0xEB) {
+ // Calculate the transformed addr.
+ addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8)
+ | ((uint32_t)buf[i + 2] << 16);
+ addr <<= 2;
+ addr -= zip->bcj_ip + (uint32_t)i;
+ addr >>= 2;
+
+ // Store the transformed addr in buf.
+ buf[i] = (uint8_t)addr;
+ buf[i + 1] = (uint8_t)(addr >> 8);
+ buf[i + 2] = (uint8_t)(addr >> 16);
+ }
+ }
+
+ zip->bcj_ip += (uint32_t)i;
+
+ return i;
+}
+
+static size_t
+arm64_Convert(struct _7zip *zip, uint8_t *buf, size_t size)
+{
+ // This function was adapted from
+ // static size_t bcj_arm64(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+ // in https://git.tukaani.org/xz-embedded.git
+
+ /*
+ * Branch/Call/Jump (BCJ) filter decoders
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <https://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+ size_t i;
+ uint32_t instr;
+ uint32_t addr;
+
+ for (i = 0; i + 4 <= size; i += 4) {
+ instr = (uint32_t)buf[i]
+ | ((uint32_t)buf[i+1] << 8)
+ | ((uint32_t)buf[i+2] << 16)
+ | ((uint32_t)buf[i+3] << 24);
+
+ if ((instr >> 26) == 0x25) {
+ /* BL instruction */
+ addr = instr - ((zip->bcj_ip + (uint32_t)i) >> 2);
+ instr = 0x94000000 | (addr & 0x03FFFFFF);
+
+ buf[i] = (uint8_t)instr;
+ buf[i+1] = (uint8_t)(instr >> 8);
+ buf[i+2] = (uint8_t)(instr >> 16);
+ buf[i+3] = (uint8_t)(instr >> 24);
+ } else if ((instr & 0x9F000000) == 0x90000000) {
+ /* ADRP instruction */
+ addr = ((instr >> 29) & 3) | ((instr >> 3) & 0x1FFFFC);
+
+ /* Only convert values in the range +/-512 MiB. */
+ if ((addr + 0x020000) & 0x1C0000)
+ continue;
+
+ addr -= (zip->bcj_ip + (uint32_t)i) >> 12;
+
+ instr &= 0x9000001F;
+ instr |= (addr & 3) << 29;
+ instr |= (addr & 0x03FFFC) << 3;
+ instr |= (0U - (addr & 0x020000)) & 0xE00000;
+
+ buf[i] = (uint8_t)instr;
+ buf[i+1] = (uint8_t)(instr >> 8);
+ buf[i+2] = (uint8_t)(instr >> 16);
+ buf[i+3] = (uint8_t)(instr >> 24);
+ }
+ }
+
+ zip->bcj_ip += (uint32_t)i;
+
+ return i;
+}
+
+/*
+ * Brought from LZMA SDK.
+ *
+ * Bcj2.c -- Converter for x86 code (BCJ2)
+ * 2008-10-04 : Igor Pavlov : Public domain
+ *
+ */
+
+#define SZ_ERROR_DATA ARCHIVE_FAILED
+
+#define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80)
+#define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1))
+
+#define kNumTopBits 24
+#define kTopValue ((uint32_t)1 << kNumTopBits)
+
+#define kNumBitModelTotalBits 11
+#define kBitModelTotal (1 << kNumBitModelTotalBits)
+#define kNumMoveBits 5
+
+#define RC_READ_BYTE (*buffer++)
+#define RC_TEST { if (buffer == bufferLim) return SZ_ERROR_DATA; }
+#define RC_INIT2 zip->bcj2_code = 0; zip->bcj2_range = 0xFFFFFFFF; \
+ { int ii; for (ii = 0; ii < 5; ii++) { RC_TEST; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; }}
+
+#define NORMALIZE if (zip->bcj2_range < kTopValue) { RC_TEST; zip->bcj2_range <<= 8; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; }
+
+#define IF_BIT_0(p) ttt = *(p); bound = (zip->bcj2_range >> kNumBitModelTotalBits) * ttt; if (zip->bcj2_code < bound)
+#define UPDATE_0(p) zip->bcj2_range = bound; *(p) = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); NORMALIZE;
+#define UPDATE_1(p) zip->bcj2_range -= bound; zip->bcj2_code -= bound; *(p) = (CProb)(ttt - (ttt >> kNumMoveBits)); NORMALIZE;
+
+static ssize_t
+Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize)
+{
+ size_t inPos = 0, outPos = 0;
+ const uint8_t *buf0, *buf1, *buf2, *buf3;
+ size_t size0, size1, size2, size3;
+ const uint8_t *buffer, *bufferLim;
+ unsigned int i, j;
+
+ size0 = zip->tmp_stream_bytes_remaining;
+ buf0 = zip->tmp_stream_buff + zip->tmp_stream_bytes_avail - size0;
+ size1 = zip->sub_stream_bytes_remaining[0];
+ buf1 = zip->sub_stream_buff[0] + zip->sub_stream_size[0] - size1;
+ size2 = zip->sub_stream_bytes_remaining[1];
+ buf2 = zip->sub_stream_buff[1] + zip->sub_stream_size[1] - size2;
+ size3 = zip->sub_stream_bytes_remaining[2];
+ buf3 = zip->sub_stream_buff[2] + zip->sub_stream_size[2] - size3;
+
+ buffer = buf3;
+ bufferLim = buffer + size3;
+
+ if (zip->bcj_state == 0) {
+ /*
+ * Initialize.
+ */
+ zip->bcj2_prevByte = 0;
+ for (i = 0;
+ i < sizeof(zip->bcj2_p) / sizeof(zip->bcj2_p[0]); i++)
+ zip->bcj2_p[i] = kBitModelTotal >> 1;
+ RC_INIT2;
+ zip->bcj_state = 1;
+ }
+
+ /*
+ * Gather the odd bytes of a previous call.
+ */
+ for (i = 0; zip->odd_bcj_size > 0 && outPos < outSize; i++) {
+ outBuf[outPos++] = zip->odd_bcj[i];
+ zip->odd_bcj_size--;
+ }
+
+ if (outSize == 0) {
+ zip->bcj2_outPos += outPos;
+ return (outPos);
+ }
+
+ for (;;) {
+ uint8_t b;
+ CProb *prob;
+ uint32_t bound;
+ uint32_t ttt;
+
+ size_t limit = size0 - inPos;
+ if (outSize - outPos < limit)
+ limit = outSize - outPos;
+
+ if (zip->bcj_state == 1) {
+ while (limit != 0) {
+ uint8_t bb = buf0[inPos];
+ outBuf[outPos++] = bb;
+ if (IsJ(zip->bcj2_prevByte, bb)) {
+ zip->bcj_state = 2;
+ break;
+ }
+ inPos++;
+ zip->bcj2_prevByte = bb;
+ limit--;
+ }
+ }
+
+ if (limit == 0 || outPos == outSize)
+ break;
+ zip->bcj_state = 1;
+
+ b = buf0[inPos++];
+
+ if (b == 0xE8)
+ prob = zip->bcj2_p + zip->bcj2_prevByte;
+ else if (b == 0xE9)
+ prob = zip->bcj2_p + 256;
+ else
+ prob = zip->bcj2_p + 257;
+
+ IF_BIT_0(prob) {
+ UPDATE_0(prob)
+ zip->bcj2_prevByte = b;
+ } else {
+ uint32_t dest;
+ const uint8_t *v;
+ uint8_t out[4];
+
+ UPDATE_1(prob)
+ if (b == 0xE8) {
+ v = buf1;
+ if (size1 < 4)
+ return SZ_ERROR_DATA;
+ buf1 += 4;
+ size1 -= 4;
+ } else {
+ v = buf2;
+ if (size2 < 4)
+ return SZ_ERROR_DATA;
+ buf2 += 4;
+ size2 -= 4;
+ }
+ dest = (((uint32_t)v[0] << 24) |
+ ((uint32_t)v[1] << 16) |
+ ((uint32_t)v[2] << 8) |
+ ((uint32_t)v[3])) -
+ ((uint32_t)zip->bcj2_outPos + (uint32_t)outPos + 4);
+ out[0] = (uint8_t)dest;
+ out[1] = (uint8_t)(dest >> 8);
+ out[2] = (uint8_t)(dest >> 16);
+ out[3] = zip->bcj2_prevByte = (uint8_t)(dest >> 24);
+
+ for (i = 0; i < 4 && outPos < outSize; i++)
+ outBuf[outPos++] = out[i];
+ if (i < 4) {
+ /*
+ * Save odd bytes which we could not add into
+ * the output buffer because of out of space.
+ */
+ zip->odd_bcj_size = 4 -i;
+ for (; i < 4; i++) {
+ j = i - 4 + (unsigned)zip->odd_bcj_size;
+ zip->odd_bcj[j] = out[i];
+ }
+ break;
+ }
+ }
+ }
+ zip->tmp_stream_bytes_remaining -= inPos;
+ zip->sub_stream_bytes_remaining[0] = size1;
+ zip->sub_stream_bytes_remaining[1] = size2;
+ zip->sub_stream_bytes_remaining[2] = bufferLim - buffer;
+ zip->bcj2_outPos += outPos;
+
+ return ((ssize_t)outPos);
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_all.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_all.c
new file mode 100644
index 00000000000..dea558bbfcc
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_all.c
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 2003-2011 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_all.c 174991 2007-12-30 04:58:22Z kientzle $");
+
+#include "archive.h"
+#include "archive_private.h"
+
+int
+archive_read_support_format_all(struct archive *a)
+{
+ archive_check_magic(a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_all");
+
+ /* TODO: It would be nice to compute the ordering
+ * here automatically so that people who enable just
+ * a few formats can still get the benefits. That
+ * may just require the format registration to include
+ * a "maximum read-ahead" value (anything that uses seek
+ * would be essentially infinite read-ahead). The core
+ * bid management can then sort the bidders before calling
+ * them.
+ *
+ * If you implement the above, please return the list below
+ * to alphabetic order.
+ */
+
+ /*
+ * These bidders are all pretty cheap; they just examine a
+ * small initial part of the archive. If one of these bids
+ * high, we can maybe avoid running any of the more expensive
+ * bidders below.
+ */
+ archive_read_support_format_ar(a);
+ archive_read_support_format_cpio(a);
+ archive_read_support_format_empty(a);
+ archive_read_support_format_lha(a);
+ archive_read_support_format_mtree(a);
+ archive_read_support_format_tar(a);
+ archive_read_support_format_xar(a);
+ archive_read_support_format_warc(a);
+
+ /*
+ * Install expensive bidders last. By doing them last, we
+ * increase the chance that a high bid from someone else will
+ * make it unnecessary for these to do anything at all.
+ */
+ /* These three have potentially large look-ahead. */
+ archive_read_support_format_7zip(a);
+ archive_read_support_format_cab(a);
+ archive_read_support_format_rar(a);
+ archive_read_support_format_rar5(a);
+ archive_read_support_format_iso9660(a);
+ /* Seek is really bad, since it forces the read-ahead
+ * logic to discard buffered data. */
+ archive_read_support_format_zip(a);
+
+ /* Note: We always return ARCHIVE_OK here, even if some of the
+ * above return ARCHIVE_WARN. The intent here is to enable
+ * "as much as possible." Clients who need specific
+ * compression should enable those individually so they can
+ * verify the level of support. */
+ /* Clear any warning messages set by the above functions. */
+ archive_clear_error(a);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_ar.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_ar.c
new file mode 100644
index 00000000000..296b7db0411
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_ar.c
@@ -0,0 +1,638 @@
+/*-
+ * Copyright (c) 2007 Kai Wang
+ * Copyright (c) 2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_ar.c 201101 2009-12-28 03:06:27Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+struct ar {
+ int64_t entry_bytes_remaining;
+ /* unconsumed is purely to track data we've gotten from readahead,
+ * but haven't yet marked as consumed. Must be paired with
+ * entry_bytes_remaining usage/modification.
+ */
+ size_t entry_bytes_unconsumed;
+ int64_t entry_offset;
+ int64_t entry_padding;
+ char *strtab;
+ size_t strtab_size;
+ char read_global_header;
+};
+
+/*
+ * Define structure of the "ar" header.
+ */
+#define AR_name_offset 0
+#define AR_name_size 16
+#define AR_date_offset 16
+#define AR_date_size 12
+#define AR_uid_offset 28
+#define AR_uid_size 6
+#define AR_gid_offset 34
+#define AR_gid_size 6
+#define AR_mode_offset 40
+#define AR_mode_size 8
+#define AR_size_offset 48
+#define AR_size_size 10
+#define AR_fmag_offset 58
+#define AR_fmag_size 2
+
+static int archive_read_format_ar_bid(struct archive_read *a, int);
+static int archive_read_format_ar_cleanup(struct archive_read *a);
+static int archive_read_format_ar_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset);
+static int archive_read_format_ar_skip(struct archive_read *a);
+static int archive_read_format_ar_read_header(struct archive_read *a,
+ struct archive_entry *e);
+static uint64_t ar_atol8(const char *p, unsigned char_cnt);
+static uint64_t ar_atol10(const char *p, unsigned char_cnt);
+static int ar_parse_gnu_filename_table(struct archive_read *a);
+static int ar_parse_common_header(struct ar *ar, struct archive_entry *,
+ const char *h);
+
+int
+archive_read_support_format_ar(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct ar *ar;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_ar");
+
+ ar = (struct ar *)calloc(1, sizeof(*ar));
+ if (ar == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ar data");
+ return (ARCHIVE_FATAL);
+ }
+ ar->strtab = NULL;
+
+ r = __archive_read_register_format(a,
+ ar,
+ "ar",
+ archive_read_format_ar_bid,
+ NULL,
+ archive_read_format_ar_read_header,
+ archive_read_format_ar_read_data,
+ archive_read_format_ar_skip,
+ NULL,
+ archive_read_format_ar_cleanup,
+ NULL,
+ NULL);
+
+ if (r != ARCHIVE_OK) {
+ free(ar);
+ return (r);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_ar_cleanup(struct archive_read *a)
+{
+ struct ar *ar;
+
+ ar = (struct ar *)(a->format->data);
+ free(ar->strtab);
+ free(ar);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_ar_bid(struct archive_read *a, int best_bid)
+{
+ const void *h;
+
+ (void)best_bid; /* UNUSED */
+
+ /*
+ * Verify the 8-byte file signature.
+ * TODO: Do we need to check more than this?
+ */
+ if ((h = __archive_read_ahead(a, 8, NULL)) == NULL)
+ return (-1);
+ if (memcmp(h, "!<arch>\n", 8) == 0) {
+ return (64);
+ }
+ return (-1);
+}
+
+static int
+_ar_read_header(struct archive_read *a, struct archive_entry *entry,
+ struct ar *ar, const char *h, size_t *unconsumed)
+{
+ char filename[AR_name_size + 1];
+ uint64_t number; /* Used to hold parsed numbers before validation. */
+ size_t bsd_name_length, entry_size;
+ char *p, *st;
+ const void *b;
+ int r;
+
+ /* Verify the magic signature on the file header. */
+ if (strncmp(h + AR_fmag_offset, "`\n", 2) != 0) {
+ archive_set_error(&a->archive, EINVAL,
+ "Incorrect file header signature");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Copy filename into work buffer. */
+ strncpy(filename, h + AR_name_offset, AR_name_size);
+ filename[AR_name_size] = '\0';
+
+ /*
+ * Guess the format variant based on the filename.
+ */
+ if (a->archive.archive_format == ARCHIVE_FORMAT_AR) {
+ /* We don't already know the variant, so let's guess. */
+ /*
+ * Biggest clue is presence of '/': GNU starts special
+ * filenames with '/', appends '/' as terminator to
+ * non-special names, so anything with '/' should be
+ * GNU except for BSD long filenames.
+ */
+ if (strncmp(filename, "#1/", 3) == 0)
+ a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
+ else if (strchr(filename, '/') != NULL)
+ a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU;
+ else if (strncmp(filename, "__.SYMDEF", 9) == 0)
+ a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
+ /*
+ * XXX Do GNU/SVR4 'ar' programs ever omit trailing '/'
+ * if name exactly fills 16-byte field? If so, we
+ * can't assume entries without '/' are BSD. XXX
+ */
+ }
+
+ /* Update format name from the code. */
+ if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU)
+ a->archive.archive_format_name = "ar (GNU/SVR4)";
+ else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD)
+ a->archive.archive_format_name = "ar (BSD)";
+ else
+ a->archive.archive_format_name = "ar";
+
+ /*
+ * Remove trailing spaces from the filename. GNU and BSD
+ * variants both pad filename area out with spaces.
+ * This will only be wrong if GNU/SVR4 'ar' implementations
+ * omit trailing '/' for 16-char filenames and we have
+ * a 16-char filename that ends in ' '.
+ */
+ p = filename + AR_name_size - 1;
+ while (p >= filename && *p == ' ') {
+ *p = '\0';
+ p--;
+ }
+
+ /*
+ * Remove trailing slash unless first character is '/'.
+ * (BSD entries never end in '/', so this will only trim
+ * GNU-format entries. GNU special entries start with '/'
+ * and are not terminated in '/', so we don't trim anything
+ * that starts with '/'.)
+ */
+ if (filename[0] != '/' && p > filename && *p == '/') {
+ *p = '\0';
+ }
+
+ if (p < filename) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Found entry with empty filename");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * '//' is the GNU filename table.
+ * Later entries can refer to names in this table.
+ */
+ if (strcmp(filename, "//") == 0) {
+ /* This must come before any call to _read_ahead. */
+ ar_parse_common_header(ar, entry, h);
+ archive_entry_copy_pathname(entry, filename);
+ archive_entry_set_filetype(entry, AE_IFREG);
+ /* Get the size of the filename table. */
+ number = ar_atol10(h + AR_size_offset, AR_size_size);
+ if (number > SIZE_MAX || number > 1024 * 1024 * 1024) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Filename table too large");
+ return (ARCHIVE_FATAL);
+ }
+ entry_size = (size_t)number;
+ if (entry_size == 0) {
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid string table");
+ return (ARCHIVE_FATAL);
+ }
+ if (ar->strtab != NULL) {
+ archive_set_error(&a->archive, EINVAL,
+ "More than one string tables exist");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Read the filename table into memory. */
+ st = malloc(entry_size);
+ if (st == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate filename table buffer");
+ return (ARCHIVE_FATAL);
+ }
+ ar->strtab = st;
+ ar->strtab_size = entry_size;
+
+ if (*unconsumed) {
+ __archive_read_consume(a, *unconsumed);
+ *unconsumed = 0;
+ }
+
+ if ((b = __archive_read_ahead(a, entry_size, NULL)) == NULL)
+ return (ARCHIVE_FATAL);
+ memcpy(st, b, entry_size);
+ __archive_read_consume(a, entry_size);
+ /* All contents are consumed. */
+ ar->entry_bytes_remaining = 0;
+ archive_entry_set_size(entry, ar->entry_bytes_remaining);
+
+ /* Parse the filename table. */
+ return (ar_parse_gnu_filename_table(a));
+ }
+
+ /*
+ * GNU variant handles long filenames by storing /<number>
+ * to indicate a name stored in the filename table.
+ * XXX TODO: Verify that it's all digits... Don't be fooled
+ * by "/9xyz" XXX
+ */
+ if (filename[0] == '/' && filename[1] >= '0' && filename[1] <= '9') {
+ number = ar_atol10(h + AR_name_offset + 1, AR_name_size - 1);
+ /*
+ * If we can't look up the real name, warn and return
+ * the entry with the wrong name.
+ */
+ if (ar->strtab == NULL || number >= ar->strtab_size) {
+ archive_set_error(&a->archive, EINVAL,
+ "Can't find long filename for GNU/SVR4 archive entry");
+ archive_entry_copy_pathname(entry, filename);
+ /* Parse the time, owner, mode, size fields. */
+ ar_parse_common_header(ar, entry, h);
+ return (ARCHIVE_FATAL);
+ }
+
+ archive_entry_copy_pathname(entry, &ar->strtab[(size_t)number]);
+ /* Parse the time, owner, mode, size fields. */
+ return (ar_parse_common_header(ar, entry, h));
+ }
+
+ /*
+ * BSD handles long filenames by storing "#1/" followed by the
+ * length of filename as a decimal number, then prepends the
+ * the filename to the file contents.
+ */
+ if (strncmp(filename, "#1/", 3) == 0) {
+ /* Parse the time, owner, mode, size fields. */
+ /* This must occur before _read_ahead is called again. */
+ ar_parse_common_header(ar, entry, h);
+
+ /* Parse the size of the name, adjust the file size. */
+ number = ar_atol10(h + AR_name_offset + 3, AR_name_size - 3);
+ /* Sanity check the filename length:
+ * = Must be <= SIZE_MAX - 1
+ * = Must be <= 1MB
+ * = Cannot be bigger than the entire entry
+ */
+ if (number > SIZE_MAX - 1
+ || number > 1024 * 1024
+ || (int64_t)number > ar->entry_bytes_remaining) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Bad input file size");
+ return (ARCHIVE_FATAL);
+ }
+ bsd_name_length = (size_t)number;
+ ar->entry_bytes_remaining -= bsd_name_length;
+ /* Adjust file size reported to client. */
+ archive_entry_set_size(entry, ar->entry_bytes_remaining);
+
+ if (*unconsumed) {
+ __archive_read_consume(a, *unconsumed);
+ *unconsumed = 0;
+ }
+
+ /* Read the long name into memory. */
+ if ((b = __archive_read_ahead(a, bsd_name_length, NULL)) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Truncated input file");
+ return (ARCHIVE_FATAL);
+ }
+ /* Store it in the entry. */
+ p = (char *)malloc(bsd_name_length + 1);
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate fname buffer");
+ return (ARCHIVE_FATAL);
+ }
+ strncpy(p, b, bsd_name_length);
+ p[bsd_name_length] = '\0';
+
+ __archive_read_consume(a, bsd_name_length);
+
+ archive_entry_copy_pathname(entry, p);
+ free(p);
+ return (ARCHIVE_OK);
+ }
+
+ /*
+ * "/" is the SVR4/GNU archive symbol table.
+ * "/SYM64/" is the SVR4/GNU 64-bit variant archive symbol table.
+ */
+ if (strcmp(filename, "/") == 0 || strcmp(filename, "/SYM64/") == 0) {
+ archive_entry_copy_pathname(entry, filename);
+ /* Parse the time, owner, mode, size fields. */
+ r = ar_parse_common_header(ar, entry, h);
+ /* Force the file type to a regular file. */
+ archive_entry_set_filetype(entry, AE_IFREG);
+ return (r);
+ }
+
+ /*
+ * "__.SYMDEF" is a BSD archive symbol table.
+ */
+ if (strcmp(filename, "__.SYMDEF") == 0) {
+ archive_entry_copy_pathname(entry, filename);
+ /* Parse the time, owner, mode, size fields. */
+ return (ar_parse_common_header(ar, entry, h));
+ }
+
+ /*
+ * Otherwise, this is a standard entry. The filename
+ * has already been trimmed as much as possible, based
+ * on our current knowledge of the format.
+ */
+ archive_entry_copy_pathname(entry, filename);
+ return (ar_parse_common_header(ar, entry, h));
+}
+
+static int
+archive_read_format_ar_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct ar *ar = (struct ar*)(a->format->data);
+ size_t unconsumed;
+ const void *header_data;
+ int ret;
+
+ if (!ar->read_global_header) {
+ /*
+ * We are now at the beginning of the archive,
+ * so we need first consume the ar global header.
+ */
+ __archive_read_consume(a, 8);
+ ar->read_global_header = 1;
+ /* Set a default format code for now. */
+ a->archive.archive_format = ARCHIVE_FORMAT_AR;
+ }
+
+ /* Read the header for the next file entry. */
+ if ((header_data = __archive_read_ahead(a, 60, NULL)) == NULL)
+ /* Broken header. */
+ return (ARCHIVE_EOF);
+
+ unconsumed = 60;
+
+ ret = _ar_read_header(a, entry, ar, (const char *)header_data, &unconsumed);
+
+ if (unconsumed)
+ __archive_read_consume(a, unconsumed);
+
+ return ret;
+}
+
+
+static int
+ar_parse_common_header(struct ar *ar, struct archive_entry *entry,
+ const char *h)
+{
+ uint64_t n;
+
+ /* Copy remaining header */
+ archive_entry_set_filetype(entry, AE_IFREG);
+ archive_entry_set_mtime(entry,
+ (time_t)ar_atol10(h + AR_date_offset, AR_date_size), 0L);
+ archive_entry_set_uid(entry,
+ (uid_t)ar_atol10(h + AR_uid_offset, AR_uid_size));
+ archive_entry_set_gid(entry,
+ (gid_t)ar_atol10(h + AR_gid_offset, AR_gid_size));
+ archive_entry_set_mode(entry,
+ (mode_t)ar_atol8(h + AR_mode_offset, AR_mode_size));
+ n = ar_atol10(h + AR_size_offset, AR_size_size);
+
+ ar->entry_offset = 0;
+ ar->entry_padding = n % 2;
+ archive_entry_set_size(entry, n);
+ ar->entry_bytes_remaining = n;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_ar_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ ssize_t bytes_read;
+ struct ar *ar;
+
+ ar = (struct ar *)(a->format->data);
+
+ if (ar->entry_bytes_unconsumed) {
+ __archive_read_consume(a, ar->entry_bytes_unconsumed);
+ ar->entry_bytes_unconsumed = 0;
+ }
+
+ if (ar->entry_bytes_remaining > 0) {
+ *buff = __archive_read_ahead(a, 1, &bytes_read);
+ if (bytes_read == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Truncated ar archive");
+ return (ARCHIVE_FATAL);
+ }
+ if (bytes_read < 0)
+ return (ARCHIVE_FATAL);
+ if (bytes_read > ar->entry_bytes_remaining)
+ bytes_read = (ssize_t)ar->entry_bytes_remaining;
+ *size = bytes_read;
+ ar->entry_bytes_unconsumed = bytes_read;
+ *offset = ar->entry_offset;
+ ar->entry_offset += bytes_read;
+ ar->entry_bytes_remaining -= bytes_read;
+ return (ARCHIVE_OK);
+ } else {
+ int64_t skipped = __archive_read_consume(a, ar->entry_padding);
+ if (skipped >= 0) {
+ ar->entry_padding -= skipped;
+ }
+ if (ar->entry_padding) {
+ if (skipped >= 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Truncated ar archive- failed consuming padding");
+ }
+ return (ARCHIVE_FATAL);
+ }
+ *buff = NULL;
+ *size = 0;
+ *offset = ar->entry_offset;
+ return (ARCHIVE_EOF);
+ }
+}
+
+static int
+archive_read_format_ar_skip(struct archive_read *a)
+{
+ int64_t bytes_skipped;
+ struct ar* ar;
+
+ ar = (struct ar *)(a->format->data);
+
+ bytes_skipped = __archive_read_consume(a,
+ ar->entry_bytes_remaining + ar->entry_padding
+ + ar->entry_bytes_unconsumed);
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+
+ ar->entry_bytes_remaining = 0;
+ ar->entry_bytes_unconsumed = 0;
+ ar->entry_padding = 0;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+ar_parse_gnu_filename_table(struct archive_read *a)
+{
+ struct ar *ar;
+ char *p;
+ size_t size;
+
+ ar = (struct ar*)(a->format->data);
+ size = ar->strtab_size;
+
+ for (p = ar->strtab; p < ar->strtab + size - 1; ++p) {
+ if (*p == '/') {
+ *p++ = '\0';
+ if (*p != '\n')
+ goto bad_string_table;
+ *p = '\0';
+ }
+ }
+ /*
+ * GNU ar always pads the table to an even size.
+ * The pad character is either '\n' or '`'.
+ */
+ if (p != ar->strtab + size && *p != '\n' && *p != '`')
+ goto bad_string_table;
+
+ /* Enforce zero termination. */
+ ar->strtab[size - 1] = '\0';
+
+ return (ARCHIVE_OK);
+
+bad_string_table:
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid string table");
+ free(ar->strtab);
+ ar->strtab = NULL;
+ return (ARCHIVE_FATAL);
+}
+
+static uint64_t
+ar_atol8(const char *p, unsigned char_cnt)
+{
+ uint64_t l, limit, last_digit_limit;
+ unsigned int digit, base;
+
+ base = 8;
+ limit = UINT64_MAX / base;
+ last_digit_limit = UINT64_MAX % base;
+
+ while ((*p == ' ' || *p == '\t') && char_cnt-- > 0)
+ p++;
+
+ l = 0;
+ digit = *p - '0';
+ while (*p >= '0' && digit < base && char_cnt-- > 0) {
+ if (l>limit || (l == limit && digit > last_digit_limit)) {
+ l = UINT64_MAX; /* Truncate on overflow. */
+ break;
+ }
+ l = (l * base) + digit;
+ digit = *++p - '0';
+ }
+ return (l);
+}
+
+static uint64_t
+ar_atol10(const char *p, unsigned char_cnt)
+{
+ uint64_t l, limit, last_digit_limit;
+ unsigned int base, digit;
+
+ base = 10;
+ limit = UINT64_MAX / base;
+ last_digit_limit = UINT64_MAX % base;
+
+ while ((*p == ' ' || *p == '\t') && char_cnt-- > 0)
+ p++;
+ l = 0;
+ digit = *p - '0';
+ while (*p >= '0' && digit < base && char_cnt-- > 0) {
+ if (l > limit || (l == limit && digit > last_digit_limit)) {
+ l = UINT64_MAX; /* Truncate on overflow. */
+ break;
+ }
+ l = (l * base) + digit;
+ digit = *++p - '0';
+ }
+ return (l);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_by_code.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_by_code.c
new file mode 100644
index 00000000000..89e96f1f591
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_by_code.c
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 2003-2011 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+int
+archive_read_support_format_by_code(struct archive *a, int format_code)
+{
+ archive_check_magic(a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_by_code");
+
+ switch (format_code & ARCHIVE_FORMAT_BASE_MASK) {
+ case ARCHIVE_FORMAT_7ZIP:
+ return archive_read_support_format_7zip(a);
+ break;
+ case ARCHIVE_FORMAT_AR:
+ return archive_read_support_format_ar(a);
+ break;
+ case ARCHIVE_FORMAT_CAB:
+ return archive_read_support_format_cab(a);
+ break;
+ case ARCHIVE_FORMAT_CPIO:
+ return archive_read_support_format_cpio(a);
+ break;
+ case ARCHIVE_FORMAT_EMPTY:
+ return archive_read_support_format_empty(a);
+ break;
+ case ARCHIVE_FORMAT_ISO9660:
+ return archive_read_support_format_iso9660(a);
+ break;
+ case ARCHIVE_FORMAT_LHA:
+ return archive_read_support_format_lha(a);
+ break;
+ case ARCHIVE_FORMAT_MTREE:
+ return archive_read_support_format_mtree(a);
+ break;
+ case ARCHIVE_FORMAT_RAR:
+ return archive_read_support_format_rar(a);
+ break;
+ case ARCHIVE_FORMAT_RAR_V5:
+ return archive_read_support_format_rar5(a);
+ break;
+ case ARCHIVE_FORMAT_RAW:
+ return archive_read_support_format_raw(a);
+ break;
+ case ARCHIVE_FORMAT_TAR:
+ return archive_read_support_format_tar(a);
+ break;
+ case ARCHIVE_FORMAT_WARC:
+ return archive_read_support_format_warc(a);
+ break;
+ case ARCHIVE_FORMAT_XAR:
+ return archive_read_support_format_xar(a);
+ break;
+ case ARCHIVE_FORMAT_ZIP:
+ return archive_read_support_format_zip(a);
+ break;
+ }
+ archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+ "Invalid format code specified");
+ return (ARCHIVE_FATAL);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_cab.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_cab.c
new file mode 100644
index 00000000000..3b552a84de1
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_cab.c
@@ -0,0 +1,3228 @@
+/*-
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_endian.h"
+
+
+struct lzx_dec {
+ /* Decoding status. */
+ int state;
+
+ /*
+ * Window to see last decoded data, from 32KBi to 2MBi.
+ */
+ int w_size;
+ int w_mask;
+ /* Window buffer, which is a loop buffer. */
+ unsigned char *w_buff;
+ /* The insert position to the window. */
+ int w_pos;
+ /* The position where we can copy decoded code from the window. */
+ int copy_pos;
+ /* The length how many bytes we can copy decoded code from
+ * the window. */
+ int copy_len;
+ /* Translation reversal for x86 processor CALL byte sequence(E8).
+ * This is used for LZX only. */
+ uint32_t translation_size;
+ char translation;
+ char block_type;
+#define VERBATIM_BLOCK 1
+#define ALIGNED_OFFSET_BLOCK 2
+#define UNCOMPRESSED_BLOCK 3
+ size_t block_size;
+ size_t block_bytes_avail;
+ /* Repeated offset. */
+ int r0, r1, r2;
+ unsigned char rbytes[4];
+ int rbytes_avail;
+ int length_header;
+ int position_slot;
+ int offset_bits;
+
+ struct lzx_pos_tbl {
+ int base;
+ int footer_bits;
+ } *pos_tbl;
+ /*
+ * Bit stream reader.
+ */
+ struct lzx_br {
+#define CACHE_TYPE uint64_t
+#define CACHE_BITS (8 * sizeof(CACHE_TYPE))
+ /* Cache buffer. */
+ CACHE_TYPE cache_buffer;
+ /* Indicates how many bits avail in cache_buffer. */
+ int cache_avail;
+ unsigned char odd;
+ char have_odd;
+ } br;
+
+ /*
+ * Huffman coding.
+ */
+ struct huffman {
+ int len_size;
+ int freq[17];
+ unsigned char *bitlen;
+
+ /*
+ * Use a index table. It's faster than searching a huffman
+ * coding tree, which is a binary tree. But a use of a large
+ * index table causes L1 cache read miss many times.
+ */
+ int max_bits;
+ int tbl_bits;
+ int tree_used;
+ /* Direct access table. */
+ uint16_t *tbl;
+ } at, lt, mt, pt;
+
+ int loop;
+ int error;
+};
+
+static const int slots[] = {
+ 30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290
+};
+#define SLOT_BASE 15
+#define SLOT_MAX 21/*->25*/
+
+struct lzx_stream {
+ const unsigned char *next_in;
+ int64_t avail_in;
+ int64_t total_in;
+ unsigned char *next_out;
+ int64_t avail_out;
+ int64_t total_out;
+ struct lzx_dec *ds;
+};
+
+/*
+ * Cabinet file definitions.
+ */
+/* CFHEADER offset */
+#define CFHEADER_signature 0
+#define CFHEADER_cbCabinet 8
+#define CFHEADER_coffFiles 16
+#define CFHEADER_versionMinor 24
+#define CFHEADER_versionMajor 25
+#define CFHEADER_cFolders 26
+#define CFHEADER_cFiles 28
+#define CFHEADER_flags 30
+#define CFHEADER_setID 32
+#define CFHEADER_iCabinet 34
+#define CFHEADER_cbCFHeader 36
+#define CFHEADER_cbCFFolder 38
+#define CFHEADER_cbCFData 39
+
+/* CFFOLDER offset */
+#define CFFOLDER_coffCabStart 0
+#define CFFOLDER_cCFData 4
+#define CFFOLDER_typeCompress 6
+#define CFFOLDER_abReserve 8
+
+/* CFFILE offset */
+#define CFFILE_cbFile 0
+#define CFFILE_uoffFolderStart 4
+#define CFFILE_iFolder 8
+#define CFFILE_date_time 10
+#define CFFILE_attribs 14
+
+/* CFDATA offset */
+#define CFDATA_csum 0
+#define CFDATA_cbData 4
+#define CFDATA_cbUncomp 6
+
+static const char * const compression_name[] = {
+ "NONE",
+ "MSZIP",
+ "Quantum",
+ "LZX",
+};
+
+struct cfdata {
+ /* Sum value of this CFDATA. */
+ uint32_t sum;
+ uint16_t compressed_size;
+ uint16_t compressed_bytes_remaining;
+ uint16_t uncompressed_size;
+ uint16_t uncompressed_bytes_remaining;
+ /* To know how many bytes we have decompressed. */
+ uint16_t uncompressed_avail;
+ /* Offset from the beginning of compressed data of this CFDATA */
+ uint16_t read_offset;
+ int64_t unconsumed;
+ /* To keep memory image of this CFDATA to compute the sum. */
+ size_t memimage_size;
+ unsigned char *memimage;
+ /* Result of calculation of sum. */
+ uint32_t sum_calculated;
+ unsigned char sum_extra[4];
+ int sum_extra_avail;
+ const void *sum_ptr;
+};
+
+struct cffolder {
+ uint32_t cfdata_offset_in_cab;
+ uint16_t cfdata_count;
+ uint16_t comptype;
+#define COMPTYPE_NONE 0x0000
+#define COMPTYPE_MSZIP 0x0001
+#define COMPTYPE_QUANTUM 0x0002
+#define COMPTYPE_LZX 0x0003
+ uint16_t compdata;
+ const char *compname;
+ /* At the time reading CFDATA */
+ struct cfdata cfdata;
+ int cfdata_index;
+ /* Flags to mark progress of decompression. */
+ char decompress_init;
+};
+
+struct cffile {
+ uint32_t uncompressed_size;
+ uint32_t offset;
+ time_t mtime;
+ uint16_t folder;
+#define iFoldCONTINUED_FROM_PREV 0xFFFD
+#define iFoldCONTINUED_TO_NEXT 0xFFFE
+#define iFoldCONTINUED_PREV_AND_NEXT 0xFFFF
+ unsigned char attr;
+#define ATTR_RDONLY 0x01
+#define ATTR_NAME_IS_UTF 0x80
+ struct archive_string pathname;
+};
+
+struct cfheader {
+ /* Total bytes of all file size in a Cabinet. */
+ uint32_t total_bytes;
+ uint32_t files_offset;
+ uint16_t folder_count;
+ uint16_t file_count;
+ uint16_t flags;
+#define PREV_CABINET 0x0001
+#define NEXT_CABINET 0x0002
+#define RESERVE_PRESENT 0x0004
+ uint16_t setid;
+ uint16_t cabinet;
+ /* Version number. */
+ unsigned char major;
+ unsigned char minor;
+ unsigned char cffolder;
+ unsigned char cfdata;
+ /* All folders in a cabinet. */
+ struct cffolder *folder_array;
+ /* All files in a cabinet. */
+ struct cffile *file_array;
+ int file_index;
+};
+
+struct cab {
+ /* entry_bytes_remaining is the number of bytes we expect. */
+ int64_t entry_offset;
+ int64_t entry_bytes_remaining;
+ int64_t entry_unconsumed;
+ int64_t entry_compressed_bytes_read;
+ int64_t entry_uncompressed_bytes_read;
+ struct cffolder *entry_cffolder;
+ struct cffile *entry_cffile;
+ struct cfdata *entry_cfdata;
+
+ /* Offset from beginning of a cabinet file. */
+ int64_t cab_offset;
+ struct cfheader cfheader;
+ struct archive_wstring ws;
+
+ /* Flag to mark progress that an archive was read their first header.*/
+ char found_header;
+ char end_of_archive;
+ char end_of_entry;
+ char end_of_entry_cleanup;
+ char read_data_invoked;
+ int64_t bytes_skipped;
+
+ unsigned char *uncompressed_buffer;
+ size_t uncompressed_buffer_size;
+
+ int init_default_conversion;
+ struct archive_string_conv *sconv;
+ struct archive_string_conv *sconv_default;
+ struct archive_string_conv *sconv_utf8;
+ char format_name[64];
+
+#ifdef HAVE_ZLIB_H
+ z_stream stream;
+ char stream_valid;
+#endif
+ struct lzx_stream xstrm;
+};
+
+static int archive_read_format_cab_bid(struct archive_read *, int);
+static int archive_read_format_cab_options(struct archive_read *,
+ const char *, const char *);
+static int archive_read_format_cab_read_header(struct archive_read *,
+ struct archive_entry *);
+static int archive_read_format_cab_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_cab_read_data_skip(struct archive_read *);
+static int archive_read_format_cab_cleanup(struct archive_read *);
+
+static int cab_skip_sfx(struct archive_read *);
+static time_t cab_dos_time(const unsigned char *);
+static int cab_read_data(struct archive_read *, const void **,
+ size_t *, int64_t *);
+static int cab_read_header(struct archive_read *);
+static uint32_t cab_checksum_cfdata_4(const void *, size_t bytes, uint32_t);
+static uint32_t cab_checksum_cfdata(const void *, size_t bytes, uint32_t);
+static void cab_checksum_update(struct archive_read *, size_t);
+static int cab_checksum_finish(struct archive_read *);
+static int cab_next_cfdata(struct archive_read *);
+static const void *cab_read_ahead_cfdata(struct archive_read *, ssize_t *);
+static const void *cab_read_ahead_cfdata_none(struct archive_read *, ssize_t *);
+static const void *cab_read_ahead_cfdata_deflate(struct archive_read *,
+ ssize_t *);
+static const void *cab_read_ahead_cfdata_lzx(struct archive_read *,
+ ssize_t *);
+static int64_t cab_consume_cfdata(struct archive_read *, int64_t);
+static int64_t cab_minimum_consume_cfdata(struct archive_read *, int64_t);
+static int lzx_decode_init(struct lzx_stream *, int);
+static int lzx_read_blocks(struct lzx_stream *, int);
+static int lzx_decode_blocks(struct lzx_stream *, int);
+static void lzx_decode_free(struct lzx_stream *);
+static void lzx_translation(struct lzx_stream *, void *, size_t, uint32_t);
+static void lzx_cleanup_bitstream(struct lzx_stream *);
+static int lzx_decode(struct lzx_stream *, int);
+static int lzx_read_pre_tree(struct lzx_stream *);
+static int lzx_read_bitlen(struct lzx_stream *, struct huffman *, int);
+static int lzx_huffman_init(struct huffman *, size_t, int);
+static void lzx_huffman_free(struct huffman *);
+static int lzx_make_huffman_table(struct huffman *);
+static inline int lzx_decode_huffman(struct huffman *, unsigned);
+
+
+int
+archive_read_support_format_cab(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct cab *cab;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_cab");
+
+ cab = (struct cab *)calloc(1, sizeof(*cab));
+ if (cab == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate CAB data");
+ return (ARCHIVE_FATAL);
+ }
+ archive_string_init(&cab->ws);
+ archive_wstring_ensure(&cab->ws, 256);
+
+ r = __archive_read_register_format(a,
+ cab,
+ "cab",
+ archive_read_format_cab_bid,
+ archive_read_format_cab_options,
+ archive_read_format_cab_read_header,
+ archive_read_format_cab_read_data,
+ archive_read_format_cab_read_data_skip,
+ NULL,
+ archive_read_format_cab_cleanup,
+ NULL,
+ NULL);
+
+ if (r != ARCHIVE_OK)
+ free(cab);
+ return (ARCHIVE_OK);
+}
+
+static int
+find_cab_magic(const char *p)
+{
+ switch (p[4]) {
+ case 0:
+ /*
+ * Note: Self-Extraction program has 'MSCF' string in their
+ * program. If we were finding 'MSCF' string only, we got
+ * wrong place for Cabinet header, thus, we have to check
+ * following four bytes which are reserved and must be set
+ * to zero.
+ */
+ if (memcmp(p, "MSCF\0\0\0\0", 8) == 0)
+ return 0;
+ return 5;
+ case 'F': return 1;
+ case 'C': return 2;
+ case 'S': return 3;
+ case 'M': return 4;
+ default: return 5;
+ }
+}
+
+static int
+archive_read_format_cab_bid(struct archive_read *a, int best_bid)
+{
+ const char *p;
+ ssize_t bytes_avail, offset, window;
+
+ /* If there's already a better bid than we can ever
+ make, don't bother testing. */
+ if (best_bid > 64)
+ return (-1);
+
+ if ((p = __archive_read_ahead(a, 8, NULL)) == NULL)
+ return (-1);
+
+ if (memcmp(p, "MSCF\0\0\0\0", 8) == 0)
+ return (64);
+
+ /*
+ * Attempt to handle self-extracting archives
+ * by noting a PE header and searching forward
+ * up to 128k for a 'MSCF' marker.
+ */
+ if (p[0] == 'M' && p[1] == 'Z') {
+ offset = 0;
+ window = 4096;
+ while (offset < (1024 * 128)) {
+ const char *h = __archive_read_ahead(a, offset + window,
+ &bytes_avail);
+ if (h == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < 128)
+ return (0);
+ continue;
+ }
+ p = h + offset;
+ while (p + 8 < h + bytes_avail) {
+ int next;
+ if ((next = find_cab_magic(p)) == 0)
+ return (64);
+ p += next;
+ }
+ offset = p - h;
+ }
+ }
+ return (0);
+}
+
+static int
+archive_read_format_cab_options(struct archive_read *a,
+ const char *key, const char *val)
+{
+ struct cab *cab;
+ int ret = ARCHIVE_FAILED;
+
+ cab = (struct cab *)(a->format->data);
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "cab: hdrcharset option needs a character-set name");
+ else {
+ cab->sconv = archive_string_conversion_from_charset(
+ &a->archive, val, 0);
+ if (cab->sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+cab_skip_sfx(struct archive_read *a)
+{
+ const char *p, *q;
+ size_t skip;
+ ssize_t bytes, window;
+
+ window = 4096;
+ for (;;) {
+ const char *h = __archive_read_ahead(a, window, &bytes);
+ if (h == NULL) {
+ /* Remaining size are less than window. */
+ window >>= 1;
+ if (window < 128) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Couldn't find out CAB header");
+ return (ARCHIVE_FATAL);
+ }
+ continue;
+ }
+ p = h;
+ q = p + bytes;
+
+ /*
+ * Scan ahead until we find something that looks
+ * like the cab header.
+ */
+ while (p + 8 < q) {
+ int next;
+ if ((next = find_cab_magic(p)) == 0) {
+ skip = p - h;
+ __archive_read_consume(a, skip);
+ return (ARCHIVE_OK);
+ }
+ p += next;
+ }
+ skip = p - h;
+ __archive_read_consume(a, skip);
+ }
+}
+
+static int
+truncated_error(struct archive_read *a)
+{
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated CAB header");
+ return (ARCHIVE_FATAL);
+}
+
+static ssize_t
+cab_strnlen(const unsigned char *p, size_t maxlen)
+{
+ size_t i;
+
+ for (i = 0; i <= maxlen; i++) {
+ if (p[i] == 0)
+ break;
+ }
+ if (i > maxlen)
+ return (-1);/* invalid */
+ return ((ssize_t)i);
+}
+
+/* Read bytes as much as remaining. */
+static const void *
+cab_read_ahead_remaining(struct archive_read *a, size_t min, ssize_t *avail)
+{
+ const void *p;
+
+ while (min > 0) {
+ p = __archive_read_ahead(a, min, avail);
+ if (p != NULL)
+ return (p);
+ min--;
+ }
+ return (NULL);
+}
+
+/* Convert a path separator '\' -> '/' */
+static int
+cab_convert_path_separator_1(struct archive_string *fn, unsigned char attr)
+{
+ size_t i;
+ int mb;
+
+ /* Easy check if we have '\' in multi-byte string. */
+ mb = 0;
+ for (i = 0; i < archive_strlen(fn); i++) {
+ if (fn->s[i] == '\\') {
+ if (mb) {
+ /* This may be second byte of multi-byte
+ * character. */
+ break;
+ }
+ fn->s[i] = '/';
+ mb = 0;
+ } else if ((fn->s[i] & 0x80) && !(attr & ATTR_NAME_IS_UTF))
+ mb = 1;
+ else
+ mb = 0;
+ }
+ if (i == archive_strlen(fn))
+ return (0);
+ return (-1);
+}
+
+/*
+ * Replace a character '\' with '/' in wide character.
+ */
+static void
+cab_convert_path_separator_2(struct cab *cab, struct archive_entry *entry)
+{
+ const wchar_t *wp;
+ size_t i;
+
+ /* If a conversion to wide character failed, force the replacement. */
+ if ((wp = archive_entry_pathname_w(entry)) != NULL) {
+ archive_wstrcpy(&(cab->ws), wp);
+ for (i = 0; i < archive_strlen(&(cab->ws)); i++) {
+ if (cab->ws.s[i] == L'\\')
+ cab->ws.s[i] = L'/';
+ }
+ archive_entry_copy_pathname_w(entry, cab->ws.s);
+ }
+}
+
+/*
+ * Read CFHEADER, CFFOLDER and CFFILE.
+ */
+static int
+cab_read_header(struct archive_read *a)
+{
+ const unsigned char *p;
+ struct cab *cab;
+ struct cfheader *hd;
+ size_t bytes, used;
+ ssize_t len;
+ int64_t skip;
+ int err, i;
+ int cur_folder, prev_folder;
+ uint32_t offset32;
+
+ a->archive.archive_format = ARCHIVE_FORMAT_CAB;
+ if (a->archive.archive_format_name == NULL)
+ a->archive.archive_format_name = "CAB";
+
+ if ((p = __archive_read_ahead(a, 42, NULL)) == NULL)
+ return (truncated_error(a));
+
+ cab = (struct cab *)(a->format->data);
+ if (cab->found_header == 0 &&
+ p[0] == 'M' && p[1] == 'Z') {
+ /* This is an executable? Must be self-extracting... */
+ err = cab_skip_sfx(a);
+ if (err < ARCHIVE_WARN)
+ return (err);
+
+ /* Re-read header after processing the SFX. */
+ if ((p = __archive_read_ahead(a, 42, NULL)) == NULL)
+ return (truncated_error(a));
+ }
+
+ cab->cab_offset = 0;
+ /*
+ * Read CFHEADER.
+ */
+ hd = &cab->cfheader;
+ if (p[CFHEADER_signature+0] != 'M' || p[CFHEADER_signature+1] != 'S' ||
+ p[CFHEADER_signature+2] != 'C' || p[CFHEADER_signature+3] != 'F') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Couldn't find out CAB header");
+ return (ARCHIVE_FATAL);
+ }
+ hd->total_bytes = archive_le32dec(p + CFHEADER_cbCabinet);
+ hd->files_offset = archive_le32dec(p + CFHEADER_coffFiles);
+ hd->minor = p[CFHEADER_versionMinor];
+ hd->major = p[CFHEADER_versionMajor];
+ hd->folder_count = archive_le16dec(p + CFHEADER_cFolders);
+ if (hd->folder_count == 0)
+ goto invalid;
+ hd->file_count = archive_le16dec(p + CFHEADER_cFiles);
+ if (hd->file_count == 0)
+ goto invalid;
+ hd->flags = archive_le16dec(p + CFHEADER_flags);
+ hd->setid = archive_le16dec(p + CFHEADER_setID);
+ hd->cabinet = archive_le16dec(p + CFHEADER_iCabinet);
+ used = CFHEADER_iCabinet + 2;
+ if (hd->flags & RESERVE_PRESENT) {
+ uint16_t cfheader;
+ cfheader = archive_le16dec(p + CFHEADER_cbCFHeader);
+ if (cfheader > 60000U)
+ goto invalid;
+ hd->cffolder = p[CFHEADER_cbCFFolder];
+ hd->cfdata = p[CFHEADER_cbCFData];
+ used += 4;/* cbCFHeader, cbCFFolder and cbCFData */
+ used += cfheader;/* abReserve */
+ } else
+ hd->cffolder = 0;/* Avoid compiling warning. */
+ if (hd->flags & PREV_CABINET) {
+ /* How many bytes are used for szCabinetPrev. */
+ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)
+ return (truncated_error(a));
+ if ((len = cab_strnlen(p + used, 255)) <= 0)
+ goto invalid;
+ used += len + 1;
+ /* How many bytes are used for szDiskPrev. */
+ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)
+ return (truncated_error(a));
+ if ((len = cab_strnlen(p + used, 255)) <= 0)
+ goto invalid;
+ used += len + 1;
+ }
+ if (hd->flags & NEXT_CABINET) {
+ /* How many bytes are used for szCabinetNext. */
+ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)
+ return (truncated_error(a));
+ if ((len = cab_strnlen(p + used, 255)) <= 0)
+ goto invalid;
+ used += len + 1;
+ /* How many bytes are used for szDiskNext. */
+ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)
+ return (truncated_error(a));
+ if ((len = cab_strnlen(p + used, 255)) <= 0)
+ goto invalid;
+ used += len + 1;
+ }
+ __archive_read_consume(a, used);
+ cab->cab_offset += used;
+ used = 0;
+
+ /*
+ * Read CFFOLDER.
+ */
+ hd->folder_array = (struct cffolder *)calloc(
+ hd->folder_count, sizeof(struct cffolder));
+ if (hd->folder_array == NULL)
+ goto nomem;
+
+ bytes = 8;
+ if (hd->flags & RESERVE_PRESENT)
+ bytes += hd->cffolder;
+ bytes *= hd->folder_count;
+ if ((p = __archive_read_ahead(a, bytes, NULL)) == NULL)
+ return (truncated_error(a));
+ offset32 = 0;
+ for (i = 0; i < hd->folder_count; i++) {
+ struct cffolder *folder = &(hd->folder_array[i]);
+ folder->cfdata_offset_in_cab =
+ archive_le32dec(p + CFFOLDER_coffCabStart);
+ folder->cfdata_count = archive_le16dec(p+CFFOLDER_cCFData);
+ folder->comptype =
+ archive_le16dec(p+CFFOLDER_typeCompress) & 0x0F;
+ folder->compdata =
+ archive_le16dec(p+CFFOLDER_typeCompress) >> 8;
+ /* Get a compression name. */
+ if (folder->comptype <
+ sizeof(compression_name) / sizeof(compression_name[0]))
+ folder->compname = compression_name[folder->comptype];
+ else
+ folder->compname = "UNKNOWN";
+ p += 8;
+ used += 8;
+ if (hd->flags & RESERVE_PRESENT) {
+ p += hd->cffolder;/* abReserve */
+ used += hd->cffolder;
+ }
+ /*
+ * Sanity check if each data is acceptable.
+ */
+ if (offset32 >= folder->cfdata_offset_in_cab)
+ goto invalid;
+ offset32 = folder->cfdata_offset_in_cab;
+
+ /* Set a request to initialize zlib for the CFDATA of
+ * this folder. */
+ folder->decompress_init = 0;
+ }
+ __archive_read_consume(a, used);
+ cab->cab_offset += used;
+
+ /*
+ * Read CFFILE.
+ */
+ /* Seek read pointer to the offset of CFFILE if needed. */
+ skip = (int64_t)hd->files_offset - cab->cab_offset;
+ if (skip < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid offset of CFFILE %jd < %jd",
+ (intmax_t)hd->files_offset, (intmax_t)cab->cab_offset);
+ return (ARCHIVE_FATAL);
+ }
+ if (skip) {
+ __archive_read_consume(a, skip);
+ cab->cab_offset += skip;
+ }
+ /* Allocate memory for CFDATA */
+ hd->file_array = (struct cffile *)calloc(
+ hd->file_count, sizeof(struct cffile));
+ if (hd->file_array == NULL)
+ goto nomem;
+
+ prev_folder = -1;
+ for (i = 0; i < hd->file_count; i++) {
+ struct cffile *file = &(hd->file_array[i]);
+ ssize_t avail;
+
+ if ((p = __archive_read_ahead(a, 16, NULL)) == NULL)
+ return (truncated_error(a));
+ file->uncompressed_size = archive_le32dec(p + CFFILE_cbFile);
+ file->offset = archive_le32dec(p + CFFILE_uoffFolderStart);
+ file->folder = archive_le16dec(p + CFFILE_iFolder);
+ file->mtime = cab_dos_time(p + CFFILE_date_time);
+ file->attr = (uint8_t)archive_le16dec(p + CFFILE_attribs);
+ __archive_read_consume(a, 16);
+
+ cab->cab_offset += 16;
+ if ((p = cab_read_ahead_remaining(a, 256, &avail)) == NULL)
+ return (truncated_error(a));
+ if ((len = cab_strnlen(p, avail-1)) <= 0)
+ goto invalid;
+
+ /* Copy a pathname. */
+ archive_string_init(&(file->pathname));
+ archive_strncpy(&(file->pathname), p, len);
+ __archive_read_consume(a, len + 1);
+ cab->cab_offset += len + 1;
+
+ /*
+ * Sanity check if each data is acceptable.
+ */
+ if (file->uncompressed_size > 0x7FFF8000)
+ goto invalid;/* Too large */
+ if ((int64_t)file->offset + (int64_t)file->uncompressed_size
+ > ARCHIVE_LITERAL_LL(0x7FFF8000))
+ goto invalid;/* Too large */
+ switch (file->folder) {
+ case iFoldCONTINUED_TO_NEXT:
+ /* This must be last file in a folder. */
+ if (i != hd->file_count -1)
+ goto invalid;
+ cur_folder = hd->folder_count -1;
+ break;
+ case iFoldCONTINUED_PREV_AND_NEXT:
+ /* This must be only one file in a folder. */
+ if (hd->file_count != 1)
+ goto invalid;
+ /* FALL THROUGH */
+ case iFoldCONTINUED_FROM_PREV:
+ /* This must be first file in a folder. */
+ if (i != 0)
+ goto invalid;
+ prev_folder = cur_folder = 0;
+ offset32 = file->offset;
+ break;
+ default:
+ if (file->folder >= hd->folder_count)
+ goto invalid;
+ cur_folder = file->folder;
+ break;
+ }
+ /* Dot not back track. */
+ if (cur_folder < prev_folder)
+ goto invalid;
+ if (cur_folder != prev_folder)
+ offset32 = 0;
+ prev_folder = cur_folder;
+
+ /* Make sure there are not any blanks from last file
+ * contents. */
+ if (offset32 != file->offset)
+ goto invalid;
+ offset32 += file->uncompressed_size;
+
+ /* CFDATA is available for file contents. */
+ if (file->uncompressed_size > 0 &&
+ hd->folder_array[cur_folder].cfdata_count == 0)
+ goto invalid;
+ }
+
+ if (hd->cabinet != 0 || hd->flags & (PREV_CABINET | NEXT_CABINET)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Multivolume cabinet file is unsupported");
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+invalid:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid CAB header");
+ return (ARCHIVE_FATAL);
+nomem:
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for CAB data");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+archive_read_format_cab_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct cab *cab;
+ struct cfheader *hd;
+ struct cffolder *prev_folder;
+ struct cffile *file;
+ struct archive_string_conv *sconv;
+ int err = ARCHIVE_OK, r;
+
+ cab = (struct cab *)(a->format->data);
+ if (cab->found_header == 0) {
+ err = cab_read_header(a);
+ if (err < ARCHIVE_WARN)
+ return (err);
+ /* We've found the header. */
+ cab->found_header = 1;
+ }
+ hd = &cab->cfheader;
+
+ if (hd->file_index >= hd->file_count) {
+ cab->end_of_archive = 1;
+ return (ARCHIVE_EOF);
+ }
+ file = &hd->file_array[hd->file_index++];
+
+ cab->end_of_entry = 0;
+ cab->end_of_entry_cleanup = 0;
+ cab->entry_compressed_bytes_read = 0;
+ cab->entry_uncompressed_bytes_read = 0;
+ cab->entry_unconsumed = 0;
+ cab->entry_cffile = file;
+
+ /*
+ * Choose a proper folder.
+ */
+ prev_folder = cab->entry_cffolder;
+ switch (file->folder) {
+ case iFoldCONTINUED_FROM_PREV:
+ case iFoldCONTINUED_PREV_AND_NEXT:
+ cab->entry_cffolder = &hd->folder_array[0];
+ break;
+ case iFoldCONTINUED_TO_NEXT:
+ cab->entry_cffolder = &hd->folder_array[hd->folder_count-1];
+ break;
+ default:
+ cab->entry_cffolder = &hd->folder_array[file->folder];
+ break;
+ }
+ /* If a cffolder of this file is changed, reset a cfdata to read
+ * file contents from next cfdata. */
+ if (prev_folder != cab->entry_cffolder)
+ cab->entry_cfdata = NULL;
+
+ /* If a pathname is UTF-8, prepare a string conversion object
+ * for UTF-8 and use it. */
+ if (file->attr & ATTR_NAME_IS_UTF) {
+ if (cab->sconv_utf8 == NULL) {
+ cab->sconv_utf8 =
+ archive_string_conversion_from_charset(
+ &(a->archive), "UTF-8", 1);
+ if (cab->sconv_utf8 == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ sconv = cab->sconv_utf8;
+ } else if (cab->sconv != NULL) {
+ /* Choose the conversion specified by the option. */
+ sconv = cab->sconv;
+ } else {
+ /* Choose the default conversion. */
+ if (!cab->init_default_conversion) {
+ cab->sconv_default =
+ archive_string_default_conversion_for_read(
+ &(a->archive));
+ cab->init_default_conversion = 1;
+ }
+ sconv = cab->sconv_default;
+ }
+
+ /*
+ * Set a default value and common data
+ */
+ r = cab_convert_path_separator_1(&(file->pathname), file->attr);
+ if (archive_entry_copy_pathname_l(entry, file->pathname.s,
+ archive_strlen(&(file->pathname)), sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname cannot be converted "
+ "from %s to current locale.",
+ archive_string_conversion_charset_name(sconv));
+ err = ARCHIVE_WARN;
+ }
+ if (r < 0) {
+ /* Convert a path separator '\' -> '/' */
+ cab_convert_path_separator_2(cab, entry);
+ }
+
+ archive_entry_set_size(entry, file->uncompressed_size);
+ if (file->attr & ATTR_RDONLY)
+ archive_entry_set_mode(entry, AE_IFREG | 0555);
+ else
+ archive_entry_set_mode(entry, AE_IFREG | 0666);
+ archive_entry_set_mtime(entry, file->mtime, 0);
+
+ cab->entry_bytes_remaining = file->uncompressed_size;
+ cab->entry_offset = 0;
+ /* We don't need compress data. */
+ if (file->uncompressed_size == 0)
+ cab->end_of_entry_cleanup = cab->end_of_entry = 1;
+
+ /* Set up a more descriptive format name. */
+ snprintf(cab->format_name, sizeof(cab->format_name), "CAB %d.%d (%s)",
+ hd->major, hd->minor, cab->entry_cffolder->compname);
+ a->archive.archive_format_name = cab->format_name;
+
+ return (err);
+}
+
+static int
+archive_read_format_cab_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ int r;
+
+ switch (cab->entry_cffile->folder) {
+ case iFoldCONTINUED_FROM_PREV:
+ case iFoldCONTINUED_TO_NEXT:
+ case iFoldCONTINUED_PREV_AND_NEXT:
+ *buff = NULL;
+ *size = 0;
+ *offset = 0;
+ archive_clear_error(&a->archive);
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Cannot restore this file split in multivolume.");
+ return (ARCHIVE_FAILED);
+ default:
+ break;
+ }
+ if (cab->read_data_invoked == 0) {
+ if (cab->bytes_skipped) {
+ if (cab->entry_cfdata == NULL) {
+ r = cab_next_cfdata(a);
+ if (r < 0)
+ return (r);
+ }
+ if (cab_consume_cfdata(a, cab->bytes_skipped) < 0)
+ return (ARCHIVE_FATAL);
+ cab->bytes_skipped = 0;
+ }
+ cab->read_data_invoked = 1;
+ }
+ if (cab->entry_unconsumed) {
+ /* Consume as much as the compressor actually used. */
+ r = (int)cab_consume_cfdata(a, cab->entry_unconsumed);
+ cab->entry_unconsumed = 0;
+ if (r < 0)
+ return (r);
+ }
+ if (cab->end_of_archive || cab->end_of_entry) {
+ if (!cab->end_of_entry_cleanup) {
+ /* End-of-entry cleanup done. */
+ cab->end_of_entry_cleanup = 1;
+ }
+ *offset = cab->entry_offset;
+ *size = 0;
+ *buff = NULL;
+ return (ARCHIVE_EOF);
+ }
+
+ return (cab_read_data(a, buff, size, offset));
+}
+
+static uint32_t
+cab_checksum_cfdata_4(const void *p, size_t bytes, uint32_t seed)
+{
+ const unsigned char *b;
+ unsigned u32num;
+ uint32_t sum;
+
+ u32num = (unsigned)bytes / 4;
+ sum = seed;
+ b = p;
+ for (;u32num > 0; --u32num) {
+ sum ^= archive_le32dec(b);
+ b += 4;
+ }
+ return (sum);
+}
+
+static uint32_t
+cab_checksum_cfdata(const void *p, size_t bytes, uint32_t seed)
+{
+ const unsigned char *b;
+ uint32_t sum;
+ uint32_t t;
+
+ sum = cab_checksum_cfdata_4(p, bytes, seed);
+ b = p;
+ b += bytes & ~3;
+ t = 0;
+ switch (bytes & 3) {
+ case 3:
+ t |= ((uint32_t)(*b++)) << 16;
+ /* FALL THROUGH */
+ case 2:
+ t |= ((uint32_t)(*b++)) << 8;
+ /* FALL THROUGH */
+ case 1:
+ t |= *b;
+ /* FALL THROUGH */
+ default:
+ break;
+ }
+ sum ^= t;
+
+ return (sum);
+}
+
+static void
+cab_checksum_update(struct archive_read *a, size_t bytes)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfdata *cfdata = cab->entry_cfdata;
+ const unsigned char *p;
+ size_t sumbytes;
+
+ if (cfdata->sum == 0 || cfdata->sum_ptr == NULL)
+ return;
+ /*
+ * Calculate the sum of this CFDATA.
+ * Make sure CFDATA must be calculated in four bytes.
+ */
+ p = cfdata->sum_ptr;
+ sumbytes = bytes;
+ if (cfdata->sum_extra_avail) {
+ while (cfdata->sum_extra_avail < 4 && sumbytes > 0) {
+ cfdata->sum_extra[
+ cfdata->sum_extra_avail++] = *p++;
+ sumbytes--;
+ }
+ if (cfdata->sum_extra_avail == 4) {
+ cfdata->sum_calculated = cab_checksum_cfdata_4(
+ cfdata->sum_extra, 4, cfdata->sum_calculated);
+ cfdata->sum_extra_avail = 0;
+ }
+ }
+ if (sumbytes) {
+ int odd = sumbytes & 3;
+ if ((int)(sumbytes - odd) > 0)
+ cfdata->sum_calculated = cab_checksum_cfdata_4(
+ p, sumbytes - odd, cfdata->sum_calculated);
+ if (odd)
+ memcpy(cfdata->sum_extra, p + sumbytes - odd, odd);
+ cfdata->sum_extra_avail = odd;
+ }
+ cfdata->sum_ptr = NULL;
+}
+
+static int
+cab_checksum_finish(struct archive_read *a)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfdata *cfdata = cab->entry_cfdata;
+ int l;
+
+ /* Do not need to compute a sum. */
+ if (cfdata->sum == 0)
+ return (ARCHIVE_OK);
+
+ /*
+ * Calculate the sum of remaining CFDATA.
+ */
+ if (cfdata->sum_extra_avail) {
+ cfdata->sum_calculated =
+ cab_checksum_cfdata(cfdata->sum_extra,
+ cfdata->sum_extra_avail, cfdata->sum_calculated);
+ cfdata->sum_extra_avail = 0;
+ }
+
+ l = 4;
+ if (cab->cfheader.flags & RESERVE_PRESENT)
+ l += cab->cfheader.cfdata;
+ cfdata->sum_calculated = cab_checksum_cfdata(
+ cfdata->memimage + CFDATA_cbData, l, cfdata->sum_calculated);
+ if (cfdata->sum_calculated != cfdata->sum) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Checksum error CFDATA[%d] %" PRIx32 ":%" PRIx32 " in %d bytes",
+ cab->entry_cffolder->cfdata_index -1,
+ cfdata->sum, cfdata->sum_calculated,
+ cfdata->compressed_size);
+ return (ARCHIVE_FAILED);
+#endif
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Read CFDATA if needed.
+ */
+static int
+cab_next_cfdata(struct archive_read *a)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfdata *cfdata = cab->entry_cfdata;
+
+ /* There are remaining bytes in current CFDATA, use it first. */
+ if (cfdata != NULL && cfdata->uncompressed_bytes_remaining > 0)
+ return (ARCHIVE_OK);
+
+ if (cfdata == NULL) {
+ int64_t skip;
+
+ cab->entry_cffolder->cfdata_index = 0;
+
+ /* Seek read pointer to the offset of CFDATA if needed. */
+ skip = cab->entry_cffolder->cfdata_offset_in_cab
+ - cab->cab_offset;
+ if (skip < 0) {
+ int folder_index;
+ switch (cab->entry_cffile->folder) {
+ case iFoldCONTINUED_FROM_PREV:
+ case iFoldCONTINUED_PREV_AND_NEXT:
+ folder_index = 0;
+ break;
+ case iFoldCONTINUED_TO_NEXT:
+ folder_index = cab->cfheader.folder_count-1;
+ break;
+ default:
+ folder_index = cab->entry_cffile->folder;
+ break;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid offset of CFDATA in folder(%d) %jd < %jd",
+ folder_index,
+ (intmax_t)cab->entry_cffolder->cfdata_offset_in_cab,
+ (intmax_t)cab->cab_offset);
+ return (ARCHIVE_FATAL);
+ }
+ if (skip > 0) {
+ if (__archive_read_consume(a, skip) < 0)
+ return (ARCHIVE_FATAL);
+ cab->cab_offset =
+ cab->entry_cffolder->cfdata_offset_in_cab;
+ }
+ }
+
+ /*
+ * Read a CFDATA.
+ */
+ if (cab->entry_cffolder->cfdata_index <
+ cab->entry_cffolder->cfdata_count) {
+ const unsigned char *p;
+ int l;
+
+ cfdata = &(cab->entry_cffolder->cfdata);
+ cab->entry_cffolder->cfdata_index++;
+ cab->entry_cfdata = cfdata;
+ cfdata->sum_calculated = 0;
+ cfdata->sum_extra_avail = 0;
+ cfdata->sum_ptr = NULL;
+ l = 8;
+ if (cab->cfheader.flags & RESERVE_PRESENT)
+ l += cab->cfheader.cfdata;
+ if ((p = __archive_read_ahead(a, l, NULL)) == NULL)
+ return (truncated_error(a));
+ cfdata->sum = archive_le32dec(p + CFDATA_csum);
+ cfdata->compressed_size = archive_le16dec(p + CFDATA_cbData);
+ cfdata->compressed_bytes_remaining = cfdata->compressed_size;
+ cfdata->uncompressed_size =
+ archive_le16dec(p + CFDATA_cbUncomp);
+ cfdata->uncompressed_bytes_remaining =
+ cfdata->uncompressed_size;
+ cfdata->uncompressed_avail = 0;
+ cfdata->read_offset = 0;
+ cfdata->unconsumed = 0;
+
+ /*
+ * Sanity check if data size is acceptable.
+ */
+ if (cfdata->compressed_size == 0 ||
+ cfdata->compressed_size > (0x8000+6144))
+ goto invalid;
+ if (cfdata->uncompressed_size > 0x8000)
+ goto invalid;
+ if (cfdata->uncompressed_size == 0) {
+ switch (cab->entry_cffile->folder) {
+ case iFoldCONTINUED_PREV_AND_NEXT:
+ case iFoldCONTINUED_TO_NEXT:
+ break;
+ case iFoldCONTINUED_FROM_PREV:
+ default:
+ goto invalid;
+ }
+ }
+ /* If CFDATA is not last in a folder, an uncompressed
+ * size must be 0x8000(32KBi) */
+ if ((cab->entry_cffolder->cfdata_index <
+ cab->entry_cffolder->cfdata_count) &&
+ cfdata->uncompressed_size != 0x8000)
+ goto invalid;
+
+ /* A compressed data size and an uncompressed data size must
+ * be the same in no compression mode. */
+ if (cab->entry_cffolder->comptype == COMPTYPE_NONE &&
+ cfdata->compressed_size != cfdata->uncompressed_size)
+ goto invalid;
+
+ /*
+ * Save CFDATA image for sum check.
+ */
+ if (cfdata->memimage_size < (size_t)l) {
+ free(cfdata->memimage);
+ cfdata->memimage = malloc(l);
+ if (cfdata->memimage == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for CAB data");
+ return (ARCHIVE_FATAL);
+ }
+ cfdata->memimage_size = l;
+ }
+ memcpy(cfdata->memimage, p, l);
+
+ /* Consume bytes as much as we used. */
+ __archive_read_consume(a, l);
+ cab->cab_offset += l;
+ } else if (cab->entry_cffolder->cfdata_count > 0) {
+ /* Run out of all CFDATA in a folder. */
+ cfdata->compressed_size = 0;
+ cfdata->uncompressed_size = 0;
+ cfdata->compressed_bytes_remaining = 0;
+ cfdata->uncompressed_bytes_remaining = 0;
+ } else {
+ /* Current folder does not have any CFDATA. */
+ cfdata = &(cab->entry_cffolder->cfdata);
+ cab->entry_cfdata = cfdata;
+ memset(cfdata, 0, sizeof(*cfdata));
+ }
+ return (ARCHIVE_OK);
+invalid:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid CFDATA");
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Read ahead CFDATA.
+ */
+static const void *
+cab_read_ahead_cfdata(struct archive_read *a, ssize_t *avail)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ int err;
+
+ err = cab_next_cfdata(a);
+ if (err < ARCHIVE_OK) {
+ *avail = err;
+ return (NULL);
+ }
+
+ switch (cab->entry_cffolder->comptype) {
+ case COMPTYPE_NONE:
+ return (cab_read_ahead_cfdata_none(a, avail));
+ case COMPTYPE_MSZIP:
+ return (cab_read_ahead_cfdata_deflate(a, avail));
+ case COMPTYPE_LZX:
+ return (cab_read_ahead_cfdata_lzx(a, avail));
+ default: /* Unsupported compression. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported CAB compression : %s",
+ cab->entry_cffolder->compname);
+ *avail = ARCHIVE_FAILED;
+ return (NULL);
+ }
+}
+
+/*
+ * Read ahead CFDATA as uncompressed data.
+ */
+static const void *
+cab_read_ahead_cfdata_none(struct archive_read *a, ssize_t *avail)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfdata *cfdata;
+ const void *d;
+
+ cfdata = cab->entry_cfdata;
+
+ /*
+ * Note: '1' here is a performance optimization.
+ * Recall that the decompression layer returns a count of
+ * available bytes; asking for more than that forces the
+ * decompressor to combine reads by copying data.
+ */
+ d = __archive_read_ahead(a, 1, avail);
+ if (*avail <= 0) {
+ *avail = truncated_error(a);
+ return (NULL);
+ }
+ if (*avail > cfdata->uncompressed_bytes_remaining)
+ *avail = cfdata->uncompressed_bytes_remaining;
+ cfdata->uncompressed_avail = cfdata->uncompressed_size;
+ cfdata->unconsumed = *avail;
+ cfdata->sum_ptr = d;
+ return (d);
+}
+
+/*
+ * Read ahead CFDATA as deflate data.
+ */
+#ifdef HAVE_ZLIB_H
+static const void *
+cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfdata *cfdata;
+ const void *d;
+ int r, mszip;
+ uint16_t uavail;
+ char eod = 0;
+
+ cfdata = cab->entry_cfdata;
+ /* If the buffer hasn't been allocated, allocate it now. */
+ if (cab->uncompressed_buffer == NULL) {
+ cab->uncompressed_buffer_size = 0x8000;
+ cab->uncompressed_buffer
+ = (unsigned char *)malloc(cab->uncompressed_buffer_size);
+ if (cab->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for CAB reader");
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ }
+
+ uavail = cfdata->uncompressed_avail;
+ if (uavail == cfdata->uncompressed_size) {
+ d = cab->uncompressed_buffer + cfdata->read_offset;
+ *avail = uavail - cfdata->read_offset;
+ return (d);
+ }
+
+ if (!cab->entry_cffolder->decompress_init) {
+ cab->stream.next_in = NULL;
+ cab->stream.avail_in = 0;
+ cab->stream.total_in = 0;
+ cab->stream.next_out = NULL;
+ cab->stream.avail_out = 0;
+ cab->stream.total_out = 0;
+ if (cab->stream_valid)
+ r = inflateReset(&cab->stream);
+ else
+ r = inflateInit2(&cab->stream,
+ -15 /* Don't check for zlib header */);
+ if (r != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't initialize deflate decompression.");
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ /* Stream structure has been set up. */
+ cab->stream_valid = 1;
+ /* We've initialized decompression for this stream. */
+ cab->entry_cffolder->decompress_init = 1;
+ }
+
+ if (cfdata->compressed_bytes_remaining == cfdata->compressed_size)
+ mszip = 2;
+ else
+ mszip = 0;
+ eod = 0;
+ cab->stream.total_out = uavail;
+ /*
+ * We always uncompress all data in current CFDATA.
+ */
+ while (!eod && cab->stream.total_out < cfdata->uncompressed_size) {
+ ssize_t bytes_avail;
+
+ cab->stream.next_out =
+ cab->uncompressed_buffer + cab->stream.total_out;
+ cab->stream.avail_out =
+ cfdata->uncompressed_size - cab->stream.total_out;
+
+ d = __archive_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail <= 0) {
+ *avail = truncated_error(a);
+ return (NULL);
+ }
+ if (bytes_avail > cfdata->compressed_bytes_remaining)
+ bytes_avail = cfdata->compressed_bytes_remaining;
+ /*
+ * A bug in zlib.h: stream.next_in should be marked 'const'
+ * but isn't (the library never alters data through the
+ * next_in pointer, only reads it). The result: this ugly
+ * cast to remove 'const'.
+ */
+ cab->stream.next_in = (Bytef *)(uintptr_t)d;
+ cab->stream.avail_in = (uInt)bytes_avail;
+ cab->stream.total_in = 0;
+
+ /* Cut out a tow-byte MSZIP signature(0x43, 0x4b). */
+ if (mszip > 0) {
+ if (bytes_avail <= 0)
+ goto nomszip;
+ if (bytes_avail <= mszip) {
+ if (mszip == 2) {
+ if (cab->stream.next_in[0] != 0x43)
+ goto nomszip;
+ if (bytes_avail > 1 &&
+ cab->stream.next_in[1] != 0x4b)
+ goto nomszip;
+ } else if (cab->stream.next_in[0] != 0x4b)
+ goto nomszip;
+ cfdata->unconsumed = bytes_avail;
+ cfdata->sum_ptr = d;
+ if (cab_minimum_consume_cfdata(
+ a, cfdata->unconsumed) < 0) {
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ mszip -= (int)bytes_avail;
+ continue;
+ }
+ if (mszip == 1 && cab->stream.next_in[0] != 0x4b)
+ goto nomszip;
+ else if (mszip == 2 && (cab->stream.next_in[0] != 0x43 ||
+ cab->stream.next_in[1] != 0x4b))
+ goto nomszip;
+ cab->stream.next_in += mszip;
+ cab->stream.avail_in -= mszip;
+ cab->stream.total_in += mszip;
+ mszip = 0;
+ }
+
+ r = inflate(&cab->stream, 0);
+ switch (r) {
+ case Z_OK:
+ break;
+ case Z_STREAM_END:
+ eod = 1;
+ break;
+ default:
+ goto zlibfailed;
+ }
+ cfdata->unconsumed = cab->stream.total_in;
+ cfdata->sum_ptr = d;
+ if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ }
+ uavail = (uint16_t)cab->stream.total_out;
+
+ if (uavail < cfdata->uncompressed_size) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid uncompressed size (%d < %d)",
+ uavail, cfdata->uncompressed_size);
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+
+ /*
+ * Note: I suspect there is a bug in makecab.exe because, in rare
+ * case, compressed bytes are still remaining regardless we have
+ * gotten all uncompressed bytes, which size is recorded in CFDATA,
+ * as much as we need, and we have to use the garbage so as to
+ * correctly compute the sum of CFDATA accordingly.
+ */
+ if (cfdata->compressed_bytes_remaining > 0) {
+ ssize_t bytes_avail;
+
+ d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining,
+ &bytes_avail);
+ if (bytes_avail <= 0) {
+ *avail = truncated_error(a);
+ return (NULL);
+ }
+ cfdata->unconsumed = cfdata->compressed_bytes_remaining;
+ cfdata->sum_ptr = d;
+ if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ }
+
+ /*
+ * Set dictionary data for decompressing of next CFDATA, which
+ * in the same folder. This is why we always do decompress CFDATA
+ * even if beginning CFDATA or some of CFDATA are not used in
+ * skipping file data.
+ */
+ if (cab->entry_cffolder->cfdata_index <
+ cab->entry_cffolder->cfdata_count) {
+ r = inflateReset(&cab->stream);
+ if (r != Z_OK)
+ goto zlibfailed;
+ r = inflateSetDictionary(&cab->stream,
+ cab->uncompressed_buffer, cfdata->uncompressed_size);
+ if (r != Z_OK)
+ goto zlibfailed;
+ }
+
+ d = cab->uncompressed_buffer + cfdata->read_offset;
+ *avail = uavail - cfdata->read_offset;
+ cfdata->uncompressed_avail = uavail;
+
+ return (d);
+
+zlibfailed:
+ switch (r) {
+ case Z_MEM_ERROR:
+ archive_set_error(&a->archive, ENOMEM,
+ "Out of memory for deflate decompression");
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Deflate decompression failed (%d)", r);
+ break;
+ }
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+nomszip:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "CFDATA incorrect(no MSZIP signature)");
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+}
+
+#else /* HAVE_ZLIB_H */
+
+static const void *
+cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
+{
+ *avail = ARCHIVE_FATAL;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "libarchive compiled without deflate support (no libz)");
+ return (NULL);
+}
+
+#endif /* HAVE_ZLIB_H */
+
+static const void *
+cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfdata *cfdata;
+ const void *d;
+ int r;
+ uint16_t uavail;
+
+ cfdata = cab->entry_cfdata;
+ /* If the buffer hasn't been allocated, allocate it now. */
+ if (cab->uncompressed_buffer == NULL) {
+ cab->uncompressed_buffer_size = 0x8000;
+ cab->uncompressed_buffer
+ = (unsigned char *)malloc(cab->uncompressed_buffer_size);
+ if (cab->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for CAB reader");
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ }
+
+ uavail = cfdata->uncompressed_avail;
+ if (uavail == cfdata->uncompressed_size) {
+ d = cab->uncompressed_buffer + cfdata->read_offset;
+ *avail = uavail - cfdata->read_offset;
+ return (d);
+ }
+
+ if (!cab->entry_cffolder->decompress_init) {
+ r = lzx_decode_init(&cab->xstrm,
+ cab->entry_cffolder->compdata);
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't initialize LZX decompression.");
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ /* We've initialized decompression for this stream. */
+ cab->entry_cffolder->decompress_init = 1;
+ }
+
+ /* Clean up remaining bits of previous CFDATA. */
+ lzx_cleanup_bitstream(&cab->xstrm);
+ cab->xstrm.total_out = uavail;
+ while (cab->xstrm.total_out < cfdata->uncompressed_size) {
+ ssize_t bytes_avail;
+
+ cab->xstrm.next_out =
+ cab->uncompressed_buffer + cab->xstrm.total_out;
+ cab->xstrm.avail_out =
+ cfdata->uncompressed_size - cab->xstrm.total_out;
+
+ d = __archive_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail <= 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated CAB file data");
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ if (bytes_avail > cfdata->compressed_bytes_remaining)
+ bytes_avail = cfdata->compressed_bytes_remaining;
+
+ cab->xstrm.next_in = d;
+ cab->xstrm.avail_in = bytes_avail;
+ cab->xstrm.total_in = 0;
+ r = lzx_decode(&cab->xstrm,
+ cfdata->compressed_bytes_remaining == bytes_avail);
+ switch (r) {
+ case ARCHIVE_OK:
+ case ARCHIVE_EOF:
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "LZX decompression failed (%d)", r);
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ cfdata->unconsumed = cab->xstrm.total_in;
+ cfdata->sum_ptr = d;
+ if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ }
+
+ uavail = (uint16_t)cab->xstrm.total_out;
+ /*
+ * Make sure a read pointer advances to next CFDATA.
+ */
+ if (cfdata->compressed_bytes_remaining > 0) {
+ ssize_t bytes_avail;
+
+ d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining,
+ &bytes_avail);
+ if (bytes_avail <= 0) {
+ *avail = truncated_error(a);
+ return (NULL);
+ }
+ cfdata->unconsumed = cfdata->compressed_bytes_remaining;
+ cfdata->sum_ptr = d;
+ if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ }
+
+ /*
+ * Translation reversal of x86 processor CALL byte sequence(E8).
+ */
+ lzx_translation(&cab->xstrm, cab->uncompressed_buffer,
+ cfdata->uncompressed_size,
+ (cab->entry_cffolder->cfdata_index-1) * 0x8000);
+
+ d = cab->uncompressed_buffer + cfdata->read_offset;
+ *avail = uavail - cfdata->read_offset;
+ cfdata->uncompressed_avail = uavail;
+
+ return (d);
+}
+
+/*
+ * Consume CFDATA.
+ * We always decompress CFDATA to consume CFDATA as much as we need
+ * in uncompressed bytes because all CFDATA in a folder are related
+ * so we do not skip any CFDATA without decompressing.
+ * Note: If the folder of a CFFILE is iFoldCONTINUED_PREV_AND_NEXT or
+ * iFoldCONTINUED_FROM_PREV, we won't decompress because a CFDATA for
+ * the CFFILE is remaining bytes of previous Multivolume CAB file.
+ */
+static int64_t
+cab_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfdata *cfdata;
+ int64_t cbytes, rbytes;
+ int err;
+
+ rbytes = cab_minimum_consume_cfdata(a, consumed_bytes);
+ if (rbytes < 0)
+ return (ARCHIVE_FATAL);
+
+ cfdata = cab->entry_cfdata;
+ while (rbytes > 0) {
+ ssize_t avail;
+
+ if (cfdata->compressed_size == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid CFDATA");
+ return (ARCHIVE_FATAL);
+ }
+ cbytes = cfdata->uncompressed_bytes_remaining;
+ if (cbytes > rbytes)
+ cbytes = rbytes;
+ rbytes -= cbytes;
+
+ if (cfdata->uncompressed_avail == 0 &&
+ (cab->entry_cffile->folder == iFoldCONTINUED_PREV_AND_NEXT ||
+ cab->entry_cffile->folder == iFoldCONTINUED_FROM_PREV)) {
+ /* We have not read any data yet. */
+ if (cbytes == cfdata->uncompressed_bytes_remaining) {
+ /* Skip whole current CFDATA. */
+ __archive_read_consume(a,
+ cfdata->compressed_size);
+ cab->cab_offset += cfdata->compressed_size;
+ cfdata->compressed_bytes_remaining = 0;
+ cfdata->uncompressed_bytes_remaining = 0;
+ err = cab_next_cfdata(a);
+ if (err < 0)
+ return (err);
+ cfdata = cab->entry_cfdata;
+ if (cfdata->uncompressed_size == 0) {
+ switch (cab->entry_cffile->folder) {
+ case iFoldCONTINUED_PREV_AND_NEXT:
+ case iFoldCONTINUED_TO_NEXT:
+ case iFoldCONTINUED_FROM_PREV:
+ rbytes = 0;
+ break;
+ default:
+ break;
+ }
+ }
+ continue;
+ }
+ cfdata->read_offset += (uint16_t)cbytes;
+ cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes;
+ break;
+ } else if (cbytes == 0) {
+ err = cab_next_cfdata(a);
+ if (err < 0)
+ return (err);
+ cfdata = cab->entry_cfdata;
+ if (cfdata->uncompressed_size == 0) {
+ switch (cab->entry_cffile->folder) {
+ case iFoldCONTINUED_PREV_AND_NEXT:
+ case iFoldCONTINUED_TO_NEXT:
+ case iFoldCONTINUED_FROM_PREV:
+ return (ARCHIVE_FATAL);
+ default:
+ break;
+ }
+ }
+ continue;
+ }
+ while (cbytes > 0) {
+ (void)cab_read_ahead_cfdata(a, &avail);
+ if (avail <= 0)
+ return (ARCHIVE_FATAL);
+ if (avail > cbytes)
+ avail = (ssize_t)cbytes;
+ if (cab_minimum_consume_cfdata(a, avail) < 0)
+ return (ARCHIVE_FATAL);
+ cbytes -= avail;
+ }
+ }
+ return (consumed_bytes);
+}
+
+/*
+ * Consume CFDATA as much as we have already gotten and
+ * compute the sum of CFDATA.
+ */
+static int64_t
+cab_minimum_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfdata *cfdata;
+ int64_t cbytes, rbytes;
+ int err;
+
+ cfdata = cab->entry_cfdata;
+ rbytes = consumed_bytes;
+ if (cab->entry_cffolder->comptype == COMPTYPE_NONE) {
+ if (consumed_bytes < cfdata->unconsumed)
+ cbytes = consumed_bytes;
+ else
+ cbytes = cfdata->unconsumed;
+ rbytes -= cbytes;
+ cfdata->read_offset += (uint16_t)cbytes;
+ cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes;
+ cfdata->unconsumed -= cbytes;
+ } else {
+ cbytes = cfdata->uncompressed_avail - cfdata->read_offset;
+ if (cbytes > 0) {
+ if (consumed_bytes < cbytes)
+ cbytes = consumed_bytes;
+ rbytes -= cbytes;
+ cfdata->read_offset += (uint16_t)cbytes;
+ cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes;
+ }
+
+ if (cfdata->unconsumed) {
+ cbytes = cfdata->unconsumed;
+ cfdata->unconsumed = 0;
+ } else
+ cbytes = 0;
+ }
+ if (cbytes) {
+ /* Compute the sum. */
+ cab_checksum_update(a, (size_t)cbytes);
+
+ /* Consume as much as the compressor actually used. */
+ __archive_read_consume(a, cbytes);
+ cab->cab_offset += cbytes;
+ cfdata->compressed_bytes_remaining -= (uint16_t)cbytes;
+ if (cfdata->compressed_bytes_remaining == 0) {
+ err = cab_checksum_finish(a);
+ if (err < 0)
+ return (err);
+ }
+ }
+ return (rbytes);
+}
+
+/*
+ * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets
+ * cab->end_of_entry if it consumes all of the data.
+ */
+static int
+cab_read_data(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ ssize_t bytes_avail;
+
+ if (cab->entry_bytes_remaining == 0) {
+ *buff = NULL;
+ *size = 0;
+ *offset = cab->entry_offset;
+ cab->end_of_entry = 1;
+ return (ARCHIVE_OK);
+ }
+
+ *buff = cab_read_ahead_cfdata(a, &bytes_avail);
+ if (bytes_avail <= 0) {
+ *buff = NULL;
+ *size = 0;
+ *offset = 0;
+ if (bytes_avail == 0 &&
+ cab->entry_cfdata->uncompressed_size == 0) {
+ /* All of CFDATA in a folder has been handled. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CFDATA");
+ return (ARCHIVE_FATAL);
+ } else
+ return ((int)bytes_avail);
+ }
+ if (bytes_avail > cab->entry_bytes_remaining)
+ bytes_avail = (ssize_t)cab->entry_bytes_remaining;
+
+ *size = bytes_avail;
+ *offset = cab->entry_offset;
+ cab->entry_offset += bytes_avail;
+ cab->entry_bytes_remaining -= bytes_avail;
+ if (cab->entry_bytes_remaining == 0)
+ cab->end_of_entry = 1;
+ cab->entry_unconsumed = bytes_avail;
+ if (cab->entry_cffolder->comptype == COMPTYPE_NONE) {
+ /* Don't consume more than current entry used. */
+ if (cab->entry_cfdata->unconsumed > cab->entry_unconsumed)
+ cab->entry_cfdata->unconsumed = cab->entry_unconsumed;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_cab_read_data_skip(struct archive_read *a)
+{
+ struct cab *cab;
+ int64_t bytes_skipped;
+ int r;
+
+ cab = (struct cab *)(a->format->data);
+
+ if (cab->end_of_archive)
+ return (ARCHIVE_EOF);
+
+ if (!cab->read_data_invoked) {
+ cab->bytes_skipped += cab->entry_bytes_remaining;
+ cab->entry_bytes_remaining = 0;
+ /* This entry is finished and done. */
+ cab->end_of_entry_cleanup = cab->end_of_entry = 1;
+ return (ARCHIVE_OK);
+ }
+
+ if (cab->entry_unconsumed) {
+ /* Consume as much as the compressor actually used. */
+ r = (int)cab_consume_cfdata(a, cab->entry_unconsumed);
+ cab->entry_unconsumed = 0;
+ if (r < 0)
+ return (r);
+ } else if (cab->entry_cfdata == NULL) {
+ r = cab_next_cfdata(a);
+ if (r < 0)
+ return (r);
+ }
+
+ /* if we've already read to end of data, we're done. */
+ if (cab->end_of_entry_cleanup)
+ return (ARCHIVE_OK);
+
+ /*
+ * If the length is at the beginning, we can skip the
+ * compressed data much more quickly.
+ */
+ bytes_skipped = cab_consume_cfdata(a, cab->entry_bytes_remaining);
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+
+ /* If the compression type is none(uncompressed), we've already
+ * consumed data as much as the current entry size. */
+ if (cab->entry_cffolder->comptype == COMPTYPE_NONE &&
+ cab->entry_cfdata != NULL)
+ cab->entry_cfdata->unconsumed = 0;
+
+ /* This entry is finished and done. */
+ cab->end_of_entry_cleanup = cab->end_of_entry = 1;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_cab_cleanup(struct archive_read *a)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfheader *hd = &cab->cfheader;
+ int i;
+
+ if (hd->folder_array != NULL) {
+ for (i = 0; i < hd->folder_count; i++)
+ free(hd->folder_array[i].cfdata.memimage);
+ free(hd->folder_array);
+ }
+ if (hd->file_array != NULL) {
+ for (i = 0; i < cab->cfheader.file_count; i++)
+ archive_string_free(&(hd->file_array[i].pathname));
+ free(hd->file_array);
+ }
+#ifdef HAVE_ZLIB_H
+ if (cab->stream_valid)
+ inflateEnd(&cab->stream);
+#endif
+ lzx_decode_free(&cab->xstrm);
+ archive_wstring_free(&cab->ws);
+ free(cab->uncompressed_buffer);
+ free(cab);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+/* Convert an MSDOS-style date/time into Unix-style time. */
+static time_t
+cab_dos_time(const unsigned char *p)
+{
+ int msTime, msDate;
+ struct tm ts;
+
+ msDate = archive_le16dec(p);
+ msTime = archive_le16dec(p+2);
+
+ memset(&ts, 0, sizeof(ts));
+ ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */
+ ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */
+ ts.tm_mday = msDate & 0x1f; /* Day of month. */
+ ts.tm_hour = (msTime >> 11) & 0x1f;
+ ts.tm_min = (msTime >> 5) & 0x3f;
+ ts.tm_sec = (msTime << 1) & 0x3e;
+ ts.tm_isdst = -1;
+ return (mktime(&ts));
+}
+
+/*****************************************************************
+ *
+ * LZX decompression code.
+ *
+ *****************************************************************/
+
+/*
+ * Initialize LZX decoder.
+ *
+ * Returns ARCHIVE_OK if initialization was successful.
+ * Returns ARCHIVE_FAILED if w_bits has unsupported value.
+ * Returns ARCHIVE_FATAL if initialization failed; memory allocation
+ * error occurred.
+ */
+static int
+lzx_decode_init(struct lzx_stream *strm, int w_bits)
+{
+ struct lzx_dec *ds;
+ int slot, w_size, w_slot;
+ int base, footer;
+ int base_inc[18];
+
+ if (strm->ds == NULL) {
+ strm->ds = calloc(1, sizeof(*strm->ds));
+ if (strm->ds == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ ds = strm->ds;
+ ds->error = ARCHIVE_FAILED;
+
+ /* Allow bits from 15(32KBi) up to 21(2MBi) */
+ if (w_bits < SLOT_BASE || w_bits > SLOT_MAX)
+ return (ARCHIVE_FAILED);
+
+ ds->error = ARCHIVE_FATAL;
+
+ /*
+ * Alloc window
+ */
+ w_size = ds->w_size;
+ w_slot = slots[w_bits - SLOT_BASE];
+ ds->w_size = 1U << w_bits;
+ ds->w_mask = ds->w_size -1;
+ if (ds->w_buff == NULL || w_size != ds->w_size) {
+ free(ds->w_buff);
+ ds->w_buff = malloc(ds->w_size);
+ if (ds->w_buff == NULL)
+ return (ARCHIVE_FATAL);
+ free(ds->pos_tbl);
+ ds->pos_tbl = malloc(sizeof(ds->pos_tbl[0]) * w_slot);
+ if (ds->pos_tbl == NULL)
+ return (ARCHIVE_FATAL);
+ }
+
+ for (footer = 0; footer < 18; footer++)
+ base_inc[footer] = 1 << footer;
+ base = footer = 0;
+ for (slot = 0; slot < w_slot; slot++) {
+ int n;
+ if (footer == 0)
+ base = slot;
+ else
+ base += base_inc[footer];
+ if (footer < 17) {
+ footer = -2;
+ for (n = base; n; n >>= 1)
+ footer++;
+ if (footer <= 0)
+ footer = 0;
+ }
+ ds->pos_tbl[slot].base = base;
+ ds->pos_tbl[slot].footer_bits = footer;
+ }
+
+ ds->w_pos = 0;
+ ds->state = 0;
+ ds->br.cache_buffer = 0;
+ ds->br.cache_avail = 0;
+ ds->r0 = ds->r1 = ds->r2 = 1;
+
+ /* Initialize aligned offset tree. */
+ if (lzx_huffman_init(&(ds->at), 8, 8) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Initialize pre-tree. */
+ if (lzx_huffman_init(&(ds->pt), 20, 10) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Initialize Main tree. */
+ if (lzx_huffman_init(&(ds->mt), 256+(w_slot<<3), 16)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Initialize Length tree. */
+ if (lzx_huffman_init(&(ds->lt), 249, 16) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ ds->error = 0;
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Release LZX decoder.
+ */
+static void
+lzx_decode_free(struct lzx_stream *strm)
+{
+
+ if (strm->ds == NULL)
+ return;
+ free(strm->ds->w_buff);
+ free(strm->ds->pos_tbl);
+ lzx_huffman_free(&(strm->ds->at));
+ lzx_huffman_free(&(strm->ds->pt));
+ lzx_huffman_free(&(strm->ds->mt));
+ lzx_huffman_free(&(strm->ds->lt));
+ free(strm->ds);
+ strm->ds = NULL;
+}
+
+/*
+ * E8 Call Translation reversal.
+ */
+static void
+lzx_translation(struct lzx_stream *strm, void *p, size_t size, uint32_t offset)
+{
+ struct lzx_dec *ds = strm->ds;
+ unsigned char *b, *end;
+
+ if (!ds->translation || size <= 10)
+ return;
+ b = p;
+ end = b + size - 10;
+ while (b < end && (b = memchr(b, 0xE8, end - b)) != NULL) {
+ size_t i = b - (unsigned char *)p;
+ int32_t cp, displacement, value;
+
+ cp = (int32_t)(offset + (uint32_t)i);
+ value = archive_le32dec(&b[1]);
+ if (value >= -cp && value < (int32_t)ds->translation_size) {
+ if (value >= 0)
+ displacement = value - cp;
+ else
+ displacement = value + ds->translation_size;
+ archive_le32enc(&b[1], (uint32_t)displacement);
+ }
+ b += 5;
+ }
+}
+
+/*
+ * Bit stream reader.
+ */
+/* Check that the cache buffer has enough bits. */
+#define lzx_br_has(br, n) ((br)->cache_avail >= n)
+/* Get compressed data by bit. */
+#define lzx_br_bits(br, n) \
+ (((uint32_t)((br)->cache_buffer >> \
+ ((br)->cache_avail - (n)))) & cache_masks[n])
+#define lzx_br_bits_forced(br, n) \
+ (((uint32_t)((br)->cache_buffer << \
+ ((n) - (br)->cache_avail))) & cache_masks[n])
+/* Read ahead to make sure the cache buffer has enough compressed data we
+ * will use.
+ * True : completed, there is enough data in the cache buffer.
+ * False : we met that strm->next_in is empty, we have to get following
+ * bytes. */
+#define lzx_br_read_ahead_0(strm, br, n) \
+ (lzx_br_has((br), (n)) || lzx_br_fillup(strm, br))
+/* True : the cache buffer has some bits as much as we need.
+ * False : there are no enough bits in the cache buffer to be used,
+ * we have to get following bytes if we could. */
+#define lzx_br_read_ahead(strm, br, n) \
+ (lzx_br_read_ahead_0((strm), (br), (n)) || lzx_br_has((br), (n)))
+
+/* Notify how many bits we consumed. */
+#define lzx_br_consume(br, n) ((br)->cache_avail -= (n))
+#define lzx_br_consume_unaligned_bits(br) ((br)->cache_avail &= ~0x0f)
+
+#define lzx_br_is_unaligned(br) ((br)->cache_avail & 0x0f)
+
+static const uint32_t cache_masks[] = {
+ 0x00000000, 0x00000001, 0x00000003, 0x00000007,
+ 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,
+ 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF,
+ 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF,
+ 0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF,
+ 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,
+ 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF,
+ 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+/*
+ * Shift away used bits in the cache data and fill it up with following bits.
+ * Call this when cache buffer does not have enough bits you need.
+ *
+ * Returns 1 if the cache buffer is full.
+ * Returns 0 if the cache buffer is not full; input buffer is empty.
+ */
+static int
+lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br)
+{
+/*
+ * x86 processor family can read misaligned data without an access error.
+ */
+ int n = CACHE_BITS - br->cache_avail;
+
+ for (;;) {
+ switch (n >> 4) {
+ case 4:
+ if (strm->avail_in >= 8) {
+ br->cache_buffer =
+ ((uint64_t)strm->next_in[1]) << 56 |
+ ((uint64_t)strm->next_in[0]) << 48 |
+ ((uint64_t)strm->next_in[3]) << 40 |
+ ((uint64_t)strm->next_in[2]) << 32 |
+ ((uint32_t)strm->next_in[5]) << 24 |
+ ((uint32_t)strm->next_in[4]) << 16 |
+ ((uint32_t)strm->next_in[7]) << 8 |
+ (uint32_t)strm->next_in[6];
+ strm->next_in += 8;
+ strm->avail_in -= 8;
+ br->cache_avail += 8 * 8;
+ return (1);
+ }
+ break;
+ case 3:
+ if (strm->avail_in >= 6) {
+ br->cache_buffer =
+ (br->cache_buffer << 48) |
+ ((uint64_t)strm->next_in[1]) << 40 |
+ ((uint64_t)strm->next_in[0]) << 32 |
+ ((uint64_t)strm->next_in[3]) << 24 |
+ ((uint64_t)strm->next_in[2]) << 16 |
+ ((uint64_t)strm->next_in[5]) << 8 |
+ (uint64_t)strm->next_in[4];
+ strm->next_in += 6;
+ strm->avail_in -= 6;
+ br->cache_avail += 6 * 8;
+ return (1);
+ }
+ break;
+ case 0:
+ /* We have enough compressed data in
+ * the cache buffer.*/
+ return (1);
+ default:
+ break;
+ }
+ if (strm->avail_in < 2) {
+ /* There is not enough compressed data to
+ * fill up the cache buffer. */
+ if (strm->avail_in == 1) {
+ br->odd = *strm->next_in++;
+ strm->avail_in--;
+ br->have_odd = 1;
+ }
+ return (0);
+ }
+ br->cache_buffer =
+ (br->cache_buffer << 16) |
+ archive_le16dec(strm->next_in);
+ strm->next_in += 2;
+ strm->avail_in -= 2;
+ br->cache_avail += 16;
+ n -= 16;
+ }
+}
+
+static void
+lzx_br_fixup(struct lzx_stream *strm, struct lzx_br *br)
+{
+ int n = CACHE_BITS - br->cache_avail;
+
+ if (br->have_odd && n >= 16 && strm->avail_in > 0) {
+ br->cache_buffer =
+ (br->cache_buffer << 16) |
+ ((uint16_t)(*strm->next_in)) << 8 | br->odd;
+ strm->next_in++;
+ strm->avail_in--;
+ br->cache_avail += 16;
+ br->have_odd = 0;
+ }
+}
+
+static void
+lzx_cleanup_bitstream(struct lzx_stream *strm)
+{
+ strm->ds->br.cache_avail = 0;
+ strm->ds->br.have_odd = 0;
+}
+
+/*
+ * Decode LZX.
+ *
+ * 1. Returns ARCHIVE_OK if output buffer or input buffer are empty.
+ * Please set available buffer and call this function again.
+ * 2. Returns ARCHIVE_EOF if decompression has been completed.
+ * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data
+ * is broken or you do not set 'last' flag properly.
+ */
+#define ST_RD_TRANSLATION 0
+#define ST_RD_TRANSLATION_SIZE 1
+#define ST_RD_BLOCK_TYPE 2
+#define ST_RD_BLOCK_SIZE 3
+#define ST_RD_ALIGNMENT 4
+#define ST_RD_R0 5
+#define ST_RD_R1 6
+#define ST_RD_R2 7
+#define ST_COPY_UNCOMP1 8
+#define ST_COPY_UNCOMP2 9
+#define ST_RD_ALIGNED_OFFSET 10
+#define ST_RD_VERBATIM 11
+#define ST_RD_PRE_MAIN_TREE_256 12
+#define ST_MAIN_TREE_256 13
+#define ST_RD_PRE_MAIN_TREE_REM 14
+#define ST_MAIN_TREE_REM 15
+#define ST_RD_PRE_LENGTH_TREE 16
+#define ST_LENGTH_TREE 17
+#define ST_MAIN 18
+#define ST_LENGTH 19
+#define ST_OFFSET 20
+#define ST_REAL_POS 21
+#define ST_COPY 22
+
+static int
+lzx_decode(struct lzx_stream *strm, int last)
+{
+ struct lzx_dec *ds = strm->ds;
+ int64_t avail_in;
+ int r;
+
+ if (ds->error)
+ return (ds->error);
+
+ avail_in = strm->avail_in;
+ lzx_br_fixup(strm, &(ds->br));
+ do {
+ if (ds->state < ST_MAIN)
+ r = lzx_read_blocks(strm, last);
+ else {
+ int64_t bytes_written = strm->avail_out;
+ r = lzx_decode_blocks(strm, last);
+ bytes_written -= strm->avail_out;
+ strm->next_out += bytes_written;
+ strm->total_out += bytes_written;
+ }
+ } while (r == 100);
+ strm->total_in += avail_in - strm->avail_in;
+ return (r);
+}
+
+static int
+lzx_read_blocks(struct lzx_stream *strm, int last)
+{
+ struct lzx_dec *ds = strm->ds;
+ struct lzx_br *br = &(ds->br);
+ int i, r;
+
+ for (;;) {
+ switch (ds->state) {
+ case ST_RD_TRANSLATION:
+ if (!lzx_br_read_ahead(strm, br, 1)) {
+ ds->state = ST_RD_TRANSLATION;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ ds->translation = lzx_br_bits(br, 1);
+ lzx_br_consume(br, 1);
+ /* FALL THROUGH */
+ case ST_RD_TRANSLATION_SIZE:
+ if (ds->translation) {
+ if (!lzx_br_read_ahead(strm, br, 32)) {
+ ds->state = ST_RD_TRANSLATION_SIZE;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ ds->translation_size = lzx_br_bits(br, 16);
+ lzx_br_consume(br, 16);
+ ds->translation_size <<= 16;
+ ds->translation_size |= lzx_br_bits(br, 16);
+ lzx_br_consume(br, 16);
+ }
+ /* FALL THROUGH */
+ case ST_RD_BLOCK_TYPE:
+ if (!lzx_br_read_ahead(strm, br, 3)) {
+ ds->state = ST_RD_BLOCK_TYPE;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ ds->block_type = lzx_br_bits(br, 3);
+ lzx_br_consume(br, 3);
+ /* Check a block type. */
+ switch (ds->block_type) {
+ case VERBATIM_BLOCK:
+ case ALIGNED_OFFSET_BLOCK:
+ case UNCOMPRESSED_BLOCK:
+ break;
+ default:
+ goto failed;/* Invalid */
+ }
+ /* FALL THROUGH */
+ case ST_RD_BLOCK_SIZE:
+ if (!lzx_br_read_ahead(strm, br, 24)) {
+ ds->state = ST_RD_BLOCK_SIZE;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ ds->block_size = lzx_br_bits(br, 8);
+ lzx_br_consume(br, 8);
+ ds->block_size <<= 16;
+ ds->block_size |= lzx_br_bits(br, 16);
+ lzx_br_consume(br, 16);
+ if (ds->block_size == 0)
+ goto failed;
+ ds->block_bytes_avail = ds->block_size;
+ if (ds->block_type != UNCOMPRESSED_BLOCK) {
+ if (ds->block_type == VERBATIM_BLOCK)
+ ds->state = ST_RD_VERBATIM;
+ else
+ ds->state = ST_RD_ALIGNED_OFFSET;
+ break;
+ }
+ /* FALL THROUGH */
+ case ST_RD_ALIGNMENT:
+ /*
+ * Handle an Uncompressed Block.
+ */
+ /* Skip padding to align following field on
+ * 16-bit boundary. */
+ if (lzx_br_is_unaligned(br))
+ lzx_br_consume_unaligned_bits(br);
+ else {
+ if (lzx_br_read_ahead(strm, br, 16))
+ lzx_br_consume(br, 16);
+ else {
+ ds->state = ST_RD_ALIGNMENT;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ }
+ /* Preparation to read repeated offsets R0,R1 and R2. */
+ ds->rbytes_avail = 0;
+ ds->state = ST_RD_R0;
+ /* FALL THROUGH */
+ case ST_RD_R0:
+ case ST_RD_R1:
+ case ST_RD_R2:
+ do {
+ uint16_t u16;
+ /* Drain bits in the cache buffer of
+ * bit-stream. */
+ if (lzx_br_has(br, 32)) {
+ u16 = lzx_br_bits(br, 16);
+ lzx_br_consume(br, 16);
+ archive_le16enc(ds->rbytes, u16);
+ u16 = lzx_br_bits(br, 16);
+ lzx_br_consume(br, 16);
+ archive_le16enc(ds->rbytes+2, u16);
+ ds->rbytes_avail = 4;
+ } else if (lzx_br_has(br, 16)) {
+ u16 = lzx_br_bits(br, 16);
+ lzx_br_consume(br, 16);
+ archive_le16enc(ds->rbytes, u16);
+ ds->rbytes_avail = 2;
+ }
+ if (ds->rbytes_avail < 4 && ds->br.have_odd) {
+ ds->rbytes[ds->rbytes_avail++] =
+ ds->br.odd;
+ ds->br.have_odd = 0;
+ }
+ while (ds->rbytes_avail < 4) {
+ if (strm->avail_in <= 0) {
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ ds->rbytes[ds->rbytes_avail++] =
+ *strm->next_in++;
+ strm->avail_in--;
+ }
+ ds->rbytes_avail = 0;
+ if (ds->state == ST_RD_R0) {
+ ds->r0 = archive_le32dec(ds->rbytes);
+ if (ds->r0 < 0)
+ goto failed;
+ ds->state = ST_RD_R1;
+ } else if (ds->state == ST_RD_R1) {
+ ds->r1 = archive_le32dec(ds->rbytes);
+ if (ds->r1 < 0)
+ goto failed;
+ ds->state = ST_RD_R2;
+ } else if (ds->state == ST_RD_R2) {
+ ds->r2 = archive_le32dec(ds->rbytes);
+ if (ds->r2 < 0)
+ goto failed;
+ /* We've gotten all repeated offsets. */
+ ds->state = ST_COPY_UNCOMP1;
+ }
+ } while (ds->state != ST_COPY_UNCOMP1);
+ /* FALL THROUGH */
+ case ST_COPY_UNCOMP1:
+ /*
+ * Copy bytes form next_in to next_out directly.
+ */
+ while (ds->block_bytes_avail) {
+ int l;
+
+ if (strm->avail_out <= 0)
+ /* Output buffer is empty. */
+ return (ARCHIVE_OK);
+ if (strm->avail_in <= 0) {
+ /* Input buffer is empty. */
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ l = (int)ds->block_bytes_avail;
+ if (l > ds->w_size - ds->w_pos)
+ l = ds->w_size - ds->w_pos;
+ if (l > strm->avail_out)
+ l = (int)strm->avail_out;
+ if (l > strm->avail_in)
+ l = (int)strm->avail_in;
+ memcpy(strm->next_out, strm->next_in, l);
+ memcpy(&(ds->w_buff[ds->w_pos]),
+ strm->next_in, l);
+ strm->next_in += l;
+ strm->avail_in -= l;
+ strm->next_out += l;
+ strm->avail_out -= l;
+ strm->total_out += l;
+ ds->w_pos = (ds->w_pos + l) & ds->w_mask;
+ ds->block_bytes_avail -= l;
+ }
+ /* FALL THROUGH */
+ case ST_COPY_UNCOMP2:
+ /* Re-align; skip padding byte. */
+ if (ds->block_size & 1) {
+ if (strm->avail_in <= 0) {
+ /* Input buffer is empty. */
+ ds->state = ST_COPY_UNCOMP2;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ strm->next_in++;
+ strm->avail_in --;
+ }
+ /* This block ended. */
+ ds->state = ST_RD_BLOCK_TYPE;
+ return (ARCHIVE_EOF);
+ /********************/
+ case ST_RD_ALIGNED_OFFSET:
+ /*
+ * Read Aligned offset tree.
+ */
+ if (!lzx_br_read_ahead(strm, br, 3 * ds->at.len_size)) {
+ ds->state = ST_RD_ALIGNED_OFFSET;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ memset(ds->at.freq, 0, sizeof(ds->at.freq));
+ for (i = 0; i < ds->at.len_size; i++) {
+ ds->at.bitlen[i] = lzx_br_bits(br, 3);
+ ds->at.freq[ds->at.bitlen[i]]++;
+ lzx_br_consume(br, 3);
+ }
+ if (!lzx_make_huffman_table(&ds->at))
+ goto failed;
+ /* FALL THROUGH */
+ case ST_RD_VERBATIM:
+ ds->loop = 0;
+ /* FALL THROUGH */
+ case ST_RD_PRE_MAIN_TREE_256:
+ /*
+ * Read Pre-tree for first 256 elements of main tree.
+ */
+ if (!lzx_read_pre_tree(strm)) {
+ ds->state = ST_RD_PRE_MAIN_TREE_256;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ if (!lzx_make_huffman_table(&ds->pt))
+ goto failed;
+ ds->loop = 0;
+ /* FALL THROUGH */
+ case ST_MAIN_TREE_256:
+ /*
+ * Get path lengths of first 256 elements of main tree.
+ */
+ r = lzx_read_bitlen(strm, &ds->mt, 256);
+ if (r < 0)
+ goto failed;
+ else if (!r) {
+ ds->state = ST_MAIN_TREE_256;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ ds->loop = 0;
+ /* FALL THROUGH */
+ case ST_RD_PRE_MAIN_TREE_REM:
+ /*
+ * Read Pre-tree for remaining elements of main tree.
+ */
+ if (!lzx_read_pre_tree(strm)) {
+ ds->state = ST_RD_PRE_MAIN_TREE_REM;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ if (!lzx_make_huffman_table(&ds->pt))
+ goto failed;
+ ds->loop = 256;
+ /* FALL THROUGH */
+ case ST_MAIN_TREE_REM:
+ /*
+ * Get path lengths of remaining elements of main tree.
+ */
+ r = lzx_read_bitlen(strm, &ds->mt, -1);
+ if (r < 0)
+ goto failed;
+ else if (!r) {
+ ds->state = ST_MAIN_TREE_REM;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ if (!lzx_make_huffman_table(&ds->mt))
+ goto failed;
+ ds->loop = 0;
+ /* FALL THROUGH */
+ case ST_RD_PRE_LENGTH_TREE:
+ /*
+ * Read Pre-tree for remaining elements of main tree.
+ */
+ if (!lzx_read_pre_tree(strm)) {
+ ds->state = ST_RD_PRE_LENGTH_TREE;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ if (!lzx_make_huffman_table(&ds->pt))
+ goto failed;
+ ds->loop = 0;
+ /* FALL THROUGH */
+ case ST_LENGTH_TREE:
+ /*
+ * Get path lengths of remaining elements of main tree.
+ */
+ r = lzx_read_bitlen(strm, &ds->lt, -1);
+ if (r < 0)
+ goto failed;
+ else if (!r) {
+ ds->state = ST_LENGTH_TREE;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ if (!lzx_make_huffman_table(&ds->lt))
+ goto failed;
+ ds->state = ST_MAIN;
+ return (100);
+ }
+ }
+failed:
+ return (ds->error = ARCHIVE_FAILED);
+}
+
+static int
+lzx_decode_blocks(struct lzx_stream *strm, int last)
+{
+ struct lzx_dec *ds = strm->ds;
+ struct lzx_br bre = ds->br;
+ struct huffman *at = &(ds->at), *lt = &(ds->lt), *mt = &(ds->mt);
+ const struct lzx_pos_tbl *pos_tbl = ds->pos_tbl;
+ unsigned char *noutp = strm->next_out;
+ unsigned char *endp = noutp + strm->avail_out;
+ unsigned char *w_buff = ds->w_buff;
+ unsigned char *at_bitlen = at->bitlen;
+ unsigned char *lt_bitlen = lt->bitlen;
+ unsigned char *mt_bitlen = mt->bitlen;
+ size_t block_bytes_avail = ds->block_bytes_avail;
+ int at_max_bits = at->max_bits;
+ int lt_max_bits = lt->max_bits;
+ int mt_max_bits = mt->max_bits;
+ int c, copy_len = ds->copy_len, copy_pos = ds->copy_pos;
+ int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size;
+ int length_header = ds->length_header;
+ int offset_bits = ds->offset_bits;
+ int position_slot = ds->position_slot;
+ int r0 = ds->r0, r1 = ds->r1, r2 = ds->r2;
+ int state = ds->state;
+ char block_type = ds->block_type;
+
+ for (;;) {
+ switch (state) {
+ case ST_MAIN:
+ for (;;) {
+ if (block_bytes_avail == 0) {
+ /* This block ended. */
+ ds->state = ST_RD_BLOCK_TYPE;
+ ds->br = bre;
+ ds->block_bytes_avail =
+ block_bytes_avail;
+ ds->copy_len = copy_len;
+ ds->copy_pos = copy_pos;
+ ds->length_header = length_header;
+ ds->position_slot = position_slot;
+ ds->r0 = r0; ds->r1 = r1; ds->r2 = r2;
+ ds->w_pos = w_pos;
+ strm->avail_out = endp - noutp;
+ return (ARCHIVE_EOF);
+ }
+ if (noutp >= endp)
+ /* Output buffer is empty. */
+ goto next_data;
+
+ if (!lzx_br_read_ahead(strm, &bre,
+ mt_max_bits)) {
+ if (!last)
+ goto next_data;
+ /* Remaining bits are less than
+ * maximum bits(mt.max_bits) but maybe
+ * it still remains as much as we need,
+ * so we should try to use it with
+ * dummy bits. */
+ c = lzx_decode_huffman(mt,
+ lzx_br_bits_forced(
+ &bre, mt_max_bits));
+ lzx_br_consume(&bre, mt_bitlen[c]);
+ if (!lzx_br_has(&bre, 0))
+ goto failed;/* Over read. */
+ } else {
+ c = lzx_decode_huffman(mt,
+ lzx_br_bits(&bre, mt_max_bits));
+ lzx_br_consume(&bre, mt_bitlen[c]);
+ }
+ if (c > UCHAR_MAX)
+ break;
+ /*
+ * 'c' is exactly literal code.
+ */
+ /* Save a decoded code to reference it
+ * afterward. */
+ w_buff[w_pos] = c;
+ w_pos = (w_pos + 1) & w_mask;
+ /* Store the decoded code to output buffer. */
+ *noutp++ = c;
+ block_bytes_avail--;
+ }
+ /*
+ * Get a match code, its length and offset.
+ */
+ c -= UCHAR_MAX + 1;
+ length_header = c & 7;
+ position_slot = c >> 3;
+ /* FALL THROUGH */
+ case ST_LENGTH:
+ /*
+ * Get a length.
+ */
+ if (length_header == 7) {
+ if (!lzx_br_read_ahead(strm, &bre,
+ lt_max_bits)) {
+ if (!last) {
+ state = ST_LENGTH;
+ goto next_data;
+ }
+ c = lzx_decode_huffman(lt,
+ lzx_br_bits_forced(
+ &bre, lt_max_bits));
+ lzx_br_consume(&bre, lt_bitlen[c]);
+ if (!lzx_br_has(&bre, 0))
+ goto failed;/* Over read. */
+ } else {
+ c = lzx_decode_huffman(lt,
+ lzx_br_bits(&bre, lt_max_bits));
+ lzx_br_consume(&bre, lt_bitlen[c]);
+ }
+ copy_len = c + 7 + 2;
+ } else
+ copy_len = length_header + 2;
+ if ((size_t)copy_len > block_bytes_avail)
+ goto failed;
+ /*
+ * Get an offset.
+ */
+ switch (position_slot) {
+ case 0: /* Use repeated offset 0. */
+ copy_pos = r0;
+ state = ST_REAL_POS;
+ continue;
+ case 1: /* Use repeated offset 1. */
+ copy_pos = r1;
+ /* Swap repeated offset. */
+ r1 = r0;
+ r0 = copy_pos;
+ state = ST_REAL_POS;
+ continue;
+ case 2: /* Use repeated offset 2. */
+ copy_pos = r2;
+ /* Swap repeated offset. */
+ r2 = r0;
+ r0 = copy_pos;
+ state = ST_REAL_POS;
+ continue;
+ default:
+ offset_bits =
+ pos_tbl[position_slot].footer_bits;
+ break;
+ }
+ /* FALL THROUGH */
+ case ST_OFFSET:
+ /*
+ * Get the offset, which is a distance from
+ * current window position.
+ */
+ if (block_type == ALIGNED_OFFSET_BLOCK &&
+ offset_bits >= 3) {
+ int offbits = offset_bits - 3;
+
+ if (!lzx_br_read_ahead(strm, &bre, offbits)) {
+ state = ST_OFFSET;
+ if (last)
+ goto failed;
+ goto next_data;
+ }
+ copy_pos = lzx_br_bits(&bre, offbits) << 3;
+
+ /* Get an aligned number. */
+ if (!lzx_br_read_ahead(strm, &bre,
+ offbits + at_max_bits)) {
+ if (!last) {
+ state = ST_OFFSET;
+ goto next_data;
+ }
+ lzx_br_consume(&bre, offbits);
+ c = lzx_decode_huffman(at,
+ lzx_br_bits_forced(&bre,
+ at_max_bits));
+ lzx_br_consume(&bre, at_bitlen[c]);
+ if (!lzx_br_has(&bre, 0))
+ goto failed;/* Over read. */
+ } else {
+ lzx_br_consume(&bre, offbits);
+ c = lzx_decode_huffman(at,
+ lzx_br_bits(&bre, at_max_bits));
+ lzx_br_consume(&bre, at_bitlen[c]);
+ }
+ /* Add an aligned number. */
+ copy_pos += c;
+ } else {
+ if (!lzx_br_read_ahead(strm, &bre,
+ offset_bits)) {
+ state = ST_OFFSET;
+ if (last)
+ goto failed;
+ goto next_data;
+ }
+ copy_pos = lzx_br_bits(&bre, offset_bits);
+ lzx_br_consume(&bre, offset_bits);
+ }
+ copy_pos += pos_tbl[position_slot].base -2;
+
+ /* Update repeated offset LRU queue. */
+ r2 = r1;
+ r1 = r0;
+ r0 = copy_pos;
+ /* FALL THROUGH */
+ case ST_REAL_POS:
+ /*
+ * Compute a real position in window.
+ */
+ copy_pos = (w_pos - copy_pos) & w_mask;
+ /* FALL THROUGH */
+ case ST_COPY:
+ /*
+ * Copy several bytes as extracted data from the window
+ * into the output buffer.
+ */
+ for (;;) {
+ const unsigned char *s;
+ int l;
+
+ l = copy_len;
+ if (copy_pos > w_pos) {
+ if (l > w_size - copy_pos)
+ l = w_size - copy_pos;
+ } else {
+ if (l > w_size - w_pos)
+ l = w_size - w_pos;
+ }
+ if (noutp + l >= endp)
+ l = (int)(endp - noutp);
+ s = w_buff + copy_pos;
+ if (l >= 8 && ((copy_pos + l < w_pos)
+ || (w_pos + l < copy_pos))) {
+ memcpy(w_buff + w_pos, s, l);
+ memcpy(noutp, s, l);
+ } else {
+ unsigned char *d;
+ int li;
+
+ d = w_buff + w_pos;
+ for (li = 0; li < l; li++)
+ noutp[li] = d[li] = s[li];
+ }
+ noutp += l;
+ copy_pos = (copy_pos + l) & w_mask;
+ w_pos = (w_pos + l) & w_mask;
+ block_bytes_avail -= l;
+ if (copy_len <= l)
+ /* A copy of current pattern ended. */
+ break;
+ copy_len -= l;
+ if (noutp >= endp) {
+ /* Output buffer is empty. */
+ state = ST_COPY;
+ goto next_data;
+ }
+ }
+ state = ST_MAIN;
+ break;
+ }
+ }
+failed:
+ return (ds->error = ARCHIVE_FAILED);
+next_data:
+ ds->br = bre;
+ ds->block_bytes_avail = block_bytes_avail;
+ ds->copy_len = copy_len;
+ ds->copy_pos = copy_pos;
+ ds->length_header = length_header;
+ ds->offset_bits = offset_bits;
+ ds->position_slot = position_slot;
+ ds->r0 = r0; ds->r1 = r1; ds->r2 = r2;
+ ds->state = state;
+ ds->w_pos = w_pos;
+ strm->avail_out = endp - noutp;
+ return (ARCHIVE_OK);
+}
+
+static int
+lzx_read_pre_tree(struct lzx_stream *strm)
+{
+ struct lzx_dec *ds = strm->ds;
+ struct lzx_br *br = &(ds->br);
+ int i;
+
+ if (ds->loop == 0)
+ memset(ds->pt.freq, 0, sizeof(ds->pt.freq));
+ for (i = ds->loop; i < ds->pt.len_size; i++) {
+ if (!lzx_br_read_ahead(strm, br, 4)) {
+ ds->loop = i;
+ return (0);
+ }
+ ds->pt.bitlen[i] = lzx_br_bits(br, 4);
+ ds->pt.freq[ds->pt.bitlen[i]]++;
+ lzx_br_consume(br, 4);
+ }
+ ds->loop = i;
+ return (1);
+}
+
+/*
+ * Read a bunch of bit-lengths from pre-tree.
+ */
+static int
+lzx_read_bitlen(struct lzx_stream *strm, struct huffman *d, int end)
+{
+ struct lzx_dec *ds = strm->ds;
+ struct lzx_br *br = &(ds->br);
+ int c, i, j, ret, same;
+ unsigned rbits;
+
+ i = ds->loop;
+ if (i == 0)
+ memset(d->freq, 0, sizeof(d->freq));
+ ret = 0;
+ if (end < 0)
+ end = d->len_size;
+ while (i < end) {
+ ds->loop = i;
+ if (!lzx_br_read_ahead(strm, br, ds->pt.max_bits))
+ goto getdata;
+ rbits = lzx_br_bits(br, ds->pt.max_bits);
+ c = lzx_decode_huffman(&(ds->pt), rbits);
+ switch (c) {
+ case 17:/* several zero lengths, from 4 to 19. */
+ if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+4))
+ goto getdata;
+ lzx_br_consume(br, ds->pt.bitlen[c]);
+ same = lzx_br_bits(br, 4) + 4;
+ if (i + same > end)
+ return (-1);/* Invalid */
+ lzx_br_consume(br, 4);
+ for (j = 0; j < same; j++)
+ d->bitlen[i++] = 0;
+ break;
+ case 18:/* many zero lengths, from 20 to 51. */
+ if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+5))
+ goto getdata;
+ lzx_br_consume(br, ds->pt.bitlen[c]);
+ same = lzx_br_bits(br, 5) + 20;
+ if (i + same > end)
+ return (-1);/* Invalid */
+ lzx_br_consume(br, 5);
+ memset(d->bitlen + i, 0, same);
+ i += same;
+ break;
+ case 19:/* a few same lengths. */
+ if (!lzx_br_read_ahead(strm, br,
+ ds->pt.bitlen[c]+1+ds->pt.max_bits))
+ goto getdata;
+ lzx_br_consume(br, ds->pt.bitlen[c]);
+ same = lzx_br_bits(br, 1) + 4;
+ if (i + same > end)
+ return (-1);
+ lzx_br_consume(br, 1);
+ rbits = lzx_br_bits(br, ds->pt.max_bits);
+ c = lzx_decode_huffman(&(ds->pt), rbits);
+ lzx_br_consume(br, ds->pt.bitlen[c]);
+ c = (d->bitlen[i] - c + 17) % 17;
+ if (c < 0)
+ return (-1);/* Invalid */
+ for (j = 0; j < same; j++)
+ d->bitlen[i++] = c;
+ d->freq[c] += same;
+ break;
+ default:
+ lzx_br_consume(br, ds->pt.bitlen[c]);
+ c = (d->bitlen[i] - c + 17) % 17;
+ if (c < 0)
+ return (-1);/* Invalid */
+ d->freq[c]++;
+ d->bitlen[i++] = c;
+ break;
+ }
+ }
+ ret = 1;
+getdata:
+ ds->loop = i;
+ return (ret);
+}
+
+static int
+lzx_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits)
+{
+
+ if (hf->bitlen == NULL || hf->len_size != (int)len_size) {
+ free(hf->bitlen);
+ hf->bitlen = calloc(len_size, sizeof(hf->bitlen[0]));
+ if (hf->bitlen == NULL)
+ return (ARCHIVE_FATAL);
+ hf->len_size = (int)len_size;
+ } else
+ memset(hf->bitlen, 0, len_size * sizeof(hf->bitlen[0]));
+ if (hf->tbl == NULL) {
+ hf->tbl = malloc(((size_t)1 << tbl_bits) * sizeof(hf->tbl[0]));
+ if (hf->tbl == NULL)
+ return (ARCHIVE_FATAL);
+ hf->tbl_bits = tbl_bits;
+ }
+ return (ARCHIVE_OK);
+}
+
+static void
+lzx_huffman_free(struct huffman *hf)
+{
+ free(hf->bitlen);
+ free(hf->tbl);
+}
+
+/*
+ * Make a huffman coding table.
+ */
+static int
+lzx_make_huffman_table(struct huffman *hf)
+{
+ uint16_t *tbl;
+ const unsigned char *bitlen;
+ int bitptn[17], weight[17];
+ int i, maxbits = 0, ptn, tbl_size, w;
+ int len_avail;
+
+ /*
+ * Initialize bit patterns.
+ */
+ ptn = 0;
+ for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) {
+ bitptn[i] = ptn;
+ weight[i] = w;
+ if (hf->freq[i]) {
+ ptn += hf->freq[i] * w;
+ maxbits = i;
+ }
+ }
+ if ((ptn & 0xffff) != 0 || maxbits > hf->tbl_bits)
+ return (0);/* Invalid */
+
+ hf->max_bits = maxbits;
+
+ /*
+ * Cut out extra bits which we won't house in the table.
+ * This preparation reduces the same calculation in the for-loop
+ * making the table.
+ */
+ if (maxbits < 16) {
+ int ebits = 16 - maxbits;
+ for (i = 1; i <= maxbits; i++) {
+ bitptn[i] >>= ebits;
+ weight[i] >>= ebits;
+ }
+ }
+
+ /*
+ * Make the table.
+ */
+ tbl_size = 1 << hf->tbl_bits;
+ tbl = hf->tbl;
+ bitlen = hf->bitlen;
+ len_avail = hf->len_size;
+ hf->tree_used = 0;
+ for (i = 0; i < len_avail; i++) {
+ uint16_t *p;
+ int len, cnt;
+
+ if (bitlen[i] == 0)
+ continue;
+ /* Get a bit pattern */
+ len = bitlen[i];
+ if (len > tbl_size)
+ return (0);
+ ptn = bitptn[len];
+ cnt = weight[len];
+ /* Calculate next bit pattern */
+ if ((bitptn[len] = ptn + cnt) > tbl_size)
+ return (0);/* Invalid */
+ /* Update the table */
+ p = &(tbl[ptn]);
+ while (--cnt >= 0)
+ p[cnt] = (uint16_t)i;
+ }
+ return (1);
+}
+
+static inline int
+lzx_decode_huffman(struct huffman *hf, unsigned rbits)
+{
+ int c;
+ c = hf->tbl[rbits];
+ if (c < hf->len_size)
+ return (c);
+ return (0);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_cpio.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_cpio.c
new file mode 100644
index 00000000000..9adcfd335b1
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_cpio.c
@@ -0,0 +1,1104 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_cpio.c 201163 2009-12-29 05:50:34Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+/* #include <stdint.h> */ /* See archive_platform.h */
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#define bin_magic_offset 0
+#define bin_magic_size 2
+#define bin_dev_offset 2
+#define bin_dev_size 2
+#define bin_ino_offset 4
+#define bin_ino_size 2
+#define bin_mode_offset 6
+#define bin_mode_size 2
+#define bin_uid_offset 8
+#define bin_uid_size 2
+#define bin_gid_offset 10
+#define bin_gid_size 2
+#define bin_nlink_offset 12
+#define bin_nlink_size 2
+#define bin_rdev_offset 14
+#define bin_rdev_size 2
+#define bin_mtime_offset 16
+#define bin_mtime_size 4
+#define bin_namesize_offset 20
+#define bin_namesize_size 2
+#define bin_filesize_offset 22
+#define bin_filesize_size 4
+#define bin_header_size 26
+
+#define odc_magic_offset 0
+#define odc_magic_size 6
+#define odc_dev_offset 6
+#define odc_dev_size 6
+#define odc_ino_offset 12
+#define odc_ino_size 6
+#define odc_mode_offset 18
+#define odc_mode_size 6
+#define odc_uid_offset 24
+#define odc_uid_size 6
+#define odc_gid_offset 30
+#define odc_gid_size 6
+#define odc_nlink_offset 36
+#define odc_nlink_size 6
+#define odc_rdev_offset 42
+#define odc_rdev_size 6
+#define odc_mtime_offset 48
+#define odc_mtime_size 11
+#define odc_namesize_offset 59
+#define odc_namesize_size 6
+#define odc_filesize_offset 65
+#define odc_filesize_size 11
+#define odc_header_size 76
+
+#define newc_magic_offset 0
+#define newc_magic_size 6
+#define newc_ino_offset 6
+#define newc_ino_size 8
+#define newc_mode_offset 14
+#define newc_mode_size 8
+#define newc_uid_offset 22
+#define newc_uid_size 8
+#define newc_gid_offset 30
+#define newc_gid_size 8
+#define newc_nlink_offset 38
+#define newc_nlink_size 8
+#define newc_mtime_offset 46
+#define newc_mtime_size 8
+#define newc_filesize_offset 54
+#define newc_filesize_size 8
+#define newc_devmajor_offset 62
+#define newc_devmajor_size 8
+#define newc_devminor_offset 70
+#define newc_devminor_size 8
+#define newc_rdevmajor_offset 78
+#define newc_rdevmajor_size 8
+#define newc_rdevminor_offset 86
+#define newc_rdevminor_size 8
+#define newc_namesize_offset 94
+#define newc_namesize_size 8
+#define newc_checksum_offset 102
+#define newc_checksum_size 8
+#define newc_header_size 110
+
+/*
+ * An afio large ASCII header, which they named itself.
+ * afio utility uses this header, if a file size is larger than 2G bytes
+ * or inode/uid/gid is bigger than 65535(0xFFFF) or mtime is bigger than
+ * 0x7fffffff, which we cannot record to odc header because of its limit.
+ * If not, uses odc header.
+ */
+#define afiol_magic_offset 0
+#define afiol_magic_size 6
+#define afiol_dev_offset 6
+#define afiol_dev_size 8 /* hex */
+#define afiol_ino_offset 14
+#define afiol_ino_size 16 /* hex */
+#define afiol_ino_m_offset 30 /* 'm' */
+#define afiol_mode_offset 31
+#define afiol_mode_size 6 /* oct */
+#define afiol_uid_offset 37
+#define afiol_uid_size 8 /* hex */
+#define afiol_gid_offset 45
+#define afiol_gid_size 8 /* hex */
+#define afiol_nlink_offset 53
+#define afiol_nlink_size 8 /* hex */
+#define afiol_rdev_offset 61
+#define afiol_rdev_size 8 /* hex */
+#define afiol_mtime_offset 69
+#define afiol_mtime_size 16 /* hex */
+#define afiol_mtime_n_offset 85 /* 'n' */
+#define afiol_namesize_offset 86
+#define afiol_namesize_size 4 /* hex */
+#define afiol_flag_offset 90
+#define afiol_flag_size 4 /* hex */
+#define afiol_xsize_offset 94
+#define afiol_xsize_size 4 /* hex */
+#define afiol_xsize_s_offset 98 /* 's' */
+#define afiol_filesize_offset 99
+#define afiol_filesize_size 16 /* hex */
+#define afiol_filesize_c_offset 115 /* ':' */
+#define afiol_header_size 116
+
+
+struct links_entry {
+ struct links_entry *next;
+ struct links_entry *previous;
+ unsigned int links;
+ dev_t dev;
+ int64_t ino;
+ char *name;
+};
+
+#define CPIO_MAGIC 0x13141516
+struct cpio {
+ int magic;
+ int (*read_header)(struct archive_read *, struct cpio *,
+ struct archive_entry *, size_t *, size_t *);
+ struct links_entry *links_head;
+ int64_t entry_bytes_remaining;
+ int64_t entry_bytes_unconsumed;
+ int64_t entry_offset;
+ int64_t entry_padding;
+
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_default;
+ int init_default_conversion;
+
+ int option_pwb;
+};
+
+static int64_t atol16(const char *, unsigned);
+static int64_t atol8(const char *, unsigned);
+static int archive_read_format_cpio_bid(struct archive_read *, int);
+static int archive_read_format_cpio_options(struct archive_read *,
+ const char *, const char *);
+static int archive_read_format_cpio_cleanup(struct archive_read *);
+static int archive_read_format_cpio_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_cpio_read_header(struct archive_read *,
+ struct archive_entry *);
+static int archive_read_format_cpio_skip(struct archive_read *);
+static int64_t be4(const unsigned char *);
+static int find_odc_header(struct archive_read *);
+static int find_newc_header(struct archive_read *);
+static int header_bin_be(struct archive_read *, struct cpio *,
+ struct archive_entry *, size_t *, size_t *);
+static int header_bin_le(struct archive_read *, struct cpio *,
+ struct archive_entry *, size_t *, size_t *);
+static int header_newc(struct archive_read *, struct cpio *,
+ struct archive_entry *, size_t *, size_t *);
+static int header_odc(struct archive_read *, struct cpio *,
+ struct archive_entry *, size_t *, size_t *);
+static int header_afiol(struct archive_read *, struct cpio *,
+ struct archive_entry *, size_t *, size_t *);
+static int is_octal(const char *, size_t);
+static int is_hex(const char *, size_t);
+static int64_t le4(const unsigned char *);
+static int record_hardlink(struct archive_read *a,
+ struct cpio *cpio, struct archive_entry *entry);
+
+int
+archive_read_support_format_cpio(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct cpio *cpio;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_cpio");
+
+ cpio = (struct cpio *)calloc(1, sizeof(*cpio));
+ if (cpio == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
+ return (ARCHIVE_FATAL);
+ }
+ cpio->magic = CPIO_MAGIC;
+
+ r = __archive_read_register_format(a,
+ cpio,
+ "cpio",
+ archive_read_format_cpio_bid,
+ archive_read_format_cpio_options,
+ archive_read_format_cpio_read_header,
+ archive_read_format_cpio_read_data,
+ archive_read_format_cpio_skip,
+ NULL,
+ archive_read_format_cpio_cleanup,
+ NULL,
+ NULL);
+
+ if (r != ARCHIVE_OK)
+ free(cpio);
+ return (ARCHIVE_OK);
+}
+
+
+static int
+archive_read_format_cpio_bid(struct archive_read *a, int best_bid)
+{
+ const unsigned char *p;
+ struct cpio *cpio;
+ int bid;
+
+ (void)best_bid; /* UNUSED */
+
+ cpio = (struct cpio *)(a->format->data);
+
+ if ((p = __archive_read_ahead(a, 6, NULL)) == NULL)
+ return (-1);
+
+ bid = 0;
+ if (memcmp(p, "070707", 6) == 0) {
+ /* ASCII cpio archive (odc, POSIX.1) */
+ cpio->read_header = header_odc;
+ bid += 48;
+ /*
+ * XXX TODO: More verification; Could check that only octal
+ * digits appear in appropriate header locations. XXX
+ */
+ } else if (memcmp(p, "070727", 6) == 0) {
+ /* afio large ASCII cpio archive */
+ cpio->read_header = header_odc;
+ bid += 48;
+ /*
+ * XXX TODO: More verification; Could check that almost hex
+ * digits appear in appropriate header locations. XXX
+ */
+ } else if (memcmp(p, "070701", 6) == 0) {
+ /* ASCII cpio archive (SVR4 without CRC) */
+ cpio->read_header = header_newc;
+ bid += 48;
+ /*
+ * XXX TODO: More verification; Could check that only hex
+ * digits appear in appropriate header locations. XXX
+ */
+ } else if (memcmp(p, "070702", 6) == 0) {
+ /* ASCII cpio archive (SVR4 with CRC) */
+ /* XXX TODO: Flag that we should check the CRC. XXX */
+ cpio->read_header = header_newc;
+ bid += 48;
+ /*
+ * XXX TODO: More verification; Could check that only hex
+ * digits appear in appropriate header locations. XXX
+ */
+ } else if (p[0] * 256 + p[1] == 070707) {
+ /* big-endian binary cpio archives */
+ cpio->read_header = header_bin_be;
+ bid += 16;
+ /* Is more verification possible here? */
+ } else if (p[0] + p[1] * 256 == 070707) {
+ /* little-endian binary cpio archives */
+ cpio->read_header = header_bin_le;
+ bid += 16;
+ /* Is more verification possible here? */
+ } else
+ return (ARCHIVE_WARN);
+
+ return (bid);
+}
+
+static int
+archive_read_format_cpio_options(struct archive_read *a,
+ const char *key, const char *val)
+{
+ struct cpio *cpio;
+ int ret = ARCHIVE_FAILED;
+
+ cpio = (struct cpio *)(a->format->data);
+ if (strcmp(key, "compat-2x") == 0) {
+ /* Handle filenames as libarchive 2.x */
+ cpio->init_default_conversion = (val != NULL)?1:0;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "cpio: hdrcharset option needs a character-set name");
+ else {
+ cpio->opt_sconv =
+ archive_string_conversion_from_charset(
+ &a->archive, val, 0);
+ if (cpio->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ } else if (strcmp(key, "pwb") == 0) {
+ if (val != NULL && val[0] != 0)
+ cpio->option_pwb = 1;
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_read_format_cpio_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct cpio *cpio;
+ const void *h, *hl;
+ struct archive_string_conv *sconv;
+ size_t namelength;
+ size_t name_pad;
+ int r;
+
+ cpio = (struct cpio *)(a->format->data);
+ sconv = cpio->opt_sconv;
+ if (sconv == NULL) {
+ if (!cpio->init_default_conversion) {
+ cpio->sconv_default =
+ archive_string_default_conversion_for_read(
+ &(a->archive));
+ cpio->init_default_conversion = 1;
+ }
+ sconv = cpio->sconv_default;
+ }
+
+ r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad));
+
+ if (r < ARCHIVE_WARN)
+ return (r);
+
+ /* Read name from buffer. */
+ h = __archive_read_ahead(a, namelength + name_pad, NULL);
+ if (h == NULL)
+ return (ARCHIVE_FATAL);
+ if (archive_entry_copy_pathname_l(entry,
+ (const char *)h, namelength, sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname can't be converted from %s to current locale.",
+ archive_string_conversion_charset_name(sconv));
+ r = ARCHIVE_WARN;
+ }
+ cpio->entry_offset = 0;
+
+ __archive_read_consume(a, namelength + name_pad);
+
+ /* If this is a symlink, read the link contents. */
+ if (archive_entry_filetype(entry) == AE_IFLNK) {
+ if (cpio->entry_bytes_remaining > 1024 * 1024) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Rejecting malformed cpio archive: symlink contents exceed 1 megabyte");
+ return (ARCHIVE_FATAL);
+ }
+ hl = __archive_read_ahead(a,
+ (size_t)cpio->entry_bytes_remaining, NULL);
+ if (hl == NULL)
+ return (ARCHIVE_FATAL);
+ if (archive_entry_copy_symlink_l(entry, (const char *)hl,
+ (size_t)cpio->entry_bytes_remaining, sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Linkname can't be converted from %s to "
+ "current locale.",
+ archive_string_conversion_charset_name(sconv));
+ r = ARCHIVE_WARN;
+ }
+ __archive_read_consume(a, cpio->entry_bytes_remaining);
+ cpio->entry_bytes_remaining = 0;
+ }
+
+ /* XXX TODO: If the full mode is 0160200, then this is a Solaris
+ * ACL description for the following entry. Read this body
+ * and parse it as a Solaris-style ACL, then read the next
+ * header. XXX */
+
+ /* Compare name to "TRAILER!!!" to test for end-of-archive. */
+ if (namelength == 11 && strncmp((const char *)h, "TRAILER!!!",
+ 10) == 0) {
+ /* TODO: Store file location of start of block. */
+ archive_clear_error(&a->archive);
+ return (ARCHIVE_EOF);
+ }
+
+ /* Detect and record hardlinks to previously-extracted entries. */
+ if (record_hardlink(a, cpio, entry) != ARCHIVE_OK) {
+ return (ARCHIVE_FATAL);
+ }
+
+ return (r);
+}
+
+static int
+archive_read_format_cpio_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ ssize_t bytes_read;
+ struct cpio *cpio;
+
+ cpio = (struct cpio *)(a->format->data);
+
+ if (cpio->entry_bytes_unconsumed) {
+ __archive_read_consume(a, cpio->entry_bytes_unconsumed);
+ cpio->entry_bytes_unconsumed = 0;
+ }
+
+ if (cpio->entry_bytes_remaining > 0) {
+ *buff = __archive_read_ahead(a, 1, &bytes_read);
+ if (bytes_read <= 0)
+ return (ARCHIVE_FATAL);
+ if (bytes_read > cpio->entry_bytes_remaining)
+ bytes_read = (ssize_t)cpio->entry_bytes_remaining;
+ *size = bytes_read;
+ cpio->entry_bytes_unconsumed = bytes_read;
+ *offset = cpio->entry_offset;
+ cpio->entry_offset += bytes_read;
+ cpio->entry_bytes_remaining -= bytes_read;
+ return (ARCHIVE_OK);
+ } else {
+ if (cpio->entry_padding !=
+ __archive_read_consume(a, cpio->entry_padding)) {
+ return (ARCHIVE_FATAL);
+ }
+ cpio->entry_padding = 0;
+ *buff = NULL;
+ *size = 0;
+ *offset = cpio->entry_offset;
+ return (ARCHIVE_EOF);
+ }
+}
+
+static int
+archive_read_format_cpio_skip(struct archive_read *a)
+{
+ struct cpio *cpio = (struct cpio *)(a->format->data);
+ int64_t to_skip = cpio->entry_bytes_remaining + cpio->entry_padding +
+ cpio->entry_bytes_unconsumed;
+
+ if (to_skip != __archive_read_consume(a, to_skip)) {
+ return (ARCHIVE_FATAL);
+ }
+ cpio->entry_bytes_remaining = 0;
+ cpio->entry_padding = 0;
+ cpio->entry_bytes_unconsumed = 0;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Skip forward to the next cpio newc header by searching for the
+ * 07070[12] string. This should be generalized and merged with
+ * find_odc_header below.
+ */
+static int
+is_hex(const char *p, size_t len)
+{
+ while (len-- > 0) {
+ if ((*p >= '0' && *p <= '9')
+ || (*p >= 'a' && *p <= 'f')
+ || (*p >= 'A' && *p <= 'F'))
+ ++p;
+ else
+ return (0);
+ }
+ return (1);
+}
+
+static int
+find_newc_header(struct archive_read *a)
+{
+ const void *h;
+ const char *p, *q;
+ size_t skip, skipped = 0;
+ ssize_t bytes;
+
+ for (;;) {
+ h = __archive_read_ahead(a, newc_header_size, &bytes);
+ if (h == NULL)
+ return (ARCHIVE_FATAL);
+ p = h;
+ q = p + bytes;
+
+ /* Try the typical case first, then go into the slow search.*/
+ if (memcmp("07070", p, 5) == 0
+ && (p[5] == '1' || p[5] == '2')
+ && is_hex(p, newc_header_size))
+ return (ARCHIVE_OK);
+
+ /*
+ * Scan ahead until we find something that looks
+ * like a newc header.
+ */
+ while (p + newc_header_size <= q) {
+ switch (p[5]) {
+ case '1':
+ case '2':
+ if (memcmp("07070", p, 5) == 0
+ && is_hex(p, newc_header_size)) {
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ skipped += skip;
+ if (skipped > 0) {
+ archive_set_error(&a->archive,
+ 0,
+ "Skipped %d bytes before "
+ "finding valid header",
+ (int)skipped);
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+ }
+ p += 2;
+ break;
+ case '0':
+ p++;
+ break;
+ default:
+ p += 6;
+ break;
+ }
+ }
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ skipped += skip;
+ }
+}
+
+static int
+header_newc(struct archive_read *a, struct cpio *cpio,
+ struct archive_entry *entry, size_t *namelength, size_t *name_pad)
+{
+ const void *h;
+ const char *header;
+ int r;
+
+ r = find_newc_header(a);
+ if (r < ARCHIVE_WARN)
+ return (r);
+
+ /* Read fixed-size portion of header. */
+ h = __archive_read_ahead(a, newc_header_size, NULL);
+ if (h == NULL)
+ return (ARCHIVE_FATAL);
+
+ /* Parse out hex fields. */
+ header = (const char *)h;
+
+ if (memcmp(header + newc_magic_offset, "070701", 6) == 0) {
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;
+ a->archive.archive_format_name = "ASCII cpio (SVR4 with no CRC)";
+ } else if (memcmp(header + newc_magic_offset, "070702", 6) == 0) {
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_CRC;
+ a->archive.archive_format_name = "ASCII cpio (SVR4 with CRC)";
+ } else {
+ /* TODO: Abort here? */
+ }
+
+ archive_entry_set_devmajor(entry,
+ (dev_t)atol16(header + newc_devmajor_offset, newc_devmajor_size));
+ archive_entry_set_devminor(entry,
+ (dev_t)atol16(header + newc_devminor_offset, newc_devminor_size));
+ archive_entry_set_ino(entry, atol16(header + newc_ino_offset, newc_ino_size));
+ archive_entry_set_mode(entry,
+ (mode_t)atol16(header + newc_mode_offset, newc_mode_size));
+ archive_entry_set_uid(entry, atol16(header + newc_uid_offset, newc_uid_size));
+ archive_entry_set_gid(entry, atol16(header + newc_gid_offset, newc_gid_size));
+ archive_entry_set_nlink(entry,
+ (unsigned int)atol16(header + newc_nlink_offset, newc_nlink_size));
+ archive_entry_set_rdevmajor(entry,
+ (dev_t)atol16(header + newc_rdevmajor_offset, newc_rdevmajor_size));
+ archive_entry_set_rdevminor(entry,
+ (dev_t)atol16(header + newc_rdevminor_offset, newc_rdevminor_size));
+ archive_entry_set_mtime(entry, atol16(header + newc_mtime_offset, newc_mtime_size), 0);
+ *namelength = (size_t)atol16(header + newc_namesize_offset, newc_namesize_size);
+ /* Pad name to 2 more than a multiple of 4. */
+ *name_pad = (2 - *namelength) & 3;
+
+ /* Make sure that the padded name length fits into size_t. */
+ if (*name_pad > SIZE_MAX - *namelength) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "cpio archive has invalid namelength");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Note: entry_bytes_remaining is at least 64 bits and
+ * therefore guaranteed to be big enough for a 33-bit file
+ * size.
+ */
+ cpio->entry_bytes_remaining =
+ atol16(header + newc_filesize_offset, newc_filesize_size);
+ archive_entry_set_size(entry, cpio->entry_bytes_remaining);
+ /* Pad file contents to a multiple of 4. */
+ cpio->entry_padding = 3 & -cpio->entry_bytes_remaining;
+ __archive_read_consume(a, newc_header_size);
+ return (r);
+}
+
+/*
+ * Skip forward to the next cpio odc header by searching for the
+ * 070707 string. This is a hand-optimized search that could
+ * probably be easily generalized to handle all character-based
+ * cpio variants.
+ */
+static int
+is_octal(const char *p, size_t len)
+{
+ while (len-- > 0) {
+ if (*p < '0' || *p > '7')
+ return (0);
+ ++p;
+ }
+ return (1);
+}
+
+static int
+is_afio_large(const char *h, size_t len)
+{
+ if (len < afiol_header_size)
+ return (0);
+ if (h[afiol_ino_m_offset] != 'm'
+ || h[afiol_mtime_n_offset] != 'n'
+ || h[afiol_xsize_s_offset] != 's'
+ || h[afiol_filesize_c_offset] != ':')
+ return (0);
+ if (!is_hex(h + afiol_dev_offset, afiol_ino_m_offset - afiol_dev_offset))
+ return (0);
+ if (!is_hex(h + afiol_mode_offset, afiol_mtime_n_offset - afiol_mode_offset))
+ return (0);
+ if (!is_hex(h + afiol_namesize_offset, afiol_xsize_s_offset - afiol_namesize_offset))
+ return (0);
+ if (!is_hex(h + afiol_filesize_offset, afiol_filesize_size))
+ return (0);
+ return (1);
+}
+
+static int
+find_odc_header(struct archive_read *a)
+{
+ const void *h;
+ const char *p, *q;
+ size_t skip, skipped = 0;
+ ssize_t bytes;
+
+ for (;;) {
+ h = __archive_read_ahead(a, odc_header_size, &bytes);
+ if (h == NULL)
+ return (ARCHIVE_FATAL);
+ p = h;
+ q = p + bytes;
+
+ /* Try the typical case first, then go into the slow search.*/
+ if (memcmp("070707", p, 6) == 0 && is_octal(p, odc_header_size))
+ return (ARCHIVE_OK);
+ if (memcmp("070727", p, 6) == 0 && is_afio_large(p, bytes)) {
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE;
+ return (ARCHIVE_OK);
+ }
+
+ /*
+ * Scan ahead until we find something that looks
+ * like an odc header.
+ */
+ while (p + odc_header_size <= q) {
+ switch (p[5]) {
+ case '7':
+ if ((memcmp("070707", p, 6) == 0
+ && is_octal(p, odc_header_size))
+ || (memcmp("070727", p, 6) == 0
+ && is_afio_large(p, q - p))) {
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ skipped += skip;
+ if (p[4] == '2')
+ a->archive.archive_format =
+ ARCHIVE_FORMAT_CPIO_AFIO_LARGE;
+ if (skipped > 0) {
+ archive_set_error(&a->archive,
+ 0,
+ "Skipped %d bytes before "
+ "finding valid header",
+ (int)skipped);
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+ }
+ p += 2;
+ break;
+ case '0':
+ p++;
+ break;
+ default:
+ p += 6;
+ break;
+ }
+ }
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ skipped += skip;
+ }
+}
+
+static int
+header_odc(struct archive_read *a, struct cpio *cpio,
+ struct archive_entry *entry, size_t *namelength, size_t *name_pad)
+{
+ const void *h;
+ int r;
+ const char *header;
+
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
+ a->archive.archive_format_name = "POSIX octet-oriented cpio";
+
+ /* Find the start of the next header. */
+ r = find_odc_header(a);
+ if (r < ARCHIVE_WARN)
+ return (r);
+
+ if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_AFIO_LARGE) {
+ int r2 = (header_afiol(a, cpio, entry, namelength, name_pad));
+ if (r2 == ARCHIVE_OK)
+ return (r);
+ else
+ return (r2);
+ }
+
+ /* Read fixed-size portion of header. */
+ h = __archive_read_ahead(a, odc_header_size, NULL);
+ if (h == NULL)
+ return (ARCHIVE_FATAL);
+
+ /* Parse out octal fields. */
+ header = (const char *)h;
+
+ archive_entry_set_dev(entry,
+ (dev_t)atol8(header + odc_dev_offset, odc_dev_size));
+ archive_entry_set_ino(entry, atol8(header + odc_ino_offset, odc_ino_size));
+ archive_entry_set_mode(entry,
+ (mode_t)atol8(header + odc_mode_offset, odc_mode_size));
+ archive_entry_set_uid(entry, atol8(header + odc_uid_offset, odc_uid_size));
+ archive_entry_set_gid(entry, atol8(header + odc_gid_offset, odc_gid_size));
+ archive_entry_set_nlink(entry,
+ (unsigned int)atol8(header + odc_nlink_offset, odc_nlink_size));
+ archive_entry_set_rdev(entry,
+ (dev_t)atol8(header + odc_rdev_offset, odc_rdev_size));
+ archive_entry_set_mtime(entry, atol8(header + odc_mtime_offset, odc_mtime_size), 0);
+ *namelength = (size_t)atol8(header + odc_namesize_offset, odc_namesize_size);
+ *name_pad = 0; /* No padding of filename. */
+
+ /*
+ * Note: entry_bytes_remaining is at least 64 bits and
+ * therefore guaranteed to be big enough for a 33-bit file
+ * size.
+ */
+ cpio->entry_bytes_remaining =
+ atol8(header + odc_filesize_offset, odc_filesize_size);
+ archive_entry_set_size(entry, cpio->entry_bytes_remaining);
+ cpio->entry_padding = 0;
+ __archive_read_consume(a, odc_header_size);
+ return (r);
+}
+
+/*
+ * NOTE: if a filename suffix is ".z", it is the file gziped by afio.
+ * it would be nice that we can show uncompressed file size and we can
+ * uncompressed file contents automatically, unfortunately we have nothing
+ * to get a uncompressed file size while reading each header. It means
+ * we also cannot uncompress file contents under our framework.
+ */
+static int
+header_afiol(struct archive_read *a, struct cpio *cpio,
+ struct archive_entry *entry, size_t *namelength, size_t *name_pad)
+{
+ const void *h;
+ const char *header;
+
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE;
+ a->archive.archive_format_name = "afio large ASCII";
+
+ /* Read fixed-size portion of header. */
+ h = __archive_read_ahead(a, afiol_header_size, NULL);
+ if (h == NULL)
+ return (ARCHIVE_FATAL);
+
+ /* Parse out octal fields. */
+ header = (const char *)h;
+
+ archive_entry_set_dev(entry,
+ (dev_t)atol16(header + afiol_dev_offset, afiol_dev_size));
+ archive_entry_set_ino(entry, atol16(header + afiol_ino_offset, afiol_ino_size));
+ archive_entry_set_mode(entry,
+ (mode_t)atol8(header + afiol_mode_offset, afiol_mode_size));
+ archive_entry_set_uid(entry, atol16(header + afiol_uid_offset, afiol_uid_size));
+ archive_entry_set_gid(entry, atol16(header + afiol_gid_offset, afiol_gid_size));
+ archive_entry_set_nlink(entry,
+ (unsigned int)atol16(header + afiol_nlink_offset, afiol_nlink_size));
+ archive_entry_set_rdev(entry,
+ (dev_t)atol16(header + afiol_rdev_offset, afiol_rdev_size));
+ archive_entry_set_mtime(entry, atol16(header + afiol_mtime_offset, afiol_mtime_size), 0);
+ *namelength = (size_t)atol16(header + afiol_namesize_offset, afiol_namesize_size);
+ *name_pad = 0; /* No padding of filename. */
+
+ cpio->entry_bytes_remaining =
+ atol16(header + afiol_filesize_offset, afiol_filesize_size);
+ archive_entry_set_size(entry, cpio->entry_bytes_remaining);
+ cpio->entry_padding = 0;
+ __archive_read_consume(a, afiol_header_size);
+ return (ARCHIVE_OK);
+}
+
+
+static int
+header_bin_le(struct archive_read *a, struct cpio *cpio,
+ struct archive_entry *entry, size_t *namelength, size_t *name_pad)
+{
+ const void *h;
+ const unsigned char *header;
+
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE;
+ a->archive.archive_format_name = "cpio (little-endian binary)";
+
+ /* Read fixed-size portion of header. */
+ h = __archive_read_ahead(a, bin_header_size, NULL);
+ if (h == NULL) {
+ archive_set_error(&a->archive, 0,
+ "End of file trying to read next cpio header");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Parse out binary fields. */
+ header = (const unsigned char *)h;
+
+ archive_entry_set_dev(entry, header[bin_dev_offset] + header[bin_dev_offset + 1] * 256);
+ archive_entry_set_ino(entry, header[bin_ino_offset] + header[bin_ino_offset + 1] * 256);
+ archive_entry_set_mode(entry, header[bin_mode_offset] + header[bin_mode_offset + 1] * 256);
+ if (cpio->option_pwb) {
+ /* turn off random bits left over from V6 inode */
+ archive_entry_set_mode(entry, archive_entry_mode(entry) & 067777);
+ if ((archive_entry_mode(entry) & AE_IFMT) == 0)
+ archive_entry_set_mode(entry, archive_entry_mode(entry) | AE_IFREG);
+ }
+ archive_entry_set_uid(entry, header[bin_uid_offset] + header[bin_uid_offset + 1] * 256);
+ archive_entry_set_gid(entry, header[bin_gid_offset] + header[bin_gid_offset + 1] * 256);
+ archive_entry_set_nlink(entry, header[bin_nlink_offset] + header[bin_nlink_offset + 1] * 256);
+ archive_entry_set_rdev(entry, header[bin_rdev_offset] + header[bin_rdev_offset + 1] * 256);
+ archive_entry_set_mtime(entry, le4(header + bin_mtime_offset), 0);
+ *namelength = header[bin_namesize_offset] + header[bin_namesize_offset + 1] * 256;
+ *name_pad = *namelength & 1; /* Pad to even. */
+
+ cpio->entry_bytes_remaining = le4(header + bin_filesize_offset);
+ archive_entry_set_size(entry, cpio->entry_bytes_remaining);
+ cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */
+ __archive_read_consume(a, bin_header_size);
+ return (ARCHIVE_OK);
+}
+
+static int
+header_bin_be(struct archive_read *a, struct cpio *cpio,
+ struct archive_entry *entry, size_t *namelength, size_t *name_pad)
+{
+ const void *h;
+ const unsigned char *header;
+
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE;
+ a->archive.archive_format_name = "cpio (big-endian binary)";
+
+ /* Read fixed-size portion of header. */
+ h = __archive_read_ahead(a, bin_header_size, NULL);
+ if (h == NULL) {
+ archive_set_error(&a->archive, 0,
+ "End of file trying to read next cpio header");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Parse out binary fields. */
+ header = (const unsigned char *)h;
+
+ archive_entry_set_dev(entry, header[bin_dev_offset] * 256 + header[bin_dev_offset + 1]);
+ archive_entry_set_ino(entry, header[bin_ino_offset] * 256 + header[bin_ino_offset + 1]);
+ archive_entry_set_mode(entry, header[bin_mode_offset] * 256 + header[bin_mode_offset + 1]);
+ if (cpio->option_pwb) {
+ /* turn off random bits left over from V6 inode */
+ archive_entry_set_mode(entry, archive_entry_mode(entry) & 067777);
+ if ((archive_entry_mode(entry) & AE_IFMT) == 0)
+ archive_entry_set_mode(entry, archive_entry_mode(entry) | AE_IFREG);
+ }
+ archive_entry_set_uid(entry, header[bin_uid_offset] * 256 + header[bin_uid_offset + 1]);
+ archive_entry_set_gid(entry, header[bin_gid_offset] * 256 + header[bin_gid_offset + 1]);
+ archive_entry_set_nlink(entry, header[bin_nlink_offset] * 256 + header[bin_nlink_offset + 1]);
+ archive_entry_set_rdev(entry, header[bin_rdev_offset] * 256 + header[bin_rdev_offset + 1]);
+ archive_entry_set_mtime(entry, be4(header + bin_mtime_offset), 0);
+ *namelength = header[bin_namesize_offset] * 256 + header[bin_namesize_offset + 1];
+ *name_pad = *namelength & 1; /* Pad to even. */
+
+ cpio->entry_bytes_remaining = be4(header + bin_filesize_offset);
+ archive_entry_set_size(entry, cpio->entry_bytes_remaining);
+ cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */
+ __archive_read_consume(a, bin_header_size);
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_cpio_cleanup(struct archive_read *a)
+{
+ struct cpio *cpio;
+
+ cpio = (struct cpio *)(a->format->data);
+ /* Free inode->name map */
+ while (cpio->links_head != NULL) {
+ struct links_entry *lp = cpio->links_head->next;
+
+ free(cpio->links_head->name);
+ free(cpio->links_head);
+ cpio->links_head = lp;
+ }
+ free(cpio);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int64_t
+le4(const unsigned char *p)
+{
+ return ((p[0] << 16) | (((int64_t)p[1]) << 24) | (p[2] << 0) | (p[3] << 8));
+}
+
+
+static int64_t
+be4(const unsigned char *p)
+{
+ return ((((int64_t)p[0]) << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]));
+}
+
+/*
+ * Note that this implementation does not (and should not!) obey
+ * locale settings; you cannot simply substitute strtol here, since
+ * it does obey locale.
+ */
+static int64_t
+atol8(const char *p, unsigned char_cnt)
+{
+ int64_t l;
+ int digit;
+
+ l = 0;
+ while (char_cnt-- > 0) {
+ if (*p >= '0' && *p <= '7')
+ digit = *p - '0';
+ else
+ return (l);
+ p++;
+ l <<= 3;
+ l |= digit;
+ }
+ return (l);
+}
+
+static int64_t
+atol16(const char *p, unsigned char_cnt)
+{
+ int64_t l;
+ int digit;
+
+ l = 0;
+ while (char_cnt-- > 0) {
+ if (*p >= 'a' && *p <= 'f')
+ digit = *p - 'a' + 10;
+ else if (*p >= 'A' && *p <= 'F')
+ digit = *p - 'A' + 10;
+ else if (*p >= '0' && *p <= '9')
+ digit = *p - '0';
+ else
+ return (l);
+ p++;
+ l <<= 4;
+ l |= digit;
+ }
+ return (l);
+}
+
+static int
+record_hardlink(struct archive_read *a,
+ struct cpio *cpio, struct archive_entry *entry)
+{
+ struct links_entry *le;
+ dev_t dev;
+ int64_t ino;
+
+ if (archive_entry_nlink(entry) <= 1)
+ return (ARCHIVE_OK);
+
+ dev = archive_entry_dev(entry);
+ ino = archive_entry_ino64(entry);
+
+ /*
+ * First look in the list of multiply-linked files. If we've
+ * already dumped it, convert this entry to a hard link entry.
+ */
+ for (le = cpio->links_head; le; le = le->next) {
+ if (le->dev == dev && le->ino == ino) {
+ archive_entry_copy_hardlink(entry, le->name);
+
+ if (--le->links <= 0) {
+ if (le->previous != NULL)
+ le->previous->next = le->next;
+ if (le->next != NULL)
+ le->next->previous = le->previous;
+ if (cpio->links_head == le)
+ cpio->links_head = le->next;
+ free(le->name);
+ free(le);
+ }
+
+ return (ARCHIVE_OK);
+ }
+ }
+
+ le = (struct links_entry *)malloc(sizeof(struct links_entry));
+ if (le == NULL) {
+ archive_set_error(&a->archive,
+ ENOMEM, "Out of memory adding file to list");
+ return (ARCHIVE_FATAL);
+ }
+ if (cpio->links_head != NULL)
+ cpio->links_head->previous = le;
+ le->next = cpio->links_head;
+ le->previous = NULL;
+ cpio->links_head = le;
+ le->dev = dev;
+ le->ino = ino;
+ le->links = archive_entry_nlink(entry) - 1;
+ le->name = strdup(archive_entry_pathname(entry));
+ if (le->name == NULL) {
+ archive_set_error(&a->archive,
+ ENOMEM, "Out of memory adding file to list");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_empty.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_empty.c
new file mode 100644
index 00000000000..53fb6cc4743
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_empty.c
@@ -0,0 +1,96 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_empty.c 191524 2009-04-26 18:24:14Z kientzle $");
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+static int archive_read_format_empty_bid(struct archive_read *, int);
+static int archive_read_format_empty_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_empty_read_header(struct archive_read *,
+ struct archive_entry *);
+int
+archive_read_support_format_empty(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_empty");
+
+ r = __archive_read_register_format(a,
+ NULL,
+ "empty",
+ archive_read_format_empty_bid,
+ NULL,
+ archive_read_format_empty_read_header,
+ archive_read_format_empty_read_data,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ return (r);
+}
+
+
+static int
+archive_read_format_empty_bid(struct archive_read *a, int best_bid)
+{
+ if (best_bid < 1 && __archive_read_ahead(a, 1, NULL) == NULL)
+ return (1);
+ return (-1);
+}
+
+static int
+archive_read_format_empty_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ (void)a; /* UNUSED */
+ (void)entry; /* UNUSED */
+
+ a->archive.archive_format = ARCHIVE_FORMAT_EMPTY;
+ a->archive.archive_format_name = "Empty file";
+
+ return (ARCHIVE_EOF);
+}
+
+static int
+archive_read_format_empty_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ (void)a; /* UNUSED */
+ (void)buff; /* UNUSED */
+ (void)size; /* UNUSED */
+ (void)offset; /* UNUSED */
+
+ return (ARCHIVE_EOF);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_iso9660.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_iso9660.c
new file mode 100644
index 00000000000..f5414be2a56
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_iso9660.c
@@ -0,0 +1,3279 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2009 Andreas Henriksson <andreas@fatal.se>
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_iso9660.c 201246 2009-12-30 05:30:35Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+/* #include <stdint.h> */ /* See archive_platform.h */
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include <time.h>
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_string.h"
+
+/*
+ * An overview of ISO 9660 format:
+ *
+ * Each disk is laid out as follows:
+ * * 32k reserved for private use
+ * * Volume descriptor table. Each volume descriptor
+ * is 2k and specifies basic format information.
+ * The "Primary Volume Descriptor" (PVD) is defined by the
+ * standard and should always be present; other volume
+ * descriptors include various vendor-specific extensions.
+ * * Files and directories. Each file/dir is specified by
+ * an "extent" (starting sector and length in bytes).
+ * Dirs are just files with directory records packed one
+ * after another. The PVD contains a single dir entry
+ * specifying the location of the root directory. Everything
+ * else follows from there.
+ *
+ * This module works by first reading the volume descriptors, then
+ * building a list of directory entries, sorted by starting
+ * sector. At each step, I look for the earliest dir entry that
+ * hasn't yet been read, seek forward to that location and read
+ * that entry. If it's a dir, I slurp in the new dir entries and
+ * add them to the heap; if it's a regular file, I return the
+ * corresponding archive_entry and wait for the client to request
+ * the file body. This strategy allows us to read most compliant
+ * CDs with a single pass through the data, as required by libarchive.
+ */
+#define LOGICAL_BLOCK_SIZE 2048
+#define SYSTEM_AREA_BLOCK 16
+
+/* Structure of on-disk primary volume descriptor. */
+#define PVD_type_offset 0
+#define PVD_type_size 1
+#define PVD_id_offset (PVD_type_offset + PVD_type_size)
+#define PVD_id_size 5
+#define PVD_version_offset (PVD_id_offset + PVD_id_size)
+#define PVD_version_size 1
+#define PVD_reserved1_offset (PVD_version_offset + PVD_version_size)
+#define PVD_reserved1_size 1
+#define PVD_system_id_offset (PVD_reserved1_offset + PVD_reserved1_size)
+#define PVD_system_id_size 32
+#define PVD_volume_id_offset (PVD_system_id_offset + PVD_system_id_size)
+#define PVD_volume_id_size 32
+#define PVD_reserved2_offset (PVD_volume_id_offset + PVD_volume_id_size)
+#define PVD_reserved2_size 8
+#define PVD_volume_space_size_offset (PVD_reserved2_offset + PVD_reserved2_size)
+#define PVD_volume_space_size_size 8
+#define PVD_reserved3_offset (PVD_volume_space_size_offset + PVD_volume_space_size_size)
+#define PVD_reserved3_size 32
+#define PVD_volume_set_size_offset (PVD_reserved3_offset + PVD_reserved3_size)
+#define PVD_volume_set_size_size 4
+#define PVD_volume_sequence_number_offset (PVD_volume_set_size_offset + PVD_volume_set_size_size)
+#define PVD_volume_sequence_number_size 4
+#define PVD_logical_block_size_offset (PVD_volume_sequence_number_offset + PVD_volume_sequence_number_size)
+#define PVD_logical_block_size_size 4
+#define PVD_path_table_size_offset (PVD_logical_block_size_offset + PVD_logical_block_size_size)
+#define PVD_path_table_size_size 8
+#define PVD_type_1_path_table_offset (PVD_path_table_size_offset + PVD_path_table_size_size)
+#define PVD_type_1_path_table_size 4
+#define PVD_opt_type_1_path_table_offset (PVD_type_1_path_table_offset + PVD_type_1_path_table_size)
+#define PVD_opt_type_1_path_table_size 4
+#define PVD_type_m_path_table_offset (PVD_opt_type_1_path_table_offset + PVD_opt_type_1_path_table_size)
+#define PVD_type_m_path_table_size 4
+#define PVD_opt_type_m_path_table_offset (PVD_type_m_path_table_offset + PVD_type_m_path_table_size)
+#define PVD_opt_type_m_path_table_size 4
+#define PVD_root_directory_record_offset (PVD_opt_type_m_path_table_offset + PVD_opt_type_m_path_table_size)
+#define PVD_root_directory_record_size 34
+#define PVD_volume_set_id_offset (PVD_root_directory_record_offset + PVD_root_directory_record_size)
+#define PVD_volume_set_id_size 128
+#define PVD_publisher_id_offset (PVD_volume_set_id_offset + PVD_volume_set_id_size)
+#define PVD_publisher_id_size 128
+#define PVD_preparer_id_offset (PVD_publisher_id_offset + PVD_publisher_id_size)
+#define PVD_preparer_id_size 128
+#define PVD_application_id_offset (PVD_preparer_id_offset + PVD_preparer_id_size)
+#define PVD_application_id_size 128
+#define PVD_copyright_file_id_offset (PVD_application_id_offset + PVD_application_id_size)
+#define PVD_copyright_file_id_size 37
+#define PVD_abstract_file_id_offset (PVD_copyright_file_id_offset + PVD_copyright_file_id_size)
+#define PVD_abstract_file_id_size 37
+#define PVD_bibliographic_file_id_offset (PVD_abstract_file_id_offset + PVD_abstract_file_id_size)
+#define PVD_bibliographic_file_id_size 37
+#define PVD_creation_date_offset (PVD_bibliographic_file_id_offset + PVD_bibliographic_file_id_size)
+#define PVD_creation_date_size 17
+#define PVD_modification_date_offset (PVD_creation_date_offset + PVD_creation_date_size)
+#define PVD_modification_date_size 17
+#define PVD_expiration_date_offset (PVD_modification_date_offset + PVD_modification_date_size)
+#define PVD_expiration_date_size 17
+#define PVD_effective_date_offset (PVD_expiration_date_offset + PVD_expiration_date_size)
+#define PVD_effective_date_size 17
+#define PVD_file_structure_version_offset (PVD_effective_date_offset + PVD_effective_date_size)
+#define PVD_file_structure_version_size 1
+#define PVD_reserved4_offset (PVD_file_structure_version_offset + PVD_file_structure_version_size)
+#define PVD_reserved4_size 1
+#define PVD_application_data_offset (PVD_reserved4_offset + PVD_reserved4_size)
+#define PVD_application_data_size 512
+#define PVD_reserved5_offset (PVD_application_data_offset + PVD_application_data_size)
+#define PVD_reserved5_size (2048 - PVD_reserved5_offset)
+
+/* TODO: It would make future maintenance easier to just hardcode the
+ * above values. In particular, ECMA119 states the offsets as part of
+ * the standard. That would eliminate the need for the following check.*/
+#if PVD_reserved5_offset != 1395
+#error PVD offset and size definitions are wrong.
+#endif
+
+
+/* Structure of optional on-disk supplementary volume descriptor. */
+#define SVD_type_offset 0
+#define SVD_type_size 1
+#define SVD_id_offset (SVD_type_offset + SVD_type_size)
+#define SVD_id_size 5
+#define SVD_version_offset (SVD_id_offset + SVD_id_size)
+#define SVD_version_size 1
+/* ... */
+#define SVD_reserved1_offset 72
+#define SVD_reserved1_size 8
+#define SVD_volume_space_size_offset 80
+#define SVD_volume_space_size_size 8
+#define SVD_escape_sequences_offset (SVD_volume_space_size_offset + SVD_volume_space_size_size)
+#define SVD_escape_sequences_size 32
+/* ... */
+#define SVD_logical_block_size_offset 128
+#define SVD_logical_block_size_size 4
+#define SVD_type_L_path_table_offset 140
+#define SVD_type_M_path_table_offset 148
+/* ... */
+#define SVD_root_directory_record_offset 156
+#define SVD_root_directory_record_size 34
+#define SVD_file_structure_version_offset 881
+#define SVD_reserved2_offset 882
+#define SVD_reserved2_size 1
+#define SVD_reserved3_offset 1395
+#define SVD_reserved3_size 653
+/* ... */
+/* FIXME: validate correctness of last SVD entry offset. */
+
+/* Structure of an on-disk directory record. */
+/* Note: ISO9660 stores each multi-byte integer twice, once in
+ * each byte order. The sizes here are the size of just one
+ * of the two integers. (This is why the offset of a field isn't
+ * the same as the offset+size of the previous field.) */
+#define DR_length_offset 0
+#define DR_length_size 1
+#define DR_ext_attr_length_offset 1
+#define DR_ext_attr_length_size 1
+#define DR_extent_offset 2
+#define DR_extent_size 4
+#define DR_size_offset 10
+#define DR_size_size 4
+#define DR_date_offset 18
+#define DR_date_size 7
+#define DR_flags_offset 25
+#define DR_flags_size 1
+#define DR_file_unit_size_offset 26
+#define DR_file_unit_size_size 1
+#define DR_interleave_offset 27
+#define DR_interleave_size 1
+#define DR_volume_sequence_number_offset 28
+#define DR_volume_sequence_number_size 2
+#define DR_name_len_offset 32
+#define DR_name_len_size 1
+#define DR_name_offset 33
+
+#ifdef HAVE_ZLIB_H
+static const unsigned char zisofs_magic[8] = {
+ 0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07
+};
+
+struct zisofs {
+ /* Set 1 if this file compressed by paged zlib */
+ int pz;
+ int pz_log2_bs; /* Log2 of block size */
+ uint64_t pz_uncompressed_size;
+
+ int initialized;
+ unsigned char *uncompressed_buffer;
+ size_t uncompressed_buffer_size;
+
+ uint32_t pz_offset;
+ unsigned char header[16];
+ size_t header_avail;
+ int header_passed;
+ unsigned char *block_pointers;
+ size_t block_pointers_alloc;
+ size_t block_pointers_size;
+ size_t block_pointers_avail;
+ size_t block_off;
+ uint32_t block_avail;
+
+ z_stream stream;
+ int stream_valid;
+};
+#else
+struct zisofs {
+ /* Set 1 if this file compressed by paged zlib */
+ int pz;
+};
+#endif
+
+struct content {
+ uint64_t offset;/* Offset on disk. */
+ uint64_t size; /* File size in bytes. */
+ struct content *next;
+};
+
+/* In-memory storage for a directory record. */
+struct file_info {
+ struct file_info *use_next;
+ struct file_info *parent;
+ struct file_info *next;
+ struct file_info *re_next;
+ int subdirs;
+ uint64_t key; /* Heap Key. */
+ uint64_t offset; /* Offset on disk. */
+ uint64_t size; /* File size in bytes. */
+ uint32_t ce_offset; /* Offset of CE. */
+ uint32_t ce_size; /* Size of CE. */
+ char rr_moved; /* Flag to rr_moved. */
+ char rr_moved_has_re_only;
+ char re; /* Having RRIP "RE" extension. */
+ char re_descendant;
+ uint64_t cl_offset; /* Having RRIP "CL" extension. */
+ int birthtime_is_set;
+ time_t birthtime; /* File created time. */
+ time_t mtime; /* File last modified time. */
+ time_t atime; /* File last accessed time. */
+ time_t ctime; /* File attribute change time. */
+ uint64_t rdev; /* Device number. */
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ int64_t number;
+ int nlinks;
+ struct archive_string name; /* Pathname */
+ unsigned char *utf16be_name;
+ size_t utf16be_bytes;
+ char name_continues; /* Non-zero if name continues */
+ struct archive_string symlink;
+ char symlink_continues; /* Non-zero if link continues */
+ /* Set 1 if this file compressed by paged zlib(zisofs) */
+ int pz;
+ int pz_log2_bs; /* Log2 of block size */
+ uint64_t pz_uncompressed_size;
+ /* Set 1 if this file is multi extent. */
+ int multi_extent;
+ struct {
+ struct content *first;
+ struct content **last;
+ } contents;
+ struct {
+ struct file_info *first;
+ struct file_info **last;
+ } rede_files;
+};
+
+struct heap_queue {
+ struct file_info **files;
+ int allocated;
+ int used;
+};
+
+struct iso9660 {
+ int magic;
+#define ISO9660_MAGIC 0x96609660
+
+ int opt_support_joliet;
+ int opt_support_rockridge;
+
+ struct archive_string pathname;
+ char seenRockridge; /* Set true if RR extensions are used. */
+ char seenSUSP; /* Set true if SUSP is being used. */
+ char seenJoliet;
+
+ unsigned char suspOffset;
+ struct file_info *rr_moved;
+ struct read_ce_queue {
+ struct read_ce_req {
+ uint64_t offset;/* Offset of CE on disk. */
+ struct file_info *file;
+ } *reqs;
+ int cnt;
+ int allocated;
+ } read_ce_req;
+
+ int64_t previous_number;
+ struct archive_string previous_pathname;
+
+ struct file_info *use_files;
+ struct heap_queue pending_files;
+ struct {
+ struct file_info *first;
+ struct file_info **last;
+ } cache_files;
+ struct {
+ struct file_info *first;
+ struct file_info **last;
+ } re_files;
+
+ uint64_t current_position;
+ ssize_t logical_block_size;
+ uint64_t volume_size; /* Total size of volume in bytes. */
+ int32_t volume_block;/* Total size of volume in logical blocks. */
+
+ struct vd {
+ int location; /* Location of Extent. */
+ uint32_t size;
+ } primary, joliet;
+
+ int64_t entry_sparse_offset;
+ int64_t entry_bytes_remaining;
+ size_t entry_bytes_unconsumed;
+ struct zisofs entry_zisofs;
+ struct content *entry_content;
+ struct archive_string_conv *sconv_utf16be;
+ /*
+ * Buffers for a full pathname in UTF-16BE in Joliet extensions.
+ */
+#define UTF16_NAME_MAX 1024
+ unsigned char *utf16be_path;
+ size_t utf16be_path_len;
+ unsigned char *utf16be_previous_path;
+ size_t utf16be_previous_path_len;
+ /* Null buffer used in bidder to improve its performance. */
+ unsigned char null[2048];
+};
+
+static int archive_read_format_iso9660_bid(struct archive_read *, int);
+static int archive_read_format_iso9660_options(struct archive_read *,
+ const char *, const char *);
+static int archive_read_format_iso9660_cleanup(struct archive_read *);
+static int archive_read_format_iso9660_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_iso9660_read_data_skip(struct archive_read *);
+static int archive_read_format_iso9660_read_header(struct archive_read *,
+ struct archive_entry *);
+static const char *build_pathname(struct archive_string *, struct file_info *, int);
+static int build_pathname_utf16be(unsigned char *, size_t, size_t *,
+ struct file_info *);
+#if DEBUG
+static void dump_isodirrec(FILE *, const unsigned char *isodirrec);
+#endif
+static time_t time_from_tm(struct tm *);
+static time_t isodate17(const unsigned char *);
+static time_t isodate7(const unsigned char *);
+static int isBootRecord(struct iso9660 *, const unsigned char *);
+static int isVolumePartition(struct iso9660 *, const unsigned char *);
+static int isVDSetTerminator(struct iso9660 *, const unsigned char *);
+static int isJolietSVD(struct iso9660 *, const unsigned char *);
+static int isSVD(struct iso9660 *, const unsigned char *);
+static int isEVD(struct iso9660 *, const unsigned char *);
+static int isPVD(struct iso9660 *, const unsigned char *);
+static int next_cache_entry(struct archive_read *, struct iso9660 *,
+ struct file_info **);
+static int next_entry_seek(struct archive_read *, struct iso9660 *,
+ struct file_info **);
+static struct file_info *
+ parse_file_info(struct archive_read *a,
+ struct file_info *parent, const unsigned char *isodirrec,
+ size_t reclen);
+static int parse_rockridge(struct archive_read *a,
+ struct file_info *file, const unsigned char *start,
+ const unsigned char *end);
+static int register_CE(struct archive_read *a, int32_t location,
+ struct file_info *file);
+static int read_CE(struct archive_read *a, struct iso9660 *iso9660);
+static void parse_rockridge_NM1(struct file_info *,
+ const unsigned char *, int);
+static void parse_rockridge_SL1(struct file_info *,
+ const unsigned char *, int);
+static void parse_rockridge_TF1(struct file_info *,
+ const unsigned char *, int);
+static void parse_rockridge_ZF1(struct file_info *,
+ const unsigned char *, int);
+static void register_file(struct iso9660 *, struct file_info *);
+static void release_files(struct iso9660 *);
+static unsigned toi(const void *p, int n);
+static inline void re_add_entry(struct iso9660 *, struct file_info *);
+static inline struct file_info * re_get_entry(struct iso9660 *);
+static inline int rede_add_entry(struct file_info *);
+static inline struct file_info * rede_get_entry(struct file_info *);
+static inline void cache_add_entry(struct iso9660 *iso9660,
+ struct file_info *file);
+static inline struct file_info *cache_get_entry(struct iso9660 *iso9660);
+static int heap_add_entry(struct archive_read *a, struct heap_queue *heap,
+ struct file_info *file, uint64_t key);
+static struct file_info *heap_get_entry(struct heap_queue *heap);
+
+#define add_entry(arch, iso9660, file) \
+ heap_add_entry(arch, &((iso9660)->pending_files), file, file->offset)
+#define next_entry(iso9660) \
+ heap_get_entry(&((iso9660)->pending_files))
+
+int
+archive_read_support_format_iso9660(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct iso9660 *iso9660;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_iso9660");
+
+ iso9660 = (struct iso9660 *)calloc(1, sizeof(*iso9660));
+ if (iso9660 == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate iso9660 data");
+ return (ARCHIVE_FATAL);
+ }
+ iso9660->magic = ISO9660_MAGIC;
+ iso9660->cache_files.first = NULL;
+ iso9660->cache_files.last = &(iso9660->cache_files.first);
+ iso9660->re_files.first = NULL;
+ iso9660->re_files.last = &(iso9660->re_files.first);
+ /* Enable to support Joliet extensions by default. */
+ iso9660->opt_support_joliet = 1;
+ /* Enable to support Rock Ridge extensions by default. */
+ iso9660->opt_support_rockridge = 1;
+
+ r = __archive_read_register_format(a,
+ iso9660,
+ "iso9660",
+ archive_read_format_iso9660_bid,
+ archive_read_format_iso9660_options,
+ archive_read_format_iso9660_read_header,
+ archive_read_format_iso9660_read_data,
+ archive_read_format_iso9660_read_data_skip,
+ NULL,
+ archive_read_format_iso9660_cleanup,
+ NULL,
+ NULL);
+
+ if (r != ARCHIVE_OK) {
+ free(iso9660);
+ return (r);
+ }
+ return (ARCHIVE_OK);
+}
+
+
+static int
+archive_read_format_iso9660_bid(struct archive_read *a, int best_bid)
+{
+ struct iso9660 *iso9660;
+ ssize_t bytes_read;
+ const unsigned char *p;
+ int seenTerminator;
+
+ /* If there's already a better bid than we can ever
+ make, don't bother testing. */
+ if (best_bid > 48)
+ return (-1);
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+
+ /*
+ * Skip the first 32k (reserved area) and get the first
+ * 8 sectors of the volume descriptor table. Of course,
+ * if the I/O layer gives us more, we'll take it.
+ */
+#define RESERVED_AREA (SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE)
+ p = __archive_read_ahead(a,
+ RESERVED_AREA + 8 * LOGICAL_BLOCK_SIZE,
+ &bytes_read);
+ if (p == NULL)
+ return (-1);
+
+ /* Skip the reserved area. */
+ bytes_read -= RESERVED_AREA;
+ p += RESERVED_AREA;
+
+ /* Check each volume descriptor. */
+ seenTerminator = 0;
+ for (; bytes_read > LOGICAL_BLOCK_SIZE;
+ bytes_read -= LOGICAL_BLOCK_SIZE, p += LOGICAL_BLOCK_SIZE) {
+ /* Do not handle undefined Volume Descriptor Type. */
+ if (p[0] >= 4 && p[0] <= 254)
+ return (0);
+ /* Standard Identifier must be "CD001" */
+ if (memcmp(p + 1, "CD001", 5) != 0)
+ return (0);
+ if (isPVD(iso9660, p))
+ continue;
+ if (!iso9660->joliet.location) {
+ if (isJolietSVD(iso9660, p))
+ continue;
+ }
+ if (isBootRecord(iso9660, p))
+ continue;
+ if (isEVD(iso9660, p))
+ continue;
+ if (isSVD(iso9660, p))
+ continue;
+ if (isVolumePartition(iso9660, p))
+ continue;
+ if (isVDSetTerminator(iso9660, p)) {
+ seenTerminator = 1;
+ break;
+ }
+ return (0);
+ }
+ /*
+ * ISO 9660 format must have Primary Volume Descriptor and
+ * Volume Descriptor Set Terminator.
+ */
+ if (seenTerminator && iso9660->primary.location > 16)
+ return (48);
+
+ /* We didn't find a valid PVD; return a bid of zero. */
+ return (0);
+}
+
+static int
+archive_read_format_iso9660_options(struct archive_read *a,
+ const char *key, const char *val)
+{
+ struct iso9660 *iso9660;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+
+ if (strcmp(key, "joliet") == 0) {
+ if (val == NULL || strcmp(val, "off") == 0 ||
+ strcmp(val, "ignore") == 0 ||
+ strcmp(val, "disable") == 0 ||
+ strcmp(val, "0") == 0)
+ iso9660->opt_support_joliet = 0;
+ else
+ iso9660->opt_support_joliet = 1;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "rockridge") == 0 ||
+ strcmp(key, "Rockridge") == 0) {
+ iso9660->opt_support_rockridge = val != NULL;
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+isNull(struct iso9660 *iso9660, const unsigned char *h, unsigned offset,
+unsigned bytes)
+{
+
+ while (bytes >= sizeof(iso9660->null)) {
+ if (!memcmp(iso9660->null, h + offset, sizeof(iso9660->null)))
+ return (0);
+ offset += sizeof(iso9660->null);
+ bytes -= sizeof(iso9660->null);
+ }
+ if (bytes)
+ return memcmp(iso9660->null, h + offset, bytes) == 0;
+ else
+ return (1);
+}
+
+static int
+isBootRecord(struct iso9660 *iso9660, const unsigned char *h)
+{
+ (void)iso9660; /* UNUSED */
+
+ /* Type of the Volume Descriptor Boot Record must be 0. */
+ if (h[0] != 0)
+ return (0);
+
+ /* Volume Descriptor Version must be 1. */
+ if (h[6] != 1)
+ return (0);
+
+ return (1);
+}
+
+static int
+isVolumePartition(struct iso9660 *iso9660, const unsigned char *h)
+{
+ int32_t location;
+
+ /* Type of the Volume Partition Descriptor must be 3. */
+ if (h[0] != 3)
+ return (0);
+
+ /* Volume Descriptor Version must be 1. */
+ if (h[6] != 1)
+ return (0);
+ /* Unused Field */
+ if (h[7] != 0)
+ return (0);
+
+ location = archive_le32dec(h + 72);
+ if (location <= SYSTEM_AREA_BLOCK ||
+ location >= iso9660->volume_block)
+ return (0);
+ if ((uint32_t)location != archive_be32dec(h + 76))
+ return (0);
+
+ return (1);
+}
+
+static int
+isVDSetTerminator(struct iso9660 *iso9660, const unsigned char *h)
+{
+ (void)iso9660; /* UNUSED */
+
+ /* Type of the Volume Descriptor Set Terminator must be 255. */
+ if (h[0] != 255)
+ return (0);
+
+ /* Volume Descriptor Version must be 1. */
+ if (h[6] != 1)
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, 7, 2048-7))
+ return (0);
+
+ return (1);
+}
+
+static int
+isJolietSVD(struct iso9660 *iso9660, const unsigned char *h)
+{
+ const unsigned char *p;
+ ssize_t logical_block_size;
+ int32_t volume_block;
+
+ /* Check if current sector is a kind of Supplementary Volume
+ * Descriptor. */
+ if (!isSVD(iso9660, h))
+ return (0);
+
+ /* FIXME: do more validations according to joliet spec. */
+
+ /* check if this SVD contains joliet extension! */
+ p = h + SVD_escape_sequences_offset;
+ /* N.B. Joliet spec says p[1] == '\\', but.... */
+ if (p[0] == '%' && p[1] == '/') {
+ int level = 0;
+
+ if (p[2] == '@')
+ level = 1;
+ else if (p[2] == 'C')
+ level = 2;
+ else if (p[2] == 'E')
+ level = 3;
+ else /* not joliet */
+ return (0);
+
+ iso9660->seenJoliet = level;
+
+ } else /* not joliet */
+ return (0);
+
+ logical_block_size =
+ archive_le16dec(h + SVD_logical_block_size_offset);
+ volume_block = archive_le32dec(h + SVD_volume_space_size_offset);
+
+ iso9660->logical_block_size = logical_block_size;
+ iso9660->volume_block = volume_block;
+ iso9660->volume_size = logical_block_size * (uint64_t)volume_block;
+ /* Read Root Directory Record in Volume Descriptor. */
+ p = h + SVD_root_directory_record_offset;
+ iso9660->joliet.location = archive_le32dec(p + DR_extent_offset);
+ iso9660->joliet.size = archive_le32dec(p + DR_size_offset);
+
+ return (48);
+}
+
+static int
+isSVD(struct iso9660 *iso9660, const unsigned char *h)
+{
+ const unsigned char *p;
+ ssize_t logical_block_size;
+ int32_t volume_block;
+ int32_t location;
+
+ (void)iso9660; /* UNUSED */
+
+ /* Type 2 means it's a SVD. */
+ if (h[SVD_type_offset] != 2)
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, SVD_reserved1_offset, SVD_reserved1_size))
+ return (0);
+ if (!isNull(iso9660, h, SVD_reserved2_offset, SVD_reserved2_size))
+ return (0);
+ if (!isNull(iso9660, h, SVD_reserved3_offset, SVD_reserved3_size))
+ return (0);
+
+ /* File structure version must be 1 for ISO9660/ECMA119. */
+ if (h[SVD_file_structure_version_offset] != 1)
+ return (0);
+
+ logical_block_size =
+ archive_le16dec(h + SVD_logical_block_size_offset);
+ if (logical_block_size <= 0)
+ return (0);
+
+ volume_block = archive_le32dec(h + SVD_volume_space_size_offset);
+ if (volume_block <= SYSTEM_AREA_BLOCK+4)
+ return (0);
+
+ /* Location of Occurrence of Type L Path Table must be
+ * available location,
+ * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+ location = archive_le32dec(h+SVD_type_L_path_table_offset);
+ if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block)
+ return (0);
+
+ /* The Type M Path Table must be at a valid location (WinISO
+ * and probably other programs omit this, so we allow zero)
+ *
+ * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+ location = archive_be32dec(h+SVD_type_M_path_table_offset);
+ if ((location > 0 && location < SYSTEM_AREA_BLOCK+2)
+ || location >= volume_block)
+ return (0);
+
+ /* Read Root Directory Record in Volume Descriptor. */
+ p = h + SVD_root_directory_record_offset;
+ if (p[DR_length_offset] != 34)
+ return (0);
+
+ return (48);
+}
+
+static int
+isEVD(struct iso9660 *iso9660, const unsigned char *h)
+{
+ const unsigned char *p;
+ ssize_t logical_block_size;
+ int32_t volume_block;
+ int32_t location;
+
+ (void)iso9660; /* UNUSED */
+
+ /* Type of the Enhanced Volume Descriptor must be 2. */
+ if (h[PVD_type_offset] != 2)
+ return (0);
+
+ /* EVD version must be 2. */
+ if (h[PVD_version_offset] != 2)
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (h[PVD_reserved1_offset] != 0)
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size))
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size))
+ return (0);
+
+ /* Logical block size must be > 0. */
+ /* I've looked at Ecma 119 and can't find any stronger
+ * restriction on this field. */
+ logical_block_size =
+ archive_le16dec(h + PVD_logical_block_size_offset);
+ if (logical_block_size <= 0)
+ return (0);
+
+ volume_block =
+ archive_le32dec(h + PVD_volume_space_size_offset);
+ if (volume_block <= SYSTEM_AREA_BLOCK+4)
+ return (0);
+
+ /* File structure version must be 2 for ISO9660:1999. */
+ if (h[PVD_file_structure_version_offset] != 2)
+ return (0);
+
+ /* Location of Occurrence of Type L Path Table must be
+ * available location,
+ * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+ location = archive_le32dec(h+PVD_type_1_path_table_offset);
+ if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block)
+ return (0);
+
+ /* Location of Occurrence of Type M Path Table must be
+ * available location,
+ * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+ location = archive_be32dec(h+PVD_type_m_path_table_offset);
+ if ((location > 0 && location < SYSTEM_AREA_BLOCK+2)
+ || location >= volume_block)
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, PVD_reserved4_offset, PVD_reserved4_size))
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size))
+ return (0);
+
+ /* Read Root Directory Record in Volume Descriptor. */
+ p = h + PVD_root_directory_record_offset;
+ if (p[DR_length_offset] != 34)
+ return (0);
+
+ return (48);
+}
+
+static int
+isPVD(struct iso9660 *iso9660, const unsigned char *h)
+{
+ const unsigned char *p;
+ ssize_t logical_block_size;
+ int32_t volume_block;
+ int32_t location;
+ int i;
+
+ /* Type of the Primary Volume Descriptor must be 1. */
+ if (h[PVD_type_offset] != 1)
+ return (0);
+
+ /* PVD version must be 1. */
+ if (h[PVD_version_offset] != 1)
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (h[PVD_reserved1_offset] != 0)
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size))
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size))
+ return (0);
+
+ /* Logical block size must be > 0. */
+ /* I've looked at Ecma 119 and can't find any stronger
+ * restriction on this field. */
+ logical_block_size =
+ archive_le16dec(h + PVD_logical_block_size_offset);
+ if (logical_block_size <= 0)
+ return (0);
+
+ volume_block = archive_le32dec(h + PVD_volume_space_size_offset);
+ if (volume_block <= SYSTEM_AREA_BLOCK+4)
+ return (0);
+
+ /* File structure version must be 1 for ISO9660/ECMA119. */
+ if (h[PVD_file_structure_version_offset] != 1)
+ return (0);
+
+ /* Location of Occurrence of Type L Path Table must be
+ * available location,
+ * > SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+ location = archive_le32dec(h+PVD_type_1_path_table_offset);
+ if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block)
+ return (0);
+
+ /* The Type M Path Table must also be at a valid location
+ * (although ECMA 119 requires a Type M Path Table, WinISO and
+ * probably other programs omit it, so we permit a zero here)
+ *
+ * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+ location = archive_be32dec(h+PVD_type_m_path_table_offset);
+ if ((location > 0 && location < SYSTEM_AREA_BLOCK+2)
+ || location >= volume_block)
+ return (0);
+
+ /* Reserved field must be 0. */
+ /* But accept NetBSD/FreeBSD "makefs" images with 0x20 here. */
+ for (i = 0; i < PVD_reserved4_size; ++i)
+ if (h[PVD_reserved4_offset + i] != 0
+ && h[PVD_reserved4_offset + i] != 0x20)
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size))
+ return (0);
+
+ /* XXX TODO: Check other values for sanity; reject more
+ * malformed PVDs. XXX */
+
+ /* Read Root Directory Record in Volume Descriptor. */
+ p = h + PVD_root_directory_record_offset;
+ if (p[DR_length_offset] != 34)
+ return (0);
+
+ if (!iso9660->primary.location) {
+ iso9660->logical_block_size = logical_block_size;
+ iso9660->volume_block = volume_block;
+ iso9660->volume_size =
+ logical_block_size * (uint64_t)volume_block;
+ iso9660->primary.location =
+ archive_le32dec(p + DR_extent_offset);
+ iso9660->primary.size = archive_le32dec(p + DR_size_offset);
+ }
+
+ return (48);
+}
+
+static int
+read_children(struct archive_read *a, struct file_info *parent)
+{
+ struct iso9660 *iso9660;
+ const unsigned char *b, *p;
+ struct file_info *multi;
+ size_t step, skip_size;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+ /* flush any remaining bytes from the last round to ensure
+ * we're positioned */
+ if (iso9660->entry_bytes_unconsumed) {
+ __archive_read_consume(a, iso9660->entry_bytes_unconsumed);
+ iso9660->entry_bytes_unconsumed = 0;
+ }
+ if (iso9660->current_position > parent->offset) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Ignoring out-of-order directory (%s) %jd > %jd",
+ parent->name.s,
+ (intmax_t)iso9660->current_position,
+ (intmax_t)parent->offset);
+ return (ARCHIVE_WARN);
+ }
+ if (parent->offset + parent->size > iso9660->volume_size) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Directory is beyond end-of-media: %s",
+ parent->name.s);
+ return (ARCHIVE_WARN);
+ }
+ if (iso9660->current_position < parent->offset) {
+ int64_t skipsize;
+
+ skipsize = parent->offset - iso9660->current_position;
+ skipsize = __archive_read_consume(a, skipsize);
+ if (skipsize < 0)
+ return ((int)skipsize);
+ iso9660->current_position = parent->offset;
+ }
+
+ step = (size_t)(((parent->size + iso9660->logical_block_size -1) /
+ iso9660->logical_block_size) * iso9660->logical_block_size);
+ b = __archive_read_ahead(a, step, NULL);
+ if (b == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to read full block when scanning "
+ "ISO9660 directory list");
+ return (ARCHIVE_FATAL);
+ }
+ iso9660->current_position += step;
+ multi = NULL;
+ skip_size = step;
+ while (step) {
+ p = b;
+ b += iso9660->logical_block_size;
+ step -= iso9660->logical_block_size;
+ for (; *p != 0 && p + DR_name_offset < b && p + *p <= b;
+ p += *p) {
+ struct file_info *child;
+
+ /* N.B.: these special directory identifiers
+ * are 8 bit "values" even on a
+ * Joliet CD with UCS-2 (16bit) encoding.
+ */
+
+ /* Skip '.' entry. */
+ if (*(p + DR_name_len_offset) == 1
+ && *(p + DR_name_offset) == '\0')
+ continue;
+ /* Skip '..' entry. */
+ if (*(p + DR_name_len_offset) == 1
+ && *(p + DR_name_offset) == '\001')
+ continue;
+ child = parse_file_info(a, parent, p, b - p);
+ if (child == NULL) {
+ __archive_read_consume(a, skip_size);
+ return (ARCHIVE_FATAL);
+ }
+ if (child->cl_offset == 0 &&
+ (child->multi_extent || multi != NULL)) {
+ struct content *con;
+
+ if (multi == NULL) {
+ multi = child;
+ multi->contents.first = NULL;
+ multi->contents.last =
+ &(multi->contents.first);
+ }
+ con = malloc(sizeof(struct content));
+ if (con == NULL) {
+ archive_set_error(
+ &a->archive, ENOMEM,
+ "No memory for multi extent");
+ __archive_read_consume(a, skip_size);
+ return (ARCHIVE_FATAL);
+ }
+ con->offset = child->offset;
+ con->size = child->size;
+ con->next = NULL;
+ *multi->contents.last = con;
+ multi->contents.last = &(con->next);
+ if (multi == child) {
+ if (add_entry(a, iso9660, child)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } else {
+ multi->size += child->size;
+ if (!child->multi_extent)
+ multi = NULL;
+ }
+ } else
+ if (add_entry(a, iso9660, child) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ __archive_read_consume(a, skip_size);
+
+ /* Read data which recorded by RRIP "CE" extension. */
+ if (read_CE(a, iso9660) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+choose_volume(struct archive_read *a, struct iso9660 *iso9660)
+{
+ struct file_info *file;
+ int64_t skipsize;
+ struct vd *vd;
+ const void *block;
+ char seenJoliet;
+
+ vd = &(iso9660->primary);
+ if (!iso9660->opt_support_joliet)
+ iso9660->seenJoliet = 0;
+ if (iso9660->seenJoliet &&
+ vd->location > iso9660->joliet.location)
+ /* This condition is unlikely; by way of caution. */
+ vd = &(iso9660->joliet);
+
+ skipsize = LOGICAL_BLOCK_SIZE * (int64_t)vd->location;
+ skipsize = __archive_read_consume(a, skipsize);
+ if (skipsize < 0)
+ return ((int)skipsize);
+ iso9660->current_position = skipsize;
+
+ block = __archive_read_ahead(a, vd->size, NULL);
+ if (block == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to read full block when scanning "
+ "ISO9660 directory list");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * While reading Root Directory, flag seenJoliet must be zero to
+ * avoid converting special name 0x00(Current Directory) and
+ * next byte to UCS2.
+ */
+ seenJoliet = iso9660->seenJoliet;/* Save flag. */
+ iso9660->seenJoliet = 0;
+ file = parse_file_info(a, NULL, block, vd->size);
+ if (file == NULL)
+ return (ARCHIVE_FATAL);
+ iso9660->seenJoliet = seenJoliet;
+
+ /*
+ * If the iso image has both RockRidge and Joliet, we preferentially
+ * use RockRidge Extensions rather than Joliet ones.
+ */
+ if (vd == &(iso9660->primary) && iso9660->seenRockridge
+ && iso9660->seenJoliet)
+ iso9660->seenJoliet = 0;
+
+ if (vd == &(iso9660->primary) && !iso9660->seenRockridge
+ && iso9660->seenJoliet) {
+ /* Switch reading data from primary to joliet. */
+ vd = &(iso9660->joliet);
+ skipsize = LOGICAL_BLOCK_SIZE * (int64_t)vd->location;
+ skipsize -= iso9660->current_position;
+ skipsize = __archive_read_consume(a, skipsize);
+ if (skipsize < 0)
+ return ((int)skipsize);
+ iso9660->current_position += skipsize;
+
+ block = __archive_read_ahead(a, vd->size, NULL);
+ if (block == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to read full block when scanning "
+ "ISO9660 directory list");
+ return (ARCHIVE_FATAL);
+ }
+ iso9660->seenJoliet = 0;
+ file = parse_file_info(a, NULL, block, vd->size);
+ if (file == NULL)
+ return (ARCHIVE_FATAL);
+ iso9660->seenJoliet = seenJoliet;
+ }
+
+ /* Store the root directory in the pending list. */
+ if (add_entry(a, iso9660, file) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ if (iso9660->seenRockridge) {
+ a->archive.archive_format = ARCHIVE_FORMAT_ISO9660_ROCKRIDGE;
+ a->archive.archive_format_name =
+ "ISO9660 with Rockridge extensions";
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_iso9660_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct iso9660 *iso9660;
+ struct file_info *file;
+ int r, rd_r = ARCHIVE_OK;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+
+ if (!a->archive.archive_format) {
+ a->archive.archive_format = ARCHIVE_FORMAT_ISO9660;
+ a->archive.archive_format_name = "ISO9660";
+ }
+
+ if (iso9660->current_position == 0) {
+ r = choose_volume(a, iso9660);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ file = NULL;/* Eliminate a warning. */
+ /* Get the next entry that appears after the current offset. */
+ r = next_entry_seek(a, iso9660, &file);
+ if (r != ARCHIVE_OK)
+ return (r);
+
+ if (iso9660->seenJoliet) {
+ /*
+ * Convert UTF-16BE of a filename to local locale MBS
+ * and store the result into a filename field.
+ */
+ if (iso9660->sconv_utf16be == NULL) {
+ iso9660->sconv_utf16be =
+ archive_string_conversion_from_charset(
+ &(a->archive), "UTF-16BE", 1);
+ if (iso9660->sconv_utf16be == NULL)
+ /* Couldn't allocate memory */
+ return (ARCHIVE_FATAL);
+ }
+ if (iso9660->utf16be_path == NULL) {
+ iso9660->utf16be_path = malloc(UTF16_NAME_MAX);
+ if (iso9660->utf16be_path == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ if (iso9660->utf16be_previous_path == NULL) {
+ iso9660->utf16be_previous_path = malloc(UTF16_NAME_MAX);
+ if (iso9660->utf16be_previous_path == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ iso9660->utf16be_path_len = 0;
+ if (build_pathname_utf16be(iso9660->utf16be_path,
+ UTF16_NAME_MAX, &(iso9660->utf16be_path_len), file) != 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname is too long");
+ return (ARCHIVE_FATAL);
+ }
+
+ r = archive_entry_copy_pathname_l(entry,
+ (const char *)iso9660->utf16be_path,
+ iso9660->utf16be_path_len,
+ iso9660->sconv_utf16be);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname cannot be converted "
+ "from %s to current locale.",
+ archive_string_conversion_charset_name(
+ iso9660->sconv_utf16be));
+
+ rd_r = ARCHIVE_WARN;
+ }
+ } else {
+ const char *path = build_pathname(&iso9660->pathname, file, 0);
+ if (path == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname is too long");
+ return (ARCHIVE_FATAL);
+ } else {
+ archive_string_empty(&iso9660->pathname);
+ archive_entry_set_pathname(entry, path);
+ }
+ }
+
+ iso9660->entry_bytes_remaining = file->size;
+ /* Offset for sparse-file-aware clients. */
+ iso9660->entry_sparse_offset = 0;
+
+ if (file->offset + file->size > iso9660->volume_size) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "File is beyond end-of-media: %s",
+ archive_entry_pathname(entry));
+ iso9660->entry_bytes_remaining = 0;
+ return (ARCHIVE_WARN);
+ }
+
+ /* Set up the entry structure with information about this entry. */
+ archive_entry_set_mode(entry, file->mode);
+ archive_entry_set_uid(entry, file->uid);
+ archive_entry_set_gid(entry, file->gid);
+ archive_entry_set_nlink(entry, file->nlinks);
+ if (file->birthtime_is_set)
+ archive_entry_set_birthtime(entry, file->birthtime, 0);
+ else
+ archive_entry_unset_birthtime(entry);
+ archive_entry_set_mtime(entry, file->mtime, 0);
+ archive_entry_set_ctime(entry, file->ctime, 0);
+ archive_entry_set_atime(entry, file->atime, 0);
+ /* N.B.: Rock Ridge supports 64-bit device numbers. */
+ archive_entry_set_rdev(entry, (dev_t)file->rdev);
+ archive_entry_set_size(entry, iso9660->entry_bytes_remaining);
+ if (file->symlink.s != NULL)
+ archive_entry_copy_symlink(entry, file->symlink.s);
+
+ /* Note: If the input isn't seekable, we can't rewind to
+ * return the same body again, so if the next entry refers to
+ * the same data, we have to return it as a hardlink to the
+ * original entry. */
+ if (file->number != -1 &&
+ file->number == iso9660->previous_number) {
+ if (iso9660->seenJoliet) {
+ r = archive_entry_copy_hardlink_l(entry,
+ (const char *)iso9660->utf16be_previous_path,
+ iso9660->utf16be_previous_path_len,
+ iso9660->sconv_utf16be);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Linkname cannot be converted "
+ "from %s to current locale.",
+ archive_string_conversion_charset_name(
+ iso9660->sconv_utf16be));
+ rd_r = ARCHIVE_WARN;
+ }
+ } else
+ archive_entry_set_hardlink(entry,
+ iso9660->previous_pathname.s);
+ archive_entry_unset_size(entry);
+ iso9660->entry_bytes_remaining = 0;
+ return (rd_r);
+ }
+
+ if ((file->mode & AE_IFMT) != AE_IFDIR &&
+ file->offset < iso9660->current_position) {
+ int64_t r64;
+
+ r64 = __archive_read_seek(a, file->offset, SEEK_SET);
+ if (r64 != (int64_t)file->offset) {
+ /* We can't seek backwards to extract it, so issue
+ * a warning. Note that this can only happen if
+ * this entry was added to the heap after we passed
+ * this offset, that is, only if the directory
+ * mentioning this entry is later than the body of
+ * the entry. Such layouts are very unusual; most
+ * ISO9660 writers lay out and record all directory
+ * information first, then store all file bodies. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Ignoring out-of-order file @%jx (%s) %jd < %jd",
+ (intmax_t)file->number,
+ iso9660->pathname.s,
+ (intmax_t)file->offset,
+ (intmax_t)iso9660->current_position);
+ iso9660->entry_bytes_remaining = 0;
+ return (ARCHIVE_WARN);
+ }
+ iso9660->current_position = (uint64_t)r64;
+ }
+
+ /* Initialize zisofs variables. */
+ iso9660->entry_zisofs.pz = file->pz;
+ if (file->pz) {
+#ifdef HAVE_ZLIB_H
+ struct zisofs *zisofs;
+
+ zisofs = &iso9660->entry_zisofs;
+ zisofs->initialized = 0;
+ zisofs->pz_log2_bs = file->pz_log2_bs;
+ zisofs->pz_uncompressed_size = file->pz_uncompressed_size;
+ zisofs->pz_offset = 0;
+ zisofs->header_avail = 0;
+ zisofs->header_passed = 0;
+ zisofs->block_pointers_avail = 0;
+#endif
+ archive_entry_set_size(entry, file->pz_uncompressed_size);
+ }
+
+ iso9660->previous_number = file->number;
+ if (iso9660->seenJoliet) {
+ memcpy(iso9660->utf16be_previous_path, iso9660->utf16be_path,
+ iso9660->utf16be_path_len);
+ iso9660->utf16be_previous_path_len = iso9660->utf16be_path_len;
+ } else
+ archive_strcpy(
+ &iso9660->previous_pathname, iso9660->pathname.s);
+
+ /* Reset entry_bytes_remaining if the file is multi extent. */
+ iso9660->entry_content = file->contents.first;
+ if (iso9660->entry_content != NULL)
+ iso9660->entry_bytes_remaining = iso9660->entry_content->size;
+
+ if (archive_entry_filetype(entry) == AE_IFDIR) {
+ /* Overwrite nlinks by proper link number which is
+ * calculated from number of sub directories. */
+ archive_entry_set_nlink(entry, 2 + file->subdirs);
+ /* Directory data has been read completely. */
+ iso9660->entry_bytes_remaining = 0;
+ }
+
+ if (rd_r != ARCHIVE_OK)
+ return (rd_r);
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_iso9660_read_data_skip(struct archive_read *a)
+{
+ /* Because read_next_header always does an explicit skip
+ * to the next entry, we don't need to do anything here. */
+ (void)a; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+#ifdef HAVE_ZLIB_H
+
+static int
+zisofs_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ struct iso9660 *iso9660;
+ struct zisofs *zisofs;
+ const unsigned char *p;
+ size_t avail;
+ ssize_t bytes_read;
+ size_t uncompressed_size;
+ int r;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+ zisofs = &iso9660->entry_zisofs;
+
+ p = __archive_read_ahead(a, 1, &bytes_read);
+ if (bytes_read <= 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated zisofs file body");
+ return (ARCHIVE_FATAL);
+ }
+ if (bytes_read > iso9660->entry_bytes_remaining)
+ bytes_read = (ssize_t)iso9660->entry_bytes_remaining;
+ avail = bytes_read;
+ uncompressed_size = 0;
+
+ if (!zisofs->initialized) {
+ size_t ceil, xsize;
+
+ /* Allocate block pointers buffer. */
+ ceil = (size_t)((zisofs->pz_uncompressed_size +
+ (((int64_t)1) << zisofs->pz_log2_bs) - 1)
+ >> zisofs->pz_log2_bs);
+ xsize = (ceil + 1) * 4;
+ if (zisofs->block_pointers_alloc < xsize) {
+ size_t alloc;
+
+ if (zisofs->block_pointers != NULL)
+ free(zisofs->block_pointers);
+ alloc = ((xsize >> 10) + 1) << 10;
+ zisofs->block_pointers = malloc(alloc);
+ if (zisofs->block_pointers == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for zisofs decompression");
+ return (ARCHIVE_FATAL);
+ }
+ zisofs->block_pointers_alloc = alloc;
+ }
+ zisofs->block_pointers_size = xsize;
+
+ /* Allocate uncompressed data buffer. */
+ xsize = (size_t)1UL << zisofs->pz_log2_bs;
+ if (zisofs->uncompressed_buffer_size < xsize) {
+ if (zisofs->uncompressed_buffer != NULL)
+ free(zisofs->uncompressed_buffer);
+ zisofs->uncompressed_buffer = malloc(xsize);
+ if (zisofs->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for zisofs decompression");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ zisofs->uncompressed_buffer_size = xsize;
+
+ /*
+ * Read the file header, and check the magic code of zisofs.
+ */
+ if (zisofs->header_avail < sizeof(zisofs->header)) {
+ xsize = sizeof(zisofs->header) - zisofs->header_avail;
+ if (avail < xsize)
+ xsize = avail;
+ memcpy(zisofs->header + zisofs->header_avail, p, xsize);
+ zisofs->header_avail += xsize;
+ avail -= xsize;
+ p += xsize;
+ }
+ if (!zisofs->header_passed &&
+ zisofs->header_avail == sizeof(zisofs->header)) {
+ int err = 0;
+
+ if (memcmp(zisofs->header, zisofs_magic,
+ sizeof(zisofs_magic)) != 0)
+ err = 1;
+ if (archive_le32dec(zisofs->header + 8)
+ != zisofs->pz_uncompressed_size)
+ err = 1;
+ if (zisofs->header[12] != 4)
+ err = 1;
+ if (zisofs->header[13] != zisofs->pz_log2_bs)
+ err = 1;
+ if (err) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs file body");
+ return (ARCHIVE_FATAL);
+ }
+ zisofs->header_passed = 1;
+ }
+ /*
+ * Read block pointers.
+ */
+ if (zisofs->header_passed &&
+ zisofs->block_pointers_avail < zisofs->block_pointers_size) {
+ xsize = zisofs->block_pointers_size
+ - zisofs->block_pointers_avail;
+ if (avail < xsize)
+ xsize = avail;
+ memcpy(zisofs->block_pointers
+ + zisofs->block_pointers_avail, p, xsize);
+ zisofs->block_pointers_avail += xsize;
+ avail -= xsize;
+ p += xsize;
+ if (zisofs->block_pointers_avail
+ == zisofs->block_pointers_size) {
+ /* We've got all block pointers and initialize
+ * related variables. */
+ zisofs->block_off = 0;
+ zisofs->block_avail = 0;
+ /* Complete a initialization */
+ zisofs->initialized = 1;
+ }
+ }
+
+ if (!zisofs->initialized)
+ goto next_data; /* We need more data. */
+ }
+
+ /*
+ * Get block offsets from block pointers.
+ */
+ if (zisofs->block_avail == 0) {
+ uint32_t bst, bed;
+
+ if (zisofs->block_off + 4 >= zisofs->block_pointers_size) {
+ /* There isn't a pair of offsets. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs block pointers");
+ return (ARCHIVE_FATAL);
+ }
+ bst = archive_le32dec(
+ zisofs->block_pointers + zisofs->block_off);
+ if (bst != zisofs->pz_offset + (bytes_read - avail)) {
+ /* TODO: Should we seek offset of current file
+ * by bst ? */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs block pointers(cannot seek)");
+ return (ARCHIVE_FATAL);
+ }
+ bed = archive_le32dec(
+ zisofs->block_pointers + zisofs->block_off + 4);
+ if (bed < bst) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs block pointers");
+ return (ARCHIVE_FATAL);
+ }
+ zisofs->block_avail = bed - bst;
+ zisofs->block_off += 4;
+
+ /* Initialize compression library for new block. */
+ if (zisofs->stream_valid)
+ r = inflateReset(&zisofs->stream);
+ else
+ r = inflateInit(&zisofs->stream);
+ if (r != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't initialize zisofs decompression.");
+ return (ARCHIVE_FATAL);
+ }
+ zisofs->stream_valid = 1;
+ zisofs->stream.total_in = 0;
+ zisofs->stream.total_out = 0;
+ }
+
+ /*
+ * Make uncompressed data.
+ */
+ if (zisofs->block_avail == 0) {
+ memset(zisofs->uncompressed_buffer, 0,
+ zisofs->uncompressed_buffer_size);
+ uncompressed_size = zisofs->uncompressed_buffer_size;
+ } else {
+ zisofs->stream.next_in = (Bytef *)(uintptr_t)(const void *)p;
+ if (avail > zisofs->block_avail)
+ zisofs->stream.avail_in = zisofs->block_avail;
+ else
+ zisofs->stream.avail_in = (uInt)avail;
+ zisofs->stream.next_out = zisofs->uncompressed_buffer;
+ zisofs->stream.avail_out =
+ (uInt)zisofs->uncompressed_buffer_size;
+
+ r = inflate(&zisofs->stream, 0);
+ switch (r) {
+ case Z_OK: /* Decompressor made some progress.*/
+ case Z_STREAM_END: /* Found end of stream. */
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "zisofs decompression failed (%d)", r);
+ return (ARCHIVE_FATAL);
+ }
+ uncompressed_size =
+ zisofs->uncompressed_buffer_size - zisofs->stream.avail_out;
+ avail -= zisofs->stream.next_in - p;
+ zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p);
+ }
+next_data:
+ bytes_read -= avail;
+ *buff = zisofs->uncompressed_buffer;
+ *size = uncompressed_size;
+ *offset = iso9660->entry_sparse_offset;
+ iso9660->entry_sparse_offset += uncompressed_size;
+ iso9660->entry_bytes_remaining -= bytes_read;
+ iso9660->current_position += bytes_read;
+ zisofs->pz_offset += (uint32_t)bytes_read;
+ iso9660->entry_bytes_unconsumed += bytes_read;
+
+ return (ARCHIVE_OK);
+}
+
+#else /* HAVE_ZLIB_H */
+
+static int
+zisofs_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+
+ (void)buff;/* UNUSED */
+ (void)size;/* UNUSED */
+ (void)offset;/* UNUSED */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "zisofs is not supported on this platform.");
+ return (ARCHIVE_FAILED);
+}
+
+#endif /* HAVE_ZLIB_H */
+
+static int
+archive_read_format_iso9660_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ ssize_t bytes_read;
+ struct iso9660 *iso9660;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+
+ if (iso9660->entry_bytes_unconsumed) {
+ __archive_read_consume(a, iso9660->entry_bytes_unconsumed);
+ iso9660->entry_bytes_unconsumed = 0;
+ }
+
+ if (iso9660->entry_bytes_remaining <= 0) {
+ if (iso9660->entry_content != NULL)
+ iso9660->entry_content = iso9660->entry_content->next;
+ if (iso9660->entry_content == NULL) {
+ *buff = NULL;
+ *size = 0;
+ *offset = iso9660->entry_sparse_offset;
+ return (ARCHIVE_EOF);
+ }
+ /* Seek forward to the start of the entry. */
+ if (iso9660->current_position < iso9660->entry_content->offset) {
+ int64_t step;
+
+ step = iso9660->entry_content->offset -
+ iso9660->current_position;
+ step = __archive_read_consume(a, step);
+ if (step < 0)
+ return ((int)step);
+ iso9660->current_position =
+ iso9660->entry_content->offset;
+ }
+ if (iso9660->entry_content->offset < iso9660->current_position) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Ignoring out-of-order file (%s) %jd < %jd",
+ iso9660->pathname.s,
+ (intmax_t)iso9660->entry_content->offset,
+ (intmax_t)iso9660->current_position);
+ *buff = NULL;
+ *size = 0;
+ *offset = iso9660->entry_sparse_offset;
+ return (ARCHIVE_WARN);
+ }
+ iso9660->entry_bytes_remaining = iso9660->entry_content->size;
+ }
+ if (iso9660->entry_zisofs.pz)
+ return (zisofs_read_data(a, buff, size, offset));
+
+ *buff = __archive_read_ahead(a, 1, &bytes_read);
+ if (bytes_read == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Truncated input file");
+ if (*buff == NULL)
+ return (ARCHIVE_FATAL);
+ if (bytes_read > iso9660->entry_bytes_remaining)
+ bytes_read = (ssize_t)iso9660->entry_bytes_remaining;
+ *size = bytes_read;
+ *offset = iso9660->entry_sparse_offset;
+ iso9660->entry_sparse_offset += bytes_read;
+ iso9660->entry_bytes_remaining -= bytes_read;
+ iso9660->entry_bytes_unconsumed = bytes_read;
+ iso9660->current_position += bytes_read;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_iso9660_cleanup(struct archive_read *a)
+{
+ struct iso9660 *iso9660;
+ int r = ARCHIVE_OK;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+ release_files(iso9660);
+ free(iso9660->read_ce_req.reqs);
+ archive_string_free(&iso9660->pathname);
+ archive_string_free(&iso9660->previous_pathname);
+ free(iso9660->pending_files.files);
+#ifdef HAVE_ZLIB_H
+ free(iso9660->entry_zisofs.uncompressed_buffer);
+ free(iso9660->entry_zisofs.block_pointers);
+ if (iso9660->entry_zisofs.stream_valid) {
+ if (inflateEnd(&iso9660->entry_zisofs.stream) != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up zlib decompressor");
+ r = ARCHIVE_FATAL;
+ }
+ }
+#endif
+ free(iso9660->utf16be_path);
+ free(iso9660->utf16be_previous_path);
+ free(iso9660);
+ (a->format->data) = NULL;
+ return (r);
+}
+
+/*
+ * This routine parses a single ISO directory record, makes sense
+ * of any extensions, and stores the result in memory.
+ */
+static struct file_info *
+parse_file_info(struct archive_read *a, struct file_info *parent,
+ const unsigned char *isodirrec, size_t reclen)
+{
+ struct iso9660 *iso9660;
+ struct file_info *file, *filep;
+ size_t name_len;
+ const unsigned char *rr_start, *rr_end;
+ const unsigned char *p;
+ size_t dr_len = 0;
+ uint64_t fsize, offset;
+ int32_t location;
+ int flags;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+
+ if (reclen != 0)
+ dr_len = (size_t)isodirrec[DR_length_offset];
+ /*
+ * Sanity check that reclen is not zero and dr_len is greater than
+ * reclen but at least 34
+ */
+ if (reclen == 0 || reclen < dr_len || dr_len < 34) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid length of directory record");
+ return (NULL);
+ }
+ name_len = (size_t)isodirrec[DR_name_len_offset];
+ location = archive_le32dec(isodirrec + DR_extent_offset);
+ fsize = toi(isodirrec + DR_size_offset, DR_size_size);
+ /* Sanity check that name_len doesn't exceed dr_len. */
+ if (dr_len - 33 < name_len || name_len == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid length of file identifier");
+ return (NULL);
+ }
+ /* Sanity check that location doesn't exceed volume block.
+ * Don't check lower limit of location; it's possibility
+ * the location has negative value when file type is symbolic
+ * link or file size is zero. As far as I know latest mkisofs
+ * do that.
+ */
+ if (location > 0 &&
+ (location + ((fsize + iso9660->logical_block_size -1)
+ / iso9660->logical_block_size))
+ > (uint32_t)iso9660->volume_block) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid location of extent of file");
+ return (NULL);
+ }
+ /* Sanity check that location doesn't have a negative value
+ * when the file is not empty. it's too large. */
+ if (fsize != 0 && location < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid location of extent of file");
+ return (NULL);
+ }
+
+ /* Sanity check that this entry does not create a cycle. */
+ offset = iso9660->logical_block_size * (uint64_t)location;
+ for (filep = parent; filep != NULL; filep = filep->parent) {
+ if (filep->offset == offset) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Directory structure contains loop");
+ return (NULL);
+ }
+ }
+
+ /* Create a new file entry and copy data from the ISO dir record. */
+ file = (struct file_info *)calloc(1, sizeof(*file));
+ if (file == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for file entry");
+ return (NULL);
+ }
+ file->parent = parent;
+ file->offset = offset;
+ file->size = fsize;
+ file->mtime = isodate7(isodirrec + DR_date_offset);
+ file->ctime = file->atime = file->mtime;
+ file->rede_files.first = NULL;
+ file->rede_files.last = &(file->rede_files.first);
+
+ p = isodirrec + DR_name_offset;
+ /* Rockridge extensions (if any) follow name. Compute this
+ * before fidgeting the name_len below. */
+ rr_start = p + name_len + (name_len & 1 ? 0 : 1);
+ rr_end = isodirrec + dr_len;
+
+ if (iso9660->seenJoliet) {
+ /* Joliet names are max 64 chars (128 bytes) according to spec,
+ * but genisoimage/mkisofs allows recording longer Joliet
+ * names which are 103 UCS2 characters(206 bytes) by their
+ * option '-joliet-long'.
+ */
+ if (name_len > 206)
+ name_len = 206;
+ name_len &= ~1;
+
+ /* trim trailing first version and dot from filename.
+ *
+ * Remember we were in UTF-16BE land!
+ * SEPARATOR 1 (.) and SEPARATOR 2 (;) are both
+ * 16 bits big endian characters on Joliet.
+ *
+ * TODO: sanitize filename?
+ * Joliet allows any UCS-2 char except:
+ * *, /, :, ;, ? and \.
+ */
+ /* Chop off trailing ';1' from files. */
+ if (name_len > 4 && p[name_len-4] == 0 && p[name_len-3] == ';'
+ && p[name_len-2] == 0 && p[name_len-1] == '1')
+ name_len -= 4;
+#if 0 /* XXX: this somehow manages to strip of single-character file extensions, like '.c'. */
+ /* Chop off trailing '.' from filenames. */
+ if (name_len > 2 && p[name_len-2] == 0 && p[name_len-1] == '.')
+ name_len -= 2;
+#endif
+ if ((file->utf16be_name = malloc(name_len)) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for file name");
+ goto fail;
+ }
+ memcpy(file->utf16be_name, p, name_len);
+ file->utf16be_bytes = name_len;
+ } else {
+ /* Chop off trailing ';1' from files. */
+ if (name_len > 2 && p[name_len - 2] == ';' &&
+ p[name_len - 1] == '1')
+ name_len -= 2;
+ /* Chop off trailing '.' from filenames. */
+ if (name_len > 1 && p[name_len - 1] == '.')
+ --name_len;
+
+ archive_strncpy(&file->name, (const char *)p, name_len);
+ }
+
+ flags = isodirrec[DR_flags_offset];
+ if (flags & 0x02)
+ file->mode = AE_IFDIR | 0700;
+ else
+ file->mode = AE_IFREG | 0400;
+ if (flags & 0x80)
+ file->multi_extent = 1;
+ else
+ file->multi_extent = 0;
+ /*
+ * Use a location for the file number, which is treated as an inode
+ * number to find out hardlink target. If Rockridge extensions is
+ * being used, the file number will be overwritten by FILE SERIAL
+ * NUMBER of RRIP "PX" extension.
+ * Note: Old mkisofs did not record that FILE SERIAL NUMBER
+ * in ISO images.
+ * Note2: xorriso set 0 to the location of a symlink file.
+ */
+ if (file->size == 0 && location >= 0) {
+ /* If file->size is zero, its location points wrong place,
+ * and so we should not use it for the file number.
+ * When the location has negative value, it can be used
+ * for the file number.
+ */
+ file->number = -1;
+ /* Do not appear before any directory entries. */
+ file->offset = -1;
+ } else
+ file->number = (int64_t)(uint32_t)location;
+
+ /* Rockridge extensions overwrite information from above. */
+ if (iso9660->opt_support_rockridge) {
+ if (parent == NULL && rr_end - rr_start >= 7) {
+ p = rr_start;
+ if (memcmp(p, "SP\x07\x01\xbe\xef", 6) == 0) {
+ /*
+ * SP extension stores the suspOffset
+ * (Number of bytes to skip between
+ * filename and SUSP records.)
+ * It is mandatory by the SUSP standard
+ * (IEEE 1281).
+ *
+ * It allows SUSP to coexist with
+ * non-SUSP uses of the System
+ * Use Area by placing non-SUSP data
+ * before SUSP data.
+ *
+ * SP extension must be in the root
+ * directory entry, disable all SUSP
+ * processing if not found.
+ */
+ iso9660->suspOffset = p[6];
+ iso9660->seenSUSP = 1;
+ rr_start += 7;
+ }
+ }
+ if (iso9660->seenSUSP) {
+ int r;
+
+ file->name_continues = 0;
+ file->symlink_continues = 0;
+ rr_start += iso9660->suspOffset;
+ r = parse_rockridge(a, file, rr_start, rr_end);
+ if (r != ARCHIVE_OK)
+ goto fail;
+ /*
+ * A file size of symbolic link files in ISO images
+ * made by makefs is not zero and its location is
+ * the same as those of next regular file. That is
+ * the same as hard like file and it causes unexpected
+ * error.
+ */
+ if (file->size > 0 &&
+ (file->mode & AE_IFMT) == AE_IFLNK) {
+ file->size = 0;
+ file->number = -1;
+ file->offset = -1;
+ }
+ } else
+ /* If there isn't SUSP, disable parsing
+ * rock ridge extensions. */
+ iso9660->opt_support_rockridge = 0;
+ }
+
+ file->nlinks = 1;/* Reset nlink. we'll calculate it later. */
+ /* Tell file's parent how many children that parent has. */
+ if (parent != NULL && (flags & 0x02))
+ parent->subdirs++;
+
+ if (iso9660->seenRockridge) {
+ if (parent != NULL && parent->parent == NULL &&
+ (flags & 0x02) && iso9660->rr_moved == NULL &&
+ file->name.s &&
+ (strcmp(file->name.s, "rr_moved") == 0 ||
+ strcmp(file->name.s, ".rr_moved") == 0)) {
+ iso9660->rr_moved = file;
+ file->rr_moved = 1;
+ file->rr_moved_has_re_only = 1;
+ file->re = 0;
+ parent->subdirs--;
+ } else if (file->re) {
+ /*
+ * Sanity check: file's parent is rr_moved.
+ */
+ if (parent == NULL || parent->rr_moved == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid Rockridge RE");
+ goto fail;
+ }
+ /*
+ * Sanity check: file does not have "CL" extension.
+ */
+ if (file->cl_offset) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid Rockridge RE and CL");
+ goto fail;
+ }
+ /*
+ * Sanity check: The file type must be a directory.
+ */
+ if ((flags & 0x02) == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid Rockridge RE");
+ goto fail;
+ }
+ } else if (parent != NULL && parent->rr_moved)
+ file->rr_moved_has_re_only = 0;
+ else if (parent != NULL && (flags & 0x02) &&
+ (parent->re || parent->re_descendant))
+ file->re_descendant = 1;
+ if (file->cl_offset) {
+ struct file_info *r;
+
+ if (parent == NULL || parent->parent == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid Rockridge CL");
+ goto fail;
+ }
+ /*
+ * Sanity check: The file type must be a regular file.
+ */
+ if ((flags & 0x02) != 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid Rockridge CL");
+ goto fail;
+ }
+ parent->subdirs++;
+ /* Overwrite an offset and a number of this "CL" entry
+ * to appear before other dirs. "+1" to those is to
+ * make sure to appear after "RE" entry which this
+ * "CL" entry should be connected with. */
+ file->offset = file->number = file->cl_offset + 1;
+
+ /*
+ * Sanity check: cl_offset does not point at its
+ * the parents or itself.
+ */
+ for (r = parent; r; r = r->parent) {
+ if (r->offset == file->cl_offset) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid Rockridge CL");
+ goto fail;
+ }
+ }
+ if (file->cl_offset == file->offset ||
+ parent->rr_moved) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid Rockridge CL");
+ goto fail;
+ }
+ }
+ }
+
+#if DEBUG
+ /* DEBUGGING: Warn about attributes I don't yet fully support. */
+ if ((flags & ~0x02) != 0) {
+ fprintf(stderr, "\n ** Unrecognized flag: ");
+ dump_isodirrec(stderr, isodirrec);
+ fprintf(stderr, "\n");
+ } else if (toi(isodirrec + DR_volume_sequence_number_offset, 2) != 1) {
+ fprintf(stderr, "\n ** Unrecognized sequence number: ");
+ dump_isodirrec(stderr, isodirrec);
+ fprintf(stderr, "\n");
+ } else if (*(isodirrec + DR_file_unit_size_offset) != 0) {
+ fprintf(stderr, "\n ** Unexpected file unit size: ");
+ dump_isodirrec(stderr, isodirrec);
+ fprintf(stderr, "\n");
+ } else if (*(isodirrec + DR_interleave_offset) != 0) {
+ fprintf(stderr, "\n ** Unexpected interleave: ");
+ dump_isodirrec(stderr, isodirrec);
+ fprintf(stderr, "\n");
+ } else if (*(isodirrec + DR_ext_attr_length_offset) != 0) {
+ fprintf(stderr, "\n ** Unexpected extended attribute length: ");
+ dump_isodirrec(stderr, isodirrec);
+ fprintf(stderr, "\n");
+ }
+#endif
+ register_file(iso9660, file);
+ return (file);
+fail:
+ archive_string_free(&file->name);
+ free(file);
+ return (NULL);
+}
+
+static int
+parse_rockridge(struct archive_read *a, struct file_info *file,
+ const unsigned char *p, const unsigned char *end)
+{
+ struct iso9660 *iso9660;
+ int entry_seen = 0;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+
+ while (p + 4 <= end /* Enough space for another entry. */
+ && p[0] >= 'A' && p[0] <= 'Z' /* Sanity-check 1st char of name. */
+ && p[1] >= 'A' && p[1] <= 'Z' /* Sanity-check 2nd char of name. */
+ && p[2] >= 4 /* Sanity-check length. */
+ && p + p[2] <= end) { /* Sanity-check length. */
+ const unsigned char *data = p + 4;
+ int data_length = p[2] - 4;
+ int version = p[3];
+
+ switch(p[0]) {
+ case 'C':
+ if (p[1] == 'E') {
+ if (version == 1 && data_length == 24) {
+ /*
+ * CE extension comprises:
+ * 8 byte sector containing extension
+ * 8 byte offset w/in above sector
+ * 8 byte length of continuation
+ */
+ int32_t location =
+ archive_le32dec(data);
+ file->ce_offset =
+ archive_le32dec(data+8);
+ file->ce_size =
+ archive_le32dec(data+16);
+ if (register_CE(a, location, file)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ }
+ else if (p[1] == 'L') {
+ if (version == 1 && data_length == 8) {
+ file->cl_offset = (uint64_t)
+ iso9660->logical_block_size *
+ (uint64_t)archive_le32dec(data);
+ iso9660->seenRockridge = 1;
+ }
+ }
+ break;
+ case 'N':
+ if (p[1] == 'M') {
+ if (version == 1) {
+ parse_rockridge_NM1(file,
+ data, data_length);
+ iso9660->seenRockridge = 1;
+ }
+ }
+ break;
+ case 'P':
+ /*
+ * PD extension is padding;
+ * contents are always ignored.
+ *
+ * PL extension won't appear;
+ * contents are always ignored.
+ */
+ if (p[1] == 'N') {
+ if (version == 1 && data_length == 16) {
+ file->rdev = toi(data,4);
+ file->rdev <<= 32;
+ file->rdev |= toi(data + 8, 4);
+ iso9660->seenRockridge = 1;
+ }
+ }
+ else if (p[1] == 'X') {
+ /*
+ * PX extension comprises:
+ * 8 bytes for mode,
+ * 8 bytes for nlinks,
+ * 8 bytes for uid,
+ * 8 bytes for gid,
+ * 8 bytes for inode.
+ */
+ if (version == 1) {
+ if (data_length >= 8)
+ file->mode
+ = toi(data, 4);
+ if (data_length >= 16)
+ file->nlinks
+ = toi(data + 8, 4);
+ if (data_length >= 24)
+ file->uid
+ = toi(data + 16, 4);
+ if (data_length >= 32)
+ file->gid
+ = toi(data + 24, 4);
+ if (data_length >= 40)
+ file->number
+ = toi(data + 32, 4);
+ iso9660->seenRockridge = 1;
+ }
+ }
+ break;
+ case 'R':
+ if (p[1] == 'E' && version == 1) {
+ file->re = 1;
+ iso9660->seenRockridge = 1;
+ }
+ else if (p[1] == 'R' && version == 1) {
+ /*
+ * RR extension comprises:
+ * one byte flag value
+ * This extension is obsolete,
+ * so contents are always ignored.
+ */
+ }
+ break;
+ case 'S':
+ if (p[1] == 'L') {
+ if (version == 1) {
+ parse_rockridge_SL1(file,
+ data, data_length);
+ iso9660->seenRockridge = 1;
+ }
+ }
+ else if (p[1] == 'T'
+ && data_length == 0 && version == 1) {
+ /*
+ * ST extension marks end of this
+ * block of SUSP entries.
+ *
+ * It allows SUSP to coexist with
+ * non-SUSP uses of the System
+ * Use Area by placing non-SUSP data
+ * after SUSP data.
+ */
+ iso9660->seenSUSP = 0;
+ iso9660->seenRockridge = 0;
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'T':
+ if (p[1] == 'F') {
+ if (version == 1) {
+ parse_rockridge_TF1(file,
+ data, data_length);
+ iso9660->seenRockridge = 1;
+ }
+ }
+ break;
+ case 'Z':
+ if (p[1] == 'F') {
+ if (version == 1)
+ parse_rockridge_ZF1(file,
+ data, data_length);
+ }
+ break;
+ default:
+ break;
+ }
+
+ p += p[2];
+ entry_seen = 1;
+ }
+
+ if (entry_seen)
+ return (ARCHIVE_OK);
+ else {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Tried to parse Rockridge extensions, but none found");
+ return (ARCHIVE_WARN);
+ }
+}
+
+static int
+register_CE(struct archive_read *a, int32_t location,
+ struct file_info *file)
+{
+ struct iso9660 *iso9660;
+ struct read_ce_queue *heap;
+ struct read_ce_req *p;
+ uint64_t offset, parent_offset;
+ int hole, parent;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+ offset = ((uint64_t)location) * (uint64_t)iso9660->logical_block_size;
+ if (((file->mode & AE_IFMT) == AE_IFREG &&
+ offset >= file->offset) ||
+ offset < iso9660->current_position ||
+ (((uint64_t)file->ce_offset) + file->ce_size)
+ > (uint64_t)iso9660->logical_block_size ||
+ offset + file->ce_offset + file->ce_size
+ > iso9660->volume_size) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid parameter in SUSP \"CE\" extension");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Expand our CE list as necessary. */
+ heap = &(iso9660->read_ce_req);
+ if (heap->cnt >= heap->allocated) {
+ int new_size;
+
+ if (heap->allocated < 16)
+ new_size = 16;
+ else
+ new_size = heap->allocated * 2;
+ /* Overflow might keep us from growing the list. */
+ if (new_size <= heap->allocated) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ p = calloc(new_size, sizeof(p[0]));
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ if (heap->reqs != NULL) {
+ memcpy(p, heap->reqs, heap->cnt * sizeof(*p));
+ free(heap->reqs);
+ }
+ heap->reqs = p;
+ heap->allocated = new_size;
+ }
+
+ /*
+ * Start with hole at end, walk it up tree to find insertion point.
+ */
+ hole = heap->cnt++;
+ while (hole > 0) {
+ parent = (hole - 1)/2;
+ parent_offset = heap->reqs[parent].offset;
+ if (offset >= parent_offset) {
+ heap->reqs[hole].offset = offset;
+ heap->reqs[hole].file = file;
+ return (ARCHIVE_OK);
+ }
+ /* Move parent into hole <==> move hole up tree. */
+ heap->reqs[hole] = heap->reqs[parent];
+ hole = parent;
+ }
+ heap->reqs[0].offset = offset;
+ heap->reqs[0].file = file;
+ return (ARCHIVE_OK);
+}
+
+static void
+next_CE(struct read_ce_queue *heap)
+{
+ uint64_t a_offset, b_offset, c_offset;
+ int a, b, c;
+ struct read_ce_req tmp;
+
+ if (heap->cnt < 1)
+ return;
+
+ /*
+ * Move the last item in the heap to the root of the tree
+ */
+ heap->reqs[0] = heap->reqs[--(heap->cnt)];
+
+ /*
+ * Rebalance the heap.
+ */
+ a = 0; /* Starting element and its offset */
+ a_offset = heap->reqs[a].offset;
+ for (;;) {
+ b = a + a + 1; /* First child */
+ if (b >= heap->cnt)
+ return;
+ b_offset = heap->reqs[b].offset;
+ c = b + 1; /* Use second child if it is smaller. */
+ if (c < heap->cnt) {
+ c_offset = heap->reqs[c].offset;
+ if (c_offset < b_offset) {
+ b = c;
+ b_offset = c_offset;
+ }
+ }
+ if (a_offset <= b_offset)
+ return;
+ tmp = heap->reqs[a];
+ heap->reqs[a] = heap->reqs[b];
+ heap->reqs[b] = tmp;
+ a = b;
+ }
+}
+
+
+static int
+read_CE(struct archive_read *a, struct iso9660 *iso9660)
+{
+ struct read_ce_queue *heap;
+ const unsigned char *b, *p, *end;
+ struct file_info *file;
+ size_t step;
+ int r;
+
+ /* Read data which RRIP "CE" extension points. */
+ heap = &(iso9660->read_ce_req);
+ step = iso9660->logical_block_size;
+ while (heap->cnt &&
+ heap->reqs[0].offset == iso9660->current_position) {
+ b = __archive_read_ahead(a, step, NULL);
+ if (b == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to read full block when scanning "
+ "ISO9660 directory list");
+ return (ARCHIVE_FATAL);
+ }
+ do {
+ file = heap->reqs[0].file;
+ if (file->ce_offset + file->ce_size > step) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Malformed CE information");
+ return (ARCHIVE_FATAL);
+ }
+ p = b + file->ce_offset;
+ end = p + file->ce_size;
+ next_CE(heap);
+ r = parse_rockridge(a, file, p, end);
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } while (heap->cnt &&
+ heap->reqs[0].offset == iso9660->current_position);
+ /* NOTE: Do not move this consume's code to front of
+ * do-while loop. Registration of nested CE extension
+ * might cause error because of current position. */
+ __archive_read_consume(a, step);
+ iso9660->current_position += step;
+ }
+ return (ARCHIVE_OK);
+}
+
+static void
+parse_rockridge_NM1(struct file_info *file,
+ const unsigned char *data, int data_length)
+{
+ if (!file->name_continues)
+ archive_string_empty(&file->name);
+ file->name_continues = 0;
+ if (data_length < 1)
+ return;
+ /*
+ * NM version 1 extension comprises:
+ * 1 byte flag, value is one of:
+ * = 0: remainder is name
+ * = 1: remainder is name, next NM entry continues name
+ * = 2: "."
+ * = 4: ".."
+ * = 32: Implementation specific
+ * All other values are reserved.
+ */
+ switch(data[0]) {
+ case 0:
+ if (data_length < 2)
+ return;
+ archive_strncat(&file->name,
+ (const char *)data + 1, data_length - 1);
+ break;
+ case 1:
+ if (data_length < 2)
+ return;
+ archive_strncat(&file->name,
+ (const char *)data + 1, data_length - 1);
+ file->name_continues = 1;
+ break;
+ case 2:
+ archive_strcat(&file->name, ".");
+ break;
+ case 4:
+ archive_strcat(&file->name, "..");
+ break;
+ default:
+ return;
+ }
+
+}
+
+static void
+parse_rockridge_TF1(struct file_info *file, const unsigned char *data,
+ int data_length)
+{
+ char flag;
+ /*
+ * TF extension comprises:
+ * one byte flag
+ * create time (optional)
+ * modify time (optional)
+ * access time (optional)
+ * attribute time (optional)
+ * Time format and presence of fields
+ * is controlled by flag bits.
+ */
+ if (data_length < 1)
+ return;
+ flag = data[0];
+ ++data;
+ --data_length;
+ if (flag & 0x80) {
+ /* Use 17-byte time format. */
+ if ((flag & 1) && data_length >= 17) {
+ /* Create time. */
+ file->birthtime_is_set = 1;
+ file->birthtime = isodate17(data);
+ data += 17;
+ data_length -= 17;
+ }
+ if ((flag & 2) && data_length >= 17) {
+ /* Modify time. */
+ file->mtime = isodate17(data);
+ data += 17;
+ data_length -= 17;
+ }
+ if ((flag & 4) && data_length >= 17) {
+ /* Access time. */
+ file->atime = isodate17(data);
+ data += 17;
+ data_length -= 17;
+ }
+ if ((flag & 8) && data_length >= 17) {
+ /* Attribute change time. */
+ file->ctime = isodate17(data);
+ }
+ } else {
+ /* Use 7-byte time format. */
+ if ((flag & 1) && data_length >= 7) {
+ /* Create time. */
+ file->birthtime_is_set = 1;
+ file->birthtime = isodate7(data);
+ data += 7;
+ data_length -= 7;
+ }
+ if ((flag & 2) && data_length >= 7) {
+ /* Modify time. */
+ file->mtime = isodate7(data);
+ data += 7;
+ data_length -= 7;
+ }
+ if ((flag & 4) && data_length >= 7) {
+ /* Access time. */
+ file->atime = isodate7(data);
+ data += 7;
+ data_length -= 7;
+ }
+ if ((flag & 8) && data_length >= 7) {
+ /* Attribute change time. */
+ file->ctime = isodate7(data);
+ }
+ }
+}
+
+static void
+parse_rockridge_SL1(struct file_info *file, const unsigned char *data,
+ int data_length)
+{
+ const char *separator = "";
+
+ if (!file->symlink_continues || file->symlink.length < 1)
+ archive_string_empty(&file->symlink);
+ file->symlink_continues = 0;
+
+ /*
+ * Defined flag values:
+ * 0: This is the last SL record for this symbolic link
+ * 1: this symbolic link field continues in next SL entry
+ * All other values are reserved.
+ */
+ if (data_length < 1)
+ return;
+ switch(*data) {
+ case 0:
+ break;
+ case 1:
+ file->symlink_continues = 1;
+ break;
+ default:
+ return;
+ }
+ ++data; /* Skip flag byte. */
+ --data_length;
+
+ /*
+ * SL extension body stores "components".
+ * Basically, this is a complicated way of storing
+ * a POSIX path. It also interferes with using
+ * symlinks for storing non-path data. <sigh>
+ *
+ * Each component is 2 bytes (flag and length)
+ * possibly followed by name data.
+ */
+ while (data_length >= 2) {
+ unsigned char flag = *data++;
+ unsigned char nlen = *data++;
+ data_length -= 2;
+
+ archive_strcat(&file->symlink, separator);
+ separator = "/";
+
+ switch(flag) {
+ case 0: /* Usual case, this is text. */
+ if (data_length < nlen)
+ return;
+ archive_strncat(&file->symlink,
+ (const char *)data, nlen);
+ break;
+ case 0x01: /* Text continues in next component. */
+ if (data_length < nlen)
+ return;
+ archive_strncat(&file->symlink,
+ (const char *)data, nlen);
+ separator = "";
+ break;
+ case 0x02: /* Current dir. */
+ archive_strcat(&file->symlink, ".");
+ break;
+ case 0x04: /* Parent dir. */
+ archive_strcat(&file->symlink, "..");
+ break;
+ case 0x08: /* Root of filesystem. */
+ archive_strcat(&file->symlink, "/");
+ separator = "";
+ break;
+ case 0x10: /* Undefined (historically "volume root" */
+ archive_string_empty(&file->symlink);
+ archive_strcat(&file->symlink, "ROOT");
+ break;
+ case 0x20: /* Undefined (historically "hostname") */
+ archive_strcat(&file->symlink, "hostname");
+ break;
+ default:
+ /* TODO: issue a warning ? */
+ return;
+ }
+ data += nlen;
+ data_length -= nlen;
+ }
+}
+
+static void
+parse_rockridge_ZF1(struct file_info *file, const unsigned char *data,
+ int data_length)
+{
+
+ if (data[0] == 0x70 && data[1] == 0x7a && data_length == 12) {
+ /* paged zlib */
+ file->pz = 1;
+ file->pz_log2_bs = data[3];
+ file->pz_uncompressed_size = archive_le32dec(&data[4]);
+ }
+}
+
+static void
+register_file(struct iso9660 *iso9660, struct file_info *file)
+{
+
+ file->use_next = iso9660->use_files;
+ iso9660->use_files = file;
+}
+
+static void
+release_files(struct iso9660 *iso9660)
+{
+ struct content *con, *connext;
+ struct file_info *file;
+
+ file = iso9660->use_files;
+ while (file != NULL) {
+ struct file_info *next = file->use_next;
+
+ archive_string_free(&file->name);
+ archive_string_free(&file->symlink);
+ free(file->utf16be_name);
+ con = file->contents.first;
+ while (con != NULL) {
+ connext = con->next;
+ free(con);
+ con = connext;
+ }
+ free(file);
+ file = next;
+ }
+}
+
+static int
+next_entry_seek(struct archive_read *a, struct iso9660 *iso9660,
+ struct file_info **pfile)
+{
+ struct file_info *file;
+ int r;
+
+ r = next_cache_entry(a, iso9660, pfile);
+ if (r != ARCHIVE_OK)
+ return (r);
+ file = *pfile;
+
+ /* Don't waste time seeking for zero-length bodies. */
+ if (file->size == 0)
+ file->offset = iso9660->current_position;
+
+ /* flush any remaining bytes from the last round to ensure
+ * we're positioned */
+ if (iso9660->entry_bytes_unconsumed) {
+ __archive_read_consume(a, iso9660->entry_bytes_unconsumed);
+ iso9660->entry_bytes_unconsumed = 0;
+ }
+
+ /* Seek forward to the start of the entry. */
+ if (iso9660->current_position < file->offset) {
+ int64_t step;
+
+ step = file->offset - iso9660->current_position;
+ step = __archive_read_consume(a, step);
+ if (step < 0)
+ return ((int)step);
+ iso9660->current_position = file->offset;
+ }
+
+ /* We found body of file; handle it now. */
+ return (ARCHIVE_OK);
+}
+
+static int
+next_cache_entry(struct archive_read *a, struct iso9660 *iso9660,
+ struct file_info **pfile)
+{
+ struct file_info *file;
+ struct {
+ struct file_info *first;
+ struct file_info **last;
+ } empty_files;
+ int64_t number;
+ int count;
+
+ file = cache_get_entry(iso9660);
+ if (file != NULL) {
+ *pfile = file;
+ return (ARCHIVE_OK);
+ }
+
+ for (;;) {
+ struct file_info *re, *d;
+
+ *pfile = file = next_entry(iso9660);
+ if (file == NULL) {
+ /*
+ * If directory entries all which are descendant of
+ * rr_moved are still remaining, expose their.
+ */
+ if (iso9660->re_files.first != NULL &&
+ iso9660->rr_moved != NULL &&
+ iso9660->rr_moved->rr_moved_has_re_only)
+ /* Expose "rr_moved" entry. */
+ cache_add_entry(iso9660, iso9660->rr_moved);
+ while ((re = re_get_entry(iso9660)) != NULL) {
+ /* Expose its descendant dirs. */
+ while ((d = rede_get_entry(re)) != NULL)
+ cache_add_entry(iso9660, d);
+ }
+ if (iso9660->cache_files.first != NULL)
+ return (next_cache_entry(a, iso9660, pfile));
+ return (ARCHIVE_EOF);
+ }
+
+ if (file->cl_offset) {
+ struct file_info *first_re = NULL;
+ int nexted_re = 0;
+
+ /*
+ * Find "RE" dir for the current file, which
+ * has "CL" flag.
+ */
+ while ((re = re_get_entry(iso9660))
+ != first_re) {
+ if (first_re == NULL)
+ first_re = re;
+ if (re->offset == file->cl_offset) {
+ re->parent->subdirs--;
+ re->parent = file->parent;
+ re->re = 0;
+ if (re->parent->re_descendant) {
+ nexted_re = 1;
+ re->re_descendant = 1;
+ if (rede_add_entry(re) < 0)
+ goto fatal_rr;
+ /* Move a list of descendants
+ * to a new ancestor. */
+ while ((d = rede_get_entry(
+ re)) != NULL)
+ if (rede_add_entry(d)
+ < 0)
+ goto fatal_rr;
+ break;
+ }
+ /* Replace the current file
+ * with "RE" dir */
+ *pfile = file = re;
+ /* Expose its descendant */
+ while ((d = rede_get_entry(
+ file)) != NULL)
+ cache_add_entry(
+ iso9660, d);
+ break;
+ } else
+ re_add_entry(iso9660, re);
+ }
+ if (nexted_re) {
+ /*
+ * Do not expose this at this time
+ * because we have not gotten its full-path
+ * name yet.
+ */
+ continue;
+ }
+ } else if ((file->mode & AE_IFMT) == AE_IFDIR) {
+ int r;
+
+ /* Read file entries in this dir. */
+ r = read_children(a, file);
+ if (r != ARCHIVE_OK)
+ return (r);
+
+ /*
+ * Handle a special dir of Rockridge extensions,
+ * "rr_moved".
+ */
+ if (file->rr_moved) {
+ /*
+ * If this has only the subdirectories which
+ * have "RE" flags, do not expose at this time.
+ */
+ if (file->rr_moved_has_re_only)
+ continue;
+ /* Otherwise expose "rr_moved" entry. */
+ } else if (file->re) {
+ /*
+ * Do not expose this at this time
+ * because we have not gotten its full-path
+ * name yet.
+ */
+ re_add_entry(iso9660, file);
+ continue;
+ } else if (file->re_descendant) {
+ /*
+ * If the top level "RE" entry of this entry
+ * is not exposed, we, accordingly, should not
+ * expose this entry at this time because
+ * we cannot make its proper full-path name.
+ */
+ if (rede_add_entry(file) == 0)
+ continue;
+ /* Otherwise we can expose this entry because
+ * it seems its top level "RE" has already been
+ * exposed. */
+ }
+ }
+ break;
+ }
+
+ if ((file->mode & AE_IFMT) != AE_IFREG || file->number == -1)
+ return (ARCHIVE_OK);
+
+ count = 0;
+ number = file->number;
+ iso9660->cache_files.first = NULL;
+ iso9660->cache_files.last = &(iso9660->cache_files.first);
+ empty_files.first = NULL;
+ empty_files.last = &empty_files.first;
+ /* Collect files which has the same file serial number.
+ * Peek pending_files so that file which number is different
+ * is not put back. */
+ while (iso9660->pending_files.used > 0 &&
+ (iso9660->pending_files.files[0]->number == -1 ||
+ iso9660->pending_files.files[0]->number == number)) {
+ if (file->number == -1) {
+ /* This file has the same offset
+ * but it's wrong offset which empty files
+ * and symlink files have.
+ * NOTE: This wrong offset was recorded by
+ * old mkisofs utility. If ISO images is
+ * created by latest mkisofs, this does not
+ * happen.
+ */
+ file->next = NULL;
+ *empty_files.last = file;
+ empty_files.last = &(file->next);
+ } else {
+ count++;
+ cache_add_entry(iso9660, file);
+ }
+ file = next_entry(iso9660);
+ }
+
+ if (count == 0) {
+ *pfile = file;
+ return ((file == NULL)?ARCHIVE_EOF:ARCHIVE_OK);
+ }
+ if (file->number == -1) {
+ file->next = NULL;
+ *empty_files.last = file;
+ empty_files.last = &(file->next);
+ } else {
+ count++;
+ cache_add_entry(iso9660, file);
+ }
+
+ if (count > 1) {
+ /* The count is the same as number of hardlink,
+ * so much so that each nlinks of files in cache_file
+ * is overwritten by value of the count.
+ */
+ for (file = iso9660->cache_files.first;
+ file != NULL; file = file->next)
+ file->nlinks = count;
+ }
+ /* If there are empty files, that files are added
+ * to the tail of the cache_files. */
+ if (empty_files.first != NULL) {
+ *iso9660->cache_files.last = empty_files.first;
+ iso9660->cache_files.last = empty_files.last;
+ }
+ *pfile = cache_get_entry(iso9660);
+ return ((*pfile == NULL)?ARCHIVE_EOF:ARCHIVE_OK);
+
+fatal_rr:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to connect 'CL' pointer to 'RE' rr_moved pointer of "
+ "Rockridge extensions: current position = %jd, CL offset = %jd",
+ (intmax_t)iso9660->current_position, (intmax_t)file->cl_offset);
+ return (ARCHIVE_FATAL);
+}
+
+static inline void
+re_add_entry(struct iso9660 *iso9660, struct file_info *file)
+{
+ file->re_next = NULL;
+ *iso9660->re_files.last = file;
+ iso9660->re_files.last = &(file->re_next);
+}
+
+static inline struct file_info *
+re_get_entry(struct iso9660 *iso9660)
+{
+ struct file_info *file;
+
+ if ((file = iso9660->re_files.first) != NULL) {
+ iso9660->re_files.first = file->re_next;
+ if (iso9660->re_files.first == NULL)
+ iso9660->re_files.last =
+ &(iso9660->re_files.first);
+ }
+ return (file);
+}
+
+static inline int
+rede_add_entry(struct file_info *file)
+{
+ struct file_info *re;
+
+ /*
+ * Find "RE" entry.
+ */
+ re = file->parent;
+ while (re != NULL && !re->re)
+ re = re->parent;
+ if (re == NULL)
+ return (-1);
+
+ file->re_next = NULL;
+ *re->rede_files.last = file;
+ re->rede_files.last = &(file->re_next);
+ return (0);
+}
+
+static inline struct file_info *
+rede_get_entry(struct file_info *re)
+{
+ struct file_info *file;
+
+ if ((file = re->rede_files.first) != NULL) {
+ re->rede_files.first = file->re_next;
+ if (re->rede_files.first == NULL)
+ re->rede_files.last =
+ &(re->rede_files.first);
+ }
+ return (file);
+}
+
+static inline void
+cache_add_entry(struct iso9660 *iso9660, struct file_info *file)
+{
+ file->next = NULL;
+ *iso9660->cache_files.last = file;
+ iso9660->cache_files.last = &(file->next);
+}
+
+static inline struct file_info *
+cache_get_entry(struct iso9660 *iso9660)
+{
+ struct file_info *file;
+
+ if ((file = iso9660->cache_files.first) != NULL) {
+ iso9660->cache_files.first = file->next;
+ if (iso9660->cache_files.first == NULL)
+ iso9660->cache_files.last =
+ &(iso9660->cache_files.first);
+ }
+ return (file);
+}
+
+static int
+heap_add_entry(struct archive_read *a, struct heap_queue *heap,
+ struct file_info *file, uint64_t key)
+{
+ uint64_t file_key, parent_key;
+ int hole, parent;
+
+ /* Expand our pending files list as necessary. */
+ if (heap->used >= heap->allocated) {
+ struct file_info **new_pending_files;
+ int new_size = heap->allocated * 2;
+
+ if (heap->allocated < 1024)
+ new_size = 1024;
+ /* Overflow might keep us from growing the list. */
+ if (new_size <= heap->allocated) {
+ archive_set_error(&a->archive,
+ ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ new_pending_files = (struct file_info **)
+ malloc(new_size * sizeof(new_pending_files[0]));
+ if (new_pending_files == NULL) {
+ archive_set_error(&a->archive,
+ ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ if (heap->allocated)
+ memcpy(new_pending_files, heap->files,
+ heap->allocated * sizeof(new_pending_files[0]));
+ free(heap->files);
+ heap->files = new_pending_files;
+ heap->allocated = new_size;
+ }
+
+ file_key = file->key = key;
+
+ /*
+ * Start with hole at end, walk it up tree to find insertion point.
+ */
+ hole = heap->used++;
+ while (hole > 0) {
+ parent = (hole - 1)/2;
+ parent_key = heap->files[parent]->key;
+ if (file_key >= parent_key) {
+ heap->files[hole] = file;
+ return (ARCHIVE_OK);
+ }
+ /* Move parent into hole <==> move hole up tree. */
+ heap->files[hole] = heap->files[parent];
+ hole = parent;
+ }
+ heap->files[0] = file;
+
+ return (ARCHIVE_OK);
+}
+
+static struct file_info *
+heap_get_entry(struct heap_queue *heap)
+{
+ uint64_t a_key, b_key, c_key;
+ int a, b, c;
+ struct file_info *r, *tmp;
+
+ if (heap->used < 1)
+ return (NULL);
+
+ /*
+ * The first file in the list is the earliest; we'll return this.
+ */
+ r = heap->files[0];
+
+ /*
+ * Move the last item in the heap to the root of the tree
+ */
+ heap->files[0] = heap->files[--(heap->used)];
+
+ /*
+ * Rebalance the heap.
+ */
+ a = 0; /* Starting element and its heap key */
+ a_key = heap->files[a]->key;
+ for (;;) {
+ b = a + a + 1; /* First child */
+ if (b >= heap->used)
+ return (r);
+ b_key = heap->files[b]->key;
+ c = b + 1; /* Use second child if it is smaller. */
+ if (c < heap->used) {
+ c_key = heap->files[c]->key;
+ if (c_key < b_key) {
+ b = c;
+ b_key = c_key;
+ }
+ }
+ if (a_key <= b_key)
+ return (r);
+ tmp = heap->files[a];
+ heap->files[a] = heap->files[b];
+ heap->files[b] = tmp;
+ a = b;
+ }
+}
+
+static unsigned int
+toi(const void *p, int n)
+{
+ const unsigned char *v = (const unsigned char *)p;
+ if (n > 1)
+ return v[0] + 256 * toi(v + 1, n - 1);
+ if (n == 1)
+ return v[0];
+ return (0);
+}
+
+static time_t
+isodate7(const unsigned char *v)
+{
+ struct tm tm;
+ int offset;
+ time_t t;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = v[0];
+ tm.tm_mon = v[1] - 1;
+ tm.tm_mday = v[2];
+ tm.tm_hour = v[3];
+ tm.tm_min = v[4];
+ tm.tm_sec = v[5];
+ /* v[6] is the signed timezone offset, in 1/4-hour increments. */
+ offset = ((const signed char *)v)[6];
+ if (offset > -48 && offset < 52) {
+ tm.tm_hour -= offset / 4;
+ tm.tm_min -= (offset % 4) * 15;
+ }
+ t = time_from_tm(&tm);
+ if (t == (time_t)-1)
+ return ((time_t)0);
+ return (t);
+}
+
+static time_t
+isodate17(const unsigned char *v)
+{
+ struct tm tm;
+ int offset;
+ time_t t;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = (v[0] - '0') * 1000 + (v[1] - '0') * 100
+ + (v[2] - '0') * 10 + (v[3] - '0')
+ - 1900;
+ tm.tm_mon = (v[4] - '0') * 10 + (v[5] - '0');
+ tm.tm_mday = (v[6] - '0') * 10 + (v[7] - '0');
+ tm.tm_hour = (v[8] - '0') * 10 + (v[9] - '0');
+ tm.tm_min = (v[10] - '0') * 10 + (v[11] - '0');
+ tm.tm_sec = (v[12] - '0') * 10 + (v[13] - '0');
+ /* v[16] is the signed timezone offset, in 1/4-hour increments. */
+ offset = ((const signed char *)v)[16];
+ if (offset > -48 && offset < 52) {
+ tm.tm_hour -= offset / 4;
+ tm.tm_min -= (offset % 4) * 15;
+ }
+ t = time_from_tm(&tm);
+ if (t == (time_t)-1)
+ return ((time_t)0);
+ return (t);
+}
+
+static time_t
+time_from_tm(struct tm *t)
+{
+#if HAVE__MKGMTIME
+ return _mkgmtime(t);
+#elif HAVE_TIMEGM
+ /* Use platform timegm() if available. */
+ return (timegm(t));
+#else
+ /* Else use direct calculation using POSIX assumptions. */
+ /* First, fix up tm_yday based on the year/month/day. */
+ if (mktime(t) == (time_t)-1)
+ return ((time_t)-1);
+ /* Then we can compute timegm() from first principles. */
+ return (t->tm_sec
+ + t->tm_min * 60
+ + t->tm_hour * 3600
+ + t->tm_yday * 86400
+ + (t->tm_year - 70) * 31536000
+ + ((t->tm_year - 69) / 4) * 86400
+ - ((t->tm_year - 1) / 100) * 86400
+ + ((t->tm_year + 299) / 400) * 86400);
+#endif
+}
+
+static const char *
+build_pathname(struct archive_string *as, struct file_info *file, int depth)
+{
+ // Plain ISO9660 only allows 8 dir levels; if we get
+ // to 1000, then something is very, very wrong.
+ if (depth > 1000) {
+ return NULL;
+ }
+ if (file->parent != NULL && archive_strlen(&file->parent->name) > 0) {
+ if (build_pathname(as, file->parent, depth + 1) == NULL) {
+ return NULL;
+ }
+ archive_strcat(as, "/");
+ }
+ if (archive_strlen(&file->name) == 0)
+ archive_strcat(as, ".");
+ else
+ archive_string_concat(as, &file->name);
+ return (as->s);
+}
+
+static int
+build_pathname_utf16be(unsigned char *p, size_t max, size_t *len,
+ struct file_info *file)
+{
+ if (file->parent != NULL && file->parent->utf16be_bytes > 0) {
+ if (build_pathname_utf16be(p, max, len, file->parent) != 0)
+ return (-1);
+ p[*len] = 0;
+ p[*len + 1] = '/';
+ *len += 2;
+ }
+ if (file->utf16be_bytes == 0) {
+ if (*len + 2 > max)
+ return (-1);/* Path is too long! */
+ p[*len] = 0;
+ p[*len + 1] = '.';
+ *len += 2;
+ } else {
+ if (*len + file->utf16be_bytes > max)
+ return (-1);/* Path is too long! */
+ memcpy(p + *len, file->utf16be_name, file->utf16be_bytes);
+ *len += file->utf16be_bytes;
+ }
+ return (0);
+}
+
+#if DEBUG
+static void
+dump_isodirrec(FILE *out, const unsigned char *isodirrec)
+{
+ fprintf(out, " l %d,",
+ toi(isodirrec + DR_length_offset, DR_length_size));
+ fprintf(out, " a %d,",
+ toi(isodirrec + DR_ext_attr_length_offset, DR_ext_attr_length_size));
+ fprintf(out, " ext 0x%x,",
+ toi(isodirrec + DR_extent_offset, DR_extent_size));
+ fprintf(out, " s %d,",
+ toi(isodirrec + DR_size_offset, DR_extent_size));
+ fprintf(out, " f 0x%x,",
+ toi(isodirrec + DR_flags_offset, DR_flags_size));
+ fprintf(out, " u %d,",
+ toi(isodirrec + DR_file_unit_size_offset, DR_file_unit_size_size));
+ fprintf(out, " ilv %d,",
+ toi(isodirrec + DR_interleave_offset, DR_interleave_size));
+ fprintf(out, " seq %d,",
+ toi(isodirrec + DR_volume_sequence_number_offset,
+ DR_volume_sequence_number_size));
+ fprintf(out, " nl %d:",
+ toi(isodirrec + DR_name_len_offset, DR_name_len_size));
+ fprintf(out, " `%.*s'",
+ toi(isodirrec + DR_name_len_offset, DR_name_len_size),
+ isodirrec + DR_name_offset);
+}
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_lha.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_lha.c
new file mode 100644
index 00000000000..1c64b2900b8
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_lha.c
@@ -0,0 +1,2919 @@
+/*-
+ * Copyright (c) 2008-2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_endian.h"
+
+
+#define MAXMATCH 256 /* Maximum match length. */
+#define MINMATCH 3 /* Minimum match length. */
+/*
+ * Literal table format:
+ * +0 +256 +510
+ * +---------------+-------------------------+
+ * | literal code | match length |
+ * | 0 ... 255 | MINMATCH ... MAXMATCH |
+ * +---------------+-------------------------+
+ * <--- LT_BITLEN_SIZE --->
+ */
+/* Literal table size. */
+#define LT_BITLEN_SIZE (UCHAR_MAX + 1 + MAXMATCH - MINMATCH + 1)
+/* Position table size.
+ * Note: this used for both position table and pre literal table.*/
+#define PT_BITLEN_SIZE (3 + 16)
+
+struct lzh_dec {
+ /* Decoding status. */
+ int state;
+
+ /*
+ * Window to see last 8Ki(lh5),32Ki(lh6),64Ki(lh7) bytes of decoded
+ * data.
+ */
+ int w_size;
+ int w_mask;
+ /* Window buffer, which is a loop buffer. */
+ unsigned char *w_buff;
+ /* The insert position to the window. */
+ int w_pos;
+ /* The position where we can copy decoded code from the window. */
+ int copy_pos;
+ /* The length how many bytes we can copy decoded code from
+ * the window. */
+ int copy_len;
+
+ /*
+ * Bit stream reader.
+ */
+ struct lzh_br {
+#define CACHE_TYPE uint64_t
+#define CACHE_BITS (8 * sizeof(CACHE_TYPE))
+ /* Cache buffer. */
+ CACHE_TYPE cache_buffer;
+ /* Indicates how many bits avail in cache_buffer. */
+ int cache_avail;
+ } br;
+
+ /*
+ * Huffman coding.
+ */
+ struct huffman {
+ int len_size;
+ int len_avail;
+ int len_bits;
+ int freq[17];
+ unsigned char *bitlen;
+
+ /*
+ * Use a index table. It's faster than searching a huffman
+ * coding tree, which is a binary tree. But a use of a large
+ * index table causes L1 cache read miss many times.
+ */
+#define HTBL_BITS 10
+ int max_bits;
+ int shift_bits;
+ int tbl_bits;
+ int tree_used;
+ int tree_avail;
+ /* Direct access table. */
+ uint16_t *tbl;
+ /* Binary tree table for extra bits over the direct access. */
+ struct htree_t {
+ uint16_t left;
+ uint16_t right;
+ } *tree;
+ } lt, pt;
+
+ int blocks_avail;
+ int pos_pt_len_size;
+ int pos_pt_len_bits;
+ int literal_pt_len_size;
+ int literal_pt_len_bits;
+ int reading_position;
+ int loop;
+ int error;
+};
+
+struct lzh_stream {
+ const unsigned char *next_in;
+ int avail_in;
+ int64_t total_in;
+ const unsigned char *ref_ptr;
+ int avail_out;
+ int64_t total_out;
+ struct lzh_dec *ds;
+};
+
+struct lha {
+ /* entry_bytes_remaining is the number of bytes we expect. */
+ int64_t entry_offset;
+ int64_t entry_bytes_remaining;
+ int64_t entry_unconsumed;
+ uint16_t entry_crc_calculated;
+
+ size_t header_size; /* header size */
+ unsigned char level; /* header level */
+ char method[3]; /* compress type */
+ int64_t compsize; /* compressed data size */
+ int64_t origsize; /* original file size */
+ int setflag;
+#define BIRTHTIME_IS_SET 1
+#define ATIME_IS_SET 2
+#define UNIX_MODE_IS_SET 4
+#define CRC_IS_SET 8
+ time_t birthtime;
+ long birthtime_tv_nsec;
+ time_t mtime;
+ long mtime_tv_nsec;
+ time_t atime;
+ long atime_tv_nsec;
+ mode_t mode;
+ int64_t uid;
+ int64_t gid;
+ struct archive_string uname;
+ struct archive_string gname;
+ uint16_t header_crc;
+ uint16_t crc;
+ /* dirname and filename could be in different codepages */
+ struct archive_string_conv *sconv_dir;
+ struct archive_string_conv *sconv_fname;
+ struct archive_string_conv *opt_sconv;
+
+ struct archive_string dirname;
+ struct archive_string filename;
+ struct archive_wstring ws;
+
+ unsigned char dos_attr;
+
+ /* Flag to mark progress that an archive was read their first header.*/
+ char found_first_header;
+ /* Flag to mark that indicates an empty directory. */
+ char directory;
+
+ /* Flags to mark progress of decompression. */
+ char decompress_init;
+ char end_of_entry;
+ char end_of_entry_cleanup;
+ char entry_is_compressed;
+
+ char format_name[64];
+
+ struct lzh_stream strm;
+};
+
+/*
+ * LHA header common member offset.
+ */
+#define H_METHOD_OFFSET 2 /* Compress type. */
+#define H_ATTR_OFFSET 19 /* DOS attribute. */
+#define H_LEVEL_OFFSET 20 /* Header Level. */
+#define H_SIZE 22 /* Minimum header size. */
+
+static int archive_read_format_lha_bid(struct archive_read *, int);
+static int archive_read_format_lha_options(struct archive_read *,
+ const char *, const char *);
+static int archive_read_format_lha_read_header(struct archive_read *,
+ struct archive_entry *);
+static int archive_read_format_lha_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_lha_read_data_skip(struct archive_read *);
+static int archive_read_format_lha_cleanup(struct archive_read *);
+
+static void lha_replace_path_separator(struct lha *,
+ struct archive_entry *);
+static int lha_read_file_header_0(struct archive_read *, struct lha *);
+static int lha_read_file_header_1(struct archive_read *, struct lha *);
+static int lha_read_file_header_2(struct archive_read *, struct lha *);
+static int lha_read_file_header_3(struct archive_read *, struct lha *);
+static int lha_read_file_extended_header(struct archive_read *,
+ struct lha *, uint16_t *, int, size_t, size_t *);
+static size_t lha_check_header_format(const void *);
+static int lha_skip_sfx(struct archive_read *);
+static time_t lha_dos_time(const unsigned char *);
+static time_t lha_win_time(uint64_t, long *);
+static unsigned char lha_calcsum(unsigned char, const void *,
+ int, size_t);
+static int lha_parse_linkname(struct archive_wstring *,
+ struct archive_wstring *);
+static int lha_read_data_none(struct archive_read *, const void **,
+ size_t *, int64_t *);
+static int lha_read_data_lzh(struct archive_read *, const void **,
+ size_t *, int64_t *);
+static void lha_crc16_init(void);
+static uint16_t lha_crc16(uint16_t, const void *, size_t);
+static int lzh_decode_init(struct lzh_stream *, const char *);
+static void lzh_decode_free(struct lzh_stream *);
+static int lzh_decode(struct lzh_stream *, int);
+static int lzh_br_fillup(struct lzh_stream *, struct lzh_br *);
+static int lzh_huffman_init(struct huffman *, size_t, int);
+static void lzh_huffman_free(struct huffman *);
+static int lzh_read_pt_bitlen(struct lzh_stream *, int start, int end);
+static int lzh_make_fake_table(struct huffman *, uint16_t);
+static int lzh_make_huffman_table(struct huffman *);
+static inline int lzh_decode_huffman(struct huffman *, unsigned);
+static int lzh_decode_huffman_tree(struct huffman *, unsigned, int);
+
+
+int
+archive_read_support_format_lha(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct lha *lha;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_lha");
+
+ lha = (struct lha *)calloc(1, sizeof(*lha));
+ if (lha == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate lha data");
+ return (ARCHIVE_FATAL);
+ }
+ archive_string_init(&lha->ws);
+
+ r = __archive_read_register_format(a,
+ lha,
+ "lha",
+ archive_read_format_lha_bid,
+ archive_read_format_lha_options,
+ archive_read_format_lha_read_header,
+ archive_read_format_lha_read_data,
+ archive_read_format_lha_read_data_skip,
+ NULL,
+ archive_read_format_lha_cleanup,
+ NULL,
+ NULL);
+
+ if (r != ARCHIVE_OK)
+ free(lha);
+ return (ARCHIVE_OK);
+}
+
+static size_t
+lha_check_header_format(const void *h)
+{
+ const unsigned char *p = h;
+ size_t next_skip_bytes;
+
+ switch (p[H_METHOD_OFFSET+3]) {
+ /*
+ * "-lh0-" ... "-lh7-" "-lhd-"
+ * "-lzs-" "-lz5-"
+ */
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case 'd':
+ case 's':
+ next_skip_bytes = 4;
+
+ /* b0 == 0 means the end of an LHa archive file. */
+ if (p[0] == 0)
+ break;
+ if (p[H_METHOD_OFFSET] != '-' || p[H_METHOD_OFFSET+1] != 'l'
+ || p[H_METHOD_OFFSET+4] != '-')
+ break;
+
+ if (p[H_METHOD_OFFSET+2] == 'h') {
+ /* "-lh?-" */
+ if (p[H_METHOD_OFFSET+3] == 's')
+ break;
+ if (p[H_LEVEL_OFFSET] == 0)
+ return (0);
+ if (p[H_LEVEL_OFFSET] <= 3 && p[H_ATTR_OFFSET] == 0x20)
+ return (0);
+ }
+ if (p[H_METHOD_OFFSET+2] == 'z') {
+ /* LArc extensions: -lzs-,-lz4- and -lz5- */
+ if (p[H_LEVEL_OFFSET] != 0)
+ break;
+ if (p[H_METHOD_OFFSET+3] == 's'
+ || p[H_METHOD_OFFSET+3] == '4'
+ || p[H_METHOD_OFFSET+3] == '5')
+ return (0);
+ }
+ break;
+ case 'h': next_skip_bytes = 1; break;
+ case 'z': next_skip_bytes = 1; break;
+ case 'l': next_skip_bytes = 2; break;
+ case '-': next_skip_bytes = 3; break;
+ default : next_skip_bytes = 4; break;
+ }
+
+ return (next_skip_bytes);
+}
+
+static int
+archive_read_format_lha_bid(struct archive_read *a, int best_bid)
+{
+ const char *p;
+ const void *buff;
+ ssize_t bytes_avail, offset, window;
+ size_t next;
+
+ /* If there's already a better bid than we can ever
+ make, don't bother testing. */
+ if (best_bid > 30)
+ return (-1);
+
+ if ((p = __archive_read_ahead(a, H_SIZE, NULL)) == NULL)
+ return (-1);
+
+ if (lha_check_header_format(p) == 0)
+ return (30);
+
+ if (p[0] == 'M' && p[1] == 'Z') {
+ /* PE file */
+ offset = 0;
+ window = 4096;
+ while (offset < (1024 * 20)) {
+ buff = __archive_read_ahead(a, offset + window,
+ &bytes_avail);
+ if (buff == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < (H_SIZE + 3))
+ return (0);
+ continue;
+ }
+ p = (const char *)buff + offset;
+ while (p + H_SIZE < (const char *)buff + bytes_avail) {
+ if ((next = lha_check_header_format(p)) == 0)
+ return (30);
+ p += next;
+ }
+ offset = p - (const char *)buff;
+ }
+ }
+ return (0);
+}
+
+static int
+archive_read_format_lha_options(struct archive_read *a,
+ const char *key, const char *val)
+{
+ struct lha *lha;
+ int ret = ARCHIVE_FAILED;
+
+ lha = (struct lha *)(a->format->data);
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "lha: hdrcharset option needs a character-set name");
+ else {
+ lha->opt_sconv =
+ archive_string_conversion_from_charset(
+ &a->archive, val, 0);
+ if (lha->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+lha_skip_sfx(struct archive_read *a)
+{
+ const void *h;
+ const char *p, *q;
+ size_t next, skip;
+ ssize_t bytes, window;
+
+ window = 4096;
+ for (;;) {
+ h = __archive_read_ahead(a, window, &bytes);
+ if (h == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < (H_SIZE + 3))
+ goto fatal;
+ continue;
+ }
+ if (bytes < H_SIZE)
+ goto fatal;
+ p = h;
+ q = p + bytes;
+
+ /*
+ * Scan ahead until we find something that looks
+ * like the lha header.
+ */
+ while (p + H_SIZE < q) {
+ if ((next = lha_check_header_format(p)) == 0) {
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ return (ARCHIVE_OK);
+ }
+ p += next;
+ }
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ }
+fatal:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Couldn't find out LHa header");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+truncated_error(struct archive_read *a)
+{
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated LHa header");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+archive_read_format_lha_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct archive_wstring linkname;
+ struct archive_wstring pathname;
+ struct lha *lha;
+ const unsigned char *p;
+ const char *signature;
+ int err;
+ struct archive_mstring conv_buffer;
+ const wchar_t *conv_buffer_p;
+
+ lha_crc16_init();
+
+ a->archive.archive_format = ARCHIVE_FORMAT_LHA;
+ if (a->archive.archive_format_name == NULL)
+ a->archive.archive_format_name = "lha";
+
+ lha = (struct lha *)(a->format->data);
+ lha->decompress_init = 0;
+ lha->end_of_entry = 0;
+ lha->end_of_entry_cleanup = 0;
+ lha->entry_unconsumed = 0;
+
+ if ((p = __archive_read_ahead(a, H_SIZE, NULL)) == NULL) {
+ /*
+ * LHa archiver added 0 to the tail of its archive file as
+ * the mark of the end of the archive.
+ */
+ signature = __archive_read_ahead(a, sizeof(signature[0]), NULL);
+ if (signature == NULL || signature[0] == 0)
+ return (ARCHIVE_EOF);
+ return (truncated_error(a));
+ }
+
+ signature = (const char *)p;
+ if (lha->found_first_header == 0 &&
+ signature[0] == 'M' && signature[1] == 'Z') {
+ /* This is an executable? Must be self-extracting... */
+ err = lha_skip_sfx(a);
+ if (err < ARCHIVE_WARN)
+ return (err);
+
+ if ((p = __archive_read_ahead(a, sizeof(*p), NULL)) == NULL)
+ return (truncated_error(a));
+ signature = (const char *)p;
+ }
+ /* signature[0] == 0 means the end of an LHa archive file. */
+ if (signature[0] == 0)
+ return (ARCHIVE_EOF);
+
+ /*
+ * Check the header format and method type.
+ */
+ if (lha_check_header_format(p) != 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Bad LHa file");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* We've found the first header. */
+ lha->found_first_header = 1;
+ /* Set a default value and common data */
+ lha->header_size = 0;
+ lha->level = p[H_LEVEL_OFFSET];
+ lha->method[0] = p[H_METHOD_OFFSET+1];
+ lha->method[1] = p[H_METHOD_OFFSET+2];
+ lha->method[2] = p[H_METHOD_OFFSET+3];
+ if (memcmp(lha->method, "lhd", 3) == 0)
+ lha->directory = 1;
+ else
+ lha->directory = 0;
+ if (memcmp(lha->method, "lh0", 3) == 0 ||
+ memcmp(lha->method, "lz4", 3) == 0)
+ lha->entry_is_compressed = 0;
+ else
+ lha->entry_is_compressed = 1;
+
+ lha->compsize = 0;
+ lha->origsize = 0;
+ lha->setflag = 0;
+ lha->birthtime = 0;
+ lha->birthtime_tv_nsec = 0;
+ lha->mtime = 0;
+ lha->mtime_tv_nsec = 0;
+ lha->atime = 0;
+ lha->atime_tv_nsec = 0;
+ lha->mode = (lha->directory)? 0777 : 0666;
+ lha->uid = 0;
+ lha->gid = 0;
+ archive_string_empty(&lha->dirname);
+ archive_string_empty(&lha->filename);
+ lha->dos_attr = 0;
+ if (lha->opt_sconv != NULL) {
+ lha->sconv_dir = lha->opt_sconv;
+ lha->sconv_fname = lha->opt_sconv;
+ } else {
+ lha->sconv_dir = NULL;
+ lha->sconv_fname = NULL;
+ }
+
+ switch (p[H_LEVEL_OFFSET]) {
+ case 0:
+ err = lha_read_file_header_0(a, lha);
+ break;
+ case 1:
+ err = lha_read_file_header_1(a, lha);
+ break;
+ case 2:
+ err = lha_read_file_header_2(a, lha);
+ break;
+ case 3:
+ err = lha_read_file_header_3(a, lha);
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported LHa header level %d", p[H_LEVEL_OFFSET]);
+ err = ARCHIVE_FATAL;
+ break;
+ }
+ if (err < ARCHIVE_WARN)
+ return (err);
+
+
+ if (!lha->directory && archive_strlen(&lha->filename) == 0)
+ /* The filename has not been set */
+ return (truncated_error(a));
+
+ /*
+ * Make a pathname from a dirname and a filename, after converting to Unicode.
+ * This is because codepages might differ between dirname and filename.
+ */
+ archive_string_init(&pathname);
+ archive_string_init(&linkname);
+ archive_string_init(&conv_buffer.aes_mbs);
+ archive_string_init(&conv_buffer.aes_mbs_in_locale);
+ archive_string_init(&conv_buffer.aes_utf8);
+ archive_string_init(&conv_buffer.aes_wcs);
+ if (0 != archive_mstring_copy_mbs_len_l(&conv_buffer, lha->dirname.s, lha->dirname.length, lha->sconv_dir)) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname cannot be converted "
+ "from %s to Unicode.",
+ archive_string_conversion_charset_name(lha->sconv_dir));
+ err = ARCHIVE_FATAL;
+ } else if (0 != archive_mstring_get_wcs(&a->archive, &conv_buffer, &conv_buffer_p))
+ err = ARCHIVE_FATAL;
+ if (err == ARCHIVE_FATAL) {
+ archive_mstring_clean(&conv_buffer);
+ archive_wstring_free(&pathname);
+ archive_wstring_free(&linkname);
+ return (err);
+ }
+ archive_wstring_copy(&pathname, &conv_buffer.aes_wcs);
+
+ archive_string_empty(&conv_buffer.aes_mbs);
+ archive_string_empty(&conv_buffer.aes_mbs_in_locale);
+ archive_string_empty(&conv_buffer.aes_utf8);
+ archive_wstring_empty(&conv_buffer.aes_wcs);
+ if (0 != archive_mstring_copy_mbs_len_l(&conv_buffer, lha->filename.s, lha->filename.length, lha->sconv_fname)) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname cannot be converted "
+ "from %s to Unicode.",
+ archive_string_conversion_charset_name(lha->sconv_fname));
+ err = ARCHIVE_FATAL;
+ }
+ else if (0 != archive_mstring_get_wcs(&a->archive, &conv_buffer, &conv_buffer_p))
+ err = ARCHIVE_FATAL;
+ if (err == ARCHIVE_FATAL) {
+ archive_mstring_clean(&conv_buffer);
+ archive_wstring_free(&pathname);
+ archive_wstring_free(&linkname);
+ return (err);
+ }
+ archive_wstring_concat(&pathname, &conv_buffer.aes_wcs);
+ archive_mstring_clean(&conv_buffer);
+
+ if ((lha->mode & AE_IFMT) == AE_IFLNK) {
+ /*
+ * Extract the symlink-name if it's included in the pathname.
+ */
+ if (!lha_parse_linkname(&linkname, &pathname)) {
+ /* We couldn't get the symlink-name. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unknown symlink-name");
+ archive_wstring_free(&pathname);
+ archive_wstring_free(&linkname);
+ return (ARCHIVE_FAILED);
+ }
+ } else {
+ /*
+ * Make sure a file-type is set.
+ * The mode has been overridden if it is in the extended data.
+ */
+ lha->mode = (lha->mode & ~AE_IFMT) |
+ ((lha->directory)? AE_IFDIR: AE_IFREG);
+ }
+ if ((lha->setflag & UNIX_MODE_IS_SET) == 0 &&
+ (lha->dos_attr & 1) != 0)
+ lha->mode &= ~(0222);/* read only. */
+
+ /*
+ * Set basic file parameters.
+ */
+ archive_entry_copy_pathname_w(entry, pathname.s);
+ archive_wstring_free(&pathname);
+ if (archive_strlen(&linkname) > 0) {
+ archive_entry_copy_symlink_w(entry, linkname.s);
+ } else
+ archive_entry_set_symlink(entry, NULL);
+ archive_wstring_free(&linkname);
+ /*
+ * When a header level is 0, there is a possibility that
+ * a pathname and a symlink has '\' character, a directory
+ * separator in DOS/Windows. So we should convert it to '/'.
+ */
+ if (p[H_LEVEL_OFFSET] == 0)
+ lha_replace_path_separator(lha, entry);
+
+ archive_entry_set_mode(entry, lha->mode);
+ archive_entry_set_uid(entry, lha->uid);
+ archive_entry_set_gid(entry, lha->gid);
+ if (archive_strlen(&lha->uname) > 0)
+ archive_entry_set_uname(entry, lha->uname.s);
+ if (archive_strlen(&lha->gname) > 0)
+ archive_entry_set_gname(entry, lha->gname.s);
+ if (lha->setflag & BIRTHTIME_IS_SET) {
+ archive_entry_set_birthtime(entry, lha->birthtime,
+ lha->birthtime_tv_nsec);
+ archive_entry_set_ctime(entry, lha->birthtime,
+ lha->birthtime_tv_nsec);
+ } else {
+ archive_entry_unset_birthtime(entry);
+ archive_entry_unset_ctime(entry);
+ }
+ archive_entry_set_mtime(entry, lha->mtime, lha->mtime_tv_nsec);
+ if (lha->setflag & ATIME_IS_SET)
+ archive_entry_set_atime(entry, lha->atime,
+ lha->atime_tv_nsec);
+ else
+ archive_entry_unset_atime(entry);
+ if (lha->directory || archive_entry_symlink(entry) != NULL)
+ archive_entry_unset_size(entry);
+ else
+ archive_entry_set_size(entry, lha->origsize);
+
+ /*
+ * Prepare variables used to read a file content.
+ */
+ lha->entry_bytes_remaining = lha->compsize;
+ if (lha->entry_bytes_remaining < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid LHa entry size");
+ return (ARCHIVE_FATAL);
+ }
+ lha->entry_offset = 0;
+ lha->entry_crc_calculated = 0;
+
+ /*
+ * This file does not have a content.
+ */
+ if (lha->directory || lha->compsize == 0)
+ lha->end_of_entry = 1;
+
+ snprintf(lha->format_name, sizeof(lha->format_name), "lha -%c%c%c-",
+ lha->method[0], lha->method[1], lha->method[2]);
+ a->archive.archive_format_name = lha->format_name;
+
+ return (err);
+}
+
+/*
+ * Replace a DOS path separator '\' by a character '/'.
+ * Some multi-byte character set have a character '\' in its second byte.
+ */
+static void
+lha_replace_path_separator(struct lha *lha, struct archive_entry *entry)
+{
+ const wchar_t *wp;
+ size_t i;
+
+ if ((wp = archive_entry_pathname_w(entry)) != NULL) {
+ archive_wstrcpy(&(lha->ws), wp);
+ for (i = 0; i < archive_strlen(&(lha->ws)); i++) {
+ if (lha->ws.s[i] == L'\\')
+ lha->ws.s[i] = L'/';
+ }
+ archive_entry_copy_pathname_w(entry, lha->ws.s);
+ }
+
+ if ((wp = archive_entry_symlink_w(entry)) != NULL) {
+ archive_wstrcpy(&(lha->ws), wp);
+ for (i = 0; i < archive_strlen(&(lha->ws)); i++) {
+ if (lha->ws.s[i] == L'\\')
+ lha->ws.s[i] = L'/';
+ }
+ archive_entry_copy_symlink_w(entry, lha->ws.s);
+ }
+}
+
+/*
+ * Header 0 format
+ *
+ * +0 +1 +2 +7 +11
+ * +---------------+----------+----------------+-------------------+
+ * |header size(*1)|header sum|compression type|compressed size(*2)|
+ * +---------------+----------+----------------+-------------------+
+ * <---------------------(*1)----------*
+ *
+ * +11 +15 +17 +19 +20 +21
+ * +-----------------+---------+---------+--------------+----------------+
+ * |uncompressed size|time(DOS)|date(DOS)|attribute(DOS)|header level(=0)|
+ * +-----------------+---------+---------+--------------+----------------+
+ * *--------------------------------(*1)---------------------------------*
+ *
+ * +21 +22 +22+(*3) +22+(*3)+2 +22+(*3)+2+(*4)
+ * +---------------+---------+----------+----------------+------------------+
+ * |name length(*3)|file name|file CRC16|extra header(*4)| compressed data |
+ * +---------------+---------+----------+----------------+------------------+
+ * <--(*3)-> <------(*2)------>
+ * *----------------------(*1)-------------------------->
+ *
+ */
+#define H0_HEADER_SIZE_OFFSET 0
+#define H0_HEADER_SUM_OFFSET 1
+#define H0_COMP_SIZE_OFFSET 7
+#define H0_ORIG_SIZE_OFFSET 11
+#define H0_DOS_TIME_OFFSET 15
+#define H0_NAME_LEN_OFFSET 21
+#define H0_FILE_NAME_OFFSET 22
+#define H0_FIXED_SIZE 24
+static int
+lha_read_file_header_0(struct archive_read *a, struct lha *lha)
+{
+ const unsigned char *p;
+ int extdsize, namelen;
+ unsigned char headersum, sum_calculated;
+
+ if ((p = __archive_read_ahead(a, H0_FIXED_SIZE, NULL)) == NULL)
+ return (truncated_error(a));
+ lha->header_size = p[H0_HEADER_SIZE_OFFSET] + 2;
+ headersum = p[H0_HEADER_SUM_OFFSET];
+ lha->compsize = archive_le32dec(p + H0_COMP_SIZE_OFFSET);
+ lha->origsize = archive_le32dec(p + H0_ORIG_SIZE_OFFSET);
+ lha->mtime = lha_dos_time(p + H0_DOS_TIME_OFFSET);
+ namelen = p[H0_NAME_LEN_OFFSET];
+ extdsize = (int)lha->header_size - H0_FIXED_SIZE - namelen;
+ if ((namelen > 221 || extdsize < 0) && extdsize != -2) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid LHa header");
+ return (ARCHIVE_FATAL);
+ }
+ if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL)
+ return (truncated_error(a));
+
+ archive_strncpy(&lha->filename, p + H0_FILE_NAME_OFFSET, namelen);
+ /* When extdsize == -2, A CRC16 value is not present in the header. */
+ if (extdsize >= 0) {
+ lha->crc = archive_le16dec(p + H0_FILE_NAME_OFFSET + namelen);
+ lha->setflag |= CRC_IS_SET;
+ }
+ sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2);
+
+ /* Read an extended header */
+ if (extdsize > 0) {
+ /* This extended data is set by 'LHa for UNIX' only.
+ * Maybe fixed size.
+ */
+ p += H0_FILE_NAME_OFFSET + namelen + 2;
+ if (p[0] == 'U' && extdsize == 12) {
+ /* p[1] is a minor version. */
+ lha->mtime = archive_le32dec(&p[2]);
+ lha->mode = archive_le16dec(&p[6]);
+ lha->uid = archive_le16dec(&p[8]);
+ lha->gid = archive_le16dec(&p[10]);
+ lha->setflag |= UNIX_MODE_IS_SET;
+ }
+ }
+ __archive_read_consume(a, lha->header_size);
+
+ if (sum_calculated != headersum) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "LHa header sum error");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Header 1 format
+ *
+ * +0 +1 +2 +7 +11
+ * +---------------+----------+----------------+-------------+
+ * |header size(*1)|header sum|compression type|skip size(*2)|
+ * +---------------+----------+----------------+-------------+
+ * <---------------(*1)----------*
+ *
+ * +11 +15 +17 +19 +20 +21
+ * +-----------------+---------+---------+--------------+----------------+
+ * |uncompressed size|time(DOS)|date(DOS)|attribute(DOS)|header level(=1)|
+ * +-----------------+---------+---------+--------------+----------------+
+ * *-------------------------------(*1)----------------------------------*
+ *
+ * +21 +22 +22+(*3) +22+(*3)+2 +22+(*3)+3 +22+(*3)+3+(*4)
+ * +---------------+---------+----------+-----------+-----------+
+ * |name length(*3)|file name|file CRC16| creator |padding(*4)|
+ * +---------------+---------+----------+-----------+-----------+
+ * <--(*3)->
+ * *----------------------------(*1)----------------------------*
+ *
+ * +22+(*3)+3+(*4) +22+(*3)+3+(*4)+2 +22+(*3)+3+(*4)+2+(*5)
+ * +----------------+---------------------+------------------------+
+ * |next header size| extended header(*5) | compressed data |
+ * +----------------+---------------------+------------------------+
+ * *------(*1)-----> <--------------------(*2)-------------------->
+ */
+#define H1_HEADER_SIZE_OFFSET 0
+#define H1_HEADER_SUM_OFFSET 1
+#define H1_COMP_SIZE_OFFSET 7
+#define H1_ORIG_SIZE_OFFSET 11
+#define H1_DOS_TIME_OFFSET 15
+#define H1_NAME_LEN_OFFSET 21
+#define H1_FILE_NAME_OFFSET 22
+#define H1_FIXED_SIZE 27
+static int
+lha_read_file_header_1(struct archive_read *a, struct lha *lha)
+{
+ const unsigned char *p;
+ size_t extdsize;
+ int i, err, err2;
+ int namelen, padding;
+ unsigned char headersum, sum_calculated;
+
+ err = ARCHIVE_OK;
+
+ if ((p = __archive_read_ahead(a, H1_FIXED_SIZE, NULL)) == NULL)
+ return (truncated_error(a));
+
+ lha->header_size = p[H1_HEADER_SIZE_OFFSET] + 2;
+ headersum = p[H1_HEADER_SUM_OFFSET];
+ /* Note: An extended header size is included in a compsize. */
+ lha->compsize = archive_le32dec(p + H1_COMP_SIZE_OFFSET);
+ lha->origsize = archive_le32dec(p + H1_ORIG_SIZE_OFFSET);
+ lha->mtime = lha_dos_time(p + H1_DOS_TIME_OFFSET);
+ namelen = p[H1_NAME_LEN_OFFSET];
+ /* Calculate a padding size. The result will be normally 0 only(?) */
+ padding = ((int)lha->header_size) - H1_FIXED_SIZE - namelen;
+
+ if (namelen > 230 || padding < 0)
+ goto invalid;
+
+ if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL)
+ return (truncated_error(a));
+
+ for (i = 0; i < namelen; i++) {
+ if (p[i + H1_FILE_NAME_OFFSET] == 0xff)
+ goto invalid;/* Invalid filename. */
+ }
+ archive_strncpy(&lha->filename, p + H1_FILE_NAME_OFFSET, namelen);
+ lha->crc = archive_le16dec(p + H1_FILE_NAME_OFFSET + namelen);
+ lha->setflag |= CRC_IS_SET;
+
+ sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2);
+ /* Consume used bytes but not include `next header size' data
+ * since it will be consumed in lha_read_file_extended_header(). */
+ __archive_read_consume(a, lha->header_size - 2);
+
+ /* Read extended headers */
+ err2 = lha_read_file_extended_header(a, lha, NULL, 2,
+ (size_t)(lha->compsize + 2), &extdsize);
+ if (err2 < ARCHIVE_WARN)
+ return (err2);
+ if (err2 < err)
+ err = err2;
+ /* Get a real compressed file size. */
+ lha->compsize -= extdsize - 2;
+
+ if (lha->compsize < 0)
+ goto invalid; /* Invalid compressed file size */
+
+ if (sum_calculated != headersum) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "LHa header sum error");
+ return (ARCHIVE_FATAL);
+ }
+ return (err);
+invalid:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid LHa header");
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Header 2 format
+ *
+ * +0 +2 +7 +11 +15
+ * +---------------+----------------+-------------------+-----------------+
+ * |header size(*1)|compression type|compressed size(*2)|uncompressed size|
+ * +---------------+----------------+-------------------+-----------------+
+ * <--------------------------------(*1)---------------------------------*
+ *
+ * +15 +19 +20 +21 +23 +24
+ * +-----------------+------------+----------------+----------+-----------+
+ * |data/time(time_t)| 0x20 fixed |header level(=2)|file CRC16| creator |
+ * +-----------------+------------+----------------+----------+-----------+
+ * *---------------------------------(*1)---------------------------------*
+ *
+ * +24 +26 +26+(*3) +26+(*3)+(*4)
+ * +----------------+-------------------+-------------+-------------------+
+ * |next header size|extended header(*3)| padding(*4) | compressed data |
+ * +----------------+-------------------+-------------+-------------------+
+ * *--------------------------(*1)-------------------> <------(*2)------->
+ *
+ */
+#define H2_HEADER_SIZE_OFFSET 0
+#define H2_COMP_SIZE_OFFSET 7
+#define H2_ORIG_SIZE_OFFSET 11
+#define H2_TIME_OFFSET 15
+#define H2_CRC_OFFSET 21
+#define H2_FIXED_SIZE 24
+static int
+lha_read_file_header_2(struct archive_read *a, struct lha *lha)
+{
+ const unsigned char *p;
+ size_t extdsize;
+ int err, padding;
+ uint16_t header_crc;
+
+ if ((p = __archive_read_ahead(a, H2_FIXED_SIZE, NULL)) == NULL)
+ return (truncated_error(a));
+
+ lha->header_size =archive_le16dec(p + H2_HEADER_SIZE_OFFSET);
+ lha->compsize = archive_le32dec(p + H2_COMP_SIZE_OFFSET);
+ lha->origsize = archive_le32dec(p + H2_ORIG_SIZE_OFFSET);
+ lha->mtime = archive_le32dec(p + H2_TIME_OFFSET);
+ lha->crc = archive_le16dec(p + H2_CRC_OFFSET);
+ lha->setflag |= CRC_IS_SET;
+
+ if (lha->header_size < H2_FIXED_SIZE) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid LHa header size");
+ return (ARCHIVE_FATAL);
+ }
+
+ header_crc = lha_crc16(0, p, H2_FIXED_SIZE);
+ __archive_read_consume(a, H2_FIXED_SIZE);
+
+ /* Read extended headers */
+ err = lha_read_file_extended_header(a, lha, &header_crc, 2,
+ lha->header_size - H2_FIXED_SIZE, &extdsize);
+ if (err < ARCHIVE_WARN)
+ return (err);
+
+ /* Calculate a padding size. The result will be normally 0 or 1. */
+ padding = (int)lha->header_size - (int)(H2_FIXED_SIZE + extdsize);
+ if (padding > 0) {
+ if ((p = __archive_read_ahead(a, padding, NULL)) == NULL)
+ return (truncated_error(a));
+ header_crc = lha_crc16(header_crc, p, padding);
+ __archive_read_consume(a, padding);
+ }
+
+ if (header_crc != lha->header_crc) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "LHa header CRC error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ return (err);
+}
+
+/*
+ * Header 3 format
+ *
+ * +0 +2 +7 +11 +15
+ * +------------+----------------+-------------------+-----------------+
+ * | 0x04 fixed |compression type|compressed size(*2)|uncompressed size|
+ * +------------+----------------+-------------------+-----------------+
+ * <-------------------------------(*1)-------------------------------*
+ *
+ * +15 +19 +20 +21 +23 +24
+ * +-----------------+------------+----------------+----------+-----------+
+ * |date/time(time_t)| 0x20 fixed |header level(=3)|file CRC16| creator |
+ * +-----------------+------------+----------------+----------+-----------+
+ * *--------------------------------(*1)----------------------------------*
+ *
+ * +24 +28 +32 +32+(*3)
+ * +---------------+----------------+-------------------+-----------------+
+ * |header size(*1)|next header size|extended header(*3)| compressed data |
+ * +---------------+----------------+-------------------+-----------------+
+ * *------------------------(*1)-----------------------> <------(*2)----->
+ *
+ */
+#define H3_FIELD_LEN_OFFSET 0
+#define H3_COMP_SIZE_OFFSET 7
+#define H3_ORIG_SIZE_OFFSET 11
+#define H3_TIME_OFFSET 15
+#define H3_CRC_OFFSET 21
+#define H3_HEADER_SIZE_OFFSET 24
+#define H3_FIXED_SIZE 28
+static int
+lha_read_file_header_3(struct archive_read *a, struct lha *lha)
+{
+ const unsigned char *p;
+ size_t extdsize;
+ int err;
+ uint16_t header_crc;
+
+ if ((p = __archive_read_ahead(a, H3_FIXED_SIZE, NULL)) == NULL)
+ return (truncated_error(a));
+
+ if (archive_le16dec(p + H3_FIELD_LEN_OFFSET) != 4)
+ goto invalid;
+ lha->header_size =archive_le32dec(p + H3_HEADER_SIZE_OFFSET);
+ lha->compsize = archive_le32dec(p + H3_COMP_SIZE_OFFSET);
+ lha->origsize = archive_le32dec(p + H3_ORIG_SIZE_OFFSET);
+ lha->mtime = archive_le32dec(p + H3_TIME_OFFSET);
+ lha->crc = archive_le16dec(p + H3_CRC_OFFSET);
+ lha->setflag |= CRC_IS_SET;
+
+ if (lha->header_size < H3_FIXED_SIZE + 4)
+ goto invalid;
+ header_crc = lha_crc16(0, p, H3_FIXED_SIZE);
+ __archive_read_consume(a, H3_FIXED_SIZE);
+
+ /* Read extended headers */
+ err = lha_read_file_extended_header(a, lha, &header_crc, 4,
+ lha->header_size - H3_FIXED_SIZE, &extdsize);
+ if (err < ARCHIVE_WARN)
+ return (err);
+
+ if (header_crc != lha->header_crc) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "LHa header CRC error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ return (err);
+invalid:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid LHa header");
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Extended header format
+ *
+ * +0 +2 +3 -- used in header 1 and 2
+ * +0 +4 +5 -- used in header 3
+ * +--------------+---------+-------------------+--------------+--
+ * |ex-header size|header id| data |ex-header size| .......
+ * +--------------+---------+-------------------+--------------+--
+ * <-------------( ex-header size)------------> <-- next extended header --*
+ *
+ * If the ex-header size is zero, it is the make of the end of extended
+ * headers.
+ *
+ */
+static int
+lha_read_file_extended_header(struct archive_read *a, struct lha *lha,
+ uint16_t *crc, int sizefield_length, size_t limitsize, size_t *total_size)
+{
+ const void *h;
+ const unsigned char *extdheader;
+ size_t extdsize;
+ size_t datasize;
+ unsigned int i;
+ unsigned char extdtype;
+
+#define EXT_HEADER_CRC 0x00 /* Header CRC and information*/
+#define EXT_FILENAME 0x01 /* Filename */
+#define EXT_DIRECTORY 0x02 /* Directory name */
+#define EXT_DOS_ATTR 0x40 /* MS-DOS attribute */
+#define EXT_TIMESTAMP 0x41 /* Windows time stamp */
+#define EXT_FILESIZE 0x42 /* Large file size */
+#define EXT_TIMEZONE 0x43 /* Time zone */
+#define EXT_UTF16_FILENAME 0x44 /* UTF-16 filename */
+#define EXT_UTF16_DIRECTORY 0x45 /* UTF-16 directory name */
+#define EXT_CODEPAGE 0x46 /* Codepage */
+#define EXT_UNIX_MODE 0x50 /* File permission */
+#define EXT_UNIX_GID_UID 0x51 /* gid,uid */
+#define EXT_UNIX_GNAME 0x52 /* Group name */
+#define EXT_UNIX_UNAME 0x53 /* User name */
+#define EXT_UNIX_MTIME 0x54 /* Modified time */
+#define EXT_OS2_NEW_ATTR 0x7f /* new attribute(OS/2 only) */
+#define EXT_NEW_ATTR 0xff /* new attribute */
+
+ *total_size = sizefield_length;
+
+ for (;;) {
+ /* Read an extended header size. */
+ if ((h =
+ __archive_read_ahead(a, sizefield_length, NULL)) == NULL)
+ return (truncated_error(a));
+ /* Check if the size is the zero indicates the end of the
+ * extended header. */
+ if (sizefield_length == sizeof(uint16_t))
+ extdsize = archive_le16dec(h);
+ else
+ extdsize = archive_le32dec(h);
+ if (extdsize == 0) {
+ /* End of extended header */
+ if (crc != NULL)
+ *crc = lha_crc16(*crc, h, sizefield_length);
+ __archive_read_consume(a, sizefield_length);
+ return (ARCHIVE_OK);
+ }
+
+ /* Sanity check to the extended header size. */
+ if (((uint64_t)*total_size + extdsize) >
+ (uint64_t)limitsize ||
+ extdsize <= (size_t)sizefield_length)
+ goto invalid;
+
+ /* Read the extended header. */
+ if ((h = __archive_read_ahead(a, extdsize, NULL)) == NULL)
+ return (truncated_error(a));
+ *total_size += extdsize;
+
+ extdheader = (const unsigned char *)h;
+ /* Get the extended header type. */
+ extdtype = extdheader[sizefield_length];
+ /* Calculate an extended data size. */
+ datasize = extdsize - (1 + sizefield_length);
+ /* Skip an extended header size field and type field. */
+ extdheader += sizefield_length + 1;
+
+ if (crc != NULL && extdtype != EXT_HEADER_CRC)
+ *crc = lha_crc16(*crc, h, extdsize);
+ switch (extdtype) {
+ case EXT_HEADER_CRC:
+ /* We only use a header CRC. Following data will not
+ * be used. */
+ if (datasize >= 2) {
+ lha->header_crc = archive_le16dec(extdheader);
+ if (crc != NULL) {
+ static const char zeros[2] = {0, 0};
+ *crc = lha_crc16(*crc, h,
+ extdsize - datasize);
+ /* CRC value itself as zero */
+ *crc = lha_crc16(*crc, zeros, 2);
+ *crc = lha_crc16(*crc,
+ extdheader+2, datasize - 2);
+ }
+ }
+ break;
+ case EXT_FILENAME:
+ if (datasize == 0) {
+ /* maybe directory header */
+ archive_string_empty(&lha->filename);
+ break;
+ }
+ if (extdheader[0] == '\0')
+ goto invalid;
+ archive_strncpy(&lha->filename,
+ (const char *)extdheader, datasize);
+ break;
+ case EXT_UTF16_FILENAME:
+ if (datasize == 0) {
+ /* maybe directory header */
+ archive_string_empty(&lha->filename);
+ break;
+ } else if (datasize & 1) {
+ /* UTF-16 characters take always 2 or 4 bytes */
+ goto invalid;
+ }
+ if (extdheader[0] == '\0')
+ goto invalid;
+ archive_string_empty(&lha->filename);
+ archive_array_append(&lha->filename,
+ (const char *)extdheader, datasize);
+ /* Setup a string conversion for a filename. */
+ lha->sconv_fname =
+ archive_string_conversion_from_charset(&a->archive,
+ "UTF-16LE", 1);
+ if (lha->sconv_fname == NULL)
+ return (ARCHIVE_FATAL);
+ break;
+ case EXT_DIRECTORY:
+ if (datasize == 0 || extdheader[0] == '\0')
+ /* no directory name data. exit this case. */
+ goto invalid;
+
+ archive_strncpy(&lha->dirname,
+ (const char *)extdheader, datasize);
+ /*
+ * Convert directory delimiter from 0xFF
+ * to '/' for local system.
+ */
+ for (i = 0; i < lha->dirname.length; i++) {
+ if ((unsigned char)lha->dirname.s[i] == 0xFF)
+ lha->dirname.s[i] = '/';
+ }
+ /* Is last character directory separator? */
+ if (lha->dirname.s[lha->dirname.length-1] != '/')
+ /* invalid directory data */
+ goto invalid;
+ break;
+ case EXT_UTF16_DIRECTORY:
+ /* UTF-16 characters take always 2 or 4 bytes */
+ if (datasize == 0 || (datasize & 1) ||
+ extdheader[0] == '\0') {
+ /* no directory name data. exit this case. */
+ goto invalid;
+ }
+
+ archive_string_empty(&lha->dirname);
+ archive_array_append(&lha->dirname,
+ (const char *)extdheader, datasize);
+ lha->sconv_dir =
+ archive_string_conversion_from_charset(&a->archive,
+ "UTF-16LE", 1);
+ if (lha->sconv_dir == NULL)
+ return (ARCHIVE_FATAL);
+ else {
+ /*
+ * Convert directory delimiter from 0xFFFF
+ * to '/' for local system.
+ */
+ uint16_t dirSep;
+ uint16_t d = 1;
+ if (archive_be16dec(&d) == 1)
+ dirSep = 0x2F00;
+ else
+ dirSep = 0x002F;
+
+ /* UTF-16LE character */
+ uint16_t *utf16name =
+ (uint16_t *)lha->dirname.s;
+ for (i = 0; i < lha->dirname.length / 2; i++) {
+ if (utf16name[i] == 0xFFFF) {
+ utf16name[i] = dirSep;
+ }
+ }
+ /* Is last character directory separator? */
+ if (utf16name[lha->dirname.length / 2 - 1] !=
+ dirSep) {
+ /* invalid directory data */
+ goto invalid;
+ }
+ }
+ break;
+ case EXT_DOS_ATTR:
+ if (datasize == 2)
+ lha->dos_attr = (unsigned char)
+ (archive_le16dec(extdheader) & 0xff);
+ break;
+ case EXT_TIMESTAMP:
+ if (datasize == (sizeof(uint64_t) * 3)) {
+ lha->birthtime = lha_win_time(
+ archive_le64dec(extdheader),
+ &lha->birthtime_tv_nsec);
+ extdheader += sizeof(uint64_t);
+ lha->mtime = lha_win_time(
+ archive_le64dec(extdheader),
+ &lha->mtime_tv_nsec);
+ extdheader += sizeof(uint64_t);
+ lha->atime = lha_win_time(
+ archive_le64dec(extdheader),
+ &lha->atime_tv_nsec);
+ lha->setflag |= BIRTHTIME_IS_SET |
+ ATIME_IS_SET;
+ }
+ break;
+ case EXT_FILESIZE:
+ if (datasize == sizeof(uint64_t) * 2) {
+ lha->compsize = archive_le64dec(extdheader);
+ extdheader += sizeof(uint64_t);
+ lha->origsize = archive_le64dec(extdheader);
+ }
+ break;
+ case EXT_CODEPAGE:
+ /* Get an archived filename charset from codepage.
+ * This overwrites the charset specified by
+ * hdrcharset option. */
+ if (datasize == sizeof(uint32_t)) {
+ struct archive_string cp;
+ const char *charset;
+
+ archive_string_init(&cp);
+ switch (archive_le32dec(extdheader)) {
+ case 65001: /* UTF-8 */
+ charset = "UTF-8";
+ break;
+ default:
+ archive_string_sprintf(&cp, "CP%d",
+ (int)archive_le32dec(extdheader));
+ charset = cp.s;
+ break;
+ }
+ lha->sconv_dir =
+ archive_string_conversion_from_charset(
+ &(a->archive), charset, 1);
+ lha->sconv_fname =
+ archive_string_conversion_from_charset(
+ &(a->archive), charset, 1);
+ archive_string_free(&cp);
+ if (lha->sconv_dir == NULL)
+ return (ARCHIVE_FATAL);
+ if (lha->sconv_fname == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ break;
+ case EXT_UNIX_MODE:
+ if (datasize == sizeof(uint16_t)) {
+ lha->mode = archive_le16dec(extdheader);
+ lha->setflag |= UNIX_MODE_IS_SET;
+ }
+ break;
+ case EXT_UNIX_GID_UID:
+ if (datasize == (sizeof(uint16_t) * 2)) {
+ lha->gid = archive_le16dec(extdheader);
+ lha->uid = archive_le16dec(extdheader+2);
+ }
+ break;
+ case EXT_UNIX_GNAME:
+ if (datasize > 0)
+ archive_strncpy(&lha->gname,
+ (const char *)extdheader, datasize);
+ break;
+ case EXT_UNIX_UNAME:
+ if (datasize > 0)
+ archive_strncpy(&lha->uname,
+ (const char *)extdheader, datasize);
+ break;
+ case EXT_UNIX_MTIME:
+ if (datasize == sizeof(uint32_t))
+ lha->mtime = archive_le32dec(extdheader);
+ break;
+ case EXT_OS2_NEW_ATTR:
+ /* This extended header is OS/2 depend. */
+ if (datasize == 16) {
+ lha->dos_attr = (unsigned char)
+ (archive_le16dec(extdheader) & 0xff);
+ lha->mode = archive_le16dec(extdheader+2);
+ lha->gid = archive_le16dec(extdheader+4);
+ lha->uid = archive_le16dec(extdheader+6);
+ lha->birthtime = archive_le32dec(extdheader+8);
+ lha->atime = archive_le32dec(extdheader+12);
+ lha->setflag |= UNIX_MODE_IS_SET
+ | BIRTHTIME_IS_SET | ATIME_IS_SET;
+ }
+ break;
+ case EXT_NEW_ATTR:
+ if (datasize == 20) {
+ lha->mode = (mode_t)archive_le32dec(extdheader);
+ lha->gid = archive_le32dec(extdheader+4);
+ lha->uid = archive_le32dec(extdheader+8);
+ lha->birthtime = archive_le32dec(extdheader+12);
+ lha->atime = archive_le32dec(extdheader+16);
+ lha->setflag |= UNIX_MODE_IS_SET
+ | BIRTHTIME_IS_SET | ATIME_IS_SET;
+ }
+ break;
+ case EXT_TIMEZONE: /* Not supported */
+ break;
+ default:
+ break;
+ }
+
+ __archive_read_consume(a, extdsize);
+ }
+invalid:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid extended LHa header");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+lha_end_of_entry(struct archive_read *a)
+{
+ struct lha *lha = (struct lha *)(a->format->data);
+ int r = ARCHIVE_EOF;
+
+ if (!lha->end_of_entry_cleanup) {
+ if ((lha->setflag & CRC_IS_SET) &&
+ lha->crc != lha->entry_crc_calculated) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "LHa data CRC error");
+ r = ARCHIVE_WARN;
+ }
+
+ /* End-of-entry cleanup done. */
+ lha->end_of_entry_cleanup = 1;
+ }
+ return (r);
+}
+
+static int
+archive_read_format_lha_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ struct lha *lha = (struct lha *)(a->format->data);
+ int r;
+
+ if (lha->entry_unconsumed) {
+ /* Consume as much as the decompressor actually used. */
+ __archive_read_consume(a, lha->entry_unconsumed);
+ lha->entry_unconsumed = 0;
+ }
+ if (lha->end_of_entry) {
+ *offset = lha->entry_offset;
+ *size = 0;
+ *buff = NULL;
+ return (lha_end_of_entry(a));
+ }
+
+ if (lha->entry_is_compressed)
+ r = lha_read_data_lzh(a, buff, size, offset);
+ else
+ /* No compression. */
+ r = lha_read_data_none(a, buff, size, offset);
+ return (r);
+}
+
+/*
+ * Read a file content in no compression.
+ *
+ * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets
+ * lha->end_of_entry if it consumes all of the data.
+ */
+static int
+lha_read_data_none(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct lha *lha = (struct lha *)(a->format->data);
+ ssize_t bytes_avail;
+
+ if (lha->entry_bytes_remaining == 0) {
+ *buff = NULL;
+ *size = 0;
+ *offset = lha->entry_offset;
+ lha->end_of_entry = 1;
+ return (ARCHIVE_OK);
+ }
+ /*
+ * Note: '1' here is a performance optimization.
+ * Recall that the decompression layer returns a count of
+ * available bytes; asking for more than that forces the
+ * decompressor to combine reads by copying data.
+ */
+ *buff = __archive_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail <= 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated LHa file data");
+ return (ARCHIVE_FATAL);
+ }
+ if (bytes_avail > lha->entry_bytes_remaining)
+ bytes_avail = (ssize_t)lha->entry_bytes_remaining;
+ lha->entry_crc_calculated =
+ lha_crc16(lha->entry_crc_calculated, *buff, bytes_avail);
+ *size = bytes_avail;
+ *offset = lha->entry_offset;
+ lha->entry_offset += bytes_avail;
+ lha->entry_bytes_remaining -= bytes_avail;
+ if (lha->entry_bytes_remaining == 0)
+ lha->end_of_entry = 1;
+ lha->entry_unconsumed = bytes_avail;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Read a file content in LZHUFF encoding.
+ *
+ * Returns ARCHIVE_OK if successful, returns ARCHIVE_WARN if compression is
+ * unsupported, ARCHIVE_FATAL otherwise, sets lha->end_of_entry if it consumes
+ * all of the data.
+ */
+static int
+lha_read_data_lzh(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct lha *lha = (struct lha *)(a->format->data);
+ ssize_t bytes_avail;
+ int r;
+
+ /* If we haven't yet read any data, initialize the decompressor. */
+ if (!lha->decompress_init) {
+ r = lzh_decode_init(&(lha->strm), lha->method);
+ switch (r) {
+ case ARCHIVE_OK:
+ break;
+ case ARCHIVE_FAILED:
+ /* Unsupported compression. */
+ *buff = NULL;
+ *size = 0;
+ *offset = 0;
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported lzh compression method -%c%c%c-",
+ lha->method[0], lha->method[1], lha->method[2]);
+ /* We know compressed size; just skip it. */
+ archive_read_format_lha_read_data_skip(a);
+ return (ARCHIVE_WARN);
+ default:
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory "
+ "for lzh decompression");
+ return (ARCHIVE_FATAL);
+ }
+ /* We've initialized decompression for this stream. */
+ lha->decompress_init = 1;
+ lha->strm.avail_out = 0;
+ lha->strm.total_out = 0;
+ }
+
+ /*
+ * Note: '1' here is a performance optimization.
+ * Recall that the decompression layer returns a count of
+ * available bytes; asking for more than that forces the
+ * decompressor to combine reads by copying data.
+ */
+ lha->strm.next_in = __archive_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail <= 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated LHa file body");
+ return (ARCHIVE_FATAL);
+ }
+ if (bytes_avail > lha->entry_bytes_remaining)
+ bytes_avail = (ssize_t)lha->entry_bytes_remaining;
+
+ lha->strm.avail_in = (int)bytes_avail;
+ lha->strm.total_in = 0;
+ lha->strm.avail_out = 0;
+
+ r = lzh_decode(&(lha->strm), bytes_avail == lha->entry_bytes_remaining);
+ switch (r) {
+ case ARCHIVE_OK:
+ break;
+ case ARCHIVE_EOF:
+ lha->end_of_entry = 1;
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Bad lzh data");
+ return (ARCHIVE_FAILED);
+ }
+ lha->entry_unconsumed = lha->strm.total_in;
+ lha->entry_bytes_remaining -= lha->strm.total_in;
+
+ if (lha->strm.avail_out) {
+ *offset = lha->entry_offset;
+ *size = lha->strm.avail_out;
+ *buff = lha->strm.ref_ptr;
+ lha->entry_crc_calculated =
+ lha_crc16(lha->entry_crc_calculated, *buff, *size);
+ lha->entry_offset += *size;
+ } else {
+ *offset = lha->entry_offset;
+ *size = 0;
+ *buff = NULL;
+ if (lha->end_of_entry)
+ return (lha_end_of_entry(a));
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Skip a file content.
+ */
+static int
+archive_read_format_lha_read_data_skip(struct archive_read *a)
+{
+ struct lha *lha;
+ int64_t bytes_skipped;
+
+ lha = (struct lha *)(a->format->data);
+
+ if (lha->entry_unconsumed) {
+ /* Consume as much as the decompressor actually used. */
+ __archive_read_consume(a, lha->entry_unconsumed);
+ lha->entry_unconsumed = 0;
+ }
+
+ /* if we've already read to end of data, we're done. */
+ if (lha->end_of_entry_cleanup)
+ return (ARCHIVE_OK);
+
+ /*
+ * If the length is at the beginning, we can skip the
+ * compressed data much more quickly.
+ */
+ bytes_skipped = __archive_read_consume(a, lha->entry_bytes_remaining);
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+
+ /* This entry is finished and done. */
+ lha->end_of_entry_cleanup = lha->end_of_entry = 1;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_lha_cleanup(struct archive_read *a)
+{
+ struct lha *lha = (struct lha *)(a->format->data);
+
+ lzh_decode_free(&(lha->strm));
+ archive_string_free(&(lha->dirname));
+ archive_string_free(&(lha->filename));
+ archive_string_free(&(lha->uname));
+ archive_string_free(&(lha->gname));
+ archive_wstring_free(&(lha->ws));
+ free(lha);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * 'LHa for UNIX' utility has archived a symbolic-link name after
+ * a pathname with '|' character.
+ * This function extracts the symbolic-link name from the pathname.
+ *
+ * example.
+ * 1. a symbolic-name is 'aaa/bb/cc'
+ * 2. a filename is 'xxx/bbb'
+ * then a archived pathname is 'xxx/bbb|aaa/bb/cc'
+ */
+static int
+lha_parse_linkname(struct archive_wstring *linkname,
+ struct archive_wstring *pathname)
+{
+ wchar_t * linkptr;
+ size_t symlen;
+
+ linkptr = wcschr(pathname->s, L'|');
+ if (linkptr != NULL) {
+ symlen = wcslen(linkptr + 1);
+ archive_wstrncpy(linkname, linkptr+1, symlen);
+
+ *linkptr = 0;
+ pathname->length = wcslen(pathname->s);
+
+ return (1);
+ }
+ return (0);
+}
+
+/* Convert an MSDOS-style date/time into Unix-style time. */
+static time_t
+lha_dos_time(const unsigned char *p)
+{
+ int msTime, msDate;
+ struct tm ts;
+
+ msTime = archive_le16dec(p);
+ msDate = archive_le16dec(p+2);
+
+ memset(&ts, 0, sizeof(ts));
+ ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */
+ ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */
+ ts.tm_mday = msDate & 0x1f; /* Day of month. */
+ ts.tm_hour = (msTime >> 11) & 0x1f;
+ ts.tm_min = (msTime >> 5) & 0x3f;
+ ts.tm_sec = (msTime << 1) & 0x3e;
+ ts.tm_isdst = -1;
+ return (mktime(&ts));
+}
+
+/* Convert an MS-Windows-style date/time into Unix-style time. */
+static time_t
+lha_win_time(uint64_t wintime, long *ns)
+{
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+
+ if (wintime >= EPOC_TIME) {
+ wintime -= EPOC_TIME; /* 1970-01-01 00:00:00 (UTC) */
+ if (ns != NULL)
+ *ns = (long)(wintime % 10000000) * 100;
+ return (wintime / 10000000);
+ } else {
+ if (ns != NULL)
+ *ns = 0;
+ return (0);
+ }
+}
+
+static unsigned char
+lha_calcsum(unsigned char sum, const void *pp, int offset, size_t size)
+{
+ unsigned char const *p = (unsigned char const *)pp;
+
+ p += offset;
+ for (;size > 0; --size)
+ sum += *p++;
+ return (sum);
+}
+
+static uint16_t crc16tbl[2][256];
+static void
+lha_crc16_init(void)
+{
+ unsigned int i;
+ static int crc16init = 0;
+
+ if (crc16init)
+ return;
+ crc16init = 1;
+
+ for (i = 0; i < 256; i++) {
+ unsigned int j;
+ uint16_t crc = (uint16_t)i;
+ for (j = 8; j; j--)
+ crc = (crc >> 1) ^ ((crc & 1) * 0xA001);
+ crc16tbl[0][i] = crc;
+ }
+
+ for (i = 0; i < 256; i++) {
+ crc16tbl[1][i] = (crc16tbl[0][i] >> 8)
+ ^ crc16tbl[0][crc16tbl[0][i] & 0xff];
+ }
+}
+
+static uint16_t
+lha_crc16(uint16_t crc, const void *pp, size_t len)
+{
+ const unsigned char *p = (const unsigned char *)pp;
+ const uint16_t *buff;
+ const union {
+ uint32_t i;
+ char c[4];
+ } u = { 0x01020304 };
+
+ if (len == 0)
+ return crc;
+
+ /* Process unaligned address. */
+ if (((uintptr_t)p) & (uintptr_t)0x1) {
+ crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff];
+ len--;
+ }
+ buff = (const uint16_t *)p;
+ /*
+ * Modern C compiler such as GCC does not unroll automatically yet
+ * without unrolling pragma, and Clang is so. So we should
+ * unroll this loop for its performance.
+ */
+ for (;len >= 8; len -= 8) {
+ /* This if statement expects compiler optimization will
+ * remove the statement which will not be executed. */
+#undef bswap16
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+#if defined(_MSC_VER) && _MSC_VER >= 1400 /* Visual Studio */
+# define bswap16(x) _byteswap_ushort(x)
+#elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ > 4)
+/* GCC 4.8 and later has __builtin_bswap16() */
+# define bswap16(x) __builtin_bswap16(x)
+#elif defined(__clang__) && __has_builtin(__builtin_bswap16)
+/* Newer clang versions have __builtin_bswap16() */
+# define bswap16(x) __builtin_bswap16(x)
+#else
+# define bswap16(x) ((((x) >> 8) & 0xff) | ((x) << 8))
+#endif
+#define CRC16W do { \
+ if(u.c[0] == 1) { /* Big endian */ \
+ crc ^= bswap16(*buff); buff++; \
+ } else \
+ crc ^= *buff++; \
+ crc = crc16tbl[1][crc & 0xff] ^ crc16tbl[0][crc >> 8];\
+} while (0)
+ CRC16W;
+ CRC16W;
+ CRC16W;
+ CRC16W;
+#undef CRC16W
+#undef bswap16
+ }
+
+ p = (const unsigned char *)buff;
+ for (;len; len--) {
+ crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff];
+ }
+ return crc;
+}
+
+/*
+ * Initialize LZHUF decoder.
+ *
+ * Returns ARCHIVE_OK if initialization was successful.
+ * Returns ARCHIVE_FAILED if method is unsupported.
+ * Returns ARCHIVE_FATAL if initialization failed; memory allocation
+ * error occurred.
+ */
+static int
+lzh_decode_init(struct lzh_stream *strm, const char *method)
+{
+ struct lzh_dec *ds;
+ int w_bits, w_size;
+
+ if (strm->ds == NULL) {
+ strm->ds = calloc(1, sizeof(*strm->ds));
+ if (strm->ds == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ ds = strm->ds;
+ ds->error = ARCHIVE_FAILED;
+ if (method == NULL || method[0] != 'l' || method[1] != 'h')
+ return (ARCHIVE_FAILED);
+ switch (method[2]) {
+ case '5':
+ w_bits = 13;/* 8KiB for window */
+ break;
+ case '6':
+ w_bits = 15;/* 32KiB for window */
+ break;
+ case '7':
+ w_bits = 16;/* 64KiB for window */
+ break;
+ default:
+ return (ARCHIVE_FAILED);/* Not supported. */
+ }
+ ds->error = ARCHIVE_FATAL;
+ /* Expand a window size up to 128 KiB for decompressing process
+ * performance whatever its original window size is. */
+ ds->w_size = 1U << 17;
+ ds->w_mask = ds->w_size -1;
+ if (ds->w_buff == NULL) {
+ ds->w_buff = malloc(ds->w_size);
+ if (ds->w_buff == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ w_size = 1U << w_bits;
+ memset(ds->w_buff + ds->w_size - w_size, 0x20, w_size);
+ ds->w_pos = 0;
+ ds->state = 0;
+ ds->pos_pt_len_size = w_bits + 1;
+ ds->pos_pt_len_bits = (w_bits == 15 || w_bits == 16)? 5: 4;
+ ds->literal_pt_len_size = PT_BITLEN_SIZE;
+ ds->literal_pt_len_bits = 5;
+ ds->br.cache_buffer = 0;
+ ds->br.cache_avail = 0;
+
+ if (lzh_huffman_init(&(ds->lt), LT_BITLEN_SIZE, 16)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ ds->lt.len_bits = 9;
+ if (lzh_huffman_init(&(ds->pt), PT_BITLEN_SIZE, 16)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ ds->error = 0;
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Release LZHUF decoder.
+ */
+static void
+lzh_decode_free(struct lzh_stream *strm)
+{
+
+ if (strm->ds == NULL)
+ return;
+ free(strm->ds->w_buff);
+ lzh_huffman_free(&(strm->ds->lt));
+ lzh_huffman_free(&(strm->ds->pt));
+ free(strm->ds);
+ strm->ds = NULL;
+}
+
+/*
+ * Bit stream reader.
+ */
+/* Check that the cache buffer has enough bits. */
+#define lzh_br_has(br, n) ((br)->cache_avail >= n)
+/* Get compressed data by bit. */
+#define lzh_br_bits(br, n) \
+ (((uint16_t)((br)->cache_buffer >> \
+ ((br)->cache_avail - (n)))) & cache_masks[n])
+#define lzh_br_bits_forced(br, n) \
+ (((uint16_t)((br)->cache_buffer << \
+ ((n) - (br)->cache_avail))) & cache_masks[n])
+/* Read ahead to make sure the cache buffer has enough compressed data we
+ * will use.
+ * True : completed, there is enough data in the cache buffer.
+ * False : we met that strm->next_in is empty, we have to get following
+ * bytes. */
+#define lzh_br_read_ahead_0(strm, br, n) \
+ (lzh_br_has(br, (n)) || lzh_br_fillup(strm, br))
+/* True : the cache buffer has some bits as much as we need.
+ * False : there are no enough bits in the cache buffer to be used,
+ * we have to get following bytes if we could. */
+#define lzh_br_read_ahead(strm, br, n) \
+ (lzh_br_read_ahead_0((strm), (br), (n)) || lzh_br_has((br), (n)))
+
+/* Notify how many bits we consumed. */
+#define lzh_br_consume(br, n) ((br)->cache_avail -= (n))
+#define lzh_br_unconsume(br, n) ((br)->cache_avail += (n))
+
+static const uint16_t cache_masks[] = {
+ 0x0000, 0x0001, 0x0003, 0x0007,
+ 0x000F, 0x001F, 0x003F, 0x007F,
+ 0x00FF, 0x01FF, 0x03FF, 0x07FF,
+ 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF
+};
+
+/*
+ * Shift away used bits in the cache data and fill it up with following bits.
+ * Call this when cache buffer does not have enough bits you need.
+ *
+ * Returns 1 if the cache buffer is full.
+ * Returns 0 if the cache buffer is not full; input buffer is empty.
+ */
+static int
+lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br)
+{
+ int n = CACHE_BITS - br->cache_avail;
+
+ for (;;) {
+ const int x = n >> 3;
+ if (strm->avail_in >= x) {
+ switch (x) {
+ case 8:
+ br->cache_buffer =
+ ((uint64_t)strm->next_in[0]) << 56 |
+ ((uint64_t)strm->next_in[1]) << 48 |
+ ((uint64_t)strm->next_in[2]) << 40 |
+ ((uint64_t)strm->next_in[3]) << 32 |
+ ((uint32_t)strm->next_in[4]) << 24 |
+ ((uint32_t)strm->next_in[5]) << 16 |
+ ((uint32_t)strm->next_in[6]) << 8 |
+ (uint32_t)strm->next_in[7];
+ strm->next_in += 8;
+ strm->avail_in -= 8;
+ br->cache_avail += 8 * 8;
+ return (1);
+ case 7:
+ br->cache_buffer =
+ (br->cache_buffer << 56) |
+ ((uint64_t)strm->next_in[0]) << 48 |
+ ((uint64_t)strm->next_in[1]) << 40 |
+ ((uint64_t)strm->next_in[2]) << 32 |
+ ((uint64_t)strm->next_in[3]) << 24 |
+ ((uint64_t)strm->next_in[4]) << 16 |
+ ((uint64_t)strm->next_in[5]) << 8 |
+ (uint64_t)strm->next_in[6];
+ strm->next_in += 7;
+ strm->avail_in -= 7;
+ br->cache_avail += 7 * 8;
+ return (1);
+ case 6:
+ br->cache_buffer =
+ (br->cache_buffer << 48) |
+ ((uint64_t)strm->next_in[0]) << 40 |
+ ((uint64_t)strm->next_in[1]) << 32 |
+ ((uint64_t)strm->next_in[2]) << 24 |
+ ((uint64_t)strm->next_in[3]) << 16 |
+ ((uint64_t)strm->next_in[4]) << 8 |
+ (uint64_t)strm->next_in[5];
+ strm->next_in += 6;
+ strm->avail_in -= 6;
+ br->cache_avail += 6 * 8;
+ return (1);
+ case 0:
+ /* We have enough compressed data in
+ * the cache buffer.*/
+ return (1);
+ default:
+ break;
+ }
+ }
+ if (strm->avail_in == 0) {
+ /* There is not enough compressed data to fill up the
+ * cache buffer. */
+ return (0);
+ }
+ br->cache_buffer =
+ (br->cache_buffer << 8) | *strm->next_in++;
+ strm->avail_in--;
+ br->cache_avail += 8;
+ n -= 8;
+ }
+}
+
+/*
+ * Decode LZHUF.
+ *
+ * 1. Returns ARCHIVE_OK if output buffer or input buffer are empty.
+ * Please set available buffer and call this function again.
+ * 2. Returns ARCHIVE_EOF if decompression has been completed.
+ * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data
+ * is broken or you do not set 'last' flag properly.
+ * 4. 'last' flag is very important, you must set 1 to the flag if there
+ * is no input data. The lha compressed data format does not provide how
+ * to know the compressed data is really finished.
+ * Note: lha command utility check if the total size of output bytes is
+ * reached the uncompressed size recorded in its header. it does not mind
+ * that the decoding process is properly finished.
+ * GNU ZIP can decompress another compressed file made by SCO LZH compress.
+ * it handles EOF as null to fill read buffer with zero until the decoding
+ * process meet 2 bytes of zeros at reading a size of a next chunk, so the
+ * zeros are treated as the mark of the end of the data although the zeros
+ * is dummy, not the file data.
+ */
+static int lzh_read_blocks(struct lzh_stream *, int);
+static int lzh_decode_blocks(struct lzh_stream *, int);
+#define ST_RD_BLOCK 0
+#define ST_RD_PT_1 1
+#define ST_RD_PT_2 2
+#define ST_RD_PT_3 3
+#define ST_RD_PT_4 4
+#define ST_RD_LITERAL_1 5
+#define ST_RD_LITERAL_2 6
+#define ST_RD_LITERAL_3 7
+#define ST_RD_POS_DATA_1 8
+#define ST_GET_LITERAL 9
+#define ST_GET_POS_1 10
+#define ST_GET_POS_2 11
+#define ST_COPY_DATA 12
+
+static int
+lzh_decode(struct lzh_stream *strm, int last)
+{
+ struct lzh_dec *ds = strm->ds;
+ int avail_in;
+ int r;
+
+ if (ds->error)
+ return (ds->error);
+
+ avail_in = strm->avail_in;
+ do {
+ if (ds->state < ST_GET_LITERAL)
+ r = lzh_read_blocks(strm, last);
+ else
+ r = lzh_decode_blocks(strm, last);
+ } while (r == 100);
+ strm->total_in += avail_in - strm->avail_in;
+ return (r);
+}
+
+static void
+lzh_emit_window(struct lzh_stream *strm, size_t s)
+{
+ strm->ref_ptr = strm->ds->w_buff;
+ strm->avail_out = (int)s;
+ strm->total_out += s;
+}
+
+static int
+lzh_read_blocks(struct lzh_stream *strm, int last)
+{
+ struct lzh_dec *ds = strm->ds;
+ struct lzh_br *br = &(ds->br);
+ int c = 0, i;
+ unsigned rbits;
+
+ for (;;) {
+ switch (ds->state) {
+ case ST_RD_BLOCK:
+ /*
+ * Read a block number indicates how many blocks
+ * we will handle. The block is composed of a
+ * literal and a match, sometimes a literal only
+ * in particular, there are no reference data at
+ * the beginning of the decompression.
+ */
+ if (!lzh_br_read_ahead_0(strm, br, 16)) {
+ if (!last)
+ /* We need following data. */
+ return (ARCHIVE_OK);
+ if (lzh_br_has(br, 8)) {
+ /*
+ * It seems there are extra bits.
+ * 1. Compressed data is broken.
+ * 2. `last' flag does not properly
+ * set.
+ */
+ goto failed;
+ }
+ if (ds->w_pos > 0) {
+ lzh_emit_window(strm, ds->w_pos);
+ ds->w_pos = 0;
+ return (ARCHIVE_OK);
+ }
+ /* End of compressed data; we have completely
+ * handled all compressed data. */
+ return (ARCHIVE_EOF);
+ }
+ ds->blocks_avail = lzh_br_bits(br, 16);
+ if (ds->blocks_avail == 0)
+ goto failed;
+ lzh_br_consume(br, 16);
+ /*
+ * Read a literal table compressed in huffman
+ * coding.
+ */
+ ds->pt.len_size = ds->literal_pt_len_size;
+ ds->pt.len_bits = ds->literal_pt_len_bits;
+ ds->reading_position = 0;
+ /* FALL THROUGH */
+ case ST_RD_PT_1:
+ /* Note: ST_RD_PT_1, ST_RD_PT_2 and ST_RD_PT_4 are
+ * used in reading both a literal table and a
+ * position table. */
+ if (!lzh_br_read_ahead(strm, br, ds->pt.len_bits)) {
+ if (last)
+ goto failed;/* Truncated data. */
+ ds->state = ST_RD_PT_1;
+ return (ARCHIVE_OK);
+ }
+ ds->pt.len_avail = lzh_br_bits(br, ds->pt.len_bits);
+ lzh_br_consume(br, ds->pt.len_bits);
+ /* FALL THROUGH */
+ case ST_RD_PT_2:
+ if (ds->pt.len_avail == 0) {
+ /* There is no bitlen. */
+ if (!lzh_br_read_ahead(strm, br,
+ ds->pt.len_bits)) {
+ if (last)
+ goto failed;/* Truncated data.*/
+ ds->state = ST_RD_PT_2;
+ return (ARCHIVE_OK);
+ }
+ if (!lzh_make_fake_table(&(ds->pt),
+ lzh_br_bits(br, ds->pt.len_bits)))
+ goto failed;/* Invalid data. */
+ lzh_br_consume(br, ds->pt.len_bits);
+ if (ds->reading_position)
+ ds->state = ST_GET_LITERAL;
+ else
+ ds->state = ST_RD_LITERAL_1;
+ break;
+ } else if (ds->pt.len_avail > ds->pt.len_size)
+ goto failed;/* Invalid data. */
+ ds->loop = 0;
+ memset(ds->pt.freq, 0, sizeof(ds->pt.freq));
+ if (ds->pt.len_avail < 3 ||
+ ds->pt.len_size == ds->pos_pt_len_size) {
+ ds->state = ST_RD_PT_4;
+ break;
+ }
+ /* FALL THROUGH */
+ case ST_RD_PT_3:
+ ds->loop = lzh_read_pt_bitlen(strm, ds->loop, 3);
+ if (ds->loop < 3) {
+ if (ds->loop < 0 || last)
+ goto failed;/* Invalid data. */
+ /* Not completed, get following data. */
+ ds->state = ST_RD_PT_3;
+ return (ARCHIVE_OK);
+ }
+ /* There are some null in bitlen of the literal. */
+ if (!lzh_br_read_ahead(strm, br, 2)) {
+ if (last)
+ goto failed;/* Truncated data. */
+ ds->state = ST_RD_PT_3;
+ return (ARCHIVE_OK);
+ }
+ c = lzh_br_bits(br, 2);
+ lzh_br_consume(br, 2);
+ if (c > ds->pt.len_avail - 3)
+ goto failed;/* Invalid data. */
+ for (i = 3; c-- > 0 ;)
+ ds->pt.bitlen[i++] = 0;
+ ds->loop = i;
+ /* FALL THROUGH */
+ case ST_RD_PT_4:
+ ds->loop = lzh_read_pt_bitlen(strm, ds->loop,
+ ds->pt.len_avail);
+ if (ds->loop < ds->pt.len_avail) {
+ if (ds->loop < 0 || last)
+ goto failed;/* Invalid data. */
+ /* Not completed, get following data. */
+ ds->state = ST_RD_PT_4;
+ return (ARCHIVE_OK);
+ }
+ if (!lzh_make_huffman_table(&(ds->pt)))
+ goto failed;/* Invalid data */
+ if (ds->reading_position) {
+ ds->state = ST_GET_LITERAL;
+ break;
+ }
+ /* FALL THROUGH */
+ case ST_RD_LITERAL_1:
+ if (!lzh_br_read_ahead(strm, br, ds->lt.len_bits)) {
+ if (last)
+ goto failed;/* Truncated data. */
+ ds->state = ST_RD_LITERAL_1;
+ return (ARCHIVE_OK);
+ }
+ ds->lt.len_avail = lzh_br_bits(br, ds->lt.len_bits);
+ lzh_br_consume(br, ds->lt.len_bits);
+ /* FALL THROUGH */
+ case ST_RD_LITERAL_2:
+ if (ds->lt.len_avail == 0) {
+ /* There is no bitlen. */
+ if (!lzh_br_read_ahead(strm, br,
+ ds->lt.len_bits)) {
+ if (last)
+ goto failed;/* Truncated data.*/
+ ds->state = ST_RD_LITERAL_2;
+ return (ARCHIVE_OK);
+ }
+ if (!lzh_make_fake_table(&(ds->lt),
+ lzh_br_bits(br, ds->lt.len_bits)))
+ goto failed;/* Invalid data */
+ lzh_br_consume(br, ds->lt.len_bits);
+ ds->state = ST_RD_POS_DATA_1;
+ break;
+ } else if (ds->lt.len_avail > ds->lt.len_size)
+ goto failed;/* Invalid data */
+ ds->loop = 0;
+ memset(ds->lt.freq, 0, sizeof(ds->lt.freq));
+ /* FALL THROUGH */
+ case ST_RD_LITERAL_3:
+ i = ds->loop;
+ while (i < ds->lt.len_avail) {
+ if (!lzh_br_read_ahead(strm, br,
+ ds->pt.max_bits)) {
+ if (last)
+ goto failed;/* Truncated data.*/
+ ds->loop = i;
+ ds->state = ST_RD_LITERAL_3;
+ return (ARCHIVE_OK);
+ }
+ rbits = lzh_br_bits(br, ds->pt.max_bits);
+ c = lzh_decode_huffman(&(ds->pt), rbits);
+ if (c > 2) {
+ /* Note: 'c' will never be more than
+ * eighteen since it's limited by
+ * PT_BITLEN_SIZE, which is being set
+ * to ds->pt.len_size through
+ * ds->literal_pt_len_size. */
+ lzh_br_consume(br, ds->pt.bitlen[c]);
+ c -= 2;
+ ds->lt.freq[c]++;
+ ds->lt.bitlen[i++] = c;
+ } else if (c == 0) {
+ lzh_br_consume(br, ds->pt.bitlen[c]);
+ ds->lt.bitlen[i++] = 0;
+ } else {
+ /* c == 1 or c == 2 */
+ int n = (c == 1)?4:9;
+ if (!lzh_br_read_ahead(strm, br,
+ ds->pt.bitlen[c] + n)) {
+ if (last) /* Truncated data. */
+ goto failed;
+ ds->loop = i;
+ ds->state = ST_RD_LITERAL_3;
+ return (ARCHIVE_OK);
+ }
+ lzh_br_consume(br, ds->pt.bitlen[c]);
+ c = lzh_br_bits(br, n);
+ lzh_br_consume(br, n);
+ c += (n == 4)?3:20;
+ if (i + c > ds->lt.len_avail)
+ goto failed;/* Invalid data */
+ memset(&(ds->lt.bitlen[i]), 0, c);
+ i += c;
+ }
+ }
+ if (i > ds->lt.len_avail ||
+ !lzh_make_huffman_table(&(ds->lt)))
+ goto failed;/* Invalid data */
+ /* FALL THROUGH */
+ case ST_RD_POS_DATA_1:
+ /*
+ * Read a position table compressed in huffman
+ * coding.
+ */
+ ds->pt.len_size = ds->pos_pt_len_size;
+ ds->pt.len_bits = ds->pos_pt_len_bits;
+ ds->reading_position = 1;
+ ds->state = ST_RD_PT_1;
+ break;
+ case ST_GET_LITERAL:
+ return (100);
+ }
+ }
+failed:
+ return (ds->error = ARCHIVE_FAILED);
+}
+
+static int
+lzh_decode_blocks(struct lzh_stream *strm, int last)
+{
+ struct lzh_dec *ds = strm->ds;
+ struct lzh_br bre = ds->br;
+ struct huffman *lt = &(ds->lt);
+ struct huffman *pt = &(ds->pt);
+ unsigned char *w_buff = ds->w_buff;
+ unsigned char *lt_bitlen = lt->bitlen;
+ unsigned char *pt_bitlen = pt->bitlen;
+ int blocks_avail = ds->blocks_avail, c = 0;
+ int copy_len = ds->copy_len, copy_pos = ds->copy_pos;
+ int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size;
+ int lt_max_bits = lt->max_bits, pt_max_bits = pt->max_bits;
+ int state = ds->state;
+
+ for (;;) {
+ switch (state) {
+ case ST_GET_LITERAL:
+ for (;;) {
+ if (blocks_avail == 0) {
+ /* We have decoded all blocks.
+ * Let's handle next blocks. */
+ ds->state = ST_RD_BLOCK;
+ ds->br = bre;
+ ds->blocks_avail = 0;
+ ds->w_pos = w_pos;
+ ds->copy_pos = 0;
+ return (100);
+ }
+
+ /* lzh_br_read_ahead() always try to fill the
+ * cache buffer up. In specific situation we
+ * are close to the end of the data, the cache
+ * buffer will not be full and thus we have to
+ * determine if the cache buffer has some bits
+ * as much as we need after lzh_br_read_ahead()
+ * failed. */
+ if (!lzh_br_read_ahead(strm, &bre,
+ lt_max_bits)) {
+ if (!last)
+ goto next_data;
+ /* Remaining bits are less than
+ * maximum bits(lt.max_bits) but maybe
+ * it still remains as much as we need,
+ * so we should try to use it with
+ * dummy bits. */
+ c = lzh_decode_huffman(lt,
+ lzh_br_bits_forced(&bre,
+ lt_max_bits));
+ lzh_br_consume(&bre, lt_bitlen[c]);
+ if (!lzh_br_has(&bre, 0))
+ goto failed;/* Over read. */
+ } else {
+ c = lzh_decode_huffman(lt,
+ lzh_br_bits(&bre, lt_max_bits));
+ lzh_br_consume(&bre, lt_bitlen[c]);
+ }
+ blocks_avail--;
+ if (c > UCHAR_MAX)
+ /* Current block is a match data. */
+ break;
+ /*
+ * 'c' is exactly a literal code.
+ */
+ /* Save a decoded code to reference it
+ * afterward. */
+ w_buff[w_pos] = c;
+ if (++w_pos >= w_size) {
+ w_pos = 0;
+ lzh_emit_window(strm, w_size);
+ goto next_data;
+ }
+ }
+ /* 'c' is the length of a match pattern we have
+ * already extracted, which has be stored in
+ * window(ds->w_buff). */
+ copy_len = c - (UCHAR_MAX + 1) + MINMATCH;
+ /* FALL THROUGH */
+ case ST_GET_POS_1:
+ /*
+ * Get a reference position.
+ */
+ if (!lzh_br_read_ahead(strm, &bre, pt_max_bits)) {
+ if (!last) {
+ state = ST_GET_POS_1;
+ ds->copy_len = copy_len;
+ goto next_data;
+ }
+ copy_pos = lzh_decode_huffman(pt,
+ lzh_br_bits_forced(&bre, pt_max_bits));
+ lzh_br_consume(&bre, pt_bitlen[copy_pos]);
+ if (!lzh_br_has(&bre, 0))
+ goto failed;/* Over read. */
+ } else {
+ copy_pos = lzh_decode_huffman(pt,
+ lzh_br_bits(&bre, pt_max_bits));
+ lzh_br_consume(&bre, pt_bitlen[copy_pos]);
+ }
+ /* FALL THROUGH */
+ case ST_GET_POS_2:
+ if (copy_pos > 1) {
+ /* We need an additional adjustment number to
+ * the position. */
+ int p = copy_pos - 1;
+ if (!lzh_br_read_ahead(strm, &bre, p)) {
+ if (last)
+ goto failed;/* Truncated data.*/
+ state = ST_GET_POS_2;
+ ds->copy_len = copy_len;
+ ds->copy_pos = copy_pos;
+ goto next_data;
+ }
+ copy_pos = (1 << p) + lzh_br_bits(&bre, p);
+ lzh_br_consume(&bre, p);
+ }
+ /* The position is actually a distance from the last
+ * code we had extracted and thus we have to convert
+ * it to a position of the window. */
+ copy_pos = (w_pos - copy_pos - 1) & w_mask;
+ /* FALL THROUGH */
+ case ST_COPY_DATA:
+ /*
+ * Copy `copy_len' bytes as extracted data from
+ * the window into the output buffer.
+ */
+ for (;;) {
+ int l;
+
+ l = copy_len;
+ if (copy_pos > w_pos) {
+ if (l > w_size - copy_pos)
+ l = w_size - copy_pos;
+ } else {
+ if (l > w_size - w_pos)
+ l = w_size - w_pos;
+ }
+ if ((copy_pos + l < w_pos)
+ || (w_pos + l < copy_pos)) {
+ /* No overlap. */
+ memcpy(w_buff + w_pos,
+ w_buff + copy_pos, l);
+ } else {
+ const unsigned char *s;
+ unsigned char *d;
+ int li;
+
+ d = w_buff + w_pos;
+ s = w_buff + copy_pos;
+ for (li = 0; li < l-1;) {
+ d[li] = s[li];li++;
+ d[li] = s[li];li++;
+ }
+ if (li < l)
+ d[li] = s[li];
+ }
+ w_pos += l;
+ if (w_pos == w_size) {
+ w_pos = 0;
+ lzh_emit_window(strm, w_size);
+ if (copy_len <= l)
+ state = ST_GET_LITERAL;
+ else {
+ state = ST_COPY_DATA;
+ ds->copy_len = copy_len - l;
+ ds->copy_pos =
+ (copy_pos + l) & w_mask;
+ }
+ goto next_data;
+ }
+ if (copy_len <= l)
+ /* A copy of current pattern ended. */
+ break;
+ copy_len -= l;
+ copy_pos = (copy_pos + l) & w_mask;
+ }
+ state = ST_GET_LITERAL;
+ break;
+ }
+ }
+failed:
+ return (ds->error = ARCHIVE_FAILED);
+next_data:
+ ds->br = bre;
+ ds->blocks_avail = blocks_avail;
+ ds->state = state;
+ ds->w_pos = w_pos;
+ return (ARCHIVE_OK);
+}
+
+static int
+lzh_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits)
+{
+ int bits;
+
+ if (hf->bitlen == NULL) {
+ hf->bitlen = malloc(len_size * sizeof(hf->bitlen[0]));
+ if (hf->bitlen == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ if (hf->tbl == NULL) {
+ if (tbl_bits < HTBL_BITS)
+ bits = tbl_bits;
+ else
+ bits = HTBL_BITS;
+ hf->tbl = malloc(((size_t)1 << bits) * sizeof(hf->tbl[0]));
+ if (hf->tbl == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ if (hf->tree == NULL && tbl_bits > HTBL_BITS) {
+ hf->tree_avail = 1 << (tbl_bits - HTBL_BITS + 4);
+ hf->tree = malloc(hf->tree_avail * sizeof(hf->tree[0]));
+ if (hf->tree == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ hf->len_size = (int)len_size;
+ hf->tbl_bits = tbl_bits;
+ return (ARCHIVE_OK);
+}
+
+static void
+lzh_huffman_free(struct huffman *hf)
+{
+ free(hf->bitlen);
+ free(hf->tbl);
+ free(hf->tree);
+}
+
+static const char bitlen_tbl[0x400] = {
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 16, 0
+};
+static int
+lzh_read_pt_bitlen(struct lzh_stream *strm, int start, int end)
+{
+ struct lzh_dec *ds = strm->ds;
+ struct lzh_br *br = &(ds->br);
+ int c, i;
+
+ for (i = start; i < end; ) {
+ /*
+ * bit pattern the number we need
+ * 000 -> 0
+ * 001 -> 1
+ * 010 -> 2
+ * ...
+ * 110 -> 6
+ * 1110 -> 7
+ * 11110 -> 8
+ * ...
+ * 1111111111110 -> 16
+ */
+ if (!lzh_br_read_ahead(strm, br, 3))
+ return (i);
+ if ((c = lzh_br_bits(br, 3)) == 7) {
+ if (!lzh_br_read_ahead(strm, br, 13))
+ return (i);
+ c = bitlen_tbl[lzh_br_bits(br, 13) & 0x3FF];
+ if (c)
+ lzh_br_consume(br, c - 3);
+ else
+ return (-1);/* Invalid data. */
+ } else
+ lzh_br_consume(br, 3);
+ ds->pt.bitlen[i++] = c;
+ ds->pt.freq[c]++;
+ }
+ return (i);
+}
+
+static int
+lzh_make_fake_table(struct huffman *hf, uint16_t c)
+{
+ if (c >= hf->len_size)
+ return (0);
+ hf->tbl[0] = c;
+ hf->max_bits = 0;
+ hf->shift_bits = 0;
+ hf->bitlen[hf->tbl[0]] = 0;
+ return (1);
+}
+
+/*
+ * Make a huffman coding table.
+ */
+static int
+lzh_make_huffman_table(struct huffman *hf)
+{
+ uint16_t *tbl;
+ const unsigned char *bitlen;
+ int bitptn[17], weight[17];
+ int i, maxbits = 0, ptn, tbl_size, w;
+ int diffbits, len_avail;
+
+ /*
+ * Initialize bit patterns.
+ */
+ ptn = 0;
+ for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) {
+ bitptn[i] = ptn;
+ weight[i] = w;
+ if (hf->freq[i]) {
+ ptn += hf->freq[i] * w;
+ maxbits = i;
+ }
+ }
+ if (ptn != 0x10000 || maxbits > hf->tbl_bits)
+ return (0);/* Invalid */
+
+ hf->max_bits = maxbits;
+
+ /*
+ * Cut out extra bits which we won't house in the table.
+ * This preparation reduces the same calculation in the for-loop
+ * making the table.
+ */
+ if (maxbits < 16) {
+ int ebits = 16 - maxbits;
+ for (i = 1; i <= maxbits; i++) {
+ bitptn[i] >>= ebits;
+ weight[i] >>= ebits;
+ }
+ }
+ if (maxbits > HTBL_BITS) {
+ unsigned htbl_max;
+ uint16_t *p;
+
+ diffbits = maxbits - HTBL_BITS;
+ for (i = 1; i <= HTBL_BITS; i++) {
+ bitptn[i] >>= diffbits;
+ weight[i] >>= diffbits;
+ }
+ htbl_max = bitptn[HTBL_BITS] +
+ weight[HTBL_BITS] * hf->freq[HTBL_BITS];
+ p = &(hf->tbl[htbl_max]);
+ while (p < &hf->tbl[1U<<HTBL_BITS])
+ *p++ = 0;
+ } else
+ diffbits = 0;
+ hf->shift_bits = diffbits;
+
+ /*
+ * Make the table.
+ */
+ tbl_size = 1 << HTBL_BITS;
+ tbl = hf->tbl;
+ bitlen = hf->bitlen;
+ len_avail = hf->len_avail;
+ hf->tree_used = 0;
+ for (i = 0; i < len_avail; i++) {
+ uint16_t *p;
+ int len, cnt;
+ uint16_t bit;
+ int extlen;
+ struct htree_t *ht;
+
+ if (bitlen[i] == 0)
+ continue;
+ /* Get a bit pattern */
+ len = bitlen[i];
+ ptn = bitptn[len];
+ cnt = weight[len];
+ if (len <= HTBL_BITS) {
+ /* Calculate next bit pattern */
+ if ((bitptn[len] = ptn + cnt) > tbl_size)
+ return (0);/* Invalid */
+ /* Update the table */
+ p = &(tbl[ptn]);
+ if (cnt > 7) {
+ uint16_t *pc;
+
+ cnt -= 8;
+ pc = &p[cnt];
+ pc[0] = (uint16_t)i;
+ pc[1] = (uint16_t)i;
+ pc[2] = (uint16_t)i;
+ pc[3] = (uint16_t)i;
+ pc[4] = (uint16_t)i;
+ pc[5] = (uint16_t)i;
+ pc[6] = (uint16_t)i;
+ pc[7] = (uint16_t)i;
+ if (cnt > 7) {
+ cnt -= 8;
+ memcpy(&p[cnt], pc,
+ 8 * sizeof(uint16_t));
+ pc = &p[cnt];
+ while (cnt > 15) {
+ cnt -= 16;
+ memcpy(&p[cnt], pc,
+ 16 * sizeof(uint16_t));
+ }
+ }
+ if (cnt)
+ memcpy(p, pc, cnt * sizeof(uint16_t));
+ } else {
+ while (cnt > 1) {
+ p[--cnt] = (uint16_t)i;
+ p[--cnt] = (uint16_t)i;
+ }
+ if (cnt)
+ p[--cnt] = (uint16_t)i;
+ }
+ continue;
+ }
+
+ /*
+ * A bit length is too big to be housed to a direct table,
+ * so we use a tree model for its extra bits.
+ */
+ bitptn[len] = ptn + cnt;
+ bit = 1U << (diffbits -1);
+ extlen = len - HTBL_BITS;
+
+ p = &(tbl[ptn >> diffbits]);
+ if (*p == 0) {
+ *p = len_avail + hf->tree_used;
+ ht = &(hf->tree[hf->tree_used++]);
+ if (hf->tree_used > hf->tree_avail)
+ return (0);/* Invalid */
+ ht->left = 0;
+ ht->right = 0;
+ } else {
+ if (*p < len_avail ||
+ *p >= (len_avail + hf->tree_used))
+ return (0);/* Invalid */
+ ht = &(hf->tree[*p - len_avail]);
+ }
+ while (--extlen > 0) {
+ if (ptn & bit) {
+ if (ht->left < len_avail) {
+ ht->left = len_avail + hf->tree_used;
+ ht = &(hf->tree[hf->tree_used++]);
+ if (hf->tree_used > hf->tree_avail)
+ return (0);/* Invalid */
+ ht->left = 0;
+ ht->right = 0;
+ } else {
+ ht = &(hf->tree[ht->left - len_avail]);
+ }
+ } else {
+ if (ht->right < len_avail) {
+ ht->right = len_avail + hf->tree_used;
+ ht = &(hf->tree[hf->tree_used++]);
+ if (hf->tree_used > hf->tree_avail)
+ return (0);/* Invalid */
+ ht->left = 0;
+ ht->right = 0;
+ } else {
+ ht = &(hf->tree[ht->right - len_avail]);
+ }
+ }
+ bit >>= 1;
+ }
+ if (ptn & bit) {
+ if (ht->left != 0)
+ return (0);/* Invalid */
+ ht->left = (uint16_t)i;
+ } else {
+ if (ht->right != 0)
+ return (0);/* Invalid */
+ ht->right = (uint16_t)i;
+ }
+ }
+ return (1);
+}
+
+static int
+lzh_decode_huffman_tree(struct huffman *hf, unsigned rbits, int c)
+{
+ struct htree_t *ht;
+ int extlen;
+
+ ht = hf->tree;
+ extlen = hf->shift_bits;
+ while (c >= hf->len_avail) {
+ c -= hf->len_avail;
+ if (extlen-- <= 0 || c >= hf->tree_used)
+ return (0);
+ if (rbits & (1U << extlen))
+ c = ht[c].left;
+ else
+ c = ht[c].right;
+ }
+ return (c);
+}
+
+static inline int
+lzh_decode_huffman(struct huffman *hf, unsigned rbits)
+{
+ int c;
+ /*
+ * At first search an index table for a bit pattern.
+ * If it fails, search a huffman tree for.
+ */
+ c = hf->tbl[rbits >> hf->shift_bits];
+ if (c < hf->len_avail || hf->len_avail == 0)
+ return (c);
+ /* This bit pattern needs to be found out at a huffman tree. */
+ return (lzh_decode_huffman_tree(hf, rbits, c));
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_mtree.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_mtree.c
new file mode 100644
index 00000000000..a5fa30e3c2b
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_mtree.c
@@ -0,0 +1,2156 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2008 Joerg Sonnenberger
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_mtree.c 201165 2009-12-29 05:52:13Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stddef.h>
+/* #include <stdint.h> */ /* See archive_platform.h */
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_private.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_read_private.h"
+#include "archive_string.h"
+#include "archive_pack_dev.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#define MTREE_HAS_DEVICE 0x0001
+#define MTREE_HAS_FFLAGS 0x0002
+#define MTREE_HAS_GID 0x0004
+#define MTREE_HAS_GNAME 0x0008
+#define MTREE_HAS_MTIME 0x0010
+#define MTREE_HAS_NLINK 0x0020
+#define MTREE_HAS_PERM 0x0040
+#define MTREE_HAS_SIZE 0x0080
+#define MTREE_HAS_TYPE 0x0100
+#define MTREE_HAS_UID 0x0200
+#define MTREE_HAS_UNAME 0x0400
+
+#define MTREE_HAS_OPTIONAL 0x0800
+#define MTREE_HAS_NOCHANGE 0x1000 /* FreeBSD specific */
+
+#define MAX_LINE_LEN (1024 * 1024)
+
+struct mtree_option {
+ struct mtree_option *next;
+ char *value;
+};
+
+struct mtree_entry {
+ struct archive_rb_node rbnode;
+ struct mtree_entry *next_dup;
+ struct mtree_entry *next;
+ struct mtree_option *options;
+ char *name;
+ char full;
+ char used;
+};
+
+struct mtree {
+ struct archive_string line;
+ size_t buffsize;
+ char *buff;
+ int64_t offset;
+ int fd;
+ int archive_format;
+ const char *archive_format_name;
+ struct mtree_entry *entries;
+ struct mtree_entry *this_entry;
+ struct archive_rb_tree entry_rbtree;
+ struct archive_string current_dir;
+ struct archive_string contents_name;
+
+ struct archive_entry_linkresolver *resolver;
+ struct archive_rb_tree rbtree;
+
+ int64_t cur_size;
+ char checkfs;
+};
+
+static int bid_keycmp(const char *, const char *, ssize_t);
+static int cleanup(struct archive_read *);
+static int detect_form(struct archive_read *, int *);
+static int mtree_bid(struct archive_read *, int);
+static int parse_file(struct archive_read *, struct archive_entry *,
+ struct mtree *, struct mtree_entry *, int *);
+static void parse_escapes(char *, struct mtree_entry *);
+static int parse_line(struct archive_read *, struct archive_entry *,
+ struct mtree *, struct mtree_entry *, int *);
+static int parse_keyword(struct archive_read *, struct mtree *,
+ struct archive_entry *, struct mtree_option *, int *);
+static int read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset);
+static ssize_t readline(struct archive_read *, struct mtree *, char **, ssize_t);
+static int skip(struct archive_read *a);
+static int read_header(struct archive_read *,
+ struct archive_entry *);
+static int64_t mtree_atol(char **, int base);
+#ifndef HAVE_STRNLEN
+static size_t mtree_strnlen(const char *, size_t);
+#endif
+
+/*
+ * There's no standard for TIME_T_MAX/TIME_T_MIN. So we compute them
+ * here. TODO: Move this to configure time, but be careful
+ * about cross-compile environments.
+ */
+static int64_t
+get_time_t_max(void)
+{
+#if defined(TIME_T_MAX)
+ return TIME_T_MAX;
+#else
+ /* ISO C allows time_t to be a floating-point type,
+ but POSIX requires an integer type. The following
+ should work on any system that follows the POSIX
+ conventions. */
+ if (((time_t)0) < ((time_t)-1)) {
+ /* Time_t is unsigned */
+ return (~(time_t)0);
+ } else {
+ /* Time_t is signed. */
+ /* Assume it's the same as int64_t or int32_t */
+ if (sizeof(time_t) == sizeof(int64_t)) {
+ return (time_t)INT64_MAX;
+ } else {
+ return (time_t)INT32_MAX;
+ }
+ }
+#endif
+}
+
+static int64_t
+get_time_t_min(void)
+{
+#if defined(TIME_T_MIN)
+ return TIME_T_MIN;
+#else
+ if (((time_t)0) < ((time_t)-1)) {
+ /* Time_t is unsigned */
+ return (time_t)0;
+ } else {
+ /* Time_t is signed. */
+ if (sizeof(time_t) == sizeof(int64_t)) {
+ return (time_t)INT64_MIN;
+ } else {
+ return (time_t)INT32_MIN;
+ }
+ }
+#endif
+}
+
+#ifdef HAVE_STRNLEN
+#define mtree_strnlen(a,b) strnlen(a,b)
+#else
+static size_t
+mtree_strnlen(const char *p, size_t maxlen)
+{
+ size_t i;
+
+ for (i = 0; i <= maxlen; i++) {
+ if (p[i] == 0)
+ break;
+ }
+ if (i > maxlen)
+ return (-1);/* invalid */
+ return (i);
+}
+#endif
+
+static int
+archive_read_format_mtree_options(struct archive_read *a,
+ const char *key, const char *val)
+{
+ struct mtree *mtree;
+
+ mtree = (struct mtree *)(a->format->data);
+ if (strcmp(key, "checkfs") == 0) {
+ /* Allows to read information missing from the mtree from the file system */
+ if (val == NULL || val[0] == 0) {
+ mtree->checkfs = 0;
+ } else {
+ mtree->checkfs = 1;
+ }
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static void
+free_options(struct mtree_option *head)
+{
+ struct mtree_option *next;
+
+ for (; head != NULL; head = next) {
+ next = head->next;
+ free(head->value);
+ free(head);
+ }
+}
+
+static int
+mtree_cmp_node(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct mtree_entry *e1 = (const struct mtree_entry *)n1;
+ const struct mtree_entry *e2 = (const struct mtree_entry *)n2;
+
+ return (strcmp(e1->name, e2->name));
+}
+
+static int
+mtree_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ const struct mtree_entry *e = (const struct mtree_entry *)n;
+
+ return (strcmp(e->name, key));
+}
+
+int
+archive_read_support_format_mtree(struct archive *_a)
+{
+ static const struct archive_rb_tree_ops rb_ops = {
+ mtree_cmp_node, mtree_cmp_key,
+ };
+ struct archive_read *a = (struct archive_read *)_a;
+ struct mtree *mtree;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_mtree");
+
+ mtree = (struct mtree *)calloc(1, sizeof(*mtree));
+ if (mtree == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate mtree data");
+ return (ARCHIVE_FATAL);
+ }
+ mtree->checkfs = 0;
+ mtree->fd = -1;
+
+ __archive_rb_tree_init(&mtree->rbtree, &rb_ops);
+
+ r = __archive_read_register_format(a, mtree, "mtree",
+ mtree_bid, archive_read_format_mtree_options, read_header, read_data, skip, NULL, cleanup, NULL, NULL);
+
+ if (r != ARCHIVE_OK)
+ free(mtree);
+ return (ARCHIVE_OK);
+}
+
+static int
+cleanup(struct archive_read *a)
+{
+ struct mtree *mtree;
+ struct mtree_entry *p, *q;
+
+ mtree = (struct mtree *)(a->format->data);
+
+ p = mtree->entries;
+ while (p != NULL) {
+ q = p->next;
+ free(p->name);
+ free_options(p->options);
+ free(p);
+ p = q;
+ }
+ archive_string_free(&mtree->line);
+ archive_string_free(&mtree->current_dir);
+ archive_string_free(&mtree->contents_name);
+ archive_entry_linkresolver_free(mtree->resolver);
+
+ free(mtree->buff);
+ free(mtree);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+get_line_size(const char *b, ssize_t avail, ssize_t *nlsize)
+{
+ ssize_t len;
+
+ len = 0;
+ while (len < avail) {
+ switch (*b) {
+ case '\0':/* Non-ascii character or control character. */
+ if (nlsize != NULL)
+ *nlsize = 0;
+ return (-1);
+ case '\r':
+ if (avail-len > 1 && b[1] == '\n') {
+ if (nlsize != NULL)
+ *nlsize = 2;
+ return (len+2);
+ }
+ /* FALL THROUGH */
+ case '\n':
+ if (nlsize != NULL)
+ *nlsize = 1;
+ return (len+1);
+ default:
+ b++;
+ len++;
+ break;
+ }
+ }
+ if (nlsize != NULL)
+ *nlsize = 0;
+ return (avail);
+}
+
+/*
+ * <---------------- ravail --------------------->
+ * <-- diff ------> <--- avail ----------------->
+ * <---- len ----------->
+ * | Previous lines | line being parsed nl extra |
+ * ^
+ * b
+ *
+ */
+static ssize_t
+next_line(struct archive_read *a,
+ const char **b, ssize_t *avail, ssize_t *ravail, ssize_t *nl)
+{
+ ssize_t len;
+ int quit;
+
+ quit = 0;
+ if (*avail == 0) {
+ *nl = 0;
+ len = 0;
+ } else
+ len = get_line_size(*b, *avail, nl);
+ /*
+ * Read bytes more while it does not reach the end of line.
+ */
+ while (*nl == 0 && len == *avail && !quit) {
+ ssize_t diff = *ravail - *avail;
+ size_t nbytes_req = (*ravail+1023) & ~1023U;
+ ssize_t tested;
+
+ /*
+ * Place an arbitrary limit on the line length.
+ * mtree is almost free-form input and without line length limits,
+ * it can consume a lot of memory.
+ */
+ if (len >= MAX_LINE_LEN)
+ return (-1);
+
+ /* Increase reading bytes if it is not enough to at least
+ * new two lines. */
+ if (nbytes_req < (size_t)*ravail + 160)
+ nbytes_req <<= 1;
+
+ *b = __archive_read_ahead(a, nbytes_req, avail);
+ if (*b == NULL) {
+ if (*ravail >= *avail)
+ return (0);
+ /* Reading bytes reaches the end of file. */
+ *b = __archive_read_ahead(a, *avail, avail);
+ quit = 1;
+ }
+ *ravail = *avail;
+ *b += diff;
+ *avail -= diff;
+ tested = len;/* Skip some bytes we already determined. */
+ len = get_line_size(*b + len, *avail - len, nl);
+ if (len >= 0)
+ len += tested;
+ }
+ return (len);
+}
+
+/*
+ * Compare characters with a mtree keyword.
+ * Returns the length of a mtree keyword if matched.
+ * Returns 0 if not matched.
+ */
+static int
+bid_keycmp(const char *p, const char *key, ssize_t len)
+{
+ int match_len = 0;
+
+ while (len > 0 && *p && *key) {
+ if (*p == *key) {
+ --len;
+ ++p;
+ ++key;
+ ++match_len;
+ continue;
+ }
+ return (0);/* Not match */
+ }
+ if (*key != '\0')
+ return (0);/* Not match */
+
+ /* A following character should be specified characters */
+ if (p[0] == '=' || p[0] == ' ' || p[0] == '\t' ||
+ p[0] == '\n' || p[0] == '\r' ||
+ (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r')))
+ return (match_len);
+ return (0);/* Not match */
+}
+
+/*
+ * Test whether the characters 'p' has is mtree keyword.
+ * Returns the length of a detected keyword.
+ * Returns 0 if any keywords were not found.
+ */
+static int
+bid_keyword(const char *p, ssize_t len)
+{
+ static const char * const keys_c[] = {
+ "content", "contents", "cksum", NULL
+ };
+ static const char * const keys_df[] = {
+ "device", "flags", NULL
+ };
+ static const char * const keys_g[] = {
+ "gid", "gname", NULL
+ };
+ static const char * const keys_il[] = {
+ "ignore", "inode", "link", NULL
+ };
+ static const char * const keys_m[] = {
+ "md5", "md5digest", "mode", NULL
+ };
+ static const char * const keys_no[] = {
+ "nlink", "nochange", "optional", NULL
+ };
+ static const char * const keys_r[] = {
+ "resdevice", "rmd160", "rmd160digest", NULL
+ };
+ static const char * const keys_s[] = {
+ "sha1", "sha1digest",
+ "sha256", "sha256digest",
+ "sha384", "sha384digest",
+ "sha512", "sha512digest",
+ "size", NULL
+ };
+ static const char * const keys_t[] = {
+ "tags", "time", "type", NULL
+ };
+ static const char * const keys_u[] = {
+ "uid", "uname", NULL
+ };
+ const char * const *keys;
+ int i;
+
+ switch (*p) {
+ case 'c': keys = keys_c; break;
+ case 'd': case 'f': keys = keys_df; break;
+ case 'g': keys = keys_g; break;
+ case 'i': case 'l': keys = keys_il; break;
+ case 'm': keys = keys_m; break;
+ case 'n': case 'o': keys = keys_no; break;
+ case 'r': keys = keys_r; break;
+ case 's': keys = keys_s; break;
+ case 't': keys = keys_t; break;
+ case 'u': keys = keys_u; break;
+ default: return (0);/* Unknown key */
+ }
+
+ for (i = 0; keys[i] != NULL; i++) {
+ int l = bid_keycmp(p, keys[i], len);
+ if (l > 0)
+ return (l);
+ }
+ return (0);/* Unknown key */
+}
+
+/*
+ * Test whether there is a set of mtree keywords.
+ * Returns the number of keyword.
+ * Returns -1 if we got incorrect sequence.
+ * This function expects a set of "<space characters>keyword=value".
+ * When "unset" is specified, expects a set of "<space characters>keyword".
+ */
+static int
+bid_keyword_list(const char *p, ssize_t len, int unset, int last_is_path)
+{
+ int l;
+ int keycnt = 0;
+
+ while (len > 0 && *p) {
+ int blank = 0;
+
+ /* Test whether there are blank characters in the line. */
+ while (len >0 && (*p == ' ' || *p == '\t')) {
+ ++p;
+ --len;
+ blank = 1;
+ }
+ if (*p == '\n' || *p == '\r')
+ break;
+ if (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r'))
+ break;
+ if (!blank && !last_is_path) /* No blank character. */
+ return (-1);
+ if (last_is_path && len == 0)
+ return (keycnt);
+
+ if (unset) {
+ l = bid_keycmp(p, "all", len);
+ if (l > 0)
+ return (1);
+ }
+ /* Test whether there is a correct key in the line. */
+ l = bid_keyword(p, len);
+ if (l == 0)
+ return (-1);/* Unknown keyword was found. */
+ p += l;
+ len -= l;
+ keycnt++;
+
+ /* Skip value */
+ if (*p == '=') {
+ int value = 0;
+ ++p;
+ --len;
+ while (len > 0 && *p != ' ' && *p != '\t') {
+ ++p;
+ --len;
+ value = 1;
+ }
+ /* A keyword should have a its value unless
+ * "/unset" operation. */
+ if (!unset && value == 0)
+ return (-1);
+ }
+ }
+ return (keycnt);
+}
+
+static int
+bid_entry(const char *p, ssize_t len, ssize_t nl, int *last_is_path)
+{
+ int f = 0;
+ static const unsigned char safe_char[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+ /* !"$%&'()*+,-./ EXCLUSION:( )(#) */
+ 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
+ /* 0123456789:;<>? EXCLUSION:(=) */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */
+ /* @ABCDEFGHIJKLMNO */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
+ /* PQRSTUVWXYZ[\]^_ */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
+ /* `abcdefghijklmno */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
+ /* pqrstuvwxyz{|}~ */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
+ };
+ ssize_t ll;
+ const char *pp = p;
+ const char * const pp_end = pp + len;
+
+ *last_is_path = 0;
+ /*
+ * Skip the path-name which is quoted.
+ */
+ for (;pp < pp_end; ++pp) {
+ if (!safe_char[*(const unsigned char *)pp]) {
+ if (*pp != ' ' && *pp != '\t' && *pp != '\r'
+ && *pp != '\n')
+ f = 0;
+ break;
+ }
+ f = 1;
+ }
+ ll = pp_end - pp;
+
+ /* If a path-name was not found at the first, try to check
+ * a mtree format(a.k.a form D) ``NetBSD's mtree -D'' creates,
+ * which places the path-name at the last. */
+ if (f == 0) {
+ const char *pb = p + len - nl;
+ int name_len = 0;
+ int slash;
+
+ /* The form D accepts only a single line for an entry. */
+ if (pb-2 >= p &&
+ pb[-1] == '\\' && (pb[-2] == ' ' || pb[-2] == '\t'))
+ return (-1);
+ if (pb-1 >= p && pb[-1] == '\\')
+ return (-1);
+
+ slash = 0;
+ while (p <= --pb && *pb != ' ' && *pb != '\t') {
+ if (!safe_char[*(const unsigned char *)pb])
+ return (-1);
+ name_len++;
+ /* The pathname should have a slash in this
+ * format. */
+ if (*pb == '/')
+ slash = 1;
+ }
+ if (name_len == 0 || slash == 0)
+ return (-1);
+ /* If '/' is placed at the first in this field, this is not
+ * a valid filename. */
+ if (pb[1] == '/')
+ return (-1);
+ ll = len - nl - name_len;
+ pp = p;
+ *last_is_path = 1;
+ }
+
+ return (bid_keyword_list(pp, ll, 0, *last_is_path));
+}
+
+#define MAX_BID_ENTRY 3
+
+static int
+mtree_bid(struct archive_read *a, int best_bid)
+{
+ const char *signature = "#mtree";
+ const char *p;
+
+ (void)best_bid; /* UNUSED */
+
+ /* Now let's look at the actual header and see if it matches. */
+ p = __archive_read_ahead(a, strlen(signature), NULL);
+ if (p == NULL)
+ return (-1);
+
+ if (memcmp(p, signature, strlen(signature)) == 0)
+ return (8 * (int)strlen(signature));
+
+ /*
+ * There is not a mtree signature. Let's try to detect mtree format.
+ */
+ return (detect_form(a, NULL));
+}
+
+static int
+detect_form(struct archive_read *a, int *is_form_d)
+{
+ const char *p;
+ ssize_t avail, ravail;
+ ssize_t len, nl;
+ int entry_cnt = 0, multiline = 0;
+ int form_D = 0;/* The archive is generated by `NetBSD mtree -D'
+ * (In this source we call it `form D') . */
+
+ if (is_form_d != NULL)
+ *is_form_d = 0;
+ p = __archive_read_ahead(a, 1, &avail);
+ if (p == NULL)
+ return (-1);
+ ravail = avail;
+ for (;;) {
+ len = next_line(a, &p, &avail, &ravail, &nl);
+ /* The terminal character of the line should be
+ * a new line character, '\r\n' or '\n'. */
+ if (len <= 0 || nl == 0)
+ break;
+ if (!multiline) {
+ /* Leading whitespace is never significant,
+ * ignore it. */
+ while (len > 0 && (*p == ' ' || *p == '\t')) {
+ ++p;
+ --avail;
+ --len;
+ }
+ /* Skip comment or empty line. */
+ if (p[0] == '#' || p[0] == '\n' || p[0] == '\r') {
+ p += len;
+ avail -= len;
+ continue;
+ }
+ } else {
+ /* A continuance line; the terminal
+ * character of previous line was '\' character. */
+ if (bid_keyword_list(p, len, 0, 0) <= 0)
+ break;
+ if (p[len-nl-1] != '\\') {
+ if (multiline == 1 &&
+ ++entry_cnt >= MAX_BID_ENTRY)
+ break;
+ multiline = 0;
+ }
+ p += len;
+ avail -= len;
+ continue;
+ }
+ if (p[0] != '/') {
+ int last_is_path, keywords;
+
+ keywords = bid_entry(p, len, nl, &last_is_path);
+ if (keywords >= 0) {
+ if (form_D == 0) {
+ if (last_is_path)
+ form_D = 1;
+ else if (keywords > 0)
+ /* This line is not `form D'. */
+ form_D = -1;
+ } else if (form_D == 1) {
+ if (!last_is_path && keywords > 0)
+ /* This this is not `form D'
+ * and We cannot accept mixed
+ * format. */
+ break;
+ }
+ if (!last_is_path && p[len-nl-1] == '\\')
+ /* This line continues. */
+ multiline = 1;
+ else {
+ /* We've got plenty of correct lines
+ * to assume that this file is a mtree
+ * format. */
+ if (++entry_cnt >= MAX_BID_ENTRY)
+ break;
+ }
+ } else
+ break;
+ } else if (len > 4 && strncmp(p, "/set", 4) == 0) {
+ if (bid_keyword_list(p+4, len-4, 0, 0) <= 0)
+ break;
+ /* This line continues. */
+ if (p[len-nl-1] == '\\')
+ multiline = 2;
+ } else if (len > 6 && strncmp(p, "/unset", 6) == 0) {
+ if (bid_keyword_list(p+6, len-6, 1, 0) <= 0)
+ break;
+ /* This line continues. */
+ if (p[len-nl-1] == '\\')
+ multiline = 2;
+ } else
+ break;
+
+ /* Test next line. */
+ p += len;
+ avail -= len;
+ }
+ if (entry_cnt >= MAX_BID_ENTRY || (entry_cnt > 0 && len == 0)) {
+ if (is_form_d != NULL) {
+ if (form_D == 1)
+ *is_form_d = 1;
+ }
+ return (32);
+ }
+
+ return (0);
+}
+
+/*
+ * The extended mtree format permits multiple lines specifying
+ * attributes for each file. For those entries, only the last line
+ * is actually used. Practically speaking, that means we have
+ * to read the entire mtree file into memory up front.
+ *
+ * The parsing is done in two steps. First, it is decided if a line
+ * changes the global defaults and if it is, processed accordingly.
+ * Otherwise, the options of the line are merged with the current
+ * global options.
+ */
+static int
+add_option(struct archive_read *a, struct mtree_option **global,
+ const char *value, size_t len)
+{
+ struct mtree_option *opt;
+
+ if ((opt = malloc(sizeof(*opt))) == NULL) {
+ archive_set_error(&a->archive, errno, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ if ((opt->value = malloc(len + 1)) == NULL) {
+ free(opt);
+ archive_set_error(&a->archive, errno, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(opt->value, value, len);
+ opt->value[len] = '\0';
+ opt->next = *global;
+ *global = opt;
+ return (ARCHIVE_OK);
+}
+
+static void
+remove_option(struct mtree_option **global, const char *value, size_t len)
+{
+ struct mtree_option *iter, *last;
+
+ last = NULL;
+ for (iter = *global; iter != NULL; last = iter, iter = iter->next) {
+ if (strncmp(iter->value, value, len) == 0 &&
+ (iter->value[len] == '\0' ||
+ iter->value[len] == '='))
+ break;
+ }
+ if (iter == NULL)
+ return;
+ if (last == NULL)
+ *global = iter->next;
+ else
+ last->next = iter->next;
+
+ free(iter->value);
+ free(iter);
+}
+
+static int
+process_global_set(struct archive_read *a,
+ struct mtree_option **global, const char *line)
+{
+ const char *next, *eq;
+ size_t len;
+ int r;
+
+ line += 4;
+ for (;;) {
+ next = line + strspn(line, " \t\r\n");
+ if (*next == '\0')
+ return (ARCHIVE_OK);
+ line = next;
+ next = line + strcspn(line, " \t\r\n");
+ eq = strchr(line, '=');
+ if (eq > next)
+ len = next - line;
+ else
+ len = eq - line;
+
+ remove_option(global, line, len);
+ r = add_option(a, global, line, next - line);
+ if (r != ARCHIVE_OK)
+ return (r);
+ line = next;
+ }
+}
+
+static int
+process_global_unset(struct archive_read *a,
+ struct mtree_option **global, const char *line)
+{
+ const char *next;
+ size_t len;
+
+ line += 6;
+ if (strchr(line, '=') != NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "/unset shall not contain `='");
+ return ARCHIVE_FATAL;
+ }
+
+ for (;;) {
+ next = line + strspn(line, " \t\r\n");
+ if (*next == '\0')
+ return (ARCHIVE_OK);
+ line = next;
+ len = strcspn(line, " \t\r\n");
+
+ if (len == 3 && strncmp(line, "all", 3) == 0) {
+ free_options(*global);
+ *global = NULL;
+ } else {
+ remove_option(global, line, len);
+ }
+
+ line += len;
+ }
+}
+
+static int
+process_add_entry(struct archive_read *a, struct mtree *mtree,
+ struct mtree_option **global, const char *line, ssize_t line_len,
+ struct mtree_entry **last_entry, int is_form_d)
+{
+ struct mtree_entry *entry;
+ struct mtree_option *iter;
+ const char *next, *eq, *name, *end;
+ size_t name_len, len;
+ int r, i;
+
+ if ((entry = malloc(sizeof(*entry))) == NULL) {
+ archive_set_error(&a->archive, errno, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ entry->next = NULL;
+ entry->options = NULL;
+ entry->name = NULL;
+ entry->used = 0;
+ entry->full = 0;
+
+ /* Add this entry to list. */
+ if (*last_entry == NULL)
+ mtree->entries = entry;
+ else
+ (*last_entry)->next = entry;
+ *last_entry = entry;
+
+ if (is_form_d) {
+ /* Filename is last item on line. */
+ /* Adjust line_len to trim trailing whitespace */
+ while (line_len > 0) {
+ char last_character = line[line_len - 1];
+ if (last_character == '\r'
+ || last_character == '\n'
+ || last_character == '\t'
+ || last_character == ' ') {
+ line_len--;
+ } else {
+ break;
+ }
+ }
+ /* Name starts after the last whitespace separator */
+ name = line;
+ for (i = 0; i < line_len; i++) {
+ if (line[i] == '\r'
+ || line[i] == '\n'
+ || line[i] == '\t'
+ || line[i] == ' ') {
+ name = line + i + 1;
+ }
+ }
+ name_len = line + line_len - name;
+ end = name;
+ } else {
+ /* Filename is first item on line */
+ name_len = strcspn(line, " \t\r\n");
+ name = line;
+ line += name_len;
+ end = line + line_len;
+ }
+ /* name/name_len is the name within the line. */
+ /* line..end brackets the entire line except the name */
+
+ if ((entry->name = malloc(name_len + 1)) == NULL) {
+ archive_set_error(&a->archive, errno, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ memcpy(entry->name, name, name_len);
+ entry->name[name_len] = '\0';
+ parse_escapes(entry->name, entry);
+
+ entry->next_dup = NULL;
+ if (entry->full) {
+ if (!__archive_rb_tree_insert_node(&mtree->rbtree, &entry->rbnode)) {
+ struct mtree_entry *alt;
+ alt = (struct mtree_entry *)__archive_rb_tree_find_node(
+ &mtree->rbtree, entry->name);
+ if (alt != NULL) {
+ while (alt->next_dup)
+ alt = alt->next_dup;
+ alt->next_dup = entry;
+ }
+ }
+ }
+
+ for (iter = *global; iter != NULL; iter = iter->next) {
+ r = add_option(a, &entry->options, iter->value,
+ strlen(iter->value));
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ for (;;) {
+ next = line + strspn(line, " \t\r\n");
+ if (*next == '\0')
+ return (ARCHIVE_OK);
+ if (next >= end)
+ return (ARCHIVE_OK);
+ line = next;
+ next = line + strcspn(line, " \t\r\n");
+ eq = strchr(line, '=');
+ if (eq == NULL || eq > next)
+ len = next - line;
+ else
+ len = eq - line;
+
+ remove_option(&entry->options, line, len);
+ r = add_option(a, &entry->options, line, next - line);
+ if (r != ARCHIVE_OK)
+ return (r);
+ line = next;
+ }
+}
+
+static int
+read_mtree(struct archive_read *a, struct mtree *mtree)
+{
+ ssize_t len;
+ uintmax_t counter;
+ char *p, *s;
+ struct mtree_option *global;
+ struct mtree_entry *last_entry;
+ int r, is_form_d;
+
+ mtree->archive_format = ARCHIVE_FORMAT_MTREE;
+ mtree->archive_format_name = "mtree";
+
+ global = NULL;
+ last_entry = NULL;
+
+ (void)detect_form(a, &is_form_d);
+
+ for (counter = 1; ; ++counter) {
+ r = ARCHIVE_OK;
+ len = readline(a, mtree, &p, 65536);
+ if (len == 0) {
+ mtree->this_entry = mtree->entries;
+ free_options(global);
+ return (ARCHIVE_OK);
+ }
+ if (len < 0) {
+ free_options(global);
+ return ((int)len);
+ }
+ /* Leading whitespace is never significant, ignore it. */
+ while (*p == ' ' || *p == '\t') {
+ ++p;
+ --len;
+ }
+ /* Skip content lines and blank lines. */
+ if (*p == '#')
+ continue;
+ if (*p == '\r' || *p == '\n' || *p == '\0')
+ continue;
+ /* Non-printable characters are not allowed */
+ for (s = p;s < p + len - 1; s++) {
+ if (!isprint((unsigned char)*s) && *s != '\t') {
+ r = ARCHIVE_FATAL;
+ break;
+ }
+ }
+ if (r != ARCHIVE_OK)
+ break;
+ if (*p != '/') {
+ r = process_add_entry(a, mtree, &global, p, len,
+ &last_entry, is_form_d);
+ } else if (len > 4 && strncmp(p, "/set", 4) == 0) {
+ if (p[4] != ' ' && p[4] != '\t')
+ break;
+ r = process_global_set(a, &global, p);
+ } else if (len > 6 && strncmp(p, "/unset", 6) == 0) {
+ if (p[6] != ' ' && p[6] != '\t')
+ break;
+ r = process_global_unset(a, &global, p);
+ } else
+ break;
+
+ if (r != ARCHIVE_OK) {
+ free_options(global);
+ return r;
+ }
+ }
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't parse line %ju", counter);
+ free_options(global);
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Read in the entire mtree file into memory on the first request.
+ * Then use the next unused file to satisfy each header request.
+ */
+static int
+read_header(struct archive_read *a, struct archive_entry *entry)
+{
+ struct mtree *mtree;
+ char *p;
+ int r, use_next;
+
+ mtree = (struct mtree *)(a->format->data);
+
+ if (mtree->fd >= 0) {
+ close(mtree->fd);
+ mtree->fd = -1;
+ }
+
+ if (mtree->entries == NULL) {
+ mtree->resolver = archive_entry_linkresolver_new();
+ if (mtree->resolver == NULL)
+ return ARCHIVE_FATAL;
+ archive_entry_linkresolver_set_strategy(mtree->resolver,
+ ARCHIVE_FORMAT_MTREE);
+ r = read_mtree(a, mtree);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ a->archive.archive_format = mtree->archive_format;
+ a->archive.archive_format_name = mtree->archive_format_name;
+
+ for (;;) {
+ if (mtree->this_entry == NULL)
+ return (ARCHIVE_EOF);
+ if (strcmp(mtree->this_entry->name, "..") == 0) {
+ mtree->this_entry->used = 1;
+ if (archive_strlen(&mtree->current_dir) > 0) {
+ /* Roll back current path. */
+ p = mtree->current_dir.s
+ + mtree->current_dir.length - 1;
+ while (p >= mtree->current_dir.s && *p != '/')
+ --p;
+ if (p >= mtree->current_dir.s)
+ --p;
+ mtree->current_dir.length
+ = p - mtree->current_dir.s + 1;
+ }
+ }
+ if (!mtree->this_entry->used) {
+ use_next = 0;
+ r = parse_file(a, entry, mtree, mtree->this_entry,
+ &use_next);
+ if (use_next == 0)
+ return (r);
+ }
+ mtree->this_entry = mtree->this_entry->next;
+ }
+}
+
+/*
+ * A single file can have multiple lines contribute specifications.
+ * Parse as many lines as necessary, then pull additional information
+ * from a backing file on disk as necessary.
+ */
+static int
+parse_file(struct archive_read *a, struct archive_entry *entry,
+ struct mtree *mtree, struct mtree_entry *mentry, int *use_next)
+{
+ const char *path;
+ struct stat st_storage, *st;
+ struct mtree_entry *mp;
+ struct archive_entry *sparse_entry;
+ int r = ARCHIVE_OK, r1, parsed_kws;
+
+ mentry->used = 1;
+
+ /* Initialize reasonable defaults. */
+ archive_entry_set_filetype(entry, AE_IFREG);
+ archive_entry_set_size(entry, 0);
+ archive_string_empty(&mtree->contents_name);
+
+ /* Parse options from this line. */
+ parsed_kws = 0;
+ r = parse_line(a, entry, mtree, mentry, &parsed_kws);
+
+ if (mentry->full) {
+ archive_entry_copy_pathname(entry, mentry->name);
+ /*
+ * "Full" entries are allowed to have multiple lines
+ * and those lines aren't required to be adjacent. We
+ * don't support multiple lines for "relative" entries
+ * nor do we make any attempt to merge data from
+ * separate "relative" and "full" entries. (Merging
+ * "relative" and "full" entries would require dealing
+ * with pathname canonicalization, which is a very
+ * tricky subject.)
+ */
+ mp = (struct mtree_entry *)__archive_rb_tree_find_node(
+ &mtree->rbtree, mentry->name);
+ for (; mp; mp = mp->next_dup) {
+ if (mp->full && !mp->used) {
+ /* Later lines override earlier ones. */
+ mp->used = 1;
+ r1 = parse_line(a, entry, mtree, mp, &parsed_kws);
+ if (r1 < r)
+ r = r1;
+ }
+ }
+ } else {
+ /*
+ * Relative entries require us to construct
+ * the full path and possibly update the
+ * current directory.
+ */
+ size_t n = archive_strlen(&mtree->current_dir);
+ if (n > 0)
+ archive_strcat(&mtree->current_dir, "/");
+ archive_strcat(&mtree->current_dir, mentry->name);
+ archive_entry_copy_pathname(entry, mtree->current_dir.s);
+ if (archive_entry_filetype(entry) != AE_IFDIR)
+ mtree->current_dir.length = n;
+ }
+
+ if (mtree->checkfs) {
+ /*
+ * Try to open and stat the file to get the real size
+ * and other file info. It would be nice to avoid
+ * this here so that getting a listing of an mtree
+ * wouldn't require opening every referenced contents
+ * file. But then we wouldn't know the actual
+ * contents size, so I don't see a really viable way
+ * around this. (Also, we may want to someday pull
+ * other unspecified info from the contents file on
+ * disk.)
+ */
+ mtree->fd = -1;
+ if (archive_strlen(&mtree->contents_name) > 0)
+ path = mtree->contents_name.s;
+ else
+ path = archive_entry_pathname(entry);
+
+ if (archive_entry_filetype(entry) == AE_IFREG ||
+ archive_entry_filetype(entry) == AE_IFDIR) {
+ mtree->fd = open(path, O_RDONLY | O_BINARY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(mtree->fd);
+ if (mtree->fd == -1 && (
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /*
+ * On Windows, attempting to open a file with an
+ * invalid name result in EINVAL (Error 22)
+ */
+ (errno != ENOENT && errno != EINVAL)
+#else
+ errno != ENOENT
+#endif
+ || archive_strlen(&mtree->contents_name) > 0)) {
+ archive_set_error(&a->archive, errno,
+ "Can't open %s", path);
+ r = ARCHIVE_WARN;
+ }
+ }
+
+ st = &st_storage;
+ if (mtree->fd >= 0) {
+ if (fstat(mtree->fd, st) == -1) {
+ archive_set_error(&a->archive, errno,
+ "Could not fstat %s", path);
+ r = ARCHIVE_WARN;
+ /* If we can't stat it, don't keep it open. */
+ close(mtree->fd);
+ mtree->fd = -1;
+ st = NULL;
+ }
+ }
+#ifdef HAVE_LSTAT
+ else if (lstat(path, st) == -1)
+#else
+ else if (la_stat(path, st) == -1)
+#endif
+ {
+ st = NULL;
+ }
+
+ /*
+ * Check for a mismatch between the type in the specification
+ * and the type of the contents object on disk.
+ */
+ if (st != NULL) {
+ if (((st->st_mode & S_IFMT) == S_IFREG &&
+ archive_entry_filetype(entry) == AE_IFREG)
+#ifdef S_IFLNK
+ ||((st->st_mode & S_IFMT) == S_IFLNK &&
+ archive_entry_filetype(entry) == AE_IFLNK)
+#endif
+#ifdef S_IFSOCK
+ ||((st->st_mode & S_IFSOCK) == S_IFSOCK &&
+ archive_entry_filetype(entry) == AE_IFSOCK)
+#endif
+#ifdef S_IFCHR
+ ||((st->st_mode & S_IFMT) == S_IFCHR &&
+ archive_entry_filetype(entry) == AE_IFCHR)
+#endif
+#ifdef S_IFBLK
+ ||((st->st_mode & S_IFMT) == S_IFBLK &&
+ archive_entry_filetype(entry) == AE_IFBLK)
+#endif
+ ||((st->st_mode & S_IFMT) == S_IFDIR &&
+ archive_entry_filetype(entry) == AE_IFDIR)
+#ifdef S_IFIFO
+ ||((st->st_mode & S_IFMT) == S_IFIFO &&
+ archive_entry_filetype(entry) == AE_IFIFO)
+#endif
+ ) {
+ /* Types match. */
+ } else {
+ /* Types don't match; bail out gracefully. */
+ if (mtree->fd >= 0)
+ close(mtree->fd);
+ mtree->fd = -1;
+ if (parsed_kws & MTREE_HAS_OPTIONAL) {
+ /* It's not an error for an optional
+ * entry to not match disk. */
+ *use_next = 1;
+ } else if (r == ARCHIVE_OK) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "mtree specification has different"
+ " type for %s",
+ archive_entry_pathname(entry));
+ r = ARCHIVE_WARN;
+ }
+ return (r);
+ }
+ }
+
+ /*
+ * If there is a contents file on disk, pick some of the
+ * metadata from that file. For most of these, we only
+ * set it from the contents if it wasn't already parsed
+ * from the specification.
+ */
+ if (st != NULL) {
+ if (((parsed_kws & MTREE_HAS_DEVICE) == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0) &&
+ (archive_entry_filetype(entry) == AE_IFCHR ||
+ archive_entry_filetype(entry) == AE_IFBLK))
+ archive_entry_set_rdev(entry, st->st_rdev);
+ if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME))
+ == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+ archive_entry_set_gid(entry, st->st_gid);
+ if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME))
+ == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+ archive_entry_set_uid(entry, st->st_uid);
+ if ((parsed_kws & MTREE_HAS_MTIME) == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0) {
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+ archive_entry_set_mtime(entry, st->st_mtime,
+ st->st_mtimespec.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+ archive_entry_set_mtime(entry, st->st_mtime,
+ st->st_mtim.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_MTIME_N
+ archive_entry_set_mtime(entry, st->st_mtime,
+ st->st_mtime_n);
+#elif HAVE_STRUCT_STAT_ST_UMTIME
+ archive_entry_set_mtime(entry, st->st_mtime,
+ st->st_umtime*1000);
+#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
+ archive_entry_set_mtime(entry, st->st_mtime,
+ st->st_mtime_usec*1000);
+#else
+ archive_entry_set_mtime(entry, st->st_mtime, 0);
+#endif
+ }
+ if ((parsed_kws & MTREE_HAS_NLINK) == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+ archive_entry_set_nlink(entry, st->st_nlink);
+ if ((parsed_kws & MTREE_HAS_PERM) == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+ archive_entry_set_perm(entry, st->st_mode);
+ if ((parsed_kws & MTREE_HAS_SIZE) == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+ archive_entry_set_size(entry, st->st_size);
+ archive_entry_set_ino(entry, st->st_ino);
+ archive_entry_set_dev(entry, st->st_dev);
+
+ archive_entry_linkify(mtree->resolver, &entry,
+ &sparse_entry);
+ } else if (parsed_kws & MTREE_HAS_OPTIONAL) {
+ /*
+ * Couldn't open the entry, stat it or the on-disk type
+ * didn't match. If this entry is optional, just
+ * ignore it and read the next header entry.
+ */
+ *use_next = 1;
+ return ARCHIVE_OK;
+ }
+ }
+
+ mtree->cur_size = archive_entry_size(entry);
+ mtree->offset = 0;
+
+ return r;
+}
+
+/*
+ * Each line contains a sequence of keywords.
+ */
+static int
+parse_line(struct archive_read *a, struct archive_entry *entry,
+ struct mtree *mtree, struct mtree_entry *mp, int *parsed_kws)
+{
+ struct mtree_option *iter;
+ int r = ARCHIVE_OK, r1;
+
+ for (iter = mp->options; iter != NULL; iter = iter->next) {
+ r1 = parse_keyword(a, mtree, entry, iter, parsed_kws);
+ if (r1 < r)
+ r = r1;
+ }
+ if (r == ARCHIVE_OK && (*parsed_kws & MTREE_HAS_TYPE) == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Missing type keyword in mtree specification");
+ return (ARCHIVE_WARN);
+ }
+ return (r);
+}
+
+/*
+ * Device entries have one of the following forms:
+ * - raw dev_t
+ * - format,major,minor[,subdevice]
+ * When parsing succeeded, `pdev' will contain the appropriate dev_t value.
+ */
+
+/* strsep() is not in C90, but strcspn() is. */
+/* Taken from http://unixpapa.com/incnote/string.html */
+static char *
+la_strsep(char **sp, const char *sep)
+{
+ char *p, *s;
+ if (sp == NULL || *sp == NULL || **sp == '\0')
+ return(NULL);
+ s = *sp;
+ p = s + strcspn(s, sep);
+ if (*p != '\0')
+ *p++ = '\0';
+ *sp = p;
+ return(s);
+}
+
+static int
+parse_device(dev_t *pdev, struct archive *a, char *val)
+{
+#define MAX_PACK_ARGS 3
+ unsigned long numbers[MAX_PACK_ARGS];
+ char *p, *dev;
+ int argc;
+ pack_t *pack;
+ dev_t result;
+ const char *error = NULL;
+
+ memset(pdev, 0, sizeof(*pdev));
+ if ((dev = strchr(val, ',')) != NULL) {
+ /*
+ * Device's major/minor are given in a specified format.
+ * Decode and pack it accordingly.
+ */
+ *dev++ = '\0';
+ if ((pack = pack_find(val)) == NULL) {
+ archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unknown format `%s'", val);
+ return ARCHIVE_WARN;
+ }
+ argc = 0;
+ while ((p = la_strsep(&dev, ",")) != NULL) {
+ if (*p == '\0') {
+ archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Missing number");
+ return ARCHIVE_WARN;
+ }
+ if (argc >= MAX_PACK_ARGS) {
+ archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Too many arguments");
+ return ARCHIVE_WARN;
+ }
+ numbers[argc++] = (unsigned long)mtree_atol(&p, 0);
+ }
+ if (argc < 2) {
+ archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Not enough arguments");
+ return ARCHIVE_WARN;
+ }
+ result = (*pack)(argc, numbers, &error);
+ if (error != NULL) {
+ archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+ "%s", error);
+ return ARCHIVE_WARN;
+ }
+ } else {
+ /* file system raw value. */
+ result = (dev_t)mtree_atol(&val, 0);
+ }
+ *pdev = result;
+ return ARCHIVE_OK;
+#undef MAX_PACK_ARGS
+}
+
+static int
+parse_hex_nibble(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return 10 + c - 'a';
+#if 0
+ /* XXX: Is uppercase something we should support? */
+ if (c >= 'A' && c <= 'F')
+ return 10 + c - 'A';
+#endif
+
+ return -1;
+}
+
+static int
+parse_digest(struct archive_read *a, struct archive_entry *entry,
+ const char *digest, int type)
+{
+ unsigned char digest_buf[64];
+ int high, low;
+ size_t i, j, len;
+
+ switch (type) {
+ case ARCHIVE_ENTRY_DIGEST_MD5:
+ len = sizeof(entry->digest.md5);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_RMD160:
+ len = sizeof(entry->digest.rmd160);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_SHA1:
+ len = sizeof(entry->digest.sha1);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_SHA256:
+ len = sizeof(entry->digest.sha256);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_SHA384:
+ len = sizeof(entry->digest.sha384);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_SHA512:
+ len = sizeof(entry->digest.sha512);
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal error: Unknown digest type");
+ return ARCHIVE_FATAL;
+ }
+
+ if (len > sizeof(digest_buf)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal error: Digest storage too large");
+ return ARCHIVE_FATAL;
+ }
+
+ len *= 2;
+
+ if (mtree_strnlen(digest, len+1) != len) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "incorrect digest length, ignoring");
+ return ARCHIVE_WARN;
+ }
+
+ for (i = 0, j = 0; i < len; i += 2, j++) {
+ high = parse_hex_nibble(digest[i]);
+ low = parse_hex_nibble(digest[i+1]);
+ if (high == -1 || low == -1) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "invalid digest data, ignoring");
+ return ARCHIVE_WARN;
+ }
+
+ digest_buf[j] = high << 4 | low;
+ }
+
+ return archive_entry_set_digest(entry, type, digest_buf);
+}
+
+/*
+ * Parse a single keyword and its value.
+ */
+static int
+parse_keyword(struct archive_read *a, struct mtree *mtree,
+ struct archive_entry *entry, struct mtree_option *opt, int *parsed_kws)
+{
+ char *val, *key;
+
+ key = opt->value;
+
+ if (*key == '\0')
+ return (ARCHIVE_OK);
+
+ if (strcmp(key, "nochange") == 0) {
+ *parsed_kws |= MTREE_HAS_NOCHANGE;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "optional") == 0) {
+ *parsed_kws |= MTREE_HAS_OPTIONAL;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "ignore") == 0) {
+ /*
+ * The mtree processing is not recursive, so
+ * recursion will only happen for explicitly listed
+ * entries.
+ */
+ return (ARCHIVE_OK);
+ }
+
+ val = strchr(key, '=');
+ if (val == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Malformed attribute \"%s\" (%d)", key, key[0]);
+ return (ARCHIVE_WARN);
+ }
+
+ *val = '\0';
+ ++val;
+
+ switch (key[0]) {
+ case 'c':
+ if (strcmp(key, "content") == 0
+ || strcmp(key, "contents") == 0) {
+ parse_escapes(val, NULL);
+ archive_strcpy(&mtree->contents_name, val);
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "cksum") == 0)
+ return (ARCHIVE_OK);
+ break;
+ case 'd':
+ if (strcmp(key, "device") == 0) {
+ /* stat(2) st_rdev field, e.g. the major/minor IDs
+ * of a char/block special file */
+ int r;
+ dev_t dev;
+
+ *parsed_kws |= MTREE_HAS_DEVICE;
+ r = parse_device(&dev, &a->archive, val);
+ if (r == ARCHIVE_OK)
+ archive_entry_set_rdev(entry, dev);
+ return r;
+ }
+ break;
+ case 'f':
+ if (strcmp(key, "flags") == 0) {
+ *parsed_kws |= MTREE_HAS_FFLAGS;
+ archive_entry_copy_fflags_text(entry, val);
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'g':
+ if (strcmp(key, "gid") == 0) {
+ *parsed_kws |= MTREE_HAS_GID;
+ archive_entry_set_gid(entry, mtree_atol(&val, 10));
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "gname") == 0) {
+ *parsed_kws |= MTREE_HAS_GNAME;
+ archive_entry_copy_gname(entry, val);
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'i':
+ if (strcmp(key, "inode") == 0) {
+ archive_entry_set_ino(entry, mtree_atol(&val, 10));
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'l':
+ if (strcmp(key, "link") == 0) {
+ parse_escapes(val, NULL);
+ archive_entry_copy_symlink(entry, val);
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'm':
+ if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0) {
+ return parse_digest(a, entry, val,
+ ARCHIVE_ENTRY_DIGEST_MD5);
+ }
+ if (strcmp(key, "mode") == 0) {
+ if (val[0] < '0' || val[0] > '7') {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Symbolic or non-octal mode \"%s\" unsupported", val);
+ return (ARCHIVE_WARN);
+ }
+ *parsed_kws |= MTREE_HAS_PERM;
+ archive_entry_set_perm(entry, (mode_t)mtree_atol(&val, 8));
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'n':
+ if (strcmp(key, "nlink") == 0) {
+ *parsed_kws |= MTREE_HAS_NLINK;
+ archive_entry_set_nlink(entry,
+ (unsigned int)mtree_atol(&val, 10));
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'r':
+ if (strcmp(key, "resdevice") == 0) {
+ /* stat(2) st_dev field, e.g. the device ID where the
+ * inode resides */
+ int r;
+ dev_t dev;
+
+ r = parse_device(&dev, &a->archive, val);
+ if (r == ARCHIVE_OK)
+ archive_entry_set_dev(entry, dev);
+ return r;
+ }
+ if (strcmp(key, "rmd160") == 0 ||
+ strcmp(key, "rmd160digest") == 0) {
+ return parse_digest(a, entry, val,
+ ARCHIVE_ENTRY_DIGEST_RMD160);
+ }
+ break;
+ case 's':
+ if (strcmp(key, "sha1") == 0 ||
+ strcmp(key, "sha1digest") == 0) {
+ return parse_digest(a, entry, val,
+ ARCHIVE_ENTRY_DIGEST_SHA1);
+ }
+ if (strcmp(key, "sha256") == 0 ||
+ strcmp(key, "sha256digest") == 0) {
+ return parse_digest(a, entry, val,
+ ARCHIVE_ENTRY_DIGEST_SHA256);
+ }
+ if (strcmp(key, "sha384") == 0 ||
+ strcmp(key, "sha384digest") == 0) {
+ return parse_digest(a, entry, val,
+ ARCHIVE_ENTRY_DIGEST_SHA384);
+ }
+ if (strcmp(key, "sha512") == 0 ||
+ strcmp(key, "sha512digest") == 0) {
+ return parse_digest(a, entry, val,
+ ARCHIVE_ENTRY_DIGEST_SHA512);
+ }
+ if (strcmp(key, "size") == 0) {
+ archive_entry_set_size(entry, mtree_atol(&val, 10));
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 't':
+ if (strcmp(key, "tags") == 0) {
+ /*
+ * Comma delimited list of tags.
+ * Ignore the tags for now, but the interface
+ * should be extended to allow inclusion/exclusion.
+ */
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "time") == 0) {
+ int64_t m;
+ int64_t my_time_t_max = get_time_t_max();
+ int64_t my_time_t_min = get_time_t_min();
+ long ns = 0;
+
+ *parsed_kws |= MTREE_HAS_MTIME;
+ m = mtree_atol(&val, 10);
+ /* Replicate an old mtree bug:
+ * 123456789.1 represents 123456789
+ * seconds and 1 nanosecond. */
+ if (*val == '.') {
+ ++val;
+ ns = (long)mtree_atol(&val, 10);
+ if (ns < 0)
+ ns = 0;
+ else if (ns > 999999999)
+ ns = 999999999;
+ }
+ if (m > my_time_t_max)
+ m = my_time_t_max;
+ else if (m < my_time_t_min)
+ m = my_time_t_min;
+ archive_entry_set_mtime(entry, (time_t)m, ns);
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "type") == 0) {
+ switch (val[0]) {
+ case 'b':
+ if (strcmp(val, "block") == 0) {
+ *parsed_kws |= MTREE_HAS_TYPE;
+ archive_entry_set_filetype(entry,
+ AE_IFBLK);
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'c':
+ if (strcmp(val, "char") == 0) {
+ *parsed_kws |= MTREE_HAS_TYPE;
+ archive_entry_set_filetype(entry,
+ AE_IFCHR);
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'd':
+ if (strcmp(val, "dir") == 0) {
+ *parsed_kws |= MTREE_HAS_TYPE;
+ archive_entry_set_filetype(entry,
+ AE_IFDIR);
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'f':
+ if (strcmp(val, "fifo") == 0) {
+ *parsed_kws |= MTREE_HAS_TYPE;
+ archive_entry_set_filetype(entry,
+ AE_IFIFO);
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(val, "file") == 0) {
+ *parsed_kws |= MTREE_HAS_TYPE;
+ archive_entry_set_filetype(entry,
+ AE_IFREG);
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'l':
+ if (strcmp(val, "link") == 0) {
+ *parsed_kws |= MTREE_HAS_TYPE;
+ archive_entry_set_filetype(entry,
+ AE_IFLNK);
+ return (ARCHIVE_OK);
+ }
+ break;
+ default:
+ break;
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unrecognized file type \"%s\"; "
+ "assuming \"file\"", val);
+ archive_entry_set_filetype(entry, AE_IFREG);
+ return (ARCHIVE_WARN);
+ }
+ break;
+ case 'u':
+ if (strcmp(key, "uid") == 0) {
+ *parsed_kws |= MTREE_HAS_UID;
+ archive_entry_set_uid(entry, mtree_atol(&val, 10));
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "uname") == 0) {
+ *parsed_kws |= MTREE_HAS_UNAME;
+ archive_entry_copy_uname(entry, val);
+ return (ARCHIVE_OK);
+ }
+ break;
+ default:
+ break;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unrecognized key %s=%s", key, val);
+ return (ARCHIVE_WARN);
+}
+
+static int
+read_data(struct archive_read *a, const void **buff, size_t *size,
+ int64_t *offset)
+{
+ size_t bytes_to_read;
+ ssize_t bytes_read;
+ struct mtree *mtree;
+
+ mtree = (struct mtree *)(a->format->data);
+ if (mtree->fd < 0) {
+ *buff = NULL;
+ *offset = 0;
+ *size = 0;
+ return (ARCHIVE_EOF);
+ }
+ if (mtree->buff == NULL) {
+ mtree->buffsize = 64 * 1024;
+ mtree->buff = malloc(mtree->buffsize);
+ if (mtree->buff == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ *buff = mtree->buff;
+ *offset = mtree->offset;
+ if ((int64_t)mtree->buffsize > mtree->cur_size - mtree->offset)
+ bytes_to_read = (size_t)(mtree->cur_size - mtree->offset);
+ else
+ bytes_to_read = mtree->buffsize;
+ bytes_read = read(mtree->fd, mtree->buff, bytes_to_read);
+ if (bytes_read < 0) {
+ archive_set_error(&a->archive, errno, "Can't read");
+ return (ARCHIVE_WARN);
+ }
+ if (bytes_read == 0) {
+ *size = 0;
+ return (ARCHIVE_EOF);
+ }
+ mtree->offset += bytes_read;
+ *size = bytes_read;
+ return (ARCHIVE_OK);
+}
+
+/* Skip does nothing except possibly close the contents file. */
+static int
+skip(struct archive_read *a)
+{
+ struct mtree *mtree;
+
+ mtree = (struct mtree *)(a->format->data);
+ if (mtree->fd >= 0) {
+ close(mtree->fd);
+ mtree->fd = -1;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Since parsing backslash sequences always makes strings shorter,
+ * we can always do this conversion in-place.
+ */
+static void
+parse_escapes(char *src, struct mtree_entry *mentry)
+{
+ char *dest = src;
+ char c;
+
+ if (mentry != NULL && strcmp(src, ".") == 0)
+ mentry->full = 1;
+
+ while (*src != '\0') {
+ c = *src++;
+ if (c == '/' && mentry != NULL)
+ mentry->full = 1;
+ if (c == '\\') {
+ switch (src[0]) {
+ case '0':
+ if (src[1] < '0' || src[1] > '7') {
+ c = 0;
+ ++src;
+ break;
+ }
+ /* FALLTHROUGH */
+ case '1':
+ case '2':
+ case '3':
+ if (src[1] >= '0' && src[1] <= '7' &&
+ src[2] >= '0' && src[2] <= '7') {
+ c = (src[0] - '0') << 6;
+ c |= (src[1] - '0') << 3;
+ c |= (src[2] - '0');
+ src += 3;
+ }
+ break;
+ case 'a':
+ c = '\a';
+ ++src;
+ break;
+ case 'b':
+ c = '\b';
+ ++src;
+ break;
+ case 'f':
+ c = '\f';
+ ++src;
+ break;
+ case 'n':
+ c = '\n';
+ ++src;
+ break;
+ case 'r':
+ c = '\r';
+ ++src;
+ break;
+ case 's':
+ c = ' ';
+ ++src;
+ break;
+ case 't':
+ c = '\t';
+ ++src;
+ break;
+ case 'v':
+ c = '\v';
+ ++src;
+ break;
+ case '\\':
+ c = '\\';
+ ++src;
+ break;
+ }
+ }
+ *dest++ = c;
+ }
+ *dest = '\0';
+}
+
+/* Parse a hex digit. */
+static int
+parsedigit(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ else if (c >= 'a' && c <= 'f')
+ return c - 'a';
+ else if (c >= 'A' && c <= 'F')
+ return c - 'A';
+ else
+ return -1;
+}
+
+/*
+ * Note that this implementation does not (and should not!) obey
+ * locale settings; you cannot simply substitute strtol here, since
+ * it does obey locale.
+ */
+static int64_t
+mtree_atol(char **p, int base)
+{
+ int64_t l, limit;
+ int digit, last_digit_limit;
+
+ if (base == 0) {
+ if (**p != '0')
+ base = 10;
+ else if ((*p)[1] == 'x' || (*p)[1] == 'X') {
+ *p += 2;
+ base = 16;
+ } else {
+ base = 8;
+ }
+ }
+
+ if (**p == '-') {
+ limit = INT64_MIN / base;
+ last_digit_limit = -(INT64_MIN % base);
+ ++(*p);
+
+ l = 0;
+ digit = parsedigit(**p);
+ while (digit >= 0 && digit < base) {
+ if (l < limit || (l == limit && digit >= last_digit_limit))
+ return INT64_MIN;
+ l = (l * base) - digit;
+ digit = parsedigit(*++(*p));
+ }
+ return l;
+ } else {
+ limit = INT64_MAX / base;
+ last_digit_limit = INT64_MAX % base;
+
+ l = 0;
+ digit = parsedigit(**p);
+ while (digit >= 0 && digit < base) {
+ if (l > limit || (l == limit && digit > last_digit_limit))
+ return INT64_MAX;
+ l = (l * base) + digit;
+ digit = parsedigit(*++(*p));
+ }
+ return l;
+ }
+}
+
+/*
+ * Returns length of line (including trailing newline)
+ * or negative on error. 'start' argument is updated to
+ * point to first character of line.
+ */
+static ssize_t
+readline(struct archive_read *a, struct mtree *mtree, char **start,
+ ssize_t limit)
+{
+ ssize_t bytes_read;
+ ssize_t total_size = 0;
+ ssize_t find_off = 0;
+ const void *t;
+ void *nl;
+ char *u;
+
+ /* Accumulate line in a line buffer. */
+ for (;;) {
+ /* Read some more. */
+ t = __archive_read_ahead(a, 1, &bytes_read);
+ if (t == NULL)
+ return (0);
+ if (bytes_read < 0)
+ return (ARCHIVE_FATAL);
+ nl = memchr(t, '\n', bytes_read);
+ /* If we found '\n', trim the read to end exactly there. */
+ if (nl != NULL) {
+ bytes_read = ((const char *)nl) - ((const char *)t) + 1;
+ }
+ if (total_size + bytes_read + 1 > limit) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Line too long");
+ return (ARCHIVE_FATAL);
+ }
+ if (archive_string_ensure(&mtree->line,
+ total_size + bytes_read + 1) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate working buffer");
+ return (ARCHIVE_FATAL);
+ }
+ /* Append new bytes to string. */
+ memcpy(mtree->line.s + total_size, t, bytes_read);
+ __archive_read_consume(a, bytes_read);
+ total_size += bytes_read;
+ mtree->line.s[total_size] = '\0';
+
+ for (u = mtree->line.s + find_off; *u; ++u) {
+ if (u[0] == '\n') {
+ /* Ends with unescaped newline. */
+ *start = mtree->line.s;
+ return total_size;
+ } else if (u[0] == '#') {
+ /* Ends with comment sequence #...\n */
+ if (nl == NULL) {
+ /* But we've not found the \n yet */
+ break;
+ }
+ } else if (u[0] == '\\') {
+ if (u[1] == '\n') {
+ /* Trim escaped newline. */
+ total_size -= 2;
+ mtree->line.s[total_size] = '\0';
+ break;
+ } else if (u[1] != '\0') {
+ /* Skip the two-char escape sequence */
+ ++u;
+ }
+ }
+ }
+ find_off = u - mtree->line.s;
+ }
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_rar.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_rar.c
new file mode 100644
index 00000000000..6452f5b5d65
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_rar.c
@@ -0,0 +1,3788 @@
+/*-
+* Copyright (c) 2003-2007 Tim Kientzle
+* Copyright (c) 2011 Andres Mejia
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "archive_platform.h"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <time.h>
+#include <limits.h>
+#ifdef HAVE_ZLIB_H
+#include <zlib.h> /* crc32 */
+#endif
+
+#include "archive.h"
+#ifndef HAVE_ZLIB_H
+#error #include "archive_crc32.h"
+#endif
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_ppmd7_private.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+/* RAR signature, also known as the mark header */
+#define RAR_SIGNATURE "\x52\x61\x72\x21\x1A\x07\x00"
+
+/* Header types */
+#define MARK_HEAD 0x72
+#define MAIN_HEAD 0x73
+#define FILE_HEAD 0x74
+#define COMM_HEAD 0x75
+#define AV_HEAD 0x76
+#define SUB_HEAD 0x77
+#define PROTECT_HEAD 0x78
+#define SIGN_HEAD 0x79
+#define NEWSUB_HEAD 0x7a
+#define ENDARC_HEAD 0x7b
+
+/* Main Header Flags */
+#define MHD_VOLUME 0x0001
+#define MHD_COMMENT 0x0002
+#define MHD_LOCK 0x0004
+#define MHD_SOLID 0x0008
+#define MHD_NEWNUMBERING 0x0010
+#define MHD_AV 0x0020
+#define MHD_PROTECT 0x0040
+#define MHD_PASSWORD 0x0080
+#define MHD_FIRSTVOLUME 0x0100
+#define MHD_ENCRYPTVER 0x0200
+
+/* Flags common to all headers */
+#define HD_MARKDELETION 0x4000
+#define HD_ADD_SIZE_PRESENT 0x8000
+
+/* File Header Flags */
+#define FHD_SPLIT_BEFORE 0x0001
+#define FHD_SPLIT_AFTER 0x0002
+#define FHD_PASSWORD 0x0004
+#define FHD_COMMENT 0x0008
+#define FHD_SOLID 0x0010
+#define FHD_LARGE 0x0100
+#define FHD_UNICODE 0x0200
+#define FHD_SALT 0x0400
+#define FHD_VERSION 0x0800
+#define FHD_EXTTIME 0x1000
+#define FHD_EXTFLAGS 0x2000
+
+/* File dictionary sizes */
+#define DICTIONARY_SIZE_64 0x00
+#define DICTIONARY_SIZE_128 0x20
+#define DICTIONARY_SIZE_256 0x40
+#define DICTIONARY_SIZE_512 0x60
+#define DICTIONARY_SIZE_1024 0x80
+#define DICTIONARY_SIZE_2048 0xA0
+#define DICTIONARY_SIZE_4096 0xC0
+#define FILE_IS_DIRECTORY 0xE0
+#define DICTIONARY_MASK FILE_IS_DIRECTORY
+
+/* OS Flags */
+#define OS_MSDOS 0
+#define OS_OS2 1
+#define OS_WIN32 2
+#define OS_UNIX 3
+#define OS_MAC_OS 4
+#define OS_BEOS 5
+
+/* Compression Methods */
+#define COMPRESS_METHOD_STORE 0x30
+/* LZSS */
+#define COMPRESS_METHOD_FASTEST 0x31
+#define COMPRESS_METHOD_FAST 0x32
+#define COMPRESS_METHOD_NORMAL 0x33
+/* PPMd Variant H */
+#define COMPRESS_METHOD_GOOD 0x34
+#define COMPRESS_METHOD_BEST 0x35
+
+#define CRC_POLYNOMIAL 0xEDB88320
+
+#define NS_UNIT 10000000
+
+#define DICTIONARY_MAX_SIZE 0x400000
+
+#define MAINCODE_SIZE 299
+#define OFFSETCODE_SIZE 60
+#define LOWOFFSETCODE_SIZE 17
+#define LENGTHCODE_SIZE 28
+#define HUFFMAN_TABLE_SIZE \
+ MAINCODE_SIZE + OFFSETCODE_SIZE + LOWOFFSETCODE_SIZE + LENGTHCODE_SIZE
+
+#define MAX_SYMBOL_LENGTH 0xF
+#define MAX_SYMBOLS 20
+
+/* Virtual Machine Properties */
+#define VM_MEMORY_SIZE 0x40000
+#define VM_MEMORY_MASK (VM_MEMORY_SIZE - 1)
+#define PROGRAM_WORK_SIZE 0x3C000
+#define PROGRAM_GLOBAL_SIZE 0x2000
+#define PROGRAM_SYSTEM_GLOBAL_ADDRESS PROGRAM_WORK_SIZE
+#define PROGRAM_SYSTEM_GLOBAL_SIZE 0x40
+#define PROGRAM_USER_GLOBAL_ADDRESS (PROGRAM_SYSTEM_GLOBAL_ADDRESS + PROGRAM_SYSTEM_GLOBAL_SIZE)
+#define PROGRAM_USER_GLOBAL_SIZE (PROGRAM_GLOBAL_SIZE - PROGRAM_SYSTEM_GLOBAL_SIZE)
+
+/*
+ * Considering L1,L2 cache miss and a calling of write system-call,
+ * the best size of the output buffer(uncompressed buffer) is 128K.
+ * If the structure of extracting process is changed, this value
+ * might be researched again.
+ */
+#define UNP_BUFFER_SIZE (128 * 1024)
+
+/* Define this here for non-Windows platforms */
+#if !((defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__))
+#define FILE_ATTRIBUTE_DIRECTORY 0x10
+#endif
+
+#undef minimum
+#define minimum(a, b) ((a)<(b)?(a):(b))
+
+/* Stack overflow check */
+#define MAX_COMPRESS_DEPTH 1024
+
+/* Fields common to all headers */
+struct rar_header
+{
+ char crc[2];
+ char type;
+ char flags[2];
+ char size[2];
+};
+
+/* Fields common to all file headers */
+struct rar_file_header
+{
+ char pack_size[4];
+ char unp_size[4];
+ char host_os;
+ char file_crc[4];
+ char file_time[4];
+ char unp_ver;
+ char method;
+ char name_size[2];
+ char file_attr[4];
+};
+
+struct huffman_tree_node
+{
+ int branches[2];
+};
+
+struct huffman_table_entry
+{
+ unsigned int length;
+ int value;
+};
+
+struct huffman_code
+{
+ struct huffman_tree_node *tree;
+ int numentries;
+ int numallocatedentries;
+ int minlength;
+ int maxlength;
+ int tablesize;
+ struct huffman_table_entry *table;
+};
+
+struct lzss
+{
+ unsigned char *window;
+ int mask;
+ int64_t position;
+};
+
+struct data_block_offsets
+{
+ int64_t header_size;
+ int64_t start_offset;
+ int64_t end_offset;
+};
+
+struct rar_program_code
+{
+ uint8_t *staticdata;
+ uint32_t staticdatalen;
+ uint8_t *globalbackup;
+ uint32_t globalbackuplen;
+ uint64_t fingerprint;
+ uint32_t usagecount;
+ uint32_t oldfilterlength;
+ struct rar_program_code *next;
+};
+
+struct rar_filter
+{
+ struct rar_program_code *prog;
+ uint32_t initialregisters[8];
+ uint8_t *globaldata;
+ uint32_t globaldatalen;
+ size_t blockstartpos;
+ uint32_t blocklength;
+ uint32_t filteredblockaddress;
+ uint32_t filteredblocklength;
+ struct rar_filter *next;
+};
+
+struct memory_bit_reader
+{
+ const uint8_t *bytes;
+ size_t length;
+ size_t offset;
+ uint64_t bits;
+ int available;
+ int at_eof;
+};
+
+struct rar_virtual_machine
+{
+ uint32_t registers[8];
+ uint8_t memory[VM_MEMORY_SIZE + sizeof(uint32_t)];
+};
+
+struct rar_filters
+{
+ struct rar_virtual_machine *vm;
+ struct rar_program_code *progs;
+ struct rar_filter *stack;
+ int64_t filterstart;
+ uint32_t lastfilternum;
+ int64_t lastend;
+ uint8_t *bytes;
+ size_t bytes_ready;
+};
+
+struct audio_state
+{
+ int8_t weight[5];
+ int16_t delta[4];
+ int8_t lastdelta;
+ int error[11];
+ int count;
+ uint8_t lastbyte;
+};
+
+struct rar
+{
+ /* Entries from main RAR header */
+ unsigned main_flags;
+ unsigned long file_crc;
+ char reserved1[2];
+ char reserved2[4];
+ char encryptver;
+
+ /* File header entries */
+ char compression_method;
+ unsigned file_flags;
+ int64_t packed_size;
+ int64_t unp_size;
+ time_t mtime;
+ long mnsec;
+ mode_t mode;
+ char *filename;
+ char *filename_save;
+ size_t filename_save_size;
+ size_t filename_allocated;
+
+ /* File header optional entries */
+ char salt[8];
+ time_t atime;
+ long ansec;
+ time_t ctime;
+ long cnsec;
+ time_t arctime;
+ long arcnsec;
+
+ /* Fields to help with tracking decompression of files. */
+ int64_t bytes_unconsumed;
+ int64_t bytes_remaining;
+ int64_t bytes_uncopied;
+ int64_t offset;
+ int64_t offset_outgoing;
+ int64_t offset_seek;
+ char valid;
+ unsigned int unp_offset;
+ unsigned int unp_buffer_size;
+ unsigned char *unp_buffer;
+ unsigned int dictionary_size;
+ char start_new_block;
+ char entry_eof;
+ unsigned long crc_calculated;
+ int found_first_header;
+ char has_endarc_header;
+ struct data_block_offsets *dbo;
+ unsigned int cursor;
+ unsigned int nodes;
+ char filename_must_match;
+
+ /* LZSS members */
+ struct huffman_code maincode;
+ struct huffman_code offsetcode;
+ struct huffman_code lowoffsetcode;
+ struct huffman_code lengthcode;
+ unsigned char lengthtable[HUFFMAN_TABLE_SIZE];
+ struct lzss lzss;
+ unsigned int lastlength;
+ unsigned int lastoffset;
+ unsigned int oldoffset[4];
+ unsigned int lastlowoffset;
+ unsigned int numlowoffsetrepeats;
+ char start_new_table;
+
+ /* Filters */
+ struct rar_filters filters;
+
+ /* PPMd Variant H members */
+ char ppmd_valid;
+ char ppmd_eod;
+ char is_ppmd_block;
+ int ppmd_escape;
+ CPpmd7 ppmd7_context;
+ CPpmd7z_RangeDec range_dec;
+ IByteIn bytein;
+
+ /*
+ * String conversion object.
+ */
+ int init_default_conversion;
+ struct archive_string_conv *sconv_default;
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_utf8;
+ struct archive_string_conv *sconv_utf16be;
+
+ /*
+ * Bit stream reader.
+ */
+ struct rar_br {
+#define CACHE_TYPE uint64_t
+#define CACHE_BITS (8 * sizeof(CACHE_TYPE))
+ /* Cache buffer. */
+ CACHE_TYPE cache_buffer;
+ /* Indicates how many bits avail in cache_buffer. */
+ int cache_avail;
+ ssize_t avail_in;
+ const unsigned char *next_in;
+ } br;
+
+ /*
+ * Custom field to denote that this archive contains encrypted entries
+ */
+ int has_encrypted_entries;
+};
+
+static int archive_read_support_format_rar_capabilities(struct archive_read *);
+static int archive_read_format_rar_has_encrypted_entries(struct archive_read *);
+static int archive_read_format_rar_bid(struct archive_read *, int);
+static int archive_read_format_rar_options(struct archive_read *,
+ const char *, const char *);
+static int archive_read_format_rar_read_header(struct archive_read *,
+ struct archive_entry *);
+static int archive_read_format_rar_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_rar_read_data_skip(struct archive_read *a);
+static int64_t archive_read_format_rar_seek_data(struct archive_read *, int64_t,
+ int);
+static int archive_read_format_rar_cleanup(struct archive_read *);
+
+/* Support functions */
+static int read_header(struct archive_read *, struct archive_entry *, char);
+static time_t get_time(int);
+static int read_exttime(const char *, struct rar *, const char *);
+static int read_symlink_stored(struct archive_read *, struct archive_entry *,
+ struct archive_string_conv *);
+static int read_data_stored(struct archive_read *, const void **, size_t *,
+ int64_t *);
+static int read_data_compressed(struct archive_read *, const void **, size_t *,
+ int64_t *, size_t);
+static int rar_br_preparation(struct archive_read *, struct rar_br *);
+static int parse_codes(struct archive_read *);
+static void free_codes(struct archive_read *);
+static int read_next_symbol(struct archive_read *, struct huffman_code *);
+static int create_code(struct archive_read *, struct huffman_code *,
+ unsigned char *, int, char);
+static int add_value(struct archive_read *, struct huffman_code *, int, int,
+ int);
+static int new_node(struct huffman_code *);
+static int make_table(struct archive_read *, struct huffman_code *);
+static int make_table_recurse(struct archive_read *, struct huffman_code *, int,
+ struct huffman_table_entry *, int, int);
+static int expand(struct archive_read *, int64_t *);
+static int copy_from_lzss_window_to_unp(struct archive_read *, const void **,
+ int64_t, int);
+static const void *rar_read_ahead(struct archive_read *, size_t, ssize_t *);
+static int parse_filter(struct archive_read *, const uint8_t *, uint16_t,
+ uint8_t);
+static int run_filters(struct archive_read *);
+static void clear_filters(struct rar_filters *);
+static struct rar_filter *create_filter(struct rar_program_code *,
+ const uint8_t *, uint32_t,
+ uint32_t[8], size_t, uint32_t);
+static void delete_filter(struct rar_filter *filter);
+static struct rar_program_code *compile_program(const uint8_t *, size_t);
+static void delete_program_code(struct rar_program_code *prog);
+static uint32_t membr_next_rarvm_number(struct memory_bit_reader *br);
+static inline uint32_t membr_bits(struct memory_bit_reader *br, int bits);
+static int membr_fill(struct memory_bit_reader *br, int bits);
+static int read_filter(struct archive_read *, int64_t *);
+static int rar_decode_byte(struct archive_read*, uint8_t *);
+static int execute_filter(struct archive_read*, struct rar_filter *,
+ struct rar_virtual_machine *, size_t);
+static int copy_from_lzss_window(struct archive_read *, void *, int64_t, int);
+static inline void vm_write_32(struct rar_virtual_machine*, size_t, uint32_t);
+static inline uint32_t vm_read_32(struct rar_virtual_machine*, size_t);
+
+/*
+ * Bit stream reader.
+ */
+/* Check that the cache buffer has enough bits. */
+#define rar_br_has(br, n) ((br)->cache_avail >= n)
+/* Get compressed data by bit. */
+#define rar_br_bits(br, n) \
+ (((uint32_t)((br)->cache_buffer >> \
+ ((br)->cache_avail - (n)))) & cache_masks[n])
+#define rar_br_bits_forced(br, n) \
+ (((uint32_t)((br)->cache_buffer << \
+ ((n) - (br)->cache_avail))) & cache_masks[n])
+/* Read ahead to make sure the cache buffer has enough compressed data we
+ * will use.
+ * True : completed, there is enough data in the cache buffer.
+ * False : there is no data in the stream. */
+#define rar_br_read_ahead(a, br, n) \
+ ((rar_br_has(br, (n)) || rar_br_fillup(a, br)) || rar_br_has(br, (n)))
+/* Notify how many bits we consumed. */
+#define rar_br_consume(br, n) ((br)->cache_avail -= (n))
+#define rar_br_consume_unalined_bits(br) ((br)->cache_avail &= ~7)
+
+static const uint32_t cache_masks[] = {
+ 0x00000000, 0x00000001, 0x00000003, 0x00000007,
+ 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,
+ 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF,
+ 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF,
+ 0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF,
+ 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,
+ 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF,
+ 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+/*
+ * Shift away used bits in the cache data and fill it up with following bits.
+ * Call this when cache buffer does not have enough bits you need.
+ *
+ * Returns 1 if the cache buffer is full.
+ * Returns 0 if the cache buffer is not full; input buffer is empty.
+ */
+static int
+rar_br_fillup(struct archive_read *a, struct rar_br *br)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+ int n = CACHE_BITS - br->cache_avail;
+
+ for (;;) {
+ switch (n >> 3) {
+ case 8:
+ if (br->avail_in >= 8) {
+ br->cache_buffer =
+ ((uint64_t)br->next_in[0]) << 56 |
+ ((uint64_t)br->next_in[1]) << 48 |
+ ((uint64_t)br->next_in[2]) << 40 |
+ ((uint64_t)br->next_in[3]) << 32 |
+ ((uint32_t)br->next_in[4]) << 24 |
+ ((uint32_t)br->next_in[5]) << 16 |
+ ((uint32_t)br->next_in[6]) << 8 |
+ (uint32_t)br->next_in[7];
+ br->next_in += 8;
+ br->avail_in -= 8;
+ br->cache_avail += 8 * 8;
+ rar->bytes_unconsumed += 8;
+ rar->bytes_remaining -= 8;
+ return (1);
+ }
+ break;
+ case 7:
+ if (br->avail_in >= 7) {
+ br->cache_buffer =
+ (br->cache_buffer << 56) |
+ ((uint64_t)br->next_in[0]) << 48 |
+ ((uint64_t)br->next_in[1]) << 40 |
+ ((uint64_t)br->next_in[2]) << 32 |
+ ((uint32_t)br->next_in[3]) << 24 |
+ ((uint32_t)br->next_in[4]) << 16 |
+ ((uint32_t)br->next_in[5]) << 8 |
+ (uint32_t)br->next_in[6];
+ br->next_in += 7;
+ br->avail_in -= 7;
+ br->cache_avail += 7 * 8;
+ rar->bytes_unconsumed += 7;
+ rar->bytes_remaining -= 7;
+ return (1);
+ }
+ break;
+ case 6:
+ if (br->avail_in >= 6) {
+ br->cache_buffer =
+ (br->cache_buffer << 48) |
+ ((uint64_t)br->next_in[0]) << 40 |
+ ((uint64_t)br->next_in[1]) << 32 |
+ ((uint32_t)br->next_in[2]) << 24 |
+ ((uint32_t)br->next_in[3]) << 16 |
+ ((uint32_t)br->next_in[4]) << 8 |
+ (uint32_t)br->next_in[5];
+ br->next_in += 6;
+ br->avail_in -= 6;
+ br->cache_avail += 6 * 8;
+ rar->bytes_unconsumed += 6;
+ rar->bytes_remaining -= 6;
+ return (1);
+ }
+ break;
+ case 0:
+ /* We have enough compressed data in
+ * the cache buffer.*/
+ return (1);
+ default:
+ break;
+ }
+ if (br->avail_in <= 0) {
+
+ if (rar->bytes_unconsumed > 0) {
+ /* Consume as much as the decompressor
+ * actually used. */
+ __archive_read_consume(a, rar->bytes_unconsumed);
+ rar->bytes_unconsumed = 0;
+ }
+ br->next_in = rar_read_ahead(a, 1, &(br->avail_in));
+ if (br->next_in == NULL)
+ return (0);
+ if (br->avail_in == 0)
+ return (0);
+ }
+ br->cache_buffer =
+ (br->cache_buffer << 8) | *br->next_in++;
+ br->avail_in--;
+ br->cache_avail += 8;
+ n -= 8;
+ rar->bytes_unconsumed++;
+ rar->bytes_remaining--;
+ }
+}
+
+static int
+rar_br_preparation(struct archive_read *a, struct rar_br *br)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+
+ if (rar->bytes_remaining > 0) {
+ br->next_in = rar_read_ahead(a, 1, &(br->avail_in));
+ if (br->next_in == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ return (ARCHIVE_FATAL);
+ }
+ if (br->cache_avail == 0)
+ (void)rar_br_fillup(a, br);
+ }
+ return (ARCHIVE_OK);
+}
+
+/* Find last bit set */
+static inline int
+rar_fls(unsigned int word)
+{
+ word |= (word >> 1);
+ word |= (word >> 2);
+ word |= (word >> 4);
+ word |= (word >> 8);
+ word |= (word >> 16);
+ return word - (word >> 1);
+}
+
+/* LZSS functions */
+static inline int64_t
+lzss_position(struct lzss *lzss)
+{
+ return lzss->position;
+}
+
+static inline int
+lzss_mask(struct lzss *lzss)
+{
+ return lzss->mask;
+}
+
+static inline int
+lzss_size(struct lzss *lzss)
+{
+ return lzss->mask + 1;
+}
+
+static inline int
+lzss_offset_for_position(struct lzss *lzss, int64_t pos)
+{
+ return (int)(pos & lzss->mask);
+}
+
+static inline unsigned char *
+lzss_pointer_for_position(struct lzss *lzss, int64_t pos)
+{
+ return &lzss->window[lzss_offset_for_position(lzss, pos)];
+}
+
+static inline int
+lzss_current_offset(struct lzss *lzss)
+{
+ return lzss_offset_for_position(lzss, lzss->position);
+}
+
+static inline uint8_t *
+lzss_current_pointer(struct lzss *lzss)
+{
+ return lzss_pointer_for_position(lzss, lzss->position);
+}
+
+static inline void
+lzss_emit_literal(struct rar *rar, uint8_t literal)
+{
+ *lzss_current_pointer(&rar->lzss) = literal;
+ rar->lzss.position++;
+}
+
+static inline void
+lzss_emit_match(struct rar *rar, int offset, int length)
+{
+ int dstoffs = lzss_current_offset(&rar->lzss);
+ int srcoffs = (dstoffs - offset) & lzss_mask(&rar->lzss);
+ int l, li, remaining;
+ unsigned char *d, *s;
+
+ remaining = length;
+ while (remaining > 0) {
+ l = remaining;
+ if (dstoffs > srcoffs) {
+ if (l > lzss_size(&rar->lzss) - dstoffs)
+ l = lzss_size(&rar->lzss) - dstoffs;
+ } else {
+ if (l > lzss_size(&rar->lzss) - srcoffs)
+ l = lzss_size(&rar->lzss) - srcoffs;
+ }
+ d = &(rar->lzss.window[dstoffs]);
+ s = &(rar->lzss.window[srcoffs]);
+ if ((dstoffs + l < srcoffs) || (srcoffs + l < dstoffs))
+ memcpy(d, s, l);
+ else {
+ for (li = 0; li < l; li++)
+ d[li] = s[li];
+ }
+ remaining -= l;
+ dstoffs = (dstoffs + l) & lzss_mask(&(rar->lzss));
+ srcoffs = (srcoffs + l) & lzss_mask(&(rar->lzss));
+ }
+ rar->lzss.position += length;
+}
+
+static Byte
+ppmd_read(void *p)
+{
+ struct archive_read *a = ((IByteIn*)p)->a;
+ struct rar *rar = (struct rar *)(a->format->data);
+ struct rar_br *br = &(rar->br);
+ Byte b;
+ if (!rar_br_read_ahead(a, br, 8))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ rar->valid = 0;
+ return 0;
+ }
+ b = rar_br_bits(br, 8);
+ rar_br_consume(br, 8);
+ return b;
+}
+
+int
+archive_read_support_format_rar(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct rar *rar;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_support_format_rar");
+
+ rar = (struct rar *)calloc(sizeof(*rar), 1);
+ if (rar == NULL)
+ {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate rar data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Until enough data has been read, we cannot tell about
+ * any encrypted entries yet.
+ */
+ rar->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+
+ r = __archive_read_register_format(a,
+ rar,
+ "rar",
+ archive_read_format_rar_bid,
+ archive_read_format_rar_options,
+ archive_read_format_rar_read_header,
+ archive_read_format_rar_read_data,
+ archive_read_format_rar_read_data_skip,
+ archive_read_format_rar_seek_data,
+ archive_read_format_rar_cleanup,
+ archive_read_support_format_rar_capabilities,
+ archive_read_format_rar_has_encrypted_entries);
+
+ if (r != ARCHIVE_OK)
+ free(rar);
+ return (r);
+}
+
+static int
+archive_read_support_format_rar_capabilities(struct archive_read * a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA
+ | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
+}
+
+static int
+archive_read_format_rar_has_encrypted_entries(struct archive_read *_a)
+{
+ if (_a && _a->format) {
+ struct rar * rar = (struct rar *)_a->format->data;
+ if (rar) {
+ return rar->has_encrypted_entries;
+ }
+ }
+ return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+}
+
+
+static int
+archive_read_format_rar_bid(struct archive_read *a, int best_bid)
+{
+ const char *p;
+
+ /* If there's already a bid > 30, we'll never win. */
+ if (best_bid > 30)
+ return (-1);
+
+ if ((p = __archive_read_ahead(a, 7, NULL)) == NULL)
+ return (-1);
+
+ if (memcmp(p, RAR_SIGNATURE, 7) == 0)
+ return (30);
+
+ if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
+ /* This is a PE file */
+ ssize_t offset = 0x10000;
+ ssize_t window = 4096;
+ ssize_t bytes_avail;
+ while (offset + window <= (1024 * 128)) {
+ const char *buff = __archive_read_ahead(a, offset + window, &bytes_avail);
+ if (buff == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < 0x40)
+ return (0);
+ continue;
+ }
+ p = buff + offset;
+ while (p + 7 < buff + bytes_avail) {
+ if (memcmp(p, RAR_SIGNATURE, 7) == 0)
+ return (30);
+ p += 0x10;
+ }
+ offset = p - buff;
+ }
+ }
+ return (0);
+}
+
+static int
+skip_sfx(struct archive_read *a)
+{
+ const void *h;
+ const char *p, *q;
+ size_t skip, total;
+ ssize_t bytes, window;
+
+ total = 0;
+ window = 4096;
+ while (total + window <= (1024 * 128)) {
+ h = __archive_read_ahead(a, window, &bytes);
+ if (h == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < 0x40)
+ goto fatal;
+ continue;
+ }
+ if (bytes < 0x40)
+ goto fatal;
+ p = h;
+ q = p + bytes;
+
+ /*
+ * Scan ahead until we find something that looks
+ * like the RAR header.
+ */
+ while (p + 7 < q) {
+ if (memcmp(p, RAR_SIGNATURE, 7) == 0) {
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ return (ARCHIVE_OK);
+ }
+ p += 0x10;
+ }
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ total += skip;
+ }
+fatal:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Couldn't find out RAR header");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+archive_read_format_rar_options(struct archive_read *a,
+ const char *key, const char *val)
+{
+ struct rar *rar;
+ int ret = ARCHIVE_FAILED;
+
+ rar = (struct rar *)(a->format->data);
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "rar: hdrcharset option needs a character-set name");
+ else {
+ rar->opt_sconv =
+ archive_string_conversion_from_charset(
+ &a->archive, val, 0);
+ if (rar->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_read_format_rar_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ const void *h;
+ const char *p;
+ struct rar *rar;
+ size_t skip;
+ char head_type;
+ int ret;
+ unsigned flags;
+ unsigned long crc32_expected;
+
+ a->archive.archive_format = ARCHIVE_FORMAT_RAR;
+ if (a->archive.archive_format_name == NULL)
+ a->archive.archive_format_name = "RAR";
+
+ rar = (struct rar *)(a->format->data);
+
+ /*
+ * It should be sufficient to call archive_read_next_header() for
+ * a reader to determine if an entry is encrypted or not. If the
+ * encryption of an entry is only detectable when calling
+ * archive_read_data(), so be it. We'll do the same check there
+ * as well.
+ */
+ if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+ rar->has_encrypted_entries = 0;
+ }
+
+ /* RAR files can be generated without EOF headers, so return ARCHIVE_EOF if
+ * this fails.
+ */
+ if ((h = __archive_read_ahead(a, 7, NULL)) == NULL)
+ return (ARCHIVE_EOF);
+
+ p = h;
+ if (rar->found_first_header == 0 &&
+ ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0)) {
+ /* This is an executable ? Must be self-extracting... */
+ ret = skip_sfx(a);
+ if (ret < ARCHIVE_WARN)
+ return (ret);
+ }
+ rar->found_first_header = 1;
+
+ while (1)
+ {
+ unsigned long crc32_val;
+
+ if ((h = __archive_read_ahead(a, 7, NULL)) == NULL)
+ return (ARCHIVE_FATAL);
+ p = h;
+
+ head_type = p[2];
+ switch(head_type)
+ {
+ case MARK_HEAD:
+ if (memcmp(p, RAR_SIGNATURE, 7) != 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid marker header");
+ return (ARCHIVE_FATAL);
+ }
+ __archive_read_consume(a, 7);
+ break;
+
+ case MAIN_HEAD:
+ rar->main_flags = archive_le16dec(p + 3);
+ skip = archive_le16dec(p + 5);
+ if (skip < 7 + sizeof(rar->reserved1) + sizeof(rar->reserved2)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid header size");
+ return (ARCHIVE_FATAL);
+ }
+ if ((h = __archive_read_ahead(a, skip, NULL)) == NULL)
+ return (ARCHIVE_FATAL);
+ p = h;
+ memcpy(rar->reserved1, p + 7, sizeof(rar->reserved1));
+ memcpy(rar->reserved2, p + 7 + sizeof(rar->reserved1),
+ sizeof(rar->reserved2));
+ if (rar->main_flags & MHD_ENCRYPTVER) {
+ if (skip < 7 + sizeof(rar->reserved1) + sizeof(rar->reserved2)+1) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid header size");
+ return (ARCHIVE_FATAL);
+ }
+ rar->encryptver = *(p + 7 + sizeof(rar->reserved1) +
+ sizeof(rar->reserved2));
+ }
+
+ /* Main header is password encrypted, so we cannot read any
+ file names or any other info about files from the header. */
+ if (rar->main_flags & MHD_PASSWORD)
+ {
+ archive_entry_set_is_metadata_encrypted(entry, 1);
+ archive_entry_set_is_data_encrypted(entry, 1);
+ rar->has_encrypted_entries = 1;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "RAR encryption support unavailable.");
+ return (ARCHIVE_FATAL);
+ }
+
+ crc32_val = crc32(0, (const unsigned char *)p + 2, (unsigned)skip - 2);
+ if ((crc32_val & 0xffff) != archive_le16dec(p)) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Header CRC error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ __archive_read_consume(a, skip);
+ break;
+
+ case FILE_HEAD:
+ return read_header(a, entry, head_type);
+
+ case COMM_HEAD:
+ case AV_HEAD:
+ case SUB_HEAD:
+ case PROTECT_HEAD:
+ case SIGN_HEAD:
+ case ENDARC_HEAD:
+ flags = archive_le16dec(p + 3);
+ skip = archive_le16dec(p + 5);
+ if (skip < 7) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid header size too small");
+ return (ARCHIVE_FATAL);
+ }
+ if (flags & HD_ADD_SIZE_PRESENT)
+ {
+ if (skip < 7 + 4) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid header size too small");
+ return (ARCHIVE_FATAL);
+ }
+ if ((h = __archive_read_ahead(a, skip, NULL)) == NULL)
+ return (ARCHIVE_FATAL);
+ p = h;
+ skip += archive_le32dec(p + 7);
+ }
+
+ /* Skip over the 2-byte CRC at the beginning of the header. */
+ crc32_expected = archive_le16dec(p);
+ __archive_read_consume(a, 2);
+ skip -= 2;
+
+ /* Skim the entire header and compute the CRC. */
+ crc32_val = 0;
+ while (skip > 0) {
+ size_t to_read = skip;
+ if (to_read > 32 * 1024)
+ to_read = 32 * 1024;
+ if ((h = __archive_read_ahead(a, to_read, NULL)) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Bad RAR file");
+ return (ARCHIVE_FATAL);
+ }
+ p = h;
+ crc32_val = crc32(crc32_val, (const unsigned char *)p, (unsigned int)to_read);
+ __archive_read_consume(a, to_read);
+ skip -= to_read;
+ }
+ if ((crc32_val & 0xffff) != crc32_expected) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Header CRC error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ if (head_type == ENDARC_HEAD)
+ return (ARCHIVE_EOF);
+ break;
+
+ case NEWSUB_HEAD:
+ if ((ret = read_header(a, entry, head_type)) < ARCHIVE_WARN)
+ return ret;
+ break;
+
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Bad RAR file");
+ return (ARCHIVE_FATAL);
+ }
+ }
+}
+
+static int
+archive_read_format_rar_read_data(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+ int ret;
+
+ if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+ rar->has_encrypted_entries = 0;
+ }
+
+ if (rar->bytes_unconsumed > 0) {
+ /* Consume as much as the decompressor actually used. */
+ __archive_read_consume(a, rar->bytes_unconsumed);
+ rar->bytes_unconsumed = 0;
+ }
+
+ *buff = NULL;
+ if (rar->entry_eof || rar->offset_seek >= rar->unp_size) {
+ *size = 0;
+ *offset = rar->offset;
+ if (*offset < rar->unp_size)
+ *offset = rar->unp_size;
+ return (ARCHIVE_EOF);
+ }
+
+ switch (rar->compression_method)
+ {
+ case COMPRESS_METHOD_STORE:
+ ret = read_data_stored(a, buff, size, offset);
+ break;
+
+ case COMPRESS_METHOD_FASTEST:
+ case COMPRESS_METHOD_FAST:
+ case COMPRESS_METHOD_NORMAL:
+ case COMPRESS_METHOD_GOOD:
+ case COMPRESS_METHOD_BEST:
+ ret = read_data_compressed(a, buff, size, offset, 0);
+ if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN) {
+ __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);
+ rar->start_new_table = 1;
+ rar->ppmd_valid = 0;
+ }
+ break;
+
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported compression method for RAR file.");
+ ret = ARCHIVE_FATAL;
+ break;
+ }
+ return (ret);
+}
+
+static int
+archive_read_format_rar_read_data_skip(struct archive_read *a)
+{
+ struct rar *rar;
+ int64_t bytes_skipped;
+ int ret;
+
+ rar = (struct rar *)(a->format->data);
+
+ if (rar->bytes_unconsumed > 0) {
+ /* Consume as much as the decompressor actually used. */
+ __archive_read_consume(a, rar->bytes_unconsumed);
+ rar->bytes_unconsumed = 0;
+ }
+
+ if (rar->bytes_remaining > 0) {
+ bytes_skipped = __archive_read_consume(a, rar->bytes_remaining);
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Compressed data to skip must be read from each header in a multivolume
+ * archive.
+ */
+ if (rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER)
+ {
+ ret = archive_read_format_rar_read_header(a, a->entry);
+ if (ret == (ARCHIVE_EOF))
+ ret = archive_read_format_rar_read_header(a, a->entry);
+ if (ret != (ARCHIVE_OK))
+ return ret;
+ return archive_read_format_rar_read_data_skip(a);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int64_t
+archive_read_format_rar_seek_data(struct archive_read *a, int64_t offset,
+ int whence)
+{
+ int64_t client_offset, ret;
+ unsigned int i;
+ struct rar *rar = (struct rar *)(a->format->data);
+
+ if (rar->compression_method == COMPRESS_METHOD_STORE)
+ {
+ /* Modify the offset for use with SEEK_SET */
+ switch (whence)
+ {
+ case SEEK_CUR:
+ client_offset = rar->offset_seek;
+ break;
+ case SEEK_END:
+ client_offset = rar->unp_size;
+ break;
+ case SEEK_SET:
+ default:
+ client_offset = 0;
+ }
+ client_offset += offset;
+ if (client_offset < 0)
+ {
+ /* Can't seek past beginning of data block */
+ return -1;
+ }
+ else if (client_offset > rar->unp_size)
+ {
+ /*
+ * Set the returned offset but only seek to the end of
+ * the data block.
+ */
+ rar->offset_seek = client_offset;
+ client_offset = rar->unp_size;
+ }
+
+ client_offset += rar->dbo[0].start_offset;
+ i = 0;
+ while (i < rar->cursor)
+ {
+ i++;
+ client_offset += rar->dbo[i].start_offset - rar->dbo[i-1].end_offset;
+ }
+ if (rar->main_flags & MHD_VOLUME)
+ {
+ /* Find the appropriate offset among the multivolume archive */
+ while (1)
+ {
+ if (client_offset < rar->dbo[rar->cursor].start_offset &&
+ rar->file_flags & FHD_SPLIT_BEFORE)
+ {
+ /* Search backwards for the correct data block */
+ if (rar->cursor == 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Attempt to seek past beginning of RAR data block");
+ return (ARCHIVE_FAILED);
+ }
+ rar->cursor--;
+ client_offset -= rar->dbo[rar->cursor+1].start_offset -
+ rar->dbo[rar->cursor].end_offset;
+ if (client_offset < rar->dbo[rar->cursor].start_offset)
+ continue;
+ ret = __archive_read_seek(a, rar->dbo[rar->cursor].start_offset -
+ rar->dbo[rar->cursor].header_size, SEEK_SET);
+ if (ret < (ARCHIVE_OK))
+ return ret;
+ ret = archive_read_format_rar_read_header(a, a->entry);
+ if (ret != (ARCHIVE_OK))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Error during seek of RAR file");
+ return (ARCHIVE_FAILED);
+ }
+ rar->cursor--;
+ break;
+ }
+ else if (client_offset > rar->dbo[rar->cursor].end_offset &&
+ rar->file_flags & FHD_SPLIT_AFTER)
+ {
+ /* Search forward for the correct data block */
+ rar->cursor++;
+ if (rar->cursor < rar->nodes &&
+ client_offset > rar->dbo[rar->cursor].end_offset)
+ {
+ client_offset += rar->dbo[rar->cursor].start_offset -
+ rar->dbo[rar->cursor-1].end_offset;
+ continue;
+ }
+ rar->cursor--;
+ ret = __archive_read_seek(a, rar->dbo[rar->cursor].end_offset,
+ SEEK_SET);
+ if (ret < (ARCHIVE_OK))
+ return ret;
+ ret = archive_read_format_rar_read_header(a, a->entry);
+ if (ret == (ARCHIVE_EOF))
+ {
+ rar->has_endarc_header = 1;
+ ret = archive_read_format_rar_read_header(a, a->entry);
+ }
+ if (ret != (ARCHIVE_OK))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Error during seek of RAR file");
+ return (ARCHIVE_FAILED);
+ }
+ client_offset += rar->dbo[rar->cursor].start_offset -
+ rar->dbo[rar->cursor-1].end_offset;
+ continue;
+ }
+ break;
+ }
+ }
+
+ ret = __archive_read_seek(a, client_offset, SEEK_SET);
+ if (ret < (ARCHIVE_OK))
+ return ret;
+ rar->bytes_remaining = rar->dbo[rar->cursor].end_offset - ret;
+ i = rar->cursor;
+ while (i > 0)
+ {
+ i--;
+ ret -= rar->dbo[i+1].start_offset - rar->dbo[i].end_offset;
+ }
+ ret -= rar->dbo[0].start_offset;
+
+ /* Always restart reading the file after a seek */
+ __archive_reset_read_data(&a->archive);
+
+ rar->bytes_unconsumed = 0;
+ rar->offset = 0;
+
+ /*
+ * If a seek past the end of file was requested, return the requested
+ * offset.
+ */
+ if (ret == rar->unp_size && rar->offset_seek > rar->unp_size)
+ return rar->offset_seek;
+
+ /* Return the new offset */
+ rar->offset_seek = ret;
+ return rar->offset_seek;
+ }
+ else
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Seeking of compressed RAR files is unsupported");
+ }
+ return (ARCHIVE_FAILED);
+}
+
+static int
+archive_read_format_rar_cleanup(struct archive_read *a)
+{
+ struct rar *rar;
+
+ rar = (struct rar *)(a->format->data);
+ free_codes(a);
+ clear_filters(&rar->filters);
+ free(rar->filename);
+ free(rar->filename_save);
+ free(rar->dbo);
+ free(rar->unp_buffer);
+ free(rar->lzss.window);
+ __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);
+ free(rar);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+read_header(struct archive_read *a, struct archive_entry *entry,
+ char head_type)
+{
+ const void *h;
+ const char *p, *endp;
+ struct rar *rar;
+ struct rar_header rar_header;
+ struct rar_file_header file_header;
+ int64_t header_size;
+ unsigned filename_size, end;
+ char *filename;
+ char *strp;
+ char packed_size[8];
+ char unp_size[8];
+ int ttime;
+ struct archive_string_conv *sconv, *fn_sconv;
+ unsigned long crc32_val;
+ int ret = (ARCHIVE_OK), ret2;
+
+ rar = (struct rar *)(a->format->data);
+
+ /* Setup a string conversion object for non-rar-unicode filenames. */
+ sconv = rar->opt_sconv;
+ if (sconv == NULL) {
+ if (!rar->init_default_conversion) {
+ rar->sconv_default =
+ archive_string_default_conversion_for_read(
+ &(a->archive));
+ rar->init_default_conversion = 1;
+ }
+ sconv = rar->sconv_default;
+ }
+
+
+ if ((h = __archive_read_ahead(a, 7, NULL)) == NULL)
+ return (ARCHIVE_FATAL);
+ p = h;
+ memcpy(&rar_header, p, sizeof(rar_header));
+ rar->file_flags = archive_le16dec(rar_header.flags);
+ header_size = archive_le16dec(rar_header.size);
+ if (header_size < (int64_t)sizeof(file_header) + 7) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid header size");
+ return (ARCHIVE_FATAL);
+ }
+ crc32_val = crc32(0, (const unsigned char *)p + 2, 7 - 2);
+ __archive_read_consume(a, 7);
+
+ if (!(rar->file_flags & FHD_SOLID))
+ {
+ rar->compression_method = 0;
+ rar->packed_size = 0;
+ rar->unp_size = 0;
+ rar->mtime = 0;
+ rar->ctime = 0;
+ rar->atime = 0;
+ rar->arctime = 0;
+ rar->mode = 0;
+ memset(&rar->salt, 0, sizeof(rar->salt));
+ rar->atime = 0;
+ rar->ansec = 0;
+ rar->ctime = 0;
+ rar->cnsec = 0;
+ rar->mtime = 0;
+ rar->mnsec = 0;
+ rar->arctime = 0;
+ rar->arcnsec = 0;
+ }
+ else
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "RAR solid archive support unavailable.");
+ return (ARCHIVE_FATAL);
+ }
+
+ if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL)
+ return (ARCHIVE_FATAL);
+
+ /* File Header CRC check. */
+ crc32_val = crc32(crc32_val, h, (unsigned)(header_size - 7));
+ if ((crc32_val & 0xffff) != archive_le16dec(rar_header.crc)) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Header CRC error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ /* If no CRC error, Go on parsing File Header. */
+ p = h;
+ endp = p + header_size - 7;
+ memcpy(&file_header, p, sizeof(file_header));
+ p += sizeof(file_header);
+
+ rar->compression_method = file_header.method;
+
+ ttime = archive_le32dec(file_header.file_time);
+ rar->mtime = get_time(ttime);
+
+ rar->file_crc = archive_le32dec(file_header.file_crc);
+
+ if (rar->file_flags & FHD_PASSWORD)
+ {
+ archive_entry_set_is_data_encrypted(entry, 1);
+ rar->has_encrypted_entries = 1;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "RAR encryption support unavailable.");
+ /* Since it is only the data part itself that is encrypted we can at least
+ extract information about the currently processed entry and don't need
+ to return ARCHIVE_FATAL here. */
+ /*return (ARCHIVE_FATAL);*/
+ }
+
+ if (rar->file_flags & FHD_LARGE)
+ {
+ memcpy(packed_size, file_header.pack_size, 4);
+ memcpy(packed_size + 4, p, 4); /* High pack size */
+ p += 4;
+ memcpy(unp_size, file_header.unp_size, 4);
+ memcpy(unp_size + 4, p, 4); /* High unpack size */
+ p += 4;
+ rar->packed_size = archive_le64dec(&packed_size);
+ rar->unp_size = archive_le64dec(&unp_size);
+ }
+ else
+ {
+ rar->packed_size = archive_le32dec(file_header.pack_size);
+ rar->unp_size = archive_le32dec(file_header.unp_size);
+ }
+
+ if (rar->packed_size < 0 || rar->unp_size < 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid sizes specified.");
+ return (ARCHIVE_FATAL);
+ }
+
+ rar->bytes_remaining = rar->packed_size;
+
+ /* TODO: RARv3 subblocks contain comments. For now the complete block is
+ * consumed at the end.
+ */
+ if (head_type == NEWSUB_HEAD) {
+ size_t distance = p - (const char *)h;
+ header_size += rar->packed_size;
+ /* Make sure we have the extended data. */
+ if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL)
+ return (ARCHIVE_FATAL);
+ p = h;
+ endp = p + header_size - 7;
+ p += distance;
+ }
+
+ filename_size = archive_le16dec(file_header.name_size);
+ if (p + filename_size > endp) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid filename size");
+ return (ARCHIVE_FATAL);
+ }
+ if (rar->filename_allocated < filename_size * 2 + 2) {
+ char *newptr;
+ size_t newsize = filename_size * 2 + 2;
+ newptr = realloc(rar->filename, newsize);
+ if (newptr == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory.");
+ return (ARCHIVE_FATAL);
+ }
+ rar->filename = newptr;
+ rar->filename_allocated = newsize;
+ }
+ filename = rar->filename;
+ memcpy(filename, p, filename_size);
+ filename[filename_size] = '\0';
+ if (rar->file_flags & FHD_UNICODE)
+ {
+ if (filename_size != strlen(filename))
+ {
+ unsigned char highbyte, flagbits, flagbyte;
+ unsigned fn_end, offset;
+
+ end = filename_size;
+ fn_end = filename_size * 2;
+ filename_size = 0;
+ offset = (unsigned)strlen(filename) + 1;
+ highbyte = *(p + offset++);
+ flagbits = 0;
+ flagbyte = 0;
+ while (offset < end && filename_size < fn_end)
+ {
+ if (!flagbits)
+ {
+ flagbyte = *(p + offset++);
+ flagbits = 8;
+ }
+
+ flagbits -= 2;
+ switch((flagbyte >> flagbits) & 3)
+ {
+ case 0:
+ filename[filename_size++] = '\0';
+ filename[filename_size++] = *(p + offset++);
+ break;
+ case 1:
+ filename[filename_size++] = highbyte;
+ filename[filename_size++] = *(p + offset++);
+ break;
+ case 2:
+ filename[filename_size++] = *(p + offset + 1);
+ filename[filename_size++] = *(p + offset);
+ offset += 2;
+ break;
+ case 3:
+ {
+ char extra, high;
+ uint8_t length = *(p + offset++);
+
+ if (length & 0x80) {
+ extra = *(p + offset++);
+ high = (char)highbyte;
+ } else
+ extra = high = 0;
+ length = (length & 0x7f) + 2;
+ while (length && filename_size < fn_end) {
+ unsigned cp = filename_size >> 1;
+ filename[filename_size++] = high;
+ filename[filename_size++] = p[cp] + extra;
+ length--;
+ }
+ }
+ break;
+ }
+ }
+ if (filename_size > fn_end) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid filename");
+ return (ARCHIVE_FATAL);
+ }
+ filename[filename_size++] = '\0';
+ /*
+ * Do not increment filename_size here as the computations below
+ * add the space for the terminating NUL explicitly.
+ */
+ filename[filename_size] = '\0';
+
+ /* Decoded unicode form is UTF-16BE, so we have to update a string
+ * conversion object for it. */
+ if (rar->sconv_utf16be == NULL) {
+ rar->sconv_utf16be = archive_string_conversion_from_charset(
+ &a->archive, "UTF-16BE", 1);
+ if (rar->sconv_utf16be == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ fn_sconv = rar->sconv_utf16be;
+
+ strp = filename;
+ while (memcmp(strp, "\x00\x00", 2))
+ {
+ if (!memcmp(strp, "\x00\\", 2))
+ *(strp + 1) = '/';
+ strp += 2;
+ }
+ p += offset;
+ } else {
+ /*
+ * If FHD_UNICODE is set but no unicode data, this file name form
+ * is UTF-8, so we have to update a string conversion object for
+ * it accordingly.
+ */
+ if (rar->sconv_utf8 == NULL) {
+ rar->sconv_utf8 = archive_string_conversion_from_charset(
+ &a->archive, "UTF-8", 1);
+ if (rar->sconv_utf8 == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ fn_sconv = rar->sconv_utf8;
+ while ((strp = strchr(filename, '\\')) != NULL)
+ *strp = '/';
+ p += filename_size;
+ }
+ }
+ else
+ {
+ fn_sconv = sconv;
+ while ((strp = strchr(filename, '\\')) != NULL)
+ *strp = '/';
+ p += filename_size;
+ }
+
+ /* Split file in multivolume RAR. No more need to process header. */
+ if (rar->filename_save &&
+ filename_size == rar->filename_save_size &&
+ !memcmp(rar->filename, rar->filename_save, filename_size + 1))
+ {
+ __archive_read_consume(a, header_size - 7);
+ rar->cursor++;
+ if (rar->cursor >= rar->nodes)
+ {
+ rar->nodes++;
+ if ((rar->dbo =
+ realloc(rar->dbo, sizeof(*rar->dbo) * rar->nodes)) == NULL)
+ {
+ archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory.");
+ return (ARCHIVE_FATAL);
+ }
+ rar->dbo[rar->cursor].header_size = header_size;
+ rar->dbo[rar->cursor].start_offset = -1;
+ rar->dbo[rar->cursor].end_offset = -1;
+ }
+ if (rar->dbo[rar->cursor].start_offset < 0)
+ {
+ rar->dbo[rar->cursor].start_offset = a->filter->position;
+ rar->dbo[rar->cursor].end_offset = rar->dbo[rar->cursor].start_offset +
+ rar->packed_size;
+ }
+ return ret;
+ }
+ else if (rar->filename_must_match)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Mismatch of file parts split across multi-volume archive");
+ return (ARCHIVE_FATAL);
+ }
+
+ rar->filename_save = (char*)realloc(rar->filename_save,
+ filename_size + 1);
+ memcpy(rar->filename_save, rar->filename, filename_size + 1);
+ rar->filename_save_size = filename_size;
+
+ /* Set info for seeking */
+ free(rar->dbo);
+ if ((rar->dbo = calloc(1, sizeof(*rar->dbo))) == NULL)
+ {
+ archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory.");
+ return (ARCHIVE_FATAL);
+ }
+ rar->dbo[0].header_size = header_size;
+ rar->dbo[0].start_offset = -1;
+ rar->dbo[0].end_offset = -1;
+ rar->cursor = 0;
+ rar->nodes = 1;
+
+ if (rar->file_flags & FHD_SALT)
+ {
+ if (p + 8 > endp) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid header size");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(rar->salt, p, 8);
+ p += 8;
+ }
+
+ if (rar->file_flags & FHD_EXTTIME) {
+ if (read_exttime(p, rar, endp) < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid header size");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ __archive_read_consume(a, header_size - 7);
+ rar->dbo[0].start_offset = a->filter->position;
+ rar->dbo[0].end_offset = rar->dbo[0].start_offset + rar->packed_size;
+
+ switch(file_header.host_os)
+ {
+ case OS_MSDOS:
+ case OS_OS2:
+ case OS_WIN32:
+ rar->mode = archive_le32dec(file_header.file_attr);
+ if (rar->mode & FILE_ATTRIBUTE_DIRECTORY)
+ rar->mode = AE_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ else
+ rar->mode = AE_IFREG;
+ rar->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ break;
+
+ case OS_UNIX:
+ case OS_MAC_OS:
+ case OS_BEOS:
+ rar->mode = archive_le32dec(file_header.file_attr);
+ break;
+
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unknown file attributes from RAR file's host OS");
+ return (ARCHIVE_FATAL);
+ }
+
+ rar->bytes_uncopied = rar->bytes_unconsumed = 0;
+ rar->lzss.position = rar->offset = 0;
+ rar->offset_seek = 0;
+ rar->dictionary_size = 0;
+ rar->offset_outgoing = 0;
+ rar->br.cache_avail = 0;
+ rar->br.avail_in = 0;
+ rar->crc_calculated = 0;
+ rar->entry_eof = 0;
+ rar->valid = 1;
+ rar->is_ppmd_block = 0;
+ rar->start_new_table = 1;
+ free(rar->unp_buffer);
+ rar->unp_buffer = NULL;
+ rar->unp_offset = 0;
+ rar->unp_buffer_size = UNP_BUFFER_SIZE;
+ memset(rar->lengthtable, 0, sizeof(rar->lengthtable));
+ __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);
+ rar->ppmd_valid = rar->ppmd_eod = 0;
+ rar->filters.filterstart = INT64_MAX;
+
+ /* Don't set any archive entries for non-file header types */
+ if (head_type == NEWSUB_HEAD)
+ return ret;
+
+ archive_entry_set_mtime(entry, rar->mtime, rar->mnsec);
+ archive_entry_set_ctime(entry, rar->ctime, rar->cnsec);
+ archive_entry_set_atime(entry, rar->atime, rar->ansec);
+ archive_entry_set_size(entry, rar->unp_size);
+ archive_entry_set_mode(entry, rar->mode);
+
+ if (archive_entry_copy_pathname_l(entry, filename, filename_size, fn_sconv))
+ {
+ if (errno == ENOMEM)
+ {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname cannot be converted from %s to current locale.",
+ archive_string_conversion_charset_name(fn_sconv));
+ ret = (ARCHIVE_WARN);
+ }
+
+ if (((rar->mode) & AE_IFMT) == AE_IFLNK)
+ {
+ /* Make sure a symbolic-link file does not have its body. */
+ rar->bytes_remaining = 0;
+ archive_entry_set_size(entry, 0);
+
+ /* Read a symbolic-link name. */
+ if ((ret2 = read_symlink_stored(a, entry, sconv)) < (ARCHIVE_WARN))
+ return ret2;
+ if (ret > ret2)
+ ret = ret2;
+ }
+
+ if (rar->bytes_remaining == 0)
+ rar->entry_eof = 1;
+
+ return ret;
+}
+
+static time_t
+get_time(int ttime)
+{
+ struct tm tm;
+ tm.tm_sec = 2 * (ttime & 0x1f);
+ tm.tm_min = (ttime >> 5) & 0x3f;
+ tm.tm_hour = (ttime >> 11) & 0x1f;
+ tm.tm_mday = (ttime >> 16) & 0x1f;
+ tm.tm_mon = ((ttime >> 21) & 0x0f) - 1;
+ tm.tm_year = ((ttime >> 25) & 0x7f) + 80;
+ tm.tm_isdst = -1;
+ return mktime(&tm);
+}
+
+static int
+read_exttime(const char *p, struct rar *rar, const char *endp)
+{
+ unsigned rmode, flags, rem, j, count;
+ int ttime, i;
+ struct tm *tm;
+ time_t t;
+ long nsec;
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
+ struct tm tmbuf;
+#endif
+
+ if (p + 2 > endp)
+ return (-1);
+ flags = archive_le16dec(p);
+ p += 2;
+
+ for (i = 3; i >= 0; i--)
+ {
+ t = 0;
+ if (i == 3)
+ t = rar->mtime;
+ rmode = flags >> i * 4;
+ if (rmode & 8)
+ {
+ if (!t)
+ {
+ if (p + 4 > endp)
+ return (-1);
+ ttime = archive_le32dec(p);
+ t = get_time(ttime);
+ p += 4;
+ }
+ rem = 0;
+ count = rmode & 3;
+ if (p + count > endp)
+ return (-1);
+ for (j = 0; j < count; j++)
+ {
+ rem = (((unsigned)(unsigned char)*p) << 16) | (rem >> 8);
+ p++;
+ }
+#if defined(HAVE_LOCALTIME_S)
+ tm = localtime_s(&tmbuf, &t) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+ tm = localtime_r(&t, &tmbuf);
+#else
+ tm = localtime(&t);
+#endif
+ nsec = tm->tm_sec + rem / NS_UNIT;
+ if (rmode & 4)
+ {
+ tm->tm_sec++;
+ t = mktime(tm);
+ }
+ if (i == 3)
+ {
+ rar->mtime = t;
+ rar->mnsec = nsec;
+ }
+ else if (i == 2)
+ {
+ rar->ctime = t;
+ rar->cnsec = nsec;
+ }
+ else if (i == 1)
+ {
+ rar->atime = t;
+ rar->ansec = nsec;
+ }
+ else
+ {
+ rar->arctime = t;
+ rar->arcnsec = nsec;
+ }
+ }
+ }
+ return (0);
+}
+
+static int
+read_symlink_stored(struct archive_read *a, struct archive_entry *entry,
+ struct archive_string_conv *sconv)
+{
+ const void *h;
+ const char *p;
+ struct rar *rar;
+ int ret = (ARCHIVE_OK);
+
+ rar = (struct rar *)(a->format->data);
+ if ((h = rar_read_ahead(a, (size_t)rar->packed_size, NULL)) == NULL)
+ return (ARCHIVE_FATAL);
+ p = h;
+
+ if (archive_entry_copy_symlink_l(entry,
+ p, (size_t)rar->packed_size, sconv))
+ {
+ if (errno == ENOMEM)
+ {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for link");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "link cannot be converted from %s to current locale.",
+ archive_string_conversion_charset_name(sconv));
+ ret = (ARCHIVE_WARN);
+ }
+ __archive_read_consume(a, rar->packed_size);
+ return ret;
+}
+
+static int
+read_data_stored(struct archive_read *a, const void **buff, size_t *size,
+ int64_t *offset)
+{
+ struct rar *rar;
+ ssize_t bytes_avail;
+
+ rar = (struct rar *)(a->format->data);
+ if (rar->bytes_remaining == 0 &&
+ !(rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER))
+ {
+ *buff = NULL;
+ *size = 0;
+ *offset = rar->offset;
+ if (rar->file_crc != rar->crc_calculated) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "File CRC error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ rar->entry_eof = 1;
+ return (ARCHIVE_EOF);
+ }
+
+ *buff = rar_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail <= 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ return (ARCHIVE_FATAL);
+ }
+
+ *size = bytes_avail;
+ *offset = rar->offset;
+ rar->offset += bytes_avail;
+ rar->offset_seek += bytes_avail;
+ rar->bytes_remaining -= bytes_avail;
+ rar->bytes_unconsumed = bytes_avail;
+ /* Calculate File CRC. */
+ rar->crc_calculated = crc32(rar->crc_calculated, *buff,
+ (unsigned)bytes_avail);
+ return (ARCHIVE_OK);
+}
+
+static int
+read_data_compressed(struct archive_read *a, const void **buff, size_t *size,
+ int64_t *offset, size_t looper)
+{
+ if (looper++ > MAX_COMPRESS_DEPTH)
+ return (ARCHIVE_FATAL);
+
+ struct rar *rar;
+ int64_t start, end;
+ size_t bs;
+ int ret = (ARCHIVE_OK), sym, code, lzss_offset, length, i;
+
+ rar = (struct rar *)(a->format->data);
+
+ do {
+ if (!rar->valid)
+ return (ARCHIVE_FATAL);
+
+ if (rar->filters.bytes_ready > 0)
+ {
+ /* Flush unp_buffer first */
+ if (rar->unp_offset > 0)
+ {
+ *buff = rar->unp_buffer;
+ *size = rar->unp_offset;
+ rar->unp_offset = 0;
+ *offset = rar->offset_outgoing;
+ rar->offset_outgoing += *size;
+ }
+ else
+ {
+ *buff = rar->filters.bytes;
+ *size = rar->filters.bytes_ready;
+
+ rar->offset += *size;
+ *offset = rar->offset_outgoing;
+ rar->offset_outgoing += *size;
+
+ rar->filters.bytes_ready -= *size;
+ rar->filters.bytes += *size;
+ }
+ goto ending_block;
+ }
+
+ if (rar->ppmd_eod ||
+ (rar->dictionary_size && rar->offset >= rar->unp_size))
+ {
+ if (rar->unp_offset > 0) {
+ /*
+ * We have unprocessed extracted data. write it out.
+ */
+ *buff = rar->unp_buffer;
+ *size = rar->unp_offset;
+ *offset = rar->offset_outgoing;
+ rar->offset_outgoing += *size;
+ /* Calculate File CRC. */
+ rar->crc_calculated = crc32(rar->crc_calculated, *buff,
+ (unsigned)*size);
+ rar->unp_offset = 0;
+ return (ARCHIVE_OK);
+ }
+ *buff = NULL;
+ *size = 0;
+ *offset = rar->offset;
+ if (rar->file_crc != rar->crc_calculated) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "File CRC error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ rar->entry_eof = 1;
+ return (ARCHIVE_EOF);
+ }
+
+ if (!rar->is_ppmd_block && rar->dictionary_size && rar->bytes_uncopied > 0)
+ {
+ if (rar->bytes_uncopied > (rar->unp_buffer_size - rar->unp_offset))
+ bs = rar->unp_buffer_size - rar->unp_offset;
+ else
+ bs = (size_t)rar->bytes_uncopied;
+ ret = copy_from_lzss_window_to_unp(a, buff, rar->offset, (int)bs);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ rar->offset += bs;
+ rar->bytes_uncopied -= bs;
+ if (*buff != NULL) {
+ rar->unp_offset = 0;
+ *size = rar->unp_buffer_size;
+ *offset = rar->offset_outgoing;
+ rar->offset_outgoing += *size;
+ /* Calculate File CRC. */
+ rar->crc_calculated = crc32(rar->crc_calculated, *buff,
+ (unsigned)*size);
+ return (ret);
+ }
+ continue;
+ }
+
+ if (rar->filters.lastend == rar->filters.filterstart)
+ {
+ if (!run_filters(a))
+ return (ARCHIVE_FATAL);
+ continue;
+ }
+
+ if (!rar->br.next_in &&
+ (ret = rar_br_preparation(a, &(rar->br))) < ARCHIVE_WARN)
+ return (ret);
+ if (rar->start_new_table && ((ret = parse_codes(a)) < (ARCHIVE_WARN)))
+ return (ret);
+
+ if (rar->is_ppmd_block)
+ {
+ if ((sym = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+ &rar->ppmd7_context, &rar->range_dec.p)) < 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid symbol");
+ return (ARCHIVE_FATAL);
+ }
+ if(sym != rar->ppmd_escape)
+ {
+ lzss_emit_literal(rar, sym);
+ rar->bytes_uncopied++;
+ }
+ else
+ {
+ if ((code = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+ &rar->ppmd7_context, &rar->range_dec.p)) < 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid symbol");
+ return (ARCHIVE_FATAL);
+ }
+
+ switch(code)
+ {
+ case 0:
+ rar->start_new_table = 1;
+ return read_data_compressed(a, buff, size, offset, looper);
+
+ case 2:
+ rar->ppmd_eod = 1;/* End Of ppmd Data. */
+ continue;
+
+ case 3:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Parsing filters is unsupported.");
+ return (ARCHIVE_FAILED);
+
+ case 4:
+ lzss_offset = 0;
+ for (i = 2; i >= 0; i--)
+ {
+ if ((code = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+ &rar->ppmd7_context, &rar->range_dec.p)) < 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid symbol");
+ return (ARCHIVE_FATAL);
+ }
+ lzss_offset |= code << (i * 8);
+ }
+ if ((length = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+ &rar->ppmd7_context, &rar->range_dec.p)) < 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid symbol");
+ return (ARCHIVE_FATAL);
+ }
+ lzss_emit_match(rar, lzss_offset + 2, length + 32);
+ rar->bytes_uncopied += length + 32;
+ break;
+
+ case 5:
+ if ((length = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+ &rar->ppmd7_context, &rar->range_dec.p)) < 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid symbol");
+ return (ARCHIVE_FATAL);
+ }
+ lzss_emit_match(rar, 1, length + 4);
+ rar->bytes_uncopied += length + 4;
+ break;
+
+ default:
+ lzss_emit_literal(rar, sym);
+ rar->bytes_uncopied++;
+ }
+ }
+ }
+ else
+ {
+ start = rar->offset;
+ end = start + rar->dictionary_size;
+ if (rar->filters.filterstart < end) {
+ end = rar->filters.filterstart;
+ }
+
+ ret = expand(a, &end);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+
+ rar->bytes_uncopied = end - start;
+ rar->filters.lastend = end;
+ if (rar->filters.lastend != rar->filters.filterstart && rar->bytes_uncopied == 0) {
+ /* Broken RAR files cause this case.
+ * NOTE: If this case were possible on a normal RAR file
+ * we would find out where it was actually bad and
+ * what we would do to solve it. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Internal error extracting RAR file");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ if (rar->bytes_uncopied > (rar->unp_buffer_size - rar->unp_offset))
+ bs = rar->unp_buffer_size - rar->unp_offset;
+ else
+ bs = (size_t)rar->bytes_uncopied;
+ ret = copy_from_lzss_window_to_unp(a, buff, rar->offset, (int)bs);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ rar->offset += bs;
+ rar->bytes_uncopied -= bs;
+ /*
+ * If *buff is NULL, it means unp_buffer is not full.
+ * So we have to continue extracting a RAR file.
+ */
+ } while (*buff == NULL);
+
+ rar->unp_offset = 0;
+ *size = rar->unp_buffer_size;
+ *offset = rar->offset_outgoing;
+ rar->offset_outgoing += *size;
+ending_block:
+ /* Calculate File CRC. */
+ rar->crc_calculated = crc32(rar->crc_calculated, *buff, (unsigned)*size);
+ return ret;
+}
+
+static int
+parse_codes(struct archive_read *a)
+{
+ int i, j, val, n, r;
+ unsigned char bitlengths[MAX_SYMBOLS], zerocount, ppmd_flags;
+ unsigned int maxorder;
+ struct huffman_code precode;
+ struct rar *rar = (struct rar *)(a->format->data);
+ struct rar_br *br = &(rar->br);
+
+ free_codes(a);
+
+ /* Skip to the next byte */
+ rar_br_consume_unalined_bits(br);
+
+ /* PPMd block flag */
+ if (!rar_br_read_ahead(a, br, 1))
+ goto truncated_data;
+ if ((rar->is_ppmd_block = rar_br_bits(br, 1)) != 0)
+ {
+ rar_br_consume(br, 1);
+ if (!rar_br_read_ahead(a, br, 7))
+ goto truncated_data;
+ ppmd_flags = rar_br_bits(br, 7);
+ rar_br_consume(br, 7);
+
+ /* Memory is allocated in MB */
+ if (ppmd_flags & 0x20)
+ {
+ if (!rar_br_read_ahead(a, br, 8))
+ goto truncated_data;
+ rar->dictionary_size = (rar_br_bits(br, 8) + 1) << 20;
+ rar_br_consume(br, 8);
+ }
+
+ if (ppmd_flags & 0x40)
+ {
+ if (!rar_br_read_ahead(a, br, 8))
+ goto truncated_data;
+ rar->ppmd_escape = rar->ppmd7_context.InitEsc = rar_br_bits(br, 8);
+ rar_br_consume(br, 8);
+ }
+ else
+ rar->ppmd_escape = 2;
+
+ if (ppmd_flags & 0x20)
+ {
+ maxorder = (ppmd_flags & 0x1F) + 1;
+ if(maxorder > 16)
+ maxorder = 16 + (maxorder - 16) * 3;
+
+ if (maxorder == 1)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Make sure ppmd7_contest is freed before Ppmd7_Construct
+ * because reading a broken file cause this abnormal sequence. */
+ __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);
+
+ rar->bytein.a = a;
+ rar->bytein.Read = &ppmd_read;
+ __archive_ppmd7_functions.PpmdRAR_RangeDec_CreateVTable(&rar->range_dec);
+ rar->range_dec.Stream = &rar->bytein;
+ __archive_ppmd7_functions.Ppmd7_Construct(&rar->ppmd7_context);
+
+ if (rar->dictionary_size == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid zero dictionary size");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (!__archive_ppmd7_functions.Ppmd7_Alloc(&rar->ppmd7_context,
+ rar->dictionary_size))
+ {
+ archive_set_error(&a->archive, ENOMEM,
+ "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ if (!__archive_ppmd7_functions.PpmdRAR_RangeDec_Init(&rar->range_dec))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unable to initialize PPMd range decoder");
+ return (ARCHIVE_FATAL);
+ }
+ __archive_ppmd7_functions.Ppmd7_Init(&rar->ppmd7_context, maxorder);
+ rar->ppmd_valid = 1;
+ }
+ else
+ {
+ if (!rar->ppmd_valid) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid PPMd sequence");
+ return (ARCHIVE_FATAL);
+ }
+ if (!__archive_ppmd7_functions.PpmdRAR_RangeDec_Init(&rar->range_dec))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unable to initialize PPMd range decoder");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ }
+ else
+ {
+ rar_br_consume(br, 1);
+
+ /* Keep existing table flag */
+ if (!rar_br_read_ahead(a, br, 1))
+ goto truncated_data;
+ if (!rar_br_bits(br, 1))
+ memset(rar->lengthtable, 0, sizeof(rar->lengthtable));
+ rar_br_consume(br, 1);
+
+ memset(&bitlengths, 0, sizeof(bitlengths));
+ for (i = 0; i < MAX_SYMBOLS;)
+ {
+ if (!rar_br_read_ahead(a, br, 4))
+ goto truncated_data;
+ bitlengths[i++] = rar_br_bits(br, 4);
+ rar_br_consume(br, 4);
+ if (bitlengths[i-1] == 0xF)
+ {
+ if (!rar_br_read_ahead(a, br, 4))
+ goto truncated_data;
+ zerocount = rar_br_bits(br, 4);
+ rar_br_consume(br, 4);
+ if (zerocount)
+ {
+ i--;
+ for (j = 0; j < zerocount + 2 && i < MAX_SYMBOLS; j++)
+ bitlengths[i++] = 0;
+ }
+ }
+ }
+
+ memset(&precode, 0, sizeof(precode));
+ r = create_code(a, &precode, bitlengths, MAX_SYMBOLS, MAX_SYMBOL_LENGTH);
+ if (r != ARCHIVE_OK) {
+ free(precode.tree);
+ free(precode.table);
+ return (r);
+ }
+
+ for (i = 0; i < HUFFMAN_TABLE_SIZE;)
+ {
+ if ((val = read_next_symbol(a, &precode)) < 0) {
+ free(precode.tree);
+ free(precode.table);
+ return (ARCHIVE_FATAL);
+ }
+ if (val < 16)
+ {
+ rar->lengthtable[i] = (rar->lengthtable[i] + val) & 0xF;
+ i++;
+ }
+ else if (val < 18)
+ {
+ if (i == 0)
+ {
+ free(precode.tree);
+ free(precode.table);
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Internal error extracting RAR file.");
+ return (ARCHIVE_FATAL);
+ }
+
+ if(val == 16) {
+ if (!rar_br_read_ahead(a, br, 3)) {
+ free(precode.tree);
+ free(precode.table);
+ goto truncated_data;
+ }
+ n = rar_br_bits(br, 3) + 3;
+ rar_br_consume(br, 3);
+ } else {
+ if (!rar_br_read_ahead(a, br, 7)) {
+ free(precode.tree);
+ free(precode.table);
+ goto truncated_data;
+ }
+ n = rar_br_bits(br, 7) + 11;
+ rar_br_consume(br, 7);
+ }
+
+ for (j = 0; j < n && i < HUFFMAN_TABLE_SIZE; j++)
+ {
+ rar->lengthtable[i] = rar->lengthtable[i-1];
+ i++;
+ }
+ }
+ else
+ {
+ if(val == 18) {
+ if (!rar_br_read_ahead(a, br, 3)) {
+ free(precode.tree);
+ free(precode.table);
+ goto truncated_data;
+ }
+ n = rar_br_bits(br, 3) + 3;
+ rar_br_consume(br, 3);
+ } else {
+ if (!rar_br_read_ahead(a, br, 7)) {
+ free(precode.tree);
+ free(precode.table);
+ goto truncated_data;
+ }
+ n = rar_br_bits(br, 7) + 11;
+ rar_br_consume(br, 7);
+ }
+
+ for(j = 0; j < n && i < HUFFMAN_TABLE_SIZE; j++)
+ rar->lengthtable[i++] = 0;
+ }
+ }
+ free(precode.tree);
+ free(precode.table);
+
+ r = create_code(a, &rar->maincode, &rar->lengthtable[0], MAINCODE_SIZE,
+ MAX_SYMBOL_LENGTH);
+ if (r != ARCHIVE_OK)
+ return (r);
+ r = create_code(a, &rar->offsetcode, &rar->lengthtable[MAINCODE_SIZE],
+ OFFSETCODE_SIZE, MAX_SYMBOL_LENGTH);
+ if (r != ARCHIVE_OK)
+ return (r);
+ r = create_code(a, &rar->lowoffsetcode,
+ &rar->lengthtable[MAINCODE_SIZE + OFFSETCODE_SIZE],
+ LOWOFFSETCODE_SIZE, MAX_SYMBOL_LENGTH);
+ if (r != ARCHIVE_OK)
+ return (r);
+ r = create_code(a, &rar->lengthcode,
+ &rar->lengthtable[MAINCODE_SIZE + OFFSETCODE_SIZE +
+ LOWOFFSETCODE_SIZE], LENGTHCODE_SIZE, MAX_SYMBOL_LENGTH);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ if (!rar->dictionary_size || !rar->lzss.window)
+ {
+ /* Seems as though dictionary sizes are not used. Even so, minimize
+ * memory usage as much as possible.
+ */
+ void *new_window;
+ unsigned int new_size;
+
+ if (rar->unp_size >= DICTIONARY_MAX_SIZE)
+ new_size = DICTIONARY_MAX_SIZE;
+ else
+ new_size = rar_fls((unsigned int)rar->unp_size) << 1;
+ if (new_size == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Zero window size is invalid.");
+ return (ARCHIVE_FATAL);
+ }
+ new_window = realloc(rar->lzss.window, new_size);
+ if (new_window == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Unable to allocate memory for uncompressed data.");
+ return (ARCHIVE_FATAL);
+ }
+ rar->lzss.window = (unsigned char *)new_window;
+ rar->dictionary_size = new_size;
+ memset(rar->lzss.window, 0, rar->dictionary_size);
+ rar->lzss.mask = rar->dictionary_size - 1;
+ }
+
+ rar->start_new_table = 0;
+ return (ARCHIVE_OK);
+truncated_data:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ rar->valid = 0;
+ return (ARCHIVE_FATAL);
+}
+
+static void
+free_codes(struct archive_read *a)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+ free(rar->maincode.tree);
+ free(rar->offsetcode.tree);
+ free(rar->lowoffsetcode.tree);
+ free(rar->lengthcode.tree);
+ free(rar->maincode.table);
+ free(rar->offsetcode.table);
+ free(rar->lowoffsetcode.table);
+ free(rar->lengthcode.table);
+ memset(&rar->maincode, 0, sizeof(rar->maincode));
+ memset(&rar->offsetcode, 0, sizeof(rar->offsetcode));
+ memset(&rar->lowoffsetcode, 0, sizeof(rar->lowoffsetcode));
+ memset(&rar->lengthcode, 0, sizeof(rar->lengthcode));
+}
+
+
+static int
+read_next_symbol(struct archive_read *a, struct huffman_code *code)
+{
+ unsigned char bit;
+ unsigned int bits;
+ int length, value, node;
+ struct rar *rar;
+ struct rar_br *br;
+
+ if (!code->table)
+ {
+ if (make_table(a, code) != (ARCHIVE_OK))
+ return -1;
+ }
+
+ rar = (struct rar *)(a->format->data);
+ br = &(rar->br);
+
+ /* Look ahead (peek) at bits */
+ if (!rar_br_read_ahead(a, br, code->tablesize)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ rar->valid = 0;
+ return -1;
+ }
+ bits = rar_br_bits(br, code->tablesize);
+
+ length = code->table[bits].length;
+ value = code->table[bits].value;
+
+ if (length < 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid prefix code in bitstream");
+ return -1;
+ }
+
+ if (length <= code->tablesize)
+ {
+ /* Skip length bits */
+ rar_br_consume(br, length);
+ return value;
+ }
+
+ /* Skip tablesize bits */
+ rar_br_consume(br, code->tablesize);
+
+ node = value;
+ while (!(code->tree[node].branches[0] ==
+ code->tree[node].branches[1]))
+ {
+ if (!rar_br_read_ahead(a, br, 1)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ rar->valid = 0;
+ return -1;
+ }
+ bit = rar_br_bits(br, 1);
+ rar_br_consume(br, 1);
+
+ if (code->tree[node].branches[bit] < 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid prefix code in bitstream");
+ return -1;
+ }
+ node = code->tree[node].branches[bit];
+ }
+
+ return code->tree[node].branches[0];
+}
+
+static int
+create_code(struct archive_read *a, struct huffman_code *code,
+ unsigned char *lengths, int numsymbols, char maxlength)
+{
+ int i, j, codebits = 0, symbolsleft = numsymbols;
+
+ code->numentries = 0;
+ code->numallocatedentries = 0;
+ if (new_node(code) < 0) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Unable to allocate memory for node data.");
+ return (ARCHIVE_FATAL);
+ }
+ code->numentries = 1;
+ code->minlength = INT_MAX;
+ code->maxlength = INT_MIN;
+ codebits = 0;
+ for(i = 1; i <= maxlength; i++)
+ {
+ for(j = 0; j < numsymbols; j++)
+ {
+ if (lengths[j] != i) continue;
+ if (add_value(a, code, j, codebits, i) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ codebits++;
+ if (--symbolsleft <= 0)
+ break;
+ }
+ if (symbolsleft <= 0)
+ break;
+ codebits <<= 1;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+add_value(struct archive_read *a, struct huffman_code *code, int value,
+ int codebits, int length)
+{
+ int lastnode, bitpos, bit;
+ /* int repeatpos, repeatnode, nextnode; */
+
+ free(code->table);
+ code->table = NULL;
+
+ if(length > code->maxlength)
+ code->maxlength = length;
+ if(length < code->minlength)
+ code->minlength = length;
+
+ /*
+ * Dead code, repeatpos was is -1
+ *
+ repeatpos = -1;
+ if (repeatpos == 0 || (repeatpos >= 0
+ && (((codebits >> (repeatpos - 1)) & 3) == 0
+ || ((codebits >> (repeatpos - 1)) & 3) == 3)))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid repeat position");
+ return (ARCHIVE_FATAL);
+ }
+ */
+
+ lastnode = 0;
+ for (bitpos = length - 1; bitpos >= 0; bitpos--)
+ {
+ bit = (codebits >> bitpos) & 1;
+
+ /* Leaf node check */
+ if (code->tree[lastnode].branches[0] ==
+ code->tree[lastnode].branches[1])
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Prefix found");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Dead code, repeatpos was -1, bitpos >=0
+ *
+ if (bitpos == repeatpos)
+ {
+ * Open branch check *
+ if (!(code->tree[lastnode].branches[bit] < 0))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid repeating code");
+ return (ARCHIVE_FATAL);
+ }
+
+ if ((repeatnode = new_node(code)) < 0) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Unable to allocate memory for node data.");
+ return (ARCHIVE_FATAL);
+ }
+ if ((nextnode = new_node(code)) < 0) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Unable to allocate memory for node data.");
+ return (ARCHIVE_FATAL);
+ }
+
+ * Set branches *
+ code->tree[lastnode].branches[bit] = repeatnode;
+ code->tree[repeatnode].branches[bit] = repeatnode;
+ code->tree[repeatnode].branches[bit^1] = nextnode;
+ lastnode = nextnode;
+
+ bitpos++; * terminating bit already handled, skip it *
+ }
+ else
+ {
+ */
+ /* Open branch check */
+ if (code->tree[lastnode].branches[bit] < 0)
+ {
+ if (new_node(code) < 0) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Unable to allocate memory for node data.");
+ return (ARCHIVE_FATAL);
+ }
+ code->tree[lastnode].branches[bit] = code->numentries++;
+ }
+
+ /* set to branch */
+ lastnode = code->tree[lastnode].branches[bit];
+ /* } */
+ }
+
+ if (!(code->tree[lastnode].branches[0] == -1
+ && code->tree[lastnode].branches[1] == -2))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Prefix found");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Set leaf value */
+ code->tree[lastnode].branches[0] = value;
+ code->tree[lastnode].branches[1] = value;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+new_node(struct huffman_code *code)
+{
+ void *new_tree;
+ if (code->numallocatedentries == code->numentries) {
+ int new_num_entries = 256;
+ if (code->numentries > 0) {
+ new_num_entries = code->numentries * 2;
+ }
+ new_tree = realloc(code->tree, new_num_entries * sizeof(*code->tree));
+ if (new_tree == NULL)
+ return (-1);
+ code->tree = (struct huffman_tree_node *)new_tree;
+ code->numallocatedentries = new_num_entries;
+ }
+ code->tree[code->numentries].branches[0] = -1;
+ code->tree[code->numentries].branches[1] = -2;
+ return 1;
+}
+
+static int
+make_table(struct archive_read *a, struct huffman_code *code)
+{
+ if (code->maxlength < code->minlength || code->maxlength > 10)
+ code->tablesize = 10;
+ else
+ code->tablesize = code->maxlength;
+
+ code->table =
+ (struct huffman_table_entry *)calloc(1, sizeof(*code->table)
+ * ((size_t)1 << code->tablesize));
+
+ return make_table_recurse(a, code, 0, code->table, 0, code->tablesize);
+}
+
+static int
+make_table_recurse(struct archive_read *a, struct huffman_code *code, int node,
+ struct huffman_table_entry *table, int depth,
+ int maxdepth)
+{
+ int currtablesize, i, ret = (ARCHIVE_OK);
+
+ if (!code->tree)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Huffman tree was not created.");
+ return (ARCHIVE_FATAL);
+ }
+ if (node < 0 || node >= code->numentries)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid location to Huffman tree specified.");
+ return (ARCHIVE_FATAL);
+ }
+
+ currtablesize = 1 << (maxdepth - depth);
+
+ if (code->tree[node].branches[0] ==
+ code->tree[node].branches[1])
+ {
+ for(i = 0; i < currtablesize; i++)
+ {
+ table[i].length = depth;
+ table[i].value = code->tree[node].branches[0];
+ }
+ }
+ /*
+ * Dead code, node >= 0
+ *
+ else if (node < 0)
+ {
+ for(i = 0; i < currtablesize; i++)
+ table[i].length = -1;
+ }
+ */
+ else
+ {
+ if(depth == maxdepth)
+ {
+ table[0].length = maxdepth + 1;
+ table[0].value = node;
+ }
+ else
+ {
+ ret |= make_table_recurse(a, code, code->tree[node].branches[0], table,
+ depth + 1, maxdepth);
+ ret |= make_table_recurse(a, code, code->tree[node].branches[1],
+ table + currtablesize / 2, depth + 1, maxdepth);
+ }
+ }
+ return ret;
+}
+
+static int
+expand(struct archive_read *a, int64_t *end)
+{
+ static const unsigned char lengthbases[] =
+ { 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 10, 12, 14, 16, 20,
+ 24, 28, 32, 40, 48, 56, 64,
+ 80, 96, 112, 128, 160, 192, 224 };
+ static const unsigned char lengthbits[] =
+ { 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 2, 2,
+ 2, 2, 3, 3, 3, 3, 4,
+ 4, 4, 4, 5, 5, 5, 5 };
+ static const int lengthb_min = minimum(
+ (int)(sizeof(lengthbases)/sizeof(lengthbases[0])),
+ (int)(sizeof(lengthbits)/sizeof(lengthbits[0]))
+ );
+ static const unsigned int offsetbases[] =
+ { 0, 1, 2, 3, 4, 6,
+ 8, 12, 16, 24, 32, 48,
+ 64, 96, 128, 192, 256, 384,
+ 512, 768, 1024, 1536, 2048, 3072,
+ 4096, 6144, 8192, 12288, 16384, 24576,
+ 32768, 49152, 65536, 98304, 131072, 196608,
+ 262144, 327680, 393216, 458752, 524288, 589824,
+ 655360, 720896, 786432, 851968, 917504, 983040,
+ 1048576, 1310720, 1572864, 1835008, 2097152, 2359296,
+ 2621440, 2883584, 3145728, 3407872, 3670016, 3932160 };
+ static const unsigned char offsetbits[] =
+ { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
+ 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 };
+ static const int offsetb_min = minimum(
+ (int)(sizeof(offsetbases)/sizeof(offsetbases[0])),
+ (int)(sizeof(offsetbits)/sizeof(offsetbits[0]))
+ );
+ static const unsigned char shortbases[] =
+ { 0, 4, 8, 16, 32, 64, 128, 192 };
+ static const unsigned char shortbits[] =
+ { 2, 2, 3, 4, 5, 6, 6, 6 };
+
+ int symbol, offs, len, offsindex, lensymbol, i, offssymbol, lowoffsetsymbol;
+ unsigned char newfile;
+ struct rar *rar = (struct rar *)(a->format->data);
+ struct rar_br *br = &(rar->br);
+
+ if (rar->filters.filterstart < *end)
+ *end = rar->filters.filterstart;
+
+ while (1)
+ {
+ if(lzss_position(&rar->lzss) >= *end) {
+ return (ARCHIVE_OK);
+ }
+
+ if(rar->is_ppmd_block) {
+ *end = lzss_position(&rar->lzss);
+ return (ARCHIVE_OK);
+ }
+
+ if ((symbol = read_next_symbol(a, &rar->maincode)) < 0)
+ return (ARCHIVE_FATAL);
+
+ if (symbol < 256)
+ {
+ lzss_emit_literal(rar, symbol);
+ continue;
+ }
+ else if (symbol == 256)
+ {
+ if (!rar_br_read_ahead(a, br, 1))
+ goto truncated_data;
+ newfile = !rar_br_bits(br, 1);
+ rar_br_consume(br, 1);
+
+ if(newfile)
+ {
+ rar->start_new_block = 1;
+ if (!rar_br_read_ahead(a, br, 1))
+ goto truncated_data;
+ rar->start_new_table = rar_br_bits(br, 1);
+ rar_br_consume(br, 1);
+ *end = lzss_position(&rar->lzss);
+ return (ARCHIVE_OK);
+ }
+ else
+ {
+ if (parse_codes(a) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ continue;
+ }
+ }
+ else if(symbol==257)
+ {
+ if (!read_filter(a, end))
+ return (ARCHIVE_FATAL);
+ continue;
+ }
+ else if(symbol==258)
+ {
+ if(rar->lastlength == 0)
+ continue;
+
+ offs = rar->lastoffset;
+ len = rar->lastlength;
+ }
+ else if (symbol <= 262)
+ {
+ offsindex = symbol - 259;
+ offs = rar->oldoffset[offsindex];
+
+ if ((lensymbol = read_next_symbol(a, &rar->lengthcode)) < 0)
+ goto bad_data;
+ if (lensymbol > lengthb_min)
+ goto bad_data;
+ len = lengthbases[lensymbol] + 2;
+ if (lengthbits[lensymbol] > 0) {
+ if (!rar_br_read_ahead(a, br, lengthbits[lensymbol]))
+ goto truncated_data;
+ len += rar_br_bits(br, lengthbits[lensymbol]);
+ rar_br_consume(br, lengthbits[lensymbol]);
+ }
+
+ for (i = offsindex; i > 0; i--)
+ rar->oldoffset[i] = rar->oldoffset[i-1];
+ rar->oldoffset[0] = offs;
+ }
+ else if(symbol<=270)
+ {
+ offs = shortbases[symbol-263] + 1;
+ if(shortbits[symbol-263] > 0) {
+ if (!rar_br_read_ahead(a, br, shortbits[symbol-263]))
+ goto truncated_data;
+ offs += rar_br_bits(br, shortbits[symbol-263]);
+ rar_br_consume(br, shortbits[symbol-263]);
+ }
+
+ len = 2;
+
+ for(i = 3; i > 0; i--)
+ rar->oldoffset[i] = rar->oldoffset[i-1];
+ rar->oldoffset[0] = offs;
+ }
+ else
+ {
+ if (symbol-271 > lengthb_min)
+ goto bad_data;
+ len = lengthbases[symbol-271]+3;
+ if(lengthbits[symbol-271] > 0) {
+ if (!rar_br_read_ahead(a, br, lengthbits[symbol-271]))
+ goto truncated_data;
+ len += rar_br_bits(br, lengthbits[symbol-271]);
+ rar_br_consume(br, lengthbits[symbol-271]);
+ }
+
+ if ((offssymbol = read_next_symbol(a, &rar->offsetcode)) < 0)
+ goto bad_data;
+ if (offssymbol > offsetb_min)
+ goto bad_data;
+ offs = offsetbases[offssymbol]+1;
+ if(offsetbits[offssymbol] > 0)
+ {
+ if(offssymbol > 9)
+ {
+ if(offsetbits[offssymbol] > 4) {
+ if (!rar_br_read_ahead(a, br, offsetbits[offssymbol] - 4))
+ goto truncated_data;
+ offs += rar_br_bits(br, offsetbits[offssymbol] - 4) << 4;
+ rar_br_consume(br, offsetbits[offssymbol] - 4);
+ }
+
+ if(rar->numlowoffsetrepeats > 0)
+ {
+ rar->numlowoffsetrepeats--;
+ offs += rar->lastlowoffset;
+ }
+ else
+ {
+ if ((lowoffsetsymbol =
+ read_next_symbol(a, &rar->lowoffsetcode)) < 0)
+ return (ARCHIVE_FATAL);
+ if(lowoffsetsymbol == 16)
+ {
+ rar->numlowoffsetrepeats = 15;
+ offs += rar->lastlowoffset;
+ }
+ else
+ {
+ offs += lowoffsetsymbol;
+ rar->lastlowoffset = lowoffsetsymbol;
+ }
+ }
+ }
+ else {
+ if (!rar_br_read_ahead(a, br, offsetbits[offssymbol]))
+ goto truncated_data;
+ offs += rar_br_bits(br, offsetbits[offssymbol]);
+ rar_br_consume(br, offsetbits[offssymbol]);
+ }
+ }
+
+ if (offs >= 0x40000)
+ len++;
+ if (offs >= 0x2000)
+ len++;
+
+ for(i = 3; i > 0; i--)
+ rar->oldoffset[i] = rar->oldoffset[i-1];
+ rar->oldoffset[0] = offs;
+ }
+
+ rar->lastoffset = offs;
+ rar->lastlength = len;
+
+ lzss_emit_match(rar, rar->lastoffset, rar->lastlength);
+ }
+truncated_data:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ rar->valid = 0;
+ return (ARCHIVE_FATAL);
+bad_data:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Bad RAR file data");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+copy_from_lzss_window(struct archive_read *a, void *buffer,
+ int64_t startpos, int length)
+{
+ int windowoffs, firstpart;
+ struct rar *rar = (struct rar *)(a->format->data);
+
+ windowoffs = lzss_offset_for_position(&rar->lzss, startpos);
+ firstpart = lzss_size(&rar->lzss) - windowoffs;
+ if (firstpart < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Bad RAR file data");
+ return (ARCHIVE_FATAL);
+ }
+ if (firstpart < length) {
+ memcpy(buffer, &rar->lzss.window[windowoffs], firstpart);
+ memcpy(buffer, &rar->lzss.window[0], length - firstpart);
+ } else {
+ memcpy(buffer, &rar->lzss.window[windowoffs], length);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+copy_from_lzss_window_to_unp(struct archive_read *a, const void **buffer,
+ int64_t startpos, int length)
+{
+ int windowoffs, firstpart;
+ struct rar *rar = (struct rar *)(a->format->data);
+
+ if (!rar->unp_buffer)
+ {
+ if ((rar->unp_buffer = malloc(rar->unp_buffer_size)) == NULL)
+ {
+ archive_set_error(&a->archive, ENOMEM,
+ "Unable to allocate memory for uncompressed data.");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ windowoffs = lzss_offset_for_position(&rar->lzss, startpos);
+ if(windowoffs + length <= lzss_size(&rar->lzss)) {
+ memcpy(&rar->unp_buffer[rar->unp_offset], &rar->lzss.window[windowoffs],
+ length);
+ } else if (length <= lzss_size(&rar->lzss)) {
+ firstpart = lzss_size(&rar->lzss) - windowoffs;
+ if (firstpart < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Bad RAR file data");
+ return (ARCHIVE_FATAL);
+ }
+ if (firstpart < length) {
+ memcpy(&rar->unp_buffer[rar->unp_offset],
+ &rar->lzss.window[windowoffs], firstpart);
+ memcpy(&rar->unp_buffer[rar->unp_offset + firstpart],
+ &rar->lzss.window[0], length - firstpart);
+ } else {
+ memcpy(&rar->unp_buffer[rar->unp_offset],
+ &rar->lzss.window[windowoffs], length);
+ }
+ } else {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Bad RAR file data");
+ return (ARCHIVE_FATAL);
+ }
+ rar->unp_offset += length;
+ if (rar->unp_offset >= rar->unp_buffer_size)
+ *buffer = rar->unp_buffer;
+ else
+ *buffer = NULL;
+ return (ARCHIVE_OK);
+}
+
+static const void *
+rar_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+ const void *h = __archive_read_ahead(a, min, avail);
+ int ret;
+ if (avail)
+ {
+ if (a->archive.read_data_is_posix_read && *avail > (ssize_t)a->archive.read_data_requested)
+ *avail = a->archive.read_data_requested;
+ if (*avail > rar->bytes_remaining)
+ *avail = (ssize_t)rar->bytes_remaining;
+ if (*avail < 0)
+ return NULL;
+ else if (*avail == 0 && rar->main_flags & MHD_VOLUME &&
+ rar->file_flags & FHD_SPLIT_AFTER)
+ {
+ rar->filename_must_match = 1;
+ ret = archive_read_format_rar_read_header(a, a->entry);
+ if (ret == (ARCHIVE_EOF))
+ {
+ rar->has_endarc_header = 1;
+ ret = archive_read_format_rar_read_header(a, a->entry);
+ }
+ rar->filename_must_match = 0;
+ if (ret != (ARCHIVE_OK))
+ return NULL;
+ return rar_read_ahead(a, min, avail);
+ }
+ }
+ return h;
+}
+
+static int
+parse_filter(struct archive_read *a, const uint8_t *bytes, uint16_t length, uint8_t flags)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+ struct rar_filters *filters = &rar->filters;
+
+ struct memory_bit_reader br = { 0 };
+ struct rar_program_code *prog;
+ struct rar_filter *filter, **nextfilter;
+
+ uint32_t numprogs, num, blocklength, globaldatalen;
+ uint8_t *globaldata;
+ size_t blockstartpos;
+ uint32_t registers[8] = { 0 };
+ uint32_t i;
+
+ br.bytes = bytes;
+ br.length = length;
+
+ numprogs = 0;
+ for (prog = filters->progs; prog; prog = prog->next)
+ numprogs++;
+
+ if ((flags & 0x80))
+ {
+ num = membr_next_rarvm_number(&br);
+ if (num == 0)
+ {
+ delete_filter(filters->stack);
+ filters->stack = NULL;
+ delete_program_code(filters->progs);
+ filters->progs = NULL;
+ }
+ else
+ num--;
+ if (num > numprogs) {
+ return 0;
+ }
+ filters->lastfilternum = num;
+ }
+ else
+ num = filters->lastfilternum;
+
+ prog = filters->progs;
+ for (i = 0; i < num; i++)
+ prog = prog->next;
+ if (prog)
+ prog->usagecount++;
+
+ blockstartpos = membr_next_rarvm_number(&br) + (size_t)lzss_position(&rar->lzss);
+ if ((flags & 0x40))
+ blockstartpos += 258;
+ if ((flags & 0x20))
+ blocklength = membr_next_rarvm_number(&br);
+ else
+ blocklength = prog ? prog->oldfilterlength : 0;
+
+ registers[3] = PROGRAM_SYSTEM_GLOBAL_ADDRESS;
+ registers[4] = blocklength;
+ registers[5] = prog ? prog->usagecount : 0;
+ registers[7] = VM_MEMORY_SIZE;
+
+ if ((flags & 0x10))
+ {
+ uint8_t mask = (uint8_t)membr_bits(&br, 7);
+ for (i = 0; i < 7; i++)
+ if ((mask & (1 << i)))
+ registers[i] = membr_next_rarvm_number(&br);
+ }
+
+ if (!prog)
+ {
+ uint32_t len = membr_next_rarvm_number(&br);
+ uint8_t *bytecode;
+ struct rar_program_code **next;
+
+ if (len == 0 || len > 0x10000)
+ return 0;
+ bytecode = malloc(len);
+ if (!bytecode)
+ return 0;
+ for (i = 0; i < len; i++)
+ bytecode[i] = (uint8_t)membr_bits(&br, 8);
+ prog = compile_program(bytecode, len);
+ if (!prog) {
+ free(bytecode);
+ return 0;
+ }
+ free(bytecode);
+ next = &filters->progs;
+ while (*next)
+ next = &(*next)->next;
+ *next = prog;
+ }
+ prog->oldfilterlength = blocklength;
+
+ globaldata = NULL;
+ globaldatalen = 0;
+ if ((flags & 0x08))
+ {
+ globaldatalen = membr_next_rarvm_number(&br);
+ if (globaldatalen > PROGRAM_USER_GLOBAL_SIZE)
+ return 0;
+ globaldata = malloc(globaldatalen + PROGRAM_SYSTEM_GLOBAL_SIZE);
+ if (!globaldata)
+ return 0;
+ for (i = 0; i < globaldatalen; i++)
+ globaldata[i + PROGRAM_SYSTEM_GLOBAL_SIZE] = (uint8_t)membr_bits(&br, 8);
+ }
+
+ if (br.at_eof)
+ {
+ free(globaldata);
+ return 0;
+ }
+
+ filter = create_filter(prog, globaldata, globaldatalen, registers, blockstartpos, blocklength);
+ free(globaldata);
+ if (!filter)
+ return 0;
+
+ for (i = 0; i < 7; i++)
+ archive_le32enc(&filter->globaldata[i * 4], registers[i]);
+ archive_le32enc(&filter->globaldata[0x1C], blocklength);
+ archive_le32enc(&filter->globaldata[0x20], 0);
+ archive_le32enc(&filter->globaldata[0x2C], prog->usagecount);
+
+ nextfilter = &filters->stack;
+ while (*nextfilter)
+ nextfilter = &(*nextfilter)->next;
+ *nextfilter = filter;
+
+ if (!filters->stack->next)
+ filters->filterstart = blockstartpos;
+
+ return 1;
+}
+
+static struct rar_filter *
+create_filter(struct rar_program_code *prog, const uint8_t *globaldata, uint32_t globaldatalen, uint32_t registers[8], size_t startpos, uint32_t length)
+{
+ struct rar_filter *filter;
+
+ filter = calloc(1, sizeof(*filter));
+ if (!filter)
+ return NULL;
+ filter->prog = prog;
+ filter->globaldatalen = globaldatalen > PROGRAM_SYSTEM_GLOBAL_SIZE ? globaldatalen : PROGRAM_SYSTEM_GLOBAL_SIZE;
+ filter->globaldata = calloc(1, filter->globaldatalen);
+ if (!filter->globaldata)
+ return NULL;
+ if (globaldata)
+ memcpy(filter->globaldata, globaldata, globaldatalen);
+ if (registers)
+ memcpy(filter->initialregisters, registers, sizeof(filter->initialregisters));
+ filter->blockstartpos = startpos;
+ filter->blocklength = length;
+
+ return filter;
+}
+
+static int
+run_filters(struct archive_read *a)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+ struct rar_filters *filters = &rar->filters;
+ struct rar_filter *filter = filters->stack;
+ struct rar_filter *f;
+ size_t start, end;
+ int64_t tend;
+ uint32_t lastfilteraddress;
+ uint32_t lastfilterlength;
+ int ret;
+
+ if (filters == NULL || filter == NULL)
+ return (0);
+
+ start = filters->filterstart;
+ end = start + filter->blocklength;
+
+ filters->filterstart = INT64_MAX;
+ tend = (int64_t)end;
+ ret = expand(a, &tend);
+ if (ret != ARCHIVE_OK)
+ return 0;
+
+ /* Check if filter stack was modified in expand() */
+ ret = ARCHIVE_FATAL;
+ f = filters->stack;
+ while (f)
+ {
+ if (f == filter)
+ {
+ ret = ARCHIVE_OK;
+ break;
+ }
+ f = f->next;
+ }
+ if (ret != ARCHIVE_OK)
+ return 0;
+
+ if (tend < 0)
+ return 0;
+ end = (size_t)tend;
+ if (end != start + filter->blocklength)
+ return 0;
+
+ if (!filters->vm)
+ {
+ filters->vm = calloc(1, sizeof(*filters->vm));
+ if (!filters->vm)
+ return 0;
+ }
+
+ ret = copy_from_lzss_window(a, filters->vm->memory, start, filter->blocklength);
+ if (ret != ARCHIVE_OK)
+ return 0;
+ if (!execute_filter(a, filter, filters->vm, rar->offset))
+ return 0;
+
+ lastfilteraddress = filter->filteredblockaddress;
+ lastfilterlength = filter->filteredblocklength;
+ filters->stack = filter->next;
+ filter->next = NULL;
+ delete_filter(filter);
+
+ while ((filter = filters->stack) != NULL && (int64_t)filter->blockstartpos == filters->filterstart && filter->blocklength == lastfilterlength)
+ {
+ memmove(&filters->vm->memory[0], &filters->vm->memory[lastfilteraddress], lastfilterlength);
+ if (!execute_filter(a, filter, filters->vm, rar->offset))
+ return 0;
+
+ lastfilteraddress = filter->filteredblockaddress;
+ lastfilterlength = filter->filteredblocklength;
+ filters->stack = filter->next;
+ filter->next = NULL;
+ delete_filter(filter);
+ }
+
+ if (filters->stack)
+ {
+ if (filters->stack->blockstartpos < end)
+ return 0;
+ filters->filterstart = filters->stack->blockstartpos;
+ }
+
+ filters->lastend = end;
+ filters->bytes = &filters->vm->memory[lastfilteraddress];
+ filters->bytes_ready = lastfilterlength;
+
+ return 1;
+}
+
+static struct rar_program_code *
+compile_program(const uint8_t *bytes, size_t length)
+{
+ struct memory_bit_reader br = { 0 };
+ struct rar_program_code *prog;
+ // uint32_t instrcount = 0;
+ uint8_t xor;
+ size_t i;
+
+ xor = 0;
+ for (i = 1; i < length; i++)
+ xor ^= bytes[i];
+ if (!length || xor != bytes[0])
+ return NULL;
+
+ br.bytes = bytes;
+ br.length = length;
+ br.offset = 1;
+
+ prog = calloc(1, sizeof(*prog));
+ if (!prog)
+ return NULL;
+ prog->fingerprint = crc32(0, bytes, (unsigned int)length) | ((uint64_t)length << 32);
+
+ if (membr_bits(&br, 1))
+ {
+ prog->staticdatalen = membr_next_rarvm_number(&br) + 1;
+ prog->staticdata = malloc(prog->staticdatalen);
+ if (!prog->staticdata)
+ {
+ delete_program_code(prog);
+ return NULL;
+ }
+ for (i = 0; i < prog->staticdatalen; i++)
+ prog->staticdata[i] = (uint8_t)membr_bits(&br, 8);
+ }
+
+ return prog;
+}
+
+static void
+delete_filter(struct rar_filter *filter)
+{
+ while (filter)
+ {
+ struct rar_filter *next = filter->next;
+ free(filter->globaldata);
+ free(filter);
+ filter = next;
+ }
+}
+
+static void
+clear_filters(struct rar_filters *filters)
+{
+ delete_filter(filters->stack);
+ delete_program_code(filters->progs);
+ free(filters->vm);
+}
+
+static void
+delete_program_code(struct rar_program_code *prog)
+{
+ while (prog)
+ {
+ struct rar_program_code *next = prog->next;
+ free(prog->staticdata);
+ free(prog->globalbackup);
+ free(prog);
+ prog = next;
+ }
+}
+
+static uint32_t
+membr_next_rarvm_number(struct memory_bit_reader *br)
+{
+ uint32_t val;
+ switch (membr_bits(br, 2))
+ {
+ case 0:
+ return membr_bits(br, 4);
+ case 1:
+ val = membr_bits(br, 8);
+ if (val >= 16)
+ return val;
+ return 0xFFFFFF00 | (val << 4) | membr_bits(br, 4);
+ case 2:
+ return membr_bits(br, 16);
+ default:
+ return membr_bits(br, 32);
+ }
+}
+
+static inline uint32_t
+membr_bits(struct memory_bit_reader *br, int bits)
+{
+ if (bits > br->available && (br->at_eof || !membr_fill(br, bits)))
+ return 0;
+ return (uint32_t)((br->bits >> (br->available -= bits)) & (((uint64_t)1 << bits) - 1));
+}
+
+static int
+membr_fill(struct memory_bit_reader *br, int bits)
+{
+ while (br->available < bits && br->offset < br->length)
+ {
+ br->bits = (br->bits << 8) | br->bytes[br->offset++];
+ br->available += 8;
+ }
+ if (bits > br->available)
+ {
+ br->at_eof = 1;
+ return 0;
+ }
+ return 1;
+}
+
+static int
+read_filter(struct archive_read *a, int64_t *end)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+ uint8_t flags, val, *code;
+ uint16_t length, i;
+
+ if (!rar_decode_byte(a, &flags))
+ return 0;
+ length = (flags & 0x07) + 1;
+ if (length == 7)
+ {
+ if (!rar_decode_byte(a, &val))
+ return 0;
+ length = val + 7;
+ }
+ else if (length == 8)
+ {
+ if (!rar_decode_byte(a, &val))
+ return 0;
+ length = val << 8;
+ if (!rar_decode_byte(a, &val))
+ return 0;
+ length |= val;
+ }
+
+ code = malloc(length);
+ if (!code)
+ return 0;
+ for (i = 0; i < length; i++)
+ {
+ if (!rar_decode_byte(a, &code[i]))
+ {
+ free(code);
+ return 0;
+ }
+ }
+ if (!parse_filter(a, code, length, flags))
+ {
+ free(code);
+ return 0;
+ }
+ free(code);
+
+ if (rar->filters.filterstart < *end)
+ *end = rar->filters.filterstart;
+
+ return 1;
+}
+
+static int
+execute_filter_delta(struct rar_filter *filter, struct rar_virtual_machine *vm)
+{
+ uint32_t length = filter->initialregisters[4];
+ uint32_t numchannels = filter->initialregisters[0];
+ uint8_t *src, *dst;
+ uint32_t i, idx;
+
+ if (length > PROGRAM_WORK_SIZE / 2)
+ return 0;
+
+ src = &vm->memory[0];
+ dst = &vm->memory[length];
+ for (i = 0; i < numchannels; i++)
+ {
+ uint8_t lastbyte = 0;
+ for (idx = i; idx < length; idx += numchannels)
+ lastbyte = dst[idx] = lastbyte - *src++;
+ }
+
+ filter->filteredblockaddress = length;
+ filter->filteredblocklength = length;
+
+ return 1;
+}
+
+static int
+execute_filter_e8(struct rar_filter *filter, struct rar_virtual_machine *vm, size_t pos, int e9also)
+{
+ uint32_t length = filter->initialregisters[4];
+ uint32_t filesize = 0x1000000;
+ uint32_t i;
+
+ if (length > PROGRAM_WORK_SIZE || length < 4)
+ return 0;
+
+ for (i = 0; i <= length - 5; i++)
+ {
+ if (vm->memory[i] == 0xE8 || (e9also && vm->memory[i] == 0xE9))
+ {
+ uint32_t currpos = (uint32_t)pos + i + 1;
+ int32_t address = (int32_t)vm_read_32(vm, i + 1);
+ if (address < 0 && currpos >= (uint32_t)-address)
+ vm_write_32(vm, i + 1, address + filesize);
+ else if (address >= 0 && (uint32_t)address < filesize)
+ vm_write_32(vm, i + 1, address - currpos);
+ i += 4;
+ }
+ }
+
+ filter->filteredblockaddress = 0;
+ filter->filteredblocklength = length;
+
+ return 1;
+}
+
+static int
+execute_filter_rgb(struct rar_filter *filter, struct rar_virtual_machine *vm)
+{
+ uint32_t stride = filter->initialregisters[0];
+ uint32_t byteoffset = filter->initialregisters[1];
+ uint32_t blocklength = filter->initialregisters[4];
+ uint8_t *src, *dst;
+ uint32_t i, j;
+
+ if (blocklength > PROGRAM_WORK_SIZE / 2 || stride > blocklength)
+ return 0;
+
+ src = &vm->memory[0];
+ dst = &vm->memory[blocklength];
+ for (i = 0; i < 3; i++) {
+ uint8_t byte = 0;
+ uint8_t *prev = dst + i - stride;
+ for (j = i; j < blocklength; j += 3)
+ {
+ if (prev >= dst)
+ {
+ uint32_t delta1 = abs(prev[3] - prev[0]);
+ uint32_t delta2 = abs(byte - prev[0]);
+ uint32_t delta3 = abs(prev[3] - prev[0] + byte - prev[0]);
+ if (delta1 > delta2 || delta1 > delta3)
+ byte = delta2 <= delta3 ? prev[3] : prev[0];
+ }
+ byte -= *src++;
+ dst[j] = byte;
+ prev += 3;
+ }
+ }
+ for (i = byteoffset; i < blocklength - 2; i += 3)
+ {
+ dst[i] += dst[i + 1];
+ dst[i + 2] += dst[i + 1];
+ }
+
+ filter->filteredblockaddress = blocklength;
+ filter->filteredblocklength = blocklength;
+
+ return 1;
+}
+
+static int
+execute_filter_audio(struct rar_filter *filter, struct rar_virtual_machine *vm)
+{
+ uint32_t length = filter->initialregisters[4];
+ uint32_t numchannels = filter->initialregisters[0];
+ uint8_t *src, *dst;
+ uint32_t i, j;
+
+ if (length > PROGRAM_WORK_SIZE / 2)
+ return 0;
+
+ src = &vm->memory[0];
+ dst = &vm->memory[length];
+ for (i = 0; i < numchannels; i++)
+ {
+ struct audio_state state;
+ memset(&state, 0, sizeof(state));
+ for (j = i; j < length; j += numchannels)
+ {
+ int8_t delta = (int8_t)*src++;
+ uint8_t predbyte, byte;
+ int prederror;
+ state.delta[2] = state.delta[1];
+ state.delta[1] = state.lastdelta - state.delta[0];
+ state.delta[0] = state.lastdelta;
+ predbyte = ((8 * state.lastbyte + state.weight[0] * state.delta[0] + state.weight[1] * state.delta[1] + state.weight[2] * state.delta[2]) >> 3) & 0xFF;
+ byte = (predbyte - delta) & 0xFF;
+ prederror = delta << 3;
+ state.error[0] += abs(prederror);
+ state.error[1] += abs(prederror - state.delta[0]); state.error[2] += abs(prederror + state.delta[0]);
+ state.error[3] += abs(prederror - state.delta[1]); state.error[4] += abs(prederror + state.delta[1]);
+ state.error[5] += abs(prederror - state.delta[2]); state.error[6] += abs(prederror + state.delta[2]);
+ state.lastdelta = (int8_t)(byte - state.lastbyte);
+ dst[j] = state.lastbyte = byte;
+ if (!(state.count++ & 0x1F))
+ {
+ uint8_t k, idx = 0;
+ for (k = 1; k < 7; k++)
+ {
+ if (state.error[k] < state.error[idx])
+ idx = k;
+ }
+ memset(state.error, 0, sizeof(state.error));
+ switch (idx)
+ {
+ case 1: if (state.weight[0] >= -16) state.weight[0]--; break;
+ case 2: if (state.weight[0] < 16) state.weight[0]++; break;
+ case 3: if (state.weight[1] >= -16) state.weight[1]--; break;
+ case 4: if (state.weight[1] < 16) state.weight[1]++; break;
+ case 5: if (state.weight[2] >= -16) state.weight[2]--; break;
+ case 6: if (state.weight[2] < 16) state.weight[2]++; break;
+ }
+ }
+ }
+ }
+
+ filter->filteredblockaddress = length;
+ filter->filteredblocklength = length;
+
+ return 1;
+}
+
+
+static int
+execute_filter(struct archive_read *a, struct rar_filter *filter, struct rar_virtual_machine *vm, size_t pos)
+{
+ if (filter->prog->fingerprint == 0x1D0E06077D)
+ return execute_filter_delta(filter, vm);
+ if (filter->prog->fingerprint == 0x35AD576887)
+ return execute_filter_e8(filter, vm, pos, 0);
+ if (filter->prog->fingerprint == 0x393CD7E57E)
+ return execute_filter_e8(filter, vm, pos, 1);
+ if (filter->prog->fingerprint == 0x951C2C5DC8)
+ return execute_filter_rgb(filter, vm);
+ if (filter->prog->fingerprint == 0xD8BC85E701)
+ return execute_filter_audio(filter, vm);
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "No support for RAR VM program filter");
+ return 0;
+}
+
+static int
+rar_decode_byte(struct archive_read *a, uint8_t *byte)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+ struct rar_br *br = &(rar->br);
+ if (!rar_br_read_ahead(a, br, 8))
+ return 0;
+ *byte = (uint8_t)rar_br_bits(br, 8);
+ rar_br_consume(br, 8);
+ return 1;
+}
+
+static inline void
+vm_write_32(struct rar_virtual_machine* vm, size_t offset, uint32_t u32)
+{
+ archive_le32enc(vm->memory + offset, u32);
+}
+
+static inline uint32_t
+vm_read_32(struct rar_virtual_machine* vm, size_t offset)
+{
+ return archive_le32dec(vm->memory + offset);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_rar5.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_rar5.c
new file mode 100644
index 00000000000..52fe8b10ffe
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_rar5.c
@@ -0,0 +1,4251 @@
+/*-
+* Copyright (c) 2018 Grzegorz Antoniak (http://antoniak.org)
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "archive_platform.h"
+#include "archive_endian.h"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <time.h>
+#ifdef HAVE_ZLIB_H
+#include <zlib.h> /* crc32 */
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "archive.h"
+#ifndef HAVE_ZLIB_H
+#error #include "archive_crc32.h"
+#endif
+
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_ppmd7_private.h"
+#include "archive_entry_private.h"
+
+#ifdef HAVE_BLAKE2_H
+#include <blake2.h>
+#else
+#error #include "archive_blake2.h"
+#endif
+
+/*#define CHECK_CRC_ON_SOLID_SKIP*/
+/*#define DONT_FAIL_ON_CRC_ERROR*/
+/*#define DEBUG*/
+
+#define rar5_min(a, b) (((a) > (b)) ? (b) : (a))
+#define rar5_max(a, b) (((a) > (b)) ? (a) : (b))
+#define rar5_countof(X) ((const ssize_t) (sizeof(X) / sizeof(*X)))
+
+#if defined DEBUG
+#define DEBUG_CODE if(1)
+#define LOG(...) do { printf("rar5: " __VA_ARGS__); puts(""); } while(0)
+#else
+#define DEBUG_CODE if(0)
+#endif
+
+/* Real RAR5 magic number is:
+ *
+ * 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00
+ * "Rar!→•☺·\x00"
+ *
+ * Retrieved with `rar5_signature()` by XOR'ing it with 0xA1, because I don't
+ * want to put this magic sequence in each binary that uses libarchive, so
+ * applications that scan through the file for this marker won't trigger on
+ * this "false" one.
+ *
+ * The array itself is decrypted in `rar5_init` function. */
+
+static unsigned char rar5_signature_xor[] = { 243, 192, 211, 128, 187, 166, 160, 161 };
+static const size_t g_unpack_window_size = 0x20000;
+
+/* These could have been static const's, but they aren't, because of
+ * Visual Studio. */
+#define MAX_NAME_IN_CHARS 2048
+#define MAX_NAME_IN_BYTES (4 * MAX_NAME_IN_CHARS)
+
+struct file_header {
+ ssize_t bytes_remaining;
+ ssize_t unpacked_size;
+ int64_t last_offset; /* Used in sanity checks. */
+ int64_t last_size; /* Used in sanity checks. */
+
+ uint8_t solid : 1; /* Is this a solid stream? */
+ uint8_t service : 1; /* Is this file a service data? */
+ uint8_t eof : 1; /* Did we finish unpacking the file? */
+ uint8_t dir : 1; /* Is this file entry a directory? */
+
+ /* Optional time fields. */
+ uint64_t e_mtime;
+ uint64_t e_ctime;
+ uint64_t e_atime;
+ uint32_t e_unix_ns;
+
+ /* Optional hash fields. */
+ uint32_t stored_crc32;
+ uint32_t calculated_crc32;
+ uint8_t blake2sp[32];
+ blake2sp_state b2state;
+ char has_blake2;
+
+ /* Optional redir fields */
+ uint64_t redir_type;
+ uint64_t redir_flags;
+
+ ssize_t solid_window_size; /* Used in file format check. */
+};
+
+enum EXTRA {
+ EX_CRYPT = 0x01,
+ EX_HASH = 0x02,
+ EX_HTIME = 0x03,
+ EX_VERSION = 0x04,
+ EX_REDIR = 0x05,
+ EX_UOWNER = 0x06,
+ EX_SUBDATA = 0x07
+};
+
+#define REDIR_SYMLINK_IS_DIR 1
+
+enum REDIR_TYPE {
+ REDIR_TYPE_NONE = 0,
+ REDIR_TYPE_UNIXSYMLINK = 1,
+ REDIR_TYPE_WINSYMLINK = 2,
+ REDIR_TYPE_JUNCTION = 3,
+ REDIR_TYPE_HARDLINK = 4,
+ REDIR_TYPE_FILECOPY = 5,
+};
+
+#define OWNER_USER_NAME 0x01
+#define OWNER_GROUP_NAME 0x02
+#define OWNER_USER_UID 0x04
+#define OWNER_GROUP_GID 0x08
+#define OWNER_MAXNAMELEN 256
+
+enum FILTER_TYPE {
+ FILTER_DELTA = 0, /* Generic pattern. */
+ FILTER_E8 = 1, /* Intel x86 code. */
+ FILTER_E8E9 = 2, /* Intel x86 code. */
+ FILTER_ARM = 3, /* ARM code. */
+ FILTER_AUDIO = 4, /* Audio filter, not used in RARv5. */
+ FILTER_RGB = 5, /* Color palette, not used in RARv5. */
+ FILTER_ITANIUM = 6, /* Intel's Itanium, not used in RARv5. */
+ FILTER_PPM = 7, /* Predictive pattern matching, not used in
+ RARv5. */
+ FILTER_NONE = 8,
+};
+
+struct filter_info {
+ int type;
+ int channels;
+ int pos_r;
+
+ int64_t block_start;
+ ssize_t block_length;
+ uint16_t width;
+};
+
+struct data_ready {
+ char used;
+ const uint8_t* buf;
+ size_t size;
+ int64_t offset;
+};
+
+struct cdeque {
+ uint16_t beg_pos;
+ uint16_t end_pos;
+ uint16_t cap_mask;
+ uint16_t size;
+ size_t* arr;
+};
+
+struct decode_table {
+ uint32_t size;
+ int32_t decode_len[16];
+ uint32_t decode_pos[16];
+ uint32_t quick_bits;
+ uint8_t quick_len[1 << 10];
+ uint16_t quick_num[1 << 10];
+ uint16_t decode_num[306];
+};
+
+struct comp_state {
+ /* Flag used to specify if unpacker needs to reinitialize the
+ uncompression context. */
+ uint8_t initialized : 1;
+
+ /* Flag used when applying filters. */
+ uint8_t all_filters_applied : 1;
+
+ /* Flag used to skip file context reinitialization, used when unpacker
+ is skipping through different multivolume archives. */
+ uint8_t switch_multivolume : 1;
+
+ /* Flag used to specify if unpacker has processed the whole data block
+ or just a part of it. */
+ uint8_t block_parsing_finished : 1;
+
+ signed int notused : 4;
+
+ int flags; /* Uncompression flags. */
+ int method; /* Uncompression algorithm method. */
+ int version; /* Uncompression algorithm version. */
+ ssize_t window_size; /* Size of window_buf. */
+ uint8_t* window_buf; /* Circular buffer used during
+ decompression. */
+ uint8_t* filtered_buf; /* Buffer used when applying filters. */
+ const uint8_t* block_buf; /* Buffer used when merging blocks. */
+ size_t window_mask; /* Convenience field; window_size - 1. */
+ int64_t write_ptr; /* This amount of data has been unpacked
+ in the window buffer. */
+ int64_t last_write_ptr; /* This amount of data has been stored in
+ the output file. */
+ int64_t last_unstore_ptr; /* Counter of bytes extracted during
+ unstoring. This is separate from
+ last_write_ptr because of how SERVICE
+ base blocks are handled during skipping
+ in solid multiarchive archives. */
+ int64_t solid_offset; /* Additional offset inside the window
+ buffer, used in unpacking solid
+ archives. */
+ ssize_t cur_block_size; /* Size of current data block. */
+ int last_len; /* Flag used in lzss decompression. */
+
+ /* Decode tables used during lzss uncompression. */
+
+#define HUFF_BC 20
+ struct decode_table bd; /* huffman bit lengths */
+#define HUFF_NC 306
+ struct decode_table ld; /* literals */
+#define HUFF_DC 64
+ struct decode_table dd; /* distances */
+#define HUFF_LDC 16
+ struct decode_table ldd; /* lower bits of distances */
+#define HUFF_RC 44
+ struct decode_table rd; /* repeating distances */
+#define HUFF_TABLE_SIZE (HUFF_NC + HUFF_DC + HUFF_RC + HUFF_LDC)
+
+ /* Circular deque for storing filters. */
+ struct cdeque filters;
+ int64_t last_block_start; /* Used for sanity checking. */
+ ssize_t last_block_length; /* Used for sanity checking. */
+
+ /* Distance cache used during lzss uncompression. */
+ int dist_cache[4];
+
+ /* Data buffer stack. */
+ struct data_ready dready[2];
+};
+
+/* Bit reader state. */
+struct bit_reader {
+ int8_t bit_addr; /* Current bit pointer inside current byte. */
+ int in_addr; /* Current byte pointer. */
+};
+
+/* RARv5 block header structure. Use bf_* functions to get values from
+ * block_flags_u8 field. I.e. bf_byte_count, etc. */
+struct compressed_block_header {
+ /* block_flags_u8 contain fields encoded in little-endian bitfield:
+ *
+ * - table present flag (shr 7, and 1),
+ * - last block flag (shr 6, and 1),
+ * - byte_count (shr 3, and 7),
+ * - bit_size (shr 0, and 7).
+ */
+ uint8_t block_flags_u8;
+ uint8_t block_cksum;
+};
+
+/* RARv5 main header structure. */
+struct main_header {
+ /* Does the archive contain solid streams? */
+ uint8_t solid : 1;
+
+ /* If this a multi-file archive? */
+ uint8_t volume : 1;
+ uint8_t endarc : 1;
+ uint8_t notused : 5;
+
+ unsigned int vol_no;
+};
+
+struct generic_header {
+ uint8_t split_after : 1;
+ uint8_t split_before : 1;
+ uint8_t padding : 6;
+ int size;
+ int last_header_id;
+};
+
+struct multivolume {
+ unsigned int expected_vol_no;
+ uint8_t* push_buf;
+};
+
+/* Main context structure. */
+struct rar5 {
+ int header_initialized;
+
+ /* Set to 1 if current file is positioned AFTER the magic value
+ * of the archive file. This is used in header reading functions. */
+ int skipped_magic;
+
+ /* Set to not zero if we're in skip mode (either by calling
+ * rar5_data_skip function or when skipping over solid streams).
+ * Set to 0 when in * extraction mode. This is used during checksum
+ * calculation functions. */
+ int skip_mode;
+
+ /* Set to not zero if we're in block merging mode (i.e. when switching
+ * to another file in multivolume archive, last block from 1st archive
+ * needs to be merged with 1st block from 2nd archive). This flag
+ * guards against recursive use of the merging function, which doesn't
+ * support recursive calls. */
+ int merge_mode;
+
+ /* An offset to QuickOpen list. This is not supported by this unpacker,
+ * because we're focusing on streaming interface. QuickOpen is designed
+ * to make things quicker for non-stream interfaces, so it's not our
+ * use case. */
+ uint64_t qlist_offset;
+
+ /* An offset to additional Recovery data. This is not supported by this
+ * unpacker. Recovery data are additional Reed-Solomon codes that could
+ * be used to calculate bytes that are missing in archive or are
+ * corrupted. */
+ uint64_t rr_offset;
+
+ /* Various context variables grouped to different structures. */
+ struct generic_header generic;
+ struct main_header main;
+ struct comp_state cstate;
+ struct file_header file;
+ struct bit_reader bits;
+ struct multivolume vol;
+
+ /* The header of currently processed RARv5 block. Used in main
+ * decompression logic loop. */
+ struct compressed_block_header last_block_hdr;
+};
+
+/* Forward function declarations. */
+
+static void rar5_signature(char *buf);
+static int verify_global_checksums(struct archive_read* a);
+static int rar5_read_data_skip(struct archive_read *a);
+static int push_data_ready(struct archive_read* a, struct rar5* rar,
+ const uint8_t* buf, size_t size, int64_t offset);
+
+/* CDE_xxx = Circular Double Ended (Queue) return values. */
+enum CDE_RETURN_VALUES {
+ CDE_OK, CDE_ALLOC, CDE_PARAM, CDE_OUT_OF_BOUNDS,
+};
+
+/* Clears the contents of this circular deque. */
+static void cdeque_clear(struct cdeque* d) {
+ d->size = 0;
+ d->beg_pos = 0;
+ d->end_pos = 0;
+}
+
+/* Creates a new circular deque object. Capacity must be power of 2: 8, 16, 32,
+ * 64, 256, etc. When the user will add another item above current capacity,
+ * the circular deque will overwrite the oldest entry. */
+static int cdeque_init(struct cdeque* d, int max_capacity_power_of_2) {
+ if(d == NULL || max_capacity_power_of_2 == 0)
+ return CDE_PARAM;
+
+ d->cap_mask = max_capacity_power_of_2 - 1;
+ d->arr = NULL;
+
+ if((max_capacity_power_of_2 & d->cap_mask) != 0)
+ return CDE_PARAM;
+
+ cdeque_clear(d);
+ d->arr = malloc(sizeof(void*) * max_capacity_power_of_2);
+
+ return d->arr ? CDE_OK : CDE_ALLOC;
+}
+
+/* Return the current size (not capacity) of circular deque `d`. */
+static size_t cdeque_size(struct cdeque* d) {
+ return d->size;
+}
+
+/* Returns the first element of current circular deque. Note that this function
+ * doesn't perform any bounds checking. If you need bounds checking, use
+ * `cdeque_front()` function instead. */
+static void cdeque_front_fast(struct cdeque* d, void** value) {
+ *value = (void*) d->arr[d->beg_pos];
+}
+
+/* Returns the first element of current circular deque. This function
+ * performs bounds checking. */
+static int cdeque_front(struct cdeque* d, void** value) {
+ if(d->size > 0) {
+ cdeque_front_fast(d, value);
+ return CDE_OK;
+ } else
+ return CDE_OUT_OF_BOUNDS;
+}
+
+/* Pushes a new element into the end of this circular deque object. If current
+ * size will exceed capacity, the oldest element will be overwritten. */
+static int cdeque_push_back(struct cdeque* d, void* item) {
+ if(d == NULL)
+ return CDE_PARAM;
+
+ if(d->size == d->cap_mask + 1)
+ return CDE_OUT_OF_BOUNDS;
+
+ d->arr[d->end_pos] = (size_t) item;
+ d->end_pos = (d->end_pos + 1) & d->cap_mask;
+ d->size++;
+
+ return CDE_OK;
+}
+
+/* Pops a front element of this circular deque object and returns its value.
+ * This function doesn't perform any bounds checking. */
+static void cdeque_pop_front_fast(struct cdeque* d, void** value) {
+ *value = (void*) d->arr[d->beg_pos];
+ d->beg_pos = (d->beg_pos + 1) & d->cap_mask;
+ d->size--;
+}
+
+/* Pops a front element of this circular deque object and returns its value.
+ * This function performs bounds checking. */
+static int cdeque_pop_front(struct cdeque* d, void** value) {
+ if(!d || !value)
+ return CDE_PARAM;
+
+ if(d->size == 0)
+ return CDE_OUT_OF_BOUNDS;
+
+ cdeque_pop_front_fast(d, value);
+ return CDE_OK;
+}
+
+/* Convenience function to cast filter_info** to void **. */
+static void** cdeque_filter_p(struct filter_info** f) {
+ return (void**) (size_t) f;
+}
+
+/* Convenience function to cast filter_info* to void *. */
+static void* cdeque_filter(struct filter_info* f) {
+ return (void**) (size_t) f;
+}
+
+/* Destroys this circular deque object. Deallocates the memory of the
+ * collection buffer, but doesn't deallocate the memory of any pointer passed
+ * to this deque as a value. */
+static void cdeque_free(struct cdeque* d) {
+ if(!d)
+ return;
+
+ if(!d->arr)
+ return;
+
+ free(d->arr);
+
+ d->arr = NULL;
+ d->beg_pos = -1;
+ d->end_pos = -1;
+ d->cap_mask = 0;
+}
+
+static inline
+uint8_t bf_bit_size(const struct compressed_block_header* hdr) {
+ return hdr->block_flags_u8 & 7;
+}
+
+static inline
+uint8_t bf_byte_count(const struct compressed_block_header* hdr) {
+ return (hdr->block_flags_u8 >> 3) & 7;
+}
+
+static inline
+uint8_t bf_is_table_present(const struct compressed_block_header* hdr) {
+ return (hdr->block_flags_u8 >> 7) & 1;
+}
+
+static inline struct rar5* get_context(struct archive_read* a) {
+ return (struct rar5*) a->format->data;
+}
+
+/* Convenience functions used by filter implementations. */
+static void circular_memcpy(uint8_t* dst, uint8_t* window, const uint64_t mask,
+ int64_t start, int64_t end)
+{
+ if((start & mask) > (end & mask)) {
+ ssize_t len1 = mask + 1 - (start & mask);
+ ssize_t len2 = end & mask;
+
+ memcpy(dst, &window[start & mask], len1);
+ memcpy(dst + len1, window, len2);
+ } else {
+ memcpy(dst, &window[start & mask], (size_t) (end - start));
+ }
+}
+
+static uint32_t read_filter_data(struct rar5* rar, uint32_t offset) {
+ uint8_t linear_buf[4];
+ circular_memcpy(linear_buf, rar->cstate.window_buf,
+ rar->cstate.window_mask, offset, offset + 4);
+ return archive_le32dec(linear_buf);
+}
+
+static void write_filter_data(struct rar5* rar, uint32_t offset,
+ uint32_t value)
+{
+ archive_le32enc(&rar->cstate.filtered_buf[offset], value);
+}
+
+/* Allocates a new filter descriptor and adds it to the filter array. */
+static struct filter_info* add_new_filter(struct rar5* rar) {
+ struct filter_info* f =
+ (struct filter_info*) calloc(1, sizeof(struct filter_info));
+
+ if(!f) {
+ return NULL;
+ }
+
+ cdeque_push_back(&rar->cstate.filters, cdeque_filter(f));
+ return f;
+}
+
+static int run_delta_filter(struct rar5* rar, struct filter_info* flt) {
+ int i;
+ ssize_t dest_pos, src_pos = 0;
+
+ for(i = 0; i < flt->channels; i++) {
+ uint8_t prev_byte = 0;
+ for(dest_pos = i;
+ dest_pos < flt->block_length;
+ dest_pos += flt->channels)
+ {
+ uint8_t byte;
+
+ byte = rar->cstate.window_buf[
+ (rar->cstate.solid_offset + flt->block_start +
+ src_pos) & rar->cstate.window_mask];
+
+ prev_byte -= byte;
+ rar->cstate.filtered_buf[dest_pos] = prev_byte;
+ src_pos++;
+ }
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt,
+ int extended)
+{
+ const uint32_t file_size = 0x1000000;
+ ssize_t i;
+
+ circular_memcpy(rar->cstate.filtered_buf,
+ rar->cstate.window_buf, rar->cstate.window_mask,
+ rar->cstate.solid_offset + flt->block_start,
+ rar->cstate.solid_offset + flt->block_start + flt->block_length);
+
+ for(i = 0; i < flt->block_length - 4;) {
+ uint8_t b = rar->cstate.window_buf[
+ (rar->cstate.solid_offset + flt->block_start +
+ i++) & rar->cstate.window_mask];
+
+ /*
+ * 0xE8 = x86's call <relative_addr_uint32> (function call)
+ * 0xE9 = x86's jmp <relative_addr_uint32> (unconditional jump)
+ */
+ if(b == 0xE8 || (extended && b == 0xE9)) {
+
+ uint32_t addr;
+ uint32_t offset = (i + flt->block_start) % file_size;
+
+ addr = read_filter_data(rar,
+ (uint32_t)(rar->cstate.solid_offset +
+ flt->block_start + i) & rar->cstate.window_mask);
+
+ if(addr & 0x80000000) {
+ if(((addr + offset) & 0x80000000) == 0) {
+ write_filter_data(rar, (uint32_t)i,
+ addr + file_size);
+ }
+ } else {
+ if((addr - file_size) & 0x80000000) {
+ uint32_t naddr = addr - offset;
+ write_filter_data(rar, (uint32_t)i,
+ naddr);
+ }
+ }
+
+ i += 4;
+ }
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int run_arm_filter(struct rar5* rar, struct filter_info* flt) {
+ ssize_t i = 0;
+ uint32_t offset;
+
+ circular_memcpy(rar->cstate.filtered_buf,
+ rar->cstate.window_buf, rar->cstate.window_mask,
+ rar->cstate.solid_offset + flt->block_start,
+ rar->cstate.solid_offset + flt->block_start + flt->block_length);
+
+ for(i = 0; i < flt->block_length - 3; i += 4) {
+ uint8_t* b = &rar->cstate.window_buf[
+ (rar->cstate.solid_offset +
+ flt->block_start + i + 3) & rar->cstate.window_mask];
+
+ if(*b == 0xEB) {
+ /* 0xEB = ARM's BL (branch + link) instruction. */
+ offset = read_filter_data(rar,
+ (rar->cstate.solid_offset + flt->block_start + i) &
+ (uint32_t)rar->cstate.window_mask) & 0x00ffffff;
+
+ offset -= (uint32_t) ((i + flt->block_start) / 4);
+ offset = (offset & 0x00ffffff) | 0xeb000000;
+ write_filter_data(rar, (uint32_t)i, offset);
+ }
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int run_filter(struct archive_read* a, struct filter_info* flt) {
+ int ret;
+ struct rar5* rar = get_context(a);
+
+ free(rar->cstate.filtered_buf);
+
+ rar->cstate.filtered_buf = malloc(flt->block_length);
+ if(!rar->cstate.filtered_buf) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for filter data.");
+ return ARCHIVE_FATAL;
+ }
+
+ switch(flt->type) {
+ case FILTER_DELTA:
+ ret = run_delta_filter(rar, flt);
+ break;
+
+ case FILTER_E8:
+ /* fallthrough */
+ case FILTER_E8E9:
+ ret = run_e8e9_filter(rar, flt,
+ flt->type == FILTER_E8E9);
+ break;
+
+ case FILTER_ARM:
+ ret = run_arm_filter(rar, flt);
+ break;
+
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported filter type: 0x%x", flt->type);
+ return ARCHIVE_FATAL;
+ }
+
+ if(ret != ARCHIVE_OK) {
+ /* Filter has failed. */
+ return ret;
+ }
+
+ if(ARCHIVE_OK != push_data_ready(a, rar, rar->cstate.filtered_buf,
+ flt->block_length, rar->cstate.last_write_ptr))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Stack overflow when submitting unpacked data");
+
+ return ARCHIVE_FATAL;
+ }
+
+ rar->cstate.last_write_ptr += flt->block_length;
+ return ARCHIVE_OK;
+}
+
+/* The `push_data` function submits the selected data range to the user.
+ * Next call of `use_data` will use the pointer, size and offset arguments
+ * that are specified here. These arguments are pushed to the FIFO stack here,
+ * and popped from the stack by the `use_data` function. */
+static void push_data(struct archive_read* a, struct rar5* rar,
+ const uint8_t* buf, int64_t idx_begin, int64_t idx_end)
+{
+ const uint64_t wmask = rar->cstate.window_mask;
+ const ssize_t solid_write_ptr = (rar->cstate.solid_offset +
+ rar->cstate.last_write_ptr) & wmask;
+
+ idx_begin += rar->cstate.solid_offset;
+ idx_end += rar->cstate.solid_offset;
+
+ /* Check if our unpacked data is wrapped inside the window circular
+ * buffer. If it's not wrapped, it can be copied out by using
+ * a single memcpy, but when it's wrapped, we need to copy the first
+ * part with one memcpy, and the second part with another memcpy. */
+
+ if((idx_begin & wmask) > (idx_end & wmask)) {
+ /* The data is wrapped (begin offset sis bigger than end
+ * offset). */
+ const ssize_t frag1_size = rar->cstate.window_size -
+ (idx_begin & wmask);
+ const ssize_t frag2_size = idx_end & wmask;
+
+ /* Copy the first part of the buffer first. */
+ push_data_ready(a, rar, buf + solid_write_ptr, frag1_size,
+ rar->cstate.last_write_ptr);
+
+ /* Copy the second part of the buffer. */
+ push_data_ready(a, rar, buf, frag2_size,
+ rar->cstate.last_write_ptr + frag1_size);
+
+ rar->cstate.last_write_ptr += frag1_size + frag2_size;
+ } else {
+ /* Data is not wrapped, so we can just use one call to copy the
+ * data. */
+ push_data_ready(a, rar,
+ buf + solid_write_ptr, (idx_end - idx_begin) & wmask,
+ rar->cstate.last_write_ptr);
+
+ rar->cstate.last_write_ptr += idx_end - idx_begin;
+ }
+}
+
+/* Convenience function that submits the data to the user. It uses the
+ * unpack window buffer as a source location. */
+static void push_window_data(struct archive_read* a, struct rar5* rar,
+ int64_t idx_begin, int64_t idx_end)
+{
+ push_data(a, rar, rar->cstate.window_buf, idx_begin, idx_end);
+}
+
+static int apply_filters(struct archive_read* a) {
+ struct filter_info* flt;
+ struct rar5* rar = get_context(a);
+ int ret;
+
+ rar->cstate.all_filters_applied = 0;
+
+ /* Get the first filter that can be applied to our data. The data
+ * needs to be fully unpacked before the filter can be run. */
+ if(CDE_OK == cdeque_front(&rar->cstate.filters,
+ cdeque_filter_p(&flt))) {
+ /* Check if our unpacked data fully covers this filter's
+ * range. */
+ if(rar->cstate.write_ptr > flt->block_start &&
+ rar->cstate.write_ptr >= flt->block_start +
+ flt->block_length) {
+ /* Check if we have some data pending to be written
+ * right before the filter's start offset. */
+ if(rar->cstate.last_write_ptr == flt->block_start) {
+ /* Run the filter specified by descriptor
+ * `flt`. */
+ ret = run_filter(a, flt);
+ if(ret != ARCHIVE_OK) {
+ /* Filter failure, return error. */
+ return ret;
+ }
+
+ /* Filter descriptor won't be needed anymore
+ * after it's used, * so remove it from the
+ * filter list and free its memory. */
+ (void) cdeque_pop_front(&rar->cstate.filters,
+ cdeque_filter_p(&flt));
+
+ free(flt);
+ } else {
+ /* We can't run filters yet, dump the memory
+ * right before the filter. */
+ push_window_data(a, rar,
+ rar->cstate.last_write_ptr,
+ flt->block_start);
+ }
+
+ /* Return 'filter applied or not needed' state to the
+ * caller. */
+ return ARCHIVE_RETRY;
+ }
+ }
+
+ rar->cstate.all_filters_applied = 1;
+ return ARCHIVE_OK;
+}
+
+static void dist_cache_push(struct rar5* rar, int value) {
+ int* q = rar->cstate.dist_cache;
+
+ q[3] = q[2];
+ q[2] = q[1];
+ q[1] = q[0];
+ q[0] = value;
+}
+
+static int dist_cache_touch(struct rar5* rar, int idx) {
+ int* q = rar->cstate.dist_cache;
+ int i, dist = q[idx];
+
+ for(i = idx; i > 0; i--)
+ q[i] = q[i - 1];
+
+ q[0] = dist;
+ return dist;
+}
+
+static void free_filters(struct rar5* rar) {
+ struct cdeque* d = &rar->cstate.filters;
+
+ /* Free any remaining filters. All filters should be naturally
+ * consumed by the unpacking function, so remaining filters after
+ * unpacking normally mean that unpacking wasn't successful.
+ * But still of course we shouldn't leak memory in such case. */
+
+ /* cdeque_size() is a fast operation, so we can use it as a loop
+ * expression. */
+ while(cdeque_size(d) > 0) {
+ struct filter_info* f = NULL;
+
+ /* Pop_front will also decrease the collection's size. */
+ if (CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f)))
+ free(f);
+ }
+
+ cdeque_clear(d);
+
+ /* Also clear out the variables needed for sanity checking. */
+ rar->cstate.last_block_start = 0;
+ rar->cstate.last_block_length = 0;
+}
+
+static void reset_file_context(struct rar5* rar) {
+ memset(&rar->file, 0, sizeof(rar->file));
+ blake2sp_init(&rar->file.b2state, 32);
+
+ if(rar->main.solid) {
+ rar->cstate.solid_offset += rar->cstate.write_ptr;
+ } else {
+ rar->cstate.solid_offset = 0;
+ }
+
+ rar->cstate.write_ptr = 0;
+ rar->cstate.last_write_ptr = 0;
+ rar->cstate.last_unstore_ptr = 0;
+
+ rar->file.redir_type = REDIR_TYPE_NONE;
+ rar->file.redir_flags = 0;
+
+ free_filters(rar);
+}
+
+static inline int get_archive_read(struct archive* a,
+ struct archive_read** ar)
+{
+ *ar = (struct archive_read*) a;
+ archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_support_format_rar5");
+
+ return ARCHIVE_OK;
+}
+
+static int read_ahead(struct archive_read* a, size_t how_many,
+ const uint8_t** ptr)
+{
+ ssize_t avail = -1;
+ if(!ptr)
+ return 0;
+
+ *ptr = __archive_read_ahead(a, how_many, &avail);
+ if(*ptr == NULL) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int consume(struct archive_read* a, int64_t how_many) {
+ int ret;
+
+ ret = how_many == __archive_read_consume(a, how_many)
+ ? ARCHIVE_OK
+ : ARCHIVE_FATAL;
+
+ return ret;
+}
+
+/**
+ * Read a RAR5 variable sized numeric value. This value will be stored in
+ * `pvalue`. The `pvalue_len` argument points to a variable that will receive
+ * the byte count that was consumed in order to decode the `pvalue` value, plus
+ * one.
+ *
+ * pvalue_len is optional and can be NULL.
+ *
+ * NOTE: if `pvalue_len` is NOT NULL, the caller needs to manually consume
+ * the number of bytes that `pvalue_len` value contains. If the `pvalue_len`
+ * is NULL, this consuming operation is done automatically.
+ *
+ * Returns 1 if *pvalue was successfully read.
+ * Returns 0 if there was an error. In this case, *pvalue contains an
+ * invalid value.
+ */
+
+static int read_var(struct archive_read* a, uint64_t* pvalue,
+ uint64_t* pvalue_len)
+{
+ uint64_t result = 0;
+ size_t shift, i;
+ const uint8_t* p;
+ uint8_t b;
+
+ /* We will read maximum of 8 bytes. We don't have to handle the
+ * situation to read the RAR5 variable-sized value stored at the end of
+ * the file, because such situation will never happen. */
+ if(!read_ahead(a, 8, &p))
+ return 0;
+
+ for(shift = 0, i = 0; i < 8; i++, shift += 7) {
+ b = p[i];
+
+ /* Strip the MSB from the input byte and add the resulting
+ * number to the `result`. */
+ result += (b & (uint64_t)0x7F) << shift;
+
+ /* MSB set to 1 means we need to continue decoding process.
+ * MSB set to 0 means we're done.
+ *
+ * This conditional checks for the second case. */
+ if((b & 0x80) == 0) {
+ if(pvalue) {
+ *pvalue = result;
+ }
+
+ /* If the caller has passed the `pvalue_len` pointer,
+ * store the number of consumed bytes in it and do NOT
+ * consume those bytes, since the caller has all the
+ * information it needs to perform */
+ if(pvalue_len) {
+ *pvalue_len = 1 + i;
+ } else {
+ /* If the caller did not provide the
+ * `pvalue_len` pointer, it will not have the
+ * possibility to advance the file pointer,
+ * because it will not know how many bytes it
+ * needs to consume. This is why we handle
+ * such situation here automatically. */
+ if(ARCHIVE_OK != consume(a, 1 + i)) {
+ return 0;
+ }
+ }
+
+ /* End of decoding process, return success. */
+ return 1;
+ }
+ }
+
+ /* The decoded value takes the maximum number of 8 bytes.
+ * It's a maximum number of bytes, so end decoding process here
+ * even if the first bit of last byte is 1. */
+ if(pvalue) {
+ *pvalue = result;
+ }
+
+ if(pvalue_len) {
+ *pvalue_len = 9;
+ } else {
+ if(ARCHIVE_OK != consume(a, 9)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int read_var_sized(struct archive_read* a, size_t* pvalue,
+ size_t* pvalue_len)
+{
+ uint64_t v;
+ uint64_t v_size = 0;
+
+ const int ret = pvalue_len ? read_var(a, &v, &v_size)
+ : read_var(a, &v, NULL);
+
+ if(ret == 1 && pvalue) {
+ *pvalue = (size_t) v;
+ }
+
+ if(pvalue_len) {
+ /* Possible data truncation should be safe. */
+ *pvalue_len = (size_t) v_size;
+ }
+
+ return ret;
+}
+
+static int read_bits_32(struct archive_read* a, struct rar5* rar,
+ const uint8_t* p, uint32_t* value)
+{
+ if(rar->bits.in_addr >= rar->cstate.cur_block_size) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Premature end of stream during extraction of data (#1)");
+ return ARCHIVE_FATAL;
+ }
+
+ uint32_t bits = ((uint32_t) p[rar->bits.in_addr]) << 24;
+ bits |= p[rar->bits.in_addr + 1] << 16;
+ bits |= p[rar->bits.in_addr + 2] << 8;
+ bits |= p[rar->bits.in_addr + 3];
+ bits <<= rar->bits.bit_addr;
+ bits |= p[rar->bits.in_addr + 4] >> (8 - rar->bits.bit_addr);
+ *value = bits;
+ return ARCHIVE_OK;
+}
+
+static int read_bits_16(struct archive_read* a, struct rar5* rar,
+ const uint8_t* p, uint16_t* value)
+{
+ if(rar->bits.in_addr >= rar->cstate.cur_block_size) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Premature end of stream during extraction of data (#2)");
+ return ARCHIVE_FATAL;
+ }
+
+ int bits = (int) ((uint32_t) p[rar->bits.in_addr]) << 16;
+ bits |= (int) p[rar->bits.in_addr + 1] << 8;
+ bits |= (int) p[rar->bits.in_addr + 2];
+ bits >>= (8 - rar->bits.bit_addr);
+ *value = bits & 0xffff;
+ return ARCHIVE_OK;
+}
+
+static void skip_bits(struct rar5* rar, int bits) {
+ const int new_bits = rar->bits.bit_addr + bits;
+ rar->bits.in_addr += new_bits >> 3;
+ rar->bits.bit_addr = new_bits & 7;
+}
+
+/* n = up to 16 */
+static int read_consume_bits(struct archive_read* a, struct rar5* rar,
+ const uint8_t* p, int n, int* value)
+{
+ uint16_t v;
+ int ret, num;
+
+ if(n == 0 || n > 16) {
+ /* This is a programmer error and should never happen
+ * in runtime. */
+ return ARCHIVE_FATAL;
+ }
+
+ ret = read_bits_16(a, rar, p, &v);
+ if(ret != ARCHIVE_OK)
+ return ret;
+
+ num = (int) v;
+ num >>= 16 - n;
+
+ skip_bits(rar, n);
+
+ if(value)
+ *value = num;
+
+ return ARCHIVE_OK;
+}
+
+static int read_u32(struct archive_read* a, uint32_t* pvalue) {
+ const uint8_t* p;
+ if(!read_ahead(a, 4, &p))
+ return 0;
+
+ *pvalue = archive_le32dec(p);
+ return ARCHIVE_OK == consume(a, 4) ? 1 : 0;
+}
+
+static int read_u64(struct archive_read* a, uint64_t* pvalue) {
+ const uint8_t* p;
+ if(!read_ahead(a, 8, &p))
+ return 0;
+
+ *pvalue = archive_le64dec(p);
+ return ARCHIVE_OK == consume(a, 8) ? 1 : 0;
+}
+
+static int bid_standard(struct archive_read* a) {
+ const uint8_t* p;
+ char signature[sizeof(rar5_signature_xor)];
+
+ rar5_signature(signature);
+
+ if(!read_ahead(a, sizeof(rar5_signature_xor), &p))
+ return -1;
+
+ if(!memcmp(signature, p, sizeof(rar5_signature_xor)))
+ return 30;
+
+ return -1;
+}
+
+static int bid_sfx(struct archive_read *a)
+{
+ const char *p;
+
+ if ((p = __archive_read_ahead(a, 7, NULL)) == NULL)
+ return -1;
+
+ if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
+ /* This is a PE file */
+ char signature[sizeof(rar5_signature_xor)];
+ ssize_t offset = 0x10000;
+ ssize_t window = 4096;
+ ssize_t bytes_avail;
+
+ rar5_signature(signature);
+
+ while (offset + window <= (1024 * 512)) {
+ const char *buff = __archive_read_ahead(a, offset + window, &bytes_avail);
+ if (buff == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < 0x40)
+ return 0;
+ continue;
+ }
+ p = buff + offset;
+ while (p + 8 < buff + bytes_avail) {
+ if (memcmp(p, signature, sizeof(signature)) == 0)
+ return 30;
+ p += 0x10;
+ }
+ offset = p - buff;
+ }
+ }
+
+ return 0;
+}
+
+static int rar5_bid(struct archive_read* a, int best_bid) {
+ int my_bid;
+
+ if(best_bid > 30)
+ return -1;
+
+ my_bid = bid_standard(a);
+ if(my_bid > -1) {
+ return my_bid;
+ }
+ my_bid = bid_sfx(a);
+ if (my_bid > -1) {
+ return my_bid;
+ }
+
+ return -1;
+}
+
+static int rar5_options(struct archive_read *a, const char *key,
+ const char *val) {
+ (void) a;
+ (void) key;
+ (void) val;
+
+ /* No options supported in this version. Return the ARCHIVE_WARN code
+ * to signal the options supervisor that the unpacker didn't handle
+ * setting this option. */
+
+ return ARCHIVE_WARN;
+}
+
+static void init_header(struct archive_read* a) {
+ a->archive.archive_format = ARCHIVE_FORMAT_RAR_V5;
+ a->archive.archive_format_name = "RAR5";
+}
+
+static void init_window_mask(struct rar5* rar) {
+ if (rar->cstate.window_size)
+ rar->cstate.window_mask = rar->cstate.window_size - 1;
+ else
+ rar->cstate.window_mask = 0;
+}
+
+enum HEADER_FLAGS {
+ HFL_EXTRA_DATA = 0x0001,
+ HFL_DATA = 0x0002,
+ HFL_SKIP_IF_UNKNOWN = 0x0004,
+ HFL_SPLIT_BEFORE = 0x0008,
+ HFL_SPLIT_AFTER = 0x0010,
+ HFL_CHILD = 0x0020,
+ HFL_INHERITED = 0x0040
+};
+
+static int process_main_locator_extra_block(struct archive_read* a,
+ struct rar5* rar)
+{
+ uint64_t locator_flags;
+
+ enum LOCATOR_FLAGS {
+ QLIST = 0x01, RECOVERY = 0x02,
+ };
+
+ if(!read_var(a, &locator_flags, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ if(locator_flags & QLIST) {
+ if(!read_var(a, &rar->qlist_offset, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ /* qlist is not used */
+ }
+
+ if(locator_flags & RECOVERY) {
+ if(!read_var(a, &rar->rr_offset, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ /* rr is not used */
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar,
+ ssize_t* extra_data_size)
+{
+ size_t hash_type = 0;
+ size_t value_len;
+
+ enum HASH_TYPE {
+ BLAKE2sp = 0x00
+ };
+
+ if(!read_var_sized(a, &hash_type, &value_len))
+ return ARCHIVE_EOF;
+
+ *extra_data_size -= value_len;
+ if(ARCHIVE_OK != consume(a, value_len)) {
+ return ARCHIVE_EOF;
+ }
+
+ /* The file uses BLAKE2sp checksum algorithm instead of plain old
+ * CRC32. */
+ if(hash_type == BLAKE2sp) {
+ const uint8_t* p;
+ const int hash_size = sizeof(rar->file.blake2sp);
+
+ if(!read_ahead(a, hash_size, &p))
+ return ARCHIVE_EOF;
+
+ rar->file.has_blake2 = 1;
+ memcpy(&rar->file.blake2sp, p, hash_size);
+
+ if(ARCHIVE_OK != consume(a, hash_size)) {
+ return ARCHIVE_EOF;
+ }
+
+ *extra_data_size -= hash_size;
+ } else {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported hash type (0x%x)", (int) hash_type);
+ return ARCHIVE_FATAL;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static uint64_t time_win_to_unix(uint64_t win_time) {
+ const size_t ns_in_sec = 10000000;
+ const uint64_t sec_to_unix = 11644473600LL;
+ return win_time / ns_in_sec - sec_to_unix;
+}
+
+static int parse_htime_item(struct archive_read* a, char unix_time,
+ uint64_t* where, ssize_t* extra_data_size)
+{
+ if(unix_time) {
+ uint32_t time_val;
+ if(!read_u32(a, &time_val))
+ return ARCHIVE_EOF;
+
+ *extra_data_size -= 4;
+ *where = (uint64_t) time_val;
+ } else {
+ uint64_t windows_time;
+ if(!read_u64(a, &windows_time))
+ return ARCHIVE_EOF;
+
+ *where = time_win_to_unix(windows_time);
+ *extra_data_size -= 8;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int parse_file_extra_version(struct archive_read* a,
+ struct archive_entry* e, ssize_t* extra_data_size)
+{
+ size_t flags = 0;
+ size_t version = 0;
+ size_t value_len = 0;
+ struct archive_string version_string;
+ struct archive_string name_utf8_string;
+ const char* cur_filename;
+
+ /* Flags are ignored. */
+ if(!read_var_sized(a, &flags, &value_len))
+ return ARCHIVE_EOF;
+
+ *extra_data_size -= value_len;
+ if(ARCHIVE_OK != consume(a, value_len))
+ return ARCHIVE_EOF;
+
+ if(!read_var_sized(a, &version, &value_len))
+ return ARCHIVE_EOF;
+
+ *extra_data_size -= value_len;
+ if(ARCHIVE_OK != consume(a, value_len))
+ return ARCHIVE_EOF;
+
+ /* extra_data_size should be zero here. */
+
+ cur_filename = archive_entry_pathname_utf8(e);
+ if(cur_filename == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Version entry without file name");
+ return ARCHIVE_FATAL;
+ }
+
+ archive_string_init(&version_string);
+ archive_string_init(&name_utf8_string);
+
+ /* Prepare a ;123 suffix for the filename, where '123' is the version
+ * value of this file. */
+ archive_string_sprintf(&version_string, ";%zu", version);
+
+ /* Build the new filename. */
+ archive_strcat(&name_utf8_string, cur_filename);
+ archive_strcat(&name_utf8_string, version_string.s);
+
+ /* Apply the new filename into this file's context. */
+ archive_entry_update_pathname_utf8(e, name_utf8_string.s);
+
+ /* Free buffers. */
+ archive_string_free(&version_string);
+ archive_string_free(&name_utf8_string);
+ return ARCHIVE_OK;
+}
+
+static int parse_file_extra_htime(struct archive_read* a,
+ struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size)
+{
+ char unix_time = 0;
+ size_t flags = 0;
+ size_t value_len;
+
+ enum HTIME_FLAGS {
+ IS_UNIX = 0x01,
+ HAS_MTIME = 0x02,
+ HAS_CTIME = 0x04,
+ HAS_ATIME = 0x08,
+ HAS_UNIX_NS = 0x10,
+ };
+
+ if(!read_var_sized(a, &flags, &value_len))
+ return ARCHIVE_EOF;
+
+ *extra_data_size -= value_len;
+ if(ARCHIVE_OK != consume(a, value_len)) {
+ return ARCHIVE_EOF;
+ }
+
+ unix_time = flags & IS_UNIX;
+
+ if(flags & HAS_MTIME) {
+ parse_htime_item(a, unix_time, &rar->file.e_mtime,
+ extra_data_size);
+ archive_entry_set_mtime(e, rar->file.e_mtime, 0);
+ }
+
+ if(flags & HAS_CTIME) {
+ parse_htime_item(a, unix_time, &rar->file.e_ctime,
+ extra_data_size);
+ archive_entry_set_ctime(e, rar->file.e_ctime, 0);
+ }
+
+ if(flags & HAS_ATIME) {
+ parse_htime_item(a, unix_time, &rar->file.e_atime,
+ extra_data_size);
+ archive_entry_set_atime(e, rar->file.e_atime, 0);
+ }
+
+ if(flags & HAS_UNIX_NS) {
+ if(!read_u32(a, &rar->file.e_unix_ns))
+ return ARCHIVE_EOF;
+
+ *extra_data_size -= 4;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int parse_file_extra_redir(struct archive_read* a,
+ struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size)
+{
+ uint64_t value_size = 0;
+ size_t target_size = 0;
+ char target_utf8_buf[MAX_NAME_IN_BYTES];
+ const uint8_t* p;
+
+ if(!read_var(a, &rar->file.redir_type, &value_size))
+ return ARCHIVE_EOF;
+ if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+ return ARCHIVE_EOF;
+ *extra_data_size -= value_size;
+
+ if(!read_var(a, &rar->file.redir_flags, &value_size))
+ return ARCHIVE_EOF;
+ if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+ return ARCHIVE_EOF;
+ *extra_data_size -= value_size;
+
+ if(!read_var_sized(a, &target_size, NULL))
+ return ARCHIVE_EOF;
+ *extra_data_size -= target_size + 1;
+
+ if(!read_ahead(a, target_size, &p))
+ return ARCHIVE_EOF;
+
+ if(target_size > (MAX_NAME_IN_CHARS - 1)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Link target is too long");
+ return ARCHIVE_FATAL;
+ }
+
+ if(target_size == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "No link target specified");
+ return ARCHIVE_FATAL;
+ }
+
+ memcpy(target_utf8_buf, p, target_size);
+ target_utf8_buf[target_size] = 0;
+
+ if(ARCHIVE_OK != consume(a, (int64_t)target_size))
+ return ARCHIVE_EOF;
+
+ switch(rar->file.redir_type) {
+ case REDIR_TYPE_UNIXSYMLINK:
+ case REDIR_TYPE_WINSYMLINK:
+ archive_entry_set_filetype(e, AE_IFLNK);
+ archive_entry_update_symlink_utf8(e, target_utf8_buf);
+ if (rar->file.redir_flags & REDIR_SYMLINK_IS_DIR) {
+ archive_entry_set_symlink_type(e,
+ AE_SYMLINK_TYPE_DIRECTORY);
+ } else {
+ archive_entry_set_symlink_type(e,
+ AE_SYMLINK_TYPE_FILE);
+ }
+ break;
+
+ case REDIR_TYPE_HARDLINK:
+ archive_entry_set_filetype(e, AE_IFREG);
+ archive_entry_update_hardlink_utf8(e, target_utf8_buf);
+ break;
+
+ default:
+ /* Unknown redir type, skip it. */
+ break;
+ }
+ return ARCHIVE_OK;
+}
+
+static int parse_file_extra_owner(struct archive_read* a,
+ struct archive_entry* e, ssize_t* extra_data_size)
+{
+ uint64_t flags = 0;
+ uint64_t value_size = 0;
+ uint64_t id = 0;
+ size_t name_len = 0;
+ size_t name_size = 0;
+ char namebuf[OWNER_MAXNAMELEN];
+ const uint8_t* p;
+
+ if(!read_var(a, &flags, &value_size))
+ return ARCHIVE_EOF;
+ if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+ return ARCHIVE_EOF;
+ *extra_data_size -= value_size;
+
+ if ((flags & OWNER_USER_NAME) != 0) {
+ if(!read_var_sized(a, &name_size, NULL))
+ return ARCHIVE_EOF;
+ *extra_data_size -= name_size + 1;
+
+ if(!read_ahead(a, name_size, &p))
+ return ARCHIVE_EOF;
+
+ if (name_size >= OWNER_MAXNAMELEN) {
+ name_len = OWNER_MAXNAMELEN - 1;
+ } else {
+ name_len = name_size;
+ }
+
+ memcpy(namebuf, p, name_len);
+ namebuf[name_len] = 0;
+ if(ARCHIVE_OK != consume(a, (int64_t)name_size))
+ return ARCHIVE_EOF;
+
+ archive_entry_set_uname(e, namebuf);
+ }
+ if ((flags & OWNER_GROUP_NAME) != 0) {
+ if(!read_var_sized(a, &name_size, NULL))
+ return ARCHIVE_EOF;
+ *extra_data_size -= name_size + 1;
+
+ if(!read_ahead(a, name_size, &p))
+ return ARCHIVE_EOF;
+
+ if (name_size >= OWNER_MAXNAMELEN) {
+ name_len = OWNER_MAXNAMELEN - 1;
+ } else {
+ name_len = name_size;
+ }
+
+ memcpy(namebuf, p, name_len);
+ namebuf[name_len] = 0;
+ if(ARCHIVE_OK != consume(a, (int64_t)name_size))
+ return ARCHIVE_EOF;
+
+ archive_entry_set_gname(e, namebuf);
+ }
+ if ((flags & OWNER_USER_UID) != 0) {
+ if(!read_var(a, &id, &value_size))
+ return ARCHIVE_EOF;
+ if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+ return ARCHIVE_EOF;
+ *extra_data_size -= value_size;
+
+ archive_entry_set_uid(e, (la_int64_t)id);
+ }
+ if ((flags & OWNER_GROUP_GID) != 0) {
+ if(!read_var(a, &id, &value_size))
+ return ARCHIVE_EOF;
+ if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+ return ARCHIVE_EOF;
+ *extra_data_size -= value_size;
+
+ archive_entry_set_gid(e, (la_int64_t)id);
+ }
+ return ARCHIVE_OK;
+}
+
+static int process_head_file_extra(struct archive_read* a,
+ struct archive_entry* e, struct rar5* rar, ssize_t extra_data_size)
+{
+ size_t extra_field_size;
+ size_t extra_field_id = 0;
+ int ret = ARCHIVE_FATAL;
+ size_t var_size;
+
+ while(extra_data_size > 0) {
+ if(!read_var_sized(a, &extra_field_size, &var_size))
+ return ARCHIVE_EOF;
+
+ extra_data_size -= var_size;
+ if(ARCHIVE_OK != consume(a, var_size)) {
+ return ARCHIVE_EOF;
+ }
+
+ if(!read_var_sized(a, &extra_field_id, &var_size))
+ return ARCHIVE_EOF;
+
+ extra_data_size -= var_size;
+ if(ARCHIVE_OK != consume(a, var_size)) {
+ return ARCHIVE_EOF;
+ }
+
+ switch(extra_field_id) {
+ case EX_HASH:
+ ret = parse_file_extra_hash(a, rar,
+ &extra_data_size);
+ break;
+ case EX_HTIME:
+ ret = parse_file_extra_htime(a, e, rar,
+ &extra_data_size);
+ break;
+ case EX_REDIR:
+ ret = parse_file_extra_redir(a, e, rar,
+ &extra_data_size);
+ break;
+ case EX_UOWNER:
+ ret = parse_file_extra_owner(a, e,
+ &extra_data_size);
+ break;
+ case EX_VERSION:
+ ret = parse_file_extra_version(a, e,
+ &extra_data_size);
+ break;
+ case EX_CRYPT:
+ /* fallthrough */
+ case EX_SUBDATA:
+ /* fallthrough */
+ default:
+ /* Skip unsupported entry. */
+ return consume(a, extra_data_size);
+ }
+ }
+
+ if(ret != ARCHIVE_OK) {
+ /* Attribute not implemented. */
+ return ret;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int process_head_file(struct archive_read* a, struct rar5* rar,
+ struct archive_entry* entry, size_t block_flags)
+{
+ ssize_t extra_data_size = 0;
+ size_t data_size = 0;
+ size_t file_flags = 0;
+ size_t file_attr = 0;
+ size_t compression_info = 0;
+ size_t host_os = 0;
+ size_t name_size = 0;
+ uint64_t unpacked_size, window_size;
+ uint32_t mtime = 0, crc = 0;
+ int c_method = 0, c_version = 0;
+ char name_utf8_buf[MAX_NAME_IN_BYTES];
+ const uint8_t* p;
+
+ enum FILE_FLAGS {
+ DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004,
+ UNKNOWN_UNPACKED_SIZE = 0x0008,
+ };
+
+ enum FILE_ATTRS {
+ ATTR_READONLY = 0x1, ATTR_HIDDEN = 0x2, ATTR_SYSTEM = 0x4,
+ ATTR_DIRECTORY = 0x10,
+ };
+
+ enum COMP_INFO_FLAGS {
+ SOLID = 0x0040,
+ };
+
+ enum HOST_OS {
+ HOST_WINDOWS = 0,
+ HOST_UNIX = 1,
+ };
+
+ archive_entry_clear(entry);
+
+ /* Do not reset file context if we're switching archives. */
+ if(!rar->cstate.switch_multivolume) {
+ reset_file_context(rar);
+ }
+
+ if(block_flags & HFL_EXTRA_DATA) {
+ size_t edata_size = 0;
+ if(!read_var_sized(a, &edata_size, NULL))
+ return ARCHIVE_EOF;
+
+ /* Intentional type cast from unsigned to signed. */
+ extra_data_size = (ssize_t) edata_size;
+ }
+
+ if(block_flags & HFL_DATA) {
+ if(!read_var_sized(a, &data_size, NULL))
+ return ARCHIVE_EOF;
+
+ rar->file.bytes_remaining = data_size;
+ } else {
+ rar->file.bytes_remaining = 0;
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "no data found in file/service block");
+ return ARCHIVE_FATAL;
+ }
+
+ if(!read_var_sized(a, &file_flags, NULL))
+ return ARCHIVE_EOF;
+
+ if(!read_var(a, &unpacked_size, NULL))
+ return ARCHIVE_EOF;
+
+ if(file_flags & UNKNOWN_UNPACKED_SIZE) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Files with unknown unpacked size are not supported");
+ return ARCHIVE_FATAL;
+ }
+
+ rar->file.dir = (uint8_t) ((file_flags & DIRECTORY) > 0);
+
+ if(!read_var_sized(a, &file_attr, NULL))
+ return ARCHIVE_EOF;
+
+ if(file_flags & UTIME) {
+ if(!read_u32(a, &mtime))
+ return ARCHIVE_EOF;
+ }
+
+ if(file_flags & CRC32) {
+ if(!read_u32(a, &crc))
+ return ARCHIVE_EOF;
+ }
+
+ if(!read_var_sized(a, &compression_info, NULL))
+ return ARCHIVE_EOF;
+
+ c_method = (int) (compression_info >> 7) & 0x7;
+ c_version = (int) (compression_info & 0x3f);
+
+ /* RAR5 seems to limit the dictionary size to 64MB. */
+ window_size = (rar->file.dir > 0) ?
+ 0 :
+ g_unpack_window_size << ((compression_info >> 10) & 15);
+ rar->cstate.method = c_method;
+ rar->cstate.version = c_version + 50;
+ rar->file.solid = (compression_info & SOLID) > 0;
+
+ /* Archives which declare solid files without initializing the window
+ * buffer first are invalid. */
+
+ if(rar->file.solid > 0 && rar->cstate.window_buf == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Declared solid file, but no window buffer "
+ "initialized yet.");
+ return ARCHIVE_FATAL;
+ }
+
+ /* Check if window_size is a sane value. Also, if the file is not
+ * declared as a directory, disallow window_size == 0. */
+ if(window_size > (64 * 1024 * 1024) ||
+ (rar->file.dir == 0 && window_size == 0))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Declared dictionary size is not supported.");
+ return ARCHIVE_FATAL;
+ }
+
+ if(rar->file.solid > 0) {
+ /* Re-check if current window size is the same as previous
+ * window size (for solid files only). */
+ if(rar->file.solid_window_size > 0 &&
+ rar->file.solid_window_size != (ssize_t) window_size)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Window size for this solid file doesn't match "
+ "the window size used in previous solid file. ");
+ return ARCHIVE_FATAL;
+ }
+ }
+
+ if(rar->cstate.window_size < (ssize_t) window_size &&
+ rar->cstate.window_buf)
+ {
+ /* If window_buf has been allocated before, reallocate it, so
+ * that its size will match new window_size. */
+
+ uint8_t* new_window_buf =
+ realloc(rar->cstate.window_buf, window_size);
+
+ if(!new_window_buf) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Not enough memory when trying to realloc the window "
+ "buffer.");
+ return ARCHIVE_FATAL;
+ }
+
+ rar->cstate.window_buf = new_window_buf;
+ }
+
+ /* Values up to 64M should fit into ssize_t on every
+ * architecture. */
+ rar->cstate.window_size = (ssize_t) window_size;
+
+ if(rar->file.solid > 0 && rar->file.solid_window_size == 0) {
+ /* Solid files have to have the same window_size across
+ whole archive. Remember the window_size parameter
+ for first solid file found. */
+ rar->file.solid_window_size = rar->cstate.window_size;
+ }
+
+ init_window_mask(rar);
+
+ rar->file.service = 0;
+
+ if(!read_var_sized(a, &host_os, NULL))
+ return ARCHIVE_EOF;
+
+ if(host_os == HOST_WINDOWS) {
+ /* Host OS is Windows */
+
+ __LA_MODE_T mode;
+
+ if(file_attr & ATTR_DIRECTORY) {
+ if (file_attr & ATTR_READONLY) {
+ mode = 0555 | AE_IFDIR;
+ } else {
+ mode = 0755 | AE_IFDIR;
+ }
+ } else {
+ if (file_attr & ATTR_READONLY) {
+ mode = 0444 | AE_IFREG;
+ } else {
+ mode = 0644 | AE_IFREG;
+ }
+ }
+
+ archive_entry_set_mode(entry, mode);
+
+ if (file_attr & (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM)) {
+ char *fflags_text, *ptr;
+ /* allocate for "rdonly,hidden,system," */
+ fflags_text = malloc(22 * sizeof(char));
+ if (fflags_text != NULL) {
+ ptr = fflags_text;
+ if (file_attr & ATTR_READONLY) {
+ strcpy(ptr, "rdonly,");
+ ptr = ptr + 7;
+ }
+ if (file_attr & ATTR_HIDDEN) {
+ strcpy(ptr, "hidden,");
+ ptr = ptr + 7;
+ }
+ if (file_attr & ATTR_SYSTEM) {
+ strcpy(ptr, "system,");
+ ptr = ptr + 7;
+ }
+ if (ptr > fflags_text) {
+ /* Delete trailing comma */
+ *(ptr - 1) = '\0';
+ archive_entry_copy_fflags_text(entry,
+ fflags_text);
+ }
+ free(fflags_text);
+ }
+ }
+ } else if(host_os == HOST_UNIX) {
+ /* Host OS is Unix */
+ archive_entry_set_mode(entry, (__LA_MODE_T) file_attr);
+ } else {
+ /* Unknown host OS */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported Host OS: 0x%x", (int) host_os);
+
+ return ARCHIVE_FATAL;
+ }
+
+ if(!read_var_sized(a, &name_size, NULL))
+ return ARCHIVE_EOF;
+
+ if(!read_ahead(a, name_size, &p))
+ return ARCHIVE_EOF;
+
+ if(name_size > (MAX_NAME_IN_CHARS - 1)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Filename is too long");
+
+ return ARCHIVE_FATAL;
+ }
+
+ if(name_size == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "No filename specified");
+
+ return ARCHIVE_FATAL;
+ }
+
+ memcpy(name_utf8_buf, p, name_size);
+ name_utf8_buf[name_size] = 0;
+ if(ARCHIVE_OK != consume(a, name_size)) {
+ return ARCHIVE_EOF;
+ }
+
+ archive_entry_update_pathname_utf8(entry, name_utf8_buf);
+
+ if(extra_data_size > 0) {
+ int ret = process_head_file_extra(a, entry, rar,
+ extra_data_size);
+
+ /*
+ * TODO: rewrite or remove useless sanity check
+ * as extra_data_size is not passed as a pointer
+ *
+ if(extra_data_size < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "File extra data size is not zero");
+ return ARCHIVE_FATAL;
+ }
+ */
+
+ if(ret != ARCHIVE_OK)
+ return ret;
+ }
+
+ if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) {
+ rar->file.unpacked_size = (ssize_t) unpacked_size;
+ if(rar->file.redir_type == REDIR_TYPE_NONE)
+ archive_entry_set_size(entry, unpacked_size);
+ }
+
+ if(file_flags & UTIME) {
+ archive_entry_set_mtime(entry, (time_t) mtime, 0);
+ }
+
+ if(file_flags & CRC32) {
+ rar->file.stored_crc32 = crc;
+ }
+
+ if(!rar->cstate.switch_multivolume) {
+ /* Do not reinitialize unpacking state if we're switching
+ * archives. */
+ rar->cstate.block_parsing_finished = 1;
+ rar->cstate.all_filters_applied = 1;
+ rar->cstate.initialized = 0;
+ }
+
+ if(rar->generic.split_before > 0) {
+ /* If now we're standing on a header that has a 'split before'
+ * mark, it means we're standing on a 'continuation' file
+ * header. Signal the caller that if it wants to move to
+ * another file, it must call rar5_read_header() function
+ * again. */
+
+ return ARCHIVE_RETRY;
+ } else {
+ return ARCHIVE_OK;
+ }
+}
+
+static int process_head_service(struct archive_read* a, struct rar5* rar,
+ struct archive_entry* entry, size_t block_flags)
+{
+ /* Process this SERVICE block the same way as FILE blocks. */
+ int ret = process_head_file(a, rar, entry, block_flags);
+ if(ret != ARCHIVE_OK)
+ return ret;
+
+ rar->file.service = 1;
+
+ /* But skip the data part automatically. It's no use for the user
+ * anyway. It contains only service data, not even needed to
+ * properly unpack the file. */
+ ret = rar5_read_data_skip(a);
+ if(ret != ARCHIVE_OK)
+ return ret;
+
+ /* After skipping, try parsing another block automatically. */
+ return ARCHIVE_RETRY;
+}
+
+static int process_head_main(struct archive_read* a, struct rar5* rar,
+ struct archive_entry* entry, size_t block_flags)
+{
+ int ret;
+ size_t extra_data_size = 0;
+ size_t extra_field_size = 0;
+ size_t extra_field_id = 0;
+ size_t archive_flags = 0;
+
+ enum MAIN_FLAGS {
+ VOLUME = 0x0001, /* multi-volume archive */
+ VOLUME_NUMBER = 0x0002, /* volume number, first vol doesn't
+ * have it */
+ SOLID = 0x0004, /* solid archive */
+ PROTECT = 0x0008, /* contains Recovery info */
+ LOCK = 0x0010, /* readonly flag, not used */
+ };
+
+ enum MAIN_EXTRA {
+ // Just one attribute here.
+ LOCATOR = 0x01,
+ };
+
+ (void) entry;
+
+ if(block_flags & HFL_EXTRA_DATA) {
+ if(!read_var_sized(a, &extra_data_size, NULL))
+ return ARCHIVE_EOF;
+ } else {
+ extra_data_size = 0;
+ }
+
+ if(!read_var_sized(a, &archive_flags, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ rar->main.volume = (archive_flags & VOLUME) > 0;
+ rar->main.solid = (archive_flags & SOLID) > 0;
+
+ if(archive_flags & VOLUME_NUMBER) {
+ size_t v = 0;
+ if(!read_var_sized(a, &v, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ if (v > UINT_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid volume number");
+ return ARCHIVE_FATAL;
+ }
+
+ rar->main.vol_no = (unsigned int) v;
+ } else {
+ rar->main.vol_no = 0;
+ }
+
+ if(rar->vol.expected_vol_no > 0 &&
+ rar->main.vol_no != rar->vol.expected_vol_no)
+ {
+ /* Returning EOF instead of FATAL because of strange
+ * libarchive behavior. When opening multiple files via
+ * archive_read_open_filenames(), after reading up the whole
+ * last file, the __archive_read_ahead function wraps up to
+ * the first archive instead of returning EOF. */
+ return ARCHIVE_EOF;
+ }
+
+ if(extra_data_size == 0) {
+ /* Early return. */
+ return ARCHIVE_OK;
+ }
+
+ if(!read_var_sized(a, &extra_field_size, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ if(!read_var_sized(a, &extra_field_id, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ if(extra_field_size == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid extra field size");
+ return ARCHIVE_FATAL;
+ }
+
+ switch(extra_field_id) {
+ case LOCATOR:
+ ret = process_main_locator_extra_block(a, rar);
+ if(ret != ARCHIVE_OK) {
+ /* Error while parsing main locator extra
+ * block. */
+ return ret;
+ }
+
+ break;
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported extra type (0x%x)",
+ (int) extra_field_id);
+ return ARCHIVE_FATAL;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int skip_unprocessed_bytes(struct archive_read* a) {
+ struct rar5* rar = get_context(a);
+ int ret;
+
+ if(rar->file.bytes_remaining) {
+ /* Use different skipping method in block merging mode than in
+ * normal mode. If merge mode is active, rar5_read_data_skip
+ * can't be used, because it could allow recursive use of
+ * merge_block() * function, and this function doesn't support
+ * recursive use. */
+ if(rar->merge_mode) {
+ /* Discard whole merged block. This is valid in solid
+ * mode as well, because the code will discard blocks
+ * only if those blocks are safe to discard (i.e.
+ * they're not FILE blocks). */
+ ret = consume(a, rar->file.bytes_remaining);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+ rar->file.bytes_remaining = 0;
+ } else {
+ /* If we're not in merge mode, use safe skipping code.
+ * This will ensure we'll handle solid archives
+ * properly. */
+ ret = rar5_read_data_skip(a);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+ }
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int scan_for_signature(struct archive_read* a);
+
+/* Base block processing function. A 'base block' is a RARv5 header block
+ * that tells the reader what kind of data is stored inside the block.
+ *
+ * From the birds-eye view a RAR file looks file this:
+ *
+ * <magic><base_block_1><base_block_2>...<base_block_n>
+ *
+ * There are a few types of base blocks. Those types are specified inside
+ * the 'switch' statement in this function. For example purposes, I'll write
+ * how a standard RARv5 file could look like here:
+ *
+ * <magic><MAIN><FILE><FILE><FILE><SERVICE><ENDARC>
+ *
+ * The structure above could describe an archive file with 3 files in it,
+ * one service "QuickOpen" block (that is ignored by this parser), and an
+ * end of file base block marker.
+ *
+ * If the file is stored in multiple archive files ("multiarchive"), it might
+ * look like this:
+ *
+ * .part01.rar: <magic><MAIN><FILE><ENDARC>
+ * .part02.rar: <magic><MAIN><FILE><ENDARC>
+ * .part03.rar: <magic><MAIN><FILE><ENDARC>
+ *
+ * This example could describe 3 RAR files that contain ONE archived file.
+ * Or it could describe 3 RAR files that contain 3 different files. Or 3
+ * RAR files than contain 2 files. It all depends what metadata is stored in
+ * the headers of <FILE> blocks.
+ *
+ * Each <FILE> block contains info about its size, the name of the file it's
+ * storing inside, and whether this FILE block is a continuation block of
+ * previous archive ('split before'), and is this FILE block should be
+ * continued in another archive ('split after'). By parsing the 'split before'
+ * and 'split after' flags, we're able to tell if multiple <FILE> base blocks
+ * are describing one file, or multiple files (with the same filename, for
+ * example).
+ *
+ * One thing to note is that if we're parsing the first <FILE> block, and
+ * we see 'split after' flag, then we need to jump over to another <FILE>
+ * block to be able to decompress rest of the data. To do this, we need
+ * to skip the <ENDARC> block, then switch to another file, then skip the
+ * <magic> block, <MAIN> block, and then we're standing on the proper
+ * <FILE> block.
+ */
+
+static int process_base_block(struct archive_read* a,
+ struct archive_entry* entry)
+{
+ const size_t SMALLEST_RAR5_BLOCK_SIZE = 3;
+
+ struct rar5* rar = get_context(a);
+ uint32_t hdr_crc, computed_crc;
+ size_t raw_hdr_size = 0, hdr_size_len, hdr_size;
+ size_t header_id = 0;
+ size_t header_flags = 0;
+ const uint8_t* p;
+ int ret;
+
+ enum HEADER_TYPE {
+ HEAD_MARK = 0x00, HEAD_MAIN = 0x01, HEAD_FILE = 0x02,
+ HEAD_SERVICE = 0x03, HEAD_CRYPT = 0x04, HEAD_ENDARC = 0x05,
+ HEAD_UNKNOWN = 0xff,
+ };
+
+ /* Skip any unprocessed data for this file. */
+ ret = skip_unprocessed_bytes(a);
+ if(ret != ARCHIVE_OK)
+ return ret;
+
+ /* Read the expected CRC32 checksum. */
+ if(!read_u32(a, &hdr_crc)) {
+ return ARCHIVE_EOF;
+ }
+
+ /* Read header size. */
+ if(!read_var_sized(a, &raw_hdr_size, &hdr_size_len)) {
+ return ARCHIVE_EOF;
+ }
+
+ hdr_size = raw_hdr_size + hdr_size_len;
+
+ /* Sanity check, maximum header size for RAR5 is 2MB. */
+ if(hdr_size > (2 * 1024 * 1024)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Base block header is too large");
+
+ return ARCHIVE_FATAL;
+ }
+
+ /* Additional sanity checks to weed out invalid files. */
+ if(raw_hdr_size == 0 || hdr_size_len == 0 ||
+ hdr_size < SMALLEST_RAR5_BLOCK_SIZE)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Too small block encountered (%zu bytes)",
+ raw_hdr_size);
+
+ return ARCHIVE_FATAL;
+ }
+
+ /* Read the whole header data into memory, maximum memory use here is
+ * 2MB. */
+ if(!read_ahead(a, hdr_size, &p)) {
+ return ARCHIVE_EOF;
+ }
+
+ /* Verify the CRC32 of the header data. */
+ computed_crc = (uint32_t) crc32(0, p, (int) hdr_size);
+ if(computed_crc != hdr_crc) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Header CRC error");
+
+ return ARCHIVE_FATAL;
+ }
+
+ /* If the checksum is OK, we proceed with parsing. */
+ if(ARCHIVE_OK != consume(a, hdr_size_len)) {
+ return ARCHIVE_EOF;
+ }
+
+ if(!read_var_sized(a, &header_id, NULL))
+ return ARCHIVE_EOF;
+
+ if(!read_var_sized(a, &header_flags, NULL))
+ return ARCHIVE_EOF;
+
+ rar->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0;
+ rar->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0;
+ rar->generic.size = (int)hdr_size;
+ rar->generic.last_header_id = (int)header_id;
+ rar->main.endarc = 0;
+
+ /* Those are possible header ids in RARv5. */
+ switch(header_id) {
+ case HEAD_MAIN:
+ ret = process_head_main(a, rar, entry, header_flags);
+
+ /* Main header doesn't have any files in it, so it's
+ * pointless to return to the caller. Retry to next
+ * header, which should be HEAD_FILE/HEAD_SERVICE. */
+ if(ret == ARCHIVE_OK)
+ return ARCHIVE_RETRY;
+
+ return ret;
+ case HEAD_SERVICE:
+ ret = process_head_service(a, rar, entry, header_flags);
+ return ret;
+ case HEAD_FILE:
+ ret = process_head_file(a, rar, entry, header_flags);
+ return ret;
+ case HEAD_CRYPT:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Encryption is not supported");
+ return ARCHIVE_FATAL;
+ case HEAD_ENDARC:
+ rar->main.endarc = 1;
+
+ /* After encountering an end of file marker, we need
+ * to take into consideration if this archive is
+ * continued in another file (i.e. is it part01.rar:
+ * is there a part02.rar?) */
+ if(rar->main.volume) {
+ /* In case there is part02.rar, position the
+ * read pointer in a proper place, so we can
+ * resume parsing. */
+ ret = scan_for_signature(a);
+ if(ret == ARCHIVE_FATAL) {
+ return ARCHIVE_EOF;
+ } else {
+ if(rar->vol.expected_vol_no ==
+ UINT_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Header error");
+ return ARCHIVE_FATAL;
+ }
+
+ rar->vol.expected_vol_no =
+ rar->main.vol_no + 1;
+ return ARCHIVE_OK;
+ }
+ } else {
+ return ARCHIVE_EOF;
+ }
+ case HEAD_MARK:
+ return ARCHIVE_EOF;
+ default:
+ if((header_flags & HFL_SKIP_IF_UNKNOWN) == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Header type error");
+ return ARCHIVE_FATAL;
+ } else {
+ /* If the block is marked as 'skip if unknown',
+ * do as the flag says: skip the block
+ * instead on failing on it. */
+ return ARCHIVE_RETRY;
+ }
+ }
+
+#if !defined WIN32
+ // Not reached.
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal unpacker error");
+ return ARCHIVE_FATAL;
+#endif
+}
+
+static int skip_base_block(struct archive_read* a) {
+ int ret;
+ struct rar5* rar = get_context(a);
+
+ /* Create a new local archive_entry structure that will be operated on
+ * by header reader; operations on this archive_entry will be discarded.
+ */
+ struct archive_entry* entry = archive_entry_new();
+ ret = process_base_block(a, entry);
+
+ /* Discard operations on this archive_entry structure. */
+ archive_entry_free(entry);
+ if(ret == ARCHIVE_FATAL)
+ return ret;
+
+ if(rar->generic.last_header_id == 2 && rar->generic.split_before > 0)
+ return ARCHIVE_OK;
+
+ if(ret == ARCHIVE_OK)
+ return ARCHIVE_RETRY;
+ else
+ return ret;
+}
+
+static int try_skip_sfx(struct archive_read *a)
+{
+ const char *p;
+
+ if ((p = __archive_read_ahead(a, 7, NULL)) == NULL)
+ return ARCHIVE_EOF;
+
+ if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0)
+ {
+ char signature[sizeof(rar5_signature_xor)];
+ const void *h;
+ const char *q;
+ size_t skip, total = 0;
+ ssize_t bytes, window = 4096;
+
+ rar5_signature(signature);
+
+ while (total + window <= (1024 * 512)) {
+ h = __archive_read_ahead(a, window, &bytes);
+ if (h == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < 0x40)
+ goto fatal;
+ continue;
+ }
+ if (bytes < 0x40)
+ goto fatal;
+ p = h;
+ q = p + bytes;
+
+ /*
+ * Scan ahead until we find something that looks
+ * like the RAR header.
+ */
+ while (p + 8 < q) {
+ if (memcmp(p, signature, sizeof(signature)) == 0) {
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ return (ARCHIVE_OK);
+ }
+ p += 0x10;
+ }
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ total += skip;
+ }
+ }
+
+ return ARCHIVE_OK;
+fatal:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Couldn't find out RAR header");
+ return (ARCHIVE_FATAL);
+}
+
+static int rar5_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct rar5* rar = get_context(a);
+ int ret;
+
+ if(rar->header_initialized == 0) {
+ init_header(a);
+ if ((ret = try_skip_sfx(a)) < ARCHIVE_WARN)
+ return ret;
+ rar->header_initialized = 1;
+ }
+
+ if(rar->skipped_magic == 0) {
+ if(ARCHIVE_OK != consume(a, sizeof(rar5_signature_xor))) {
+ return ARCHIVE_EOF;
+ }
+
+ rar->skipped_magic = 1;
+ }
+
+ do {
+ ret = process_base_block(a, entry);
+ } while(ret == ARCHIVE_RETRY ||
+ (rar->main.endarc > 0 && ret == ARCHIVE_OK));
+
+ return ret;
+}
+
+static void init_unpack(struct rar5* rar) {
+ rar->file.calculated_crc32 = 0;
+ init_window_mask(rar);
+
+ free(rar->cstate.window_buf);
+ free(rar->cstate.filtered_buf);
+
+ if(rar->cstate.window_size > 0) {
+ rar->cstate.window_buf = calloc(1, rar->cstate.window_size);
+ rar->cstate.filtered_buf = calloc(1, rar->cstate.window_size);
+ } else {
+ rar->cstate.window_buf = NULL;
+ rar->cstate.filtered_buf = NULL;
+ }
+
+ rar->cstate.write_ptr = 0;
+ rar->cstate.last_write_ptr = 0;
+
+ memset(&rar->cstate.bd, 0, sizeof(rar->cstate.bd));
+ memset(&rar->cstate.ld, 0, sizeof(rar->cstate.ld));
+ memset(&rar->cstate.dd, 0, sizeof(rar->cstate.dd));
+ memset(&rar->cstate.ldd, 0, sizeof(rar->cstate.ldd));
+ memset(&rar->cstate.rd, 0, sizeof(rar->cstate.rd));
+}
+
+static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) {
+ int verify_crc;
+
+ if(rar->skip_mode) {
+#if defined CHECK_CRC_ON_SOLID_SKIP
+ verify_crc = 1;
+#else
+ verify_crc = 0;
+#endif
+ } else
+ verify_crc = 1;
+
+ if(verify_crc) {
+ /* Don't update CRC32 if the file doesn't have the
+ * `stored_crc32` info filled in. */
+ if(rar->file.stored_crc32 > 0) {
+ rar->file.calculated_crc32 =
+ crc32(rar->file.calculated_crc32, p, (unsigned int)to_read);
+ }
+
+ /* Check if the file uses an optional BLAKE2sp checksum
+ * algorithm. */
+ if(rar->file.has_blake2 > 0) {
+ /* Return value of the `update` function is always 0,
+ * so we can explicitly ignore it here. */
+ (void) blake2sp_update(&rar->file.b2state, p, to_read);
+ }
+ }
+}
+
+static int create_decode_tables(uint8_t* bit_length,
+ struct decode_table* table, int size)
+{
+ int code, upper_limit = 0, i, lc[16];
+ uint32_t decode_pos_clone[rar5_countof(table->decode_pos)];
+ ssize_t cur_len, quick_data_size;
+
+ memset(&lc, 0, sizeof(lc));
+ memset(table->decode_num, 0, sizeof(table->decode_num));
+ table->size = size;
+ table->quick_bits = size == HUFF_NC ? 10 : 7;
+
+ for(i = 0; i < size; i++) {
+ lc[bit_length[i] & 15]++;
+ }
+
+ lc[0] = 0;
+ table->decode_pos[0] = 0;
+ table->decode_len[0] = 0;
+
+ for(i = 1; i < 16; i++) {
+ upper_limit += lc[i];
+
+ table->decode_len[i] = upper_limit << (16 - i);
+ table->decode_pos[i] = table->decode_pos[i - 1] + lc[i - 1];
+
+ upper_limit <<= 1;
+ }
+
+ memcpy(decode_pos_clone, table->decode_pos, sizeof(decode_pos_clone));
+
+ for(i = 0; i < size; i++) {
+ uint8_t clen = bit_length[i] & 15;
+ if(clen > 0) {
+ int last_pos = decode_pos_clone[clen];
+ table->decode_num[last_pos] = i;
+ decode_pos_clone[clen]++;
+ }
+ }
+
+ quick_data_size = (int64_t)1 << table->quick_bits;
+ cur_len = 1;
+ for(code = 0; code < quick_data_size; code++) {
+ int bit_field = code << (16 - table->quick_bits);
+ int dist, pos;
+
+ while(cur_len < rar5_countof(table->decode_len) &&
+ bit_field >= table->decode_len[cur_len]) {
+ cur_len++;
+ }
+
+ table->quick_len[code] = (uint8_t) cur_len;
+
+ dist = bit_field - table->decode_len[cur_len - 1];
+ dist >>= (16 - cur_len);
+
+ pos = table->decode_pos[cur_len & 15] + dist;
+ if(cur_len < rar5_countof(table->decode_pos) && pos < size) {
+ table->quick_num[code] = table->decode_num[pos];
+ } else {
+ table->quick_num[code] = 0;
+ }
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int decode_number(struct archive_read* a, struct decode_table* table,
+ const uint8_t* p, uint16_t* num)
+{
+ int i, bits, dist, ret;
+ uint16_t bitfield;
+ uint32_t pos;
+ struct rar5* rar = get_context(a);
+
+ if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &bitfield))) {
+ return ret;
+ }
+
+ bitfield &= 0xfffe;
+
+ if(bitfield < table->decode_len[table->quick_bits]) {
+ int code = bitfield >> (16 - table->quick_bits);
+ skip_bits(rar, table->quick_len[code]);
+ *num = table->quick_num[code];
+ return ARCHIVE_OK;
+ }
+
+ bits = 15;
+
+ for(i = table->quick_bits + 1; i < 15; i++) {
+ if(bitfield < table->decode_len[i]) {
+ bits = i;
+ break;
+ }
+ }
+
+ skip_bits(rar, bits);
+
+ dist = bitfield - table->decode_len[bits - 1];
+ dist >>= (16 - bits);
+ pos = table->decode_pos[bits] + dist;
+
+ if(pos >= table->size)
+ pos = 0;
+
+ *num = table->decode_num[pos];
+ return ARCHIVE_OK;
+}
+
+/* Reads and parses Huffman tables from the beginning of the block. */
+static int parse_tables(struct archive_read* a, struct rar5* rar,
+ const uint8_t* p)
+{
+ int ret, value, i, w, idx = 0;
+ uint8_t bit_length[HUFF_BC],
+ table[HUFF_TABLE_SIZE],
+ nibble_mask = 0xF0,
+ nibble_shift = 4;
+
+ enum { ESCAPE = 15 };
+
+ /* The data for table generation is compressed using a simple RLE-like
+ * algorithm when storing zeroes, so we need to unpack it first. */
+ for(w = 0, i = 0; w < HUFF_BC;) {
+ if(i >= rar->cstate.cur_block_size) {
+ /* Truncated data, can't continue. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated data in huffman tables");
+ return ARCHIVE_FATAL;
+ }
+
+ value = (p[i] & nibble_mask) >> nibble_shift;
+
+ if(nibble_mask == 0x0F)
+ ++i;
+
+ nibble_mask ^= 0xFF;
+ nibble_shift ^= 4;
+
+ /* Values smaller than 15 is data, so we write it directly.
+ * Value 15 is a flag telling us that we need to unpack more
+ * bytes. */
+ if(value == ESCAPE) {
+ value = (p[i] & nibble_mask) >> nibble_shift;
+ if(nibble_mask == 0x0F)
+ ++i;
+ nibble_mask ^= 0xFF;
+ nibble_shift ^= 4;
+
+ if(value == 0) {
+ /* We sometimes need to write the actual value
+ * of 15, so this case handles that. */
+ bit_length[w++] = ESCAPE;
+ } else {
+ int k;
+
+ /* Fill zeroes. */
+ for(k = 0; (k < value + 2) && (w < HUFF_BC);
+ k++) {
+ bit_length[w++] = 0;
+ }
+ }
+ } else {
+ bit_length[w++] = value;
+ }
+ }
+
+ rar->bits.in_addr = i;
+ rar->bits.bit_addr = nibble_shift ^ 4;
+
+ ret = create_decode_tables(bit_length, &rar->cstate.bd, HUFF_BC);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Decoding huffman tables failed");
+ return ARCHIVE_FATAL;
+ }
+
+ for(i = 0; i < HUFF_TABLE_SIZE;) {
+ uint16_t num;
+
+ ret = decode_number(a, &rar->cstate.bd, p, &num);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Decoding huffman tables failed");
+ return ARCHIVE_FATAL;
+ }
+
+ if(num < 16) {
+ /* 0..15: store directly */
+ table[i] = (uint8_t) num;
+ i++;
+ } else if(num < 18) {
+ /* 16..17: repeat previous code */
+ uint16_t n;
+
+ if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &n)))
+ return ret;
+
+ if(num == 16) {
+ n >>= 13;
+ n += 3;
+ skip_bits(rar, 3);
+ } else {
+ n >>= 9;
+ n += 11;
+ skip_bits(rar, 7);
+ }
+
+ if(i > 0) {
+ while(n-- > 0 && i < HUFF_TABLE_SIZE) {
+ table[i] = table[i - 1];
+ i++;
+ }
+ } else {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unexpected error when decoding "
+ "huffman tables");
+ return ARCHIVE_FATAL;
+ }
+ } else {
+ /* other codes: fill with zeroes `n` times */
+ uint16_t n;
+
+ if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &n)))
+ return ret;
+
+ if(num == 18) {
+ n >>= 13;
+ n += 3;
+ skip_bits(rar, 3);
+ } else {
+ n >>= 9;
+ n += 11;
+ skip_bits(rar, 7);
+ }
+
+ while(n-- > 0 && i < HUFF_TABLE_SIZE)
+ table[i++] = 0;
+ }
+ }
+
+ ret = create_decode_tables(&table[idx], &rar->cstate.ld, HUFF_NC);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Failed to create literal table");
+ return ARCHIVE_FATAL;
+ }
+
+ idx += HUFF_NC;
+
+ ret = create_decode_tables(&table[idx], &rar->cstate.dd, HUFF_DC);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Failed to create distance table");
+ return ARCHIVE_FATAL;
+ }
+
+ idx += HUFF_DC;
+
+ ret = create_decode_tables(&table[idx], &rar->cstate.ldd, HUFF_LDC);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Failed to create lower bits of distances table");
+ return ARCHIVE_FATAL;
+ }
+
+ idx += HUFF_LDC;
+
+ ret = create_decode_tables(&table[idx], &rar->cstate.rd, HUFF_RC);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Failed to create repeating distances table");
+ return ARCHIVE_FATAL;
+ }
+
+ return ARCHIVE_OK;
+}
+
+/* Parses the block header, verifies its CRC byte, and saves the header
+ * fields inside the `hdr` pointer. */
+static int parse_block_header(struct archive_read* a, const uint8_t* p,
+ ssize_t* block_size, struct compressed_block_header* hdr)
+{
+ uint8_t calculated_cksum;
+ memcpy(hdr, p, sizeof(struct compressed_block_header));
+
+ if(bf_byte_count(hdr) > 2) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported block header size (was %d, max is 2)",
+ bf_byte_count(hdr));
+ return ARCHIVE_FATAL;
+ }
+
+ /* This should probably use bit reader interface in order to be more
+ * future-proof. */
+ *block_size = 0;
+ switch(bf_byte_count(hdr)) {
+ /* 1-byte block size */
+ case 0:
+ *block_size = *(const uint8_t*) &p[2];
+ break;
+
+ /* 2-byte block size */
+ case 1:
+ *block_size = archive_le16dec(&p[2]);
+ break;
+
+ /* 3-byte block size */
+ case 2:
+ *block_size = archive_le32dec(&p[2]);
+ *block_size &= 0x00FFFFFF;
+ break;
+
+ /* Other block sizes are not supported. This case is not
+ * reached, because we have an 'if' guard before the switch
+ * that makes sure of it. */
+ default:
+ return ARCHIVE_FATAL;
+ }
+
+ /* Verify the block header checksum. 0x5A is a magic value and is
+ * always * constant. */
+ calculated_cksum = 0x5A
+ ^ (uint8_t) hdr->block_flags_u8
+ ^ (uint8_t) *block_size
+ ^ (uint8_t) (*block_size >> 8)
+ ^ (uint8_t) (*block_size >> 16);
+
+ if(calculated_cksum != hdr->block_cksum) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Block checksum error: got 0x%x, expected 0x%x",
+ hdr->block_cksum, calculated_cksum);
+
+ return ARCHIVE_FATAL;
+#endif
+ }
+
+ return ARCHIVE_OK;
+}
+
+/* Convenience function used during filter processing. */
+static int parse_filter_data(struct archive_read* a, struct rar5* rar,
+ const uint8_t* p, uint32_t* filter_data)
+{
+ int i, bytes, ret;
+ uint32_t data = 0;
+
+ if(ARCHIVE_OK != (ret = read_consume_bits(a, rar, p, 2, &bytes)))
+ return ret;
+
+ bytes++;
+
+ for(i = 0; i < bytes; i++) {
+ uint16_t byte;
+
+ if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &byte))) {
+ return ret;
+ }
+
+ /* Cast to uint32_t will ensure the shift operation will not
+ * produce undefined result. */
+ data += ((uint32_t) byte >> 8) << (i * 8);
+ skip_bits(rar, 8);
+ }
+
+ *filter_data = data;
+ return ARCHIVE_OK;
+}
+
+/* Function is used during sanity checking. */
+static int is_valid_filter_block_start(struct rar5* rar,
+ uint32_t start)
+{
+ const int64_t block_start = (ssize_t) start + rar->cstate.write_ptr;
+ const int64_t last_bs = rar->cstate.last_block_start;
+ const ssize_t last_bl = rar->cstate.last_block_length;
+
+ if(last_bs == 0 || last_bl == 0) {
+ /* We didn't have any filters yet, so accept this offset. */
+ return 1;
+ }
+
+ if(block_start >= last_bs + last_bl) {
+ /* Current offset is bigger than last block's end offset, so
+ * accept current offset. */
+ return 1;
+ }
+
+ /* Any other case is not a normal situation and we should fail. */
+ return 0;
+}
+
+/* The function will create a new filter, read its parameters from the input
+ * stream and add it to the filter collection. */
+static int parse_filter(struct archive_read* ar, const uint8_t* p) {
+ uint32_t block_start, block_length;
+ uint16_t filter_type;
+ struct filter_info* filt = NULL;
+ struct rar5* rar = get_context(ar);
+ int ret;
+
+ /* Read the parameters from the input stream. */
+ if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_start)))
+ return ret;
+
+ if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_length)))
+ return ret;
+
+ if(ARCHIVE_OK != (ret = read_bits_16(ar, rar, p, &filter_type)))
+ return ret;
+
+ filter_type >>= 13;
+ skip_bits(rar, 3);
+
+ /* Perform some sanity checks on this filter parameters. Note that we
+ * allow only DELTA, E8/E9 and ARM filters here, because rest of
+ * filters are not used in RARv5. */
+
+ if(block_length < 4 ||
+ block_length > 0x400000 ||
+ filter_type > FILTER_ARM ||
+ !is_valid_filter_block_start(rar, block_start))
+ {
+ archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid filter encountered");
+ return ARCHIVE_FATAL;
+ }
+
+ /* Allocate a new filter. */
+ filt = add_new_filter(rar);
+ if(filt == NULL) {
+ archive_set_error(&ar->archive, ENOMEM,
+ "Can't allocate memory for a filter descriptor.");
+ return ARCHIVE_FATAL;
+ }
+
+ filt->type = filter_type;
+ filt->block_start = rar->cstate.write_ptr + block_start;
+ filt->block_length = block_length;
+
+ rar->cstate.last_block_start = filt->block_start;
+ rar->cstate.last_block_length = filt->block_length;
+
+ /* Read some more data in case this is a DELTA filter. Other filter
+ * types don't require any additional data over what was already
+ * read. */
+ if(filter_type == FILTER_DELTA) {
+ int channels;
+
+ if(ARCHIVE_OK != (ret = read_consume_bits(ar, rar, p, 5, &channels)))
+ return ret;
+
+ filt->channels = channels + 1;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int decode_code_length(struct archive_read* a, struct rar5* rar,
+ const uint8_t* p, uint16_t code)
+{
+ int lbits, length = 2;
+
+ if(code < 8) {
+ lbits = 0;
+ length += code;
+ } else {
+ lbits = code / 4 - 1;
+ length += (4 | (code & 3)) << lbits;
+ }
+
+ if(lbits > 0) {
+ int add;
+
+ if(ARCHIVE_OK != read_consume_bits(a, rar, p, lbits, &add))
+ return -1;
+
+ length += add;
+ }
+
+ return length;
+}
+
+static int copy_string(struct archive_read* a, int len, int dist) {
+ struct rar5* rar = get_context(a);
+ const uint64_t cmask = rar->cstate.window_mask;
+ const uint64_t write_ptr = rar->cstate.write_ptr +
+ rar->cstate.solid_offset;
+ int i;
+
+ if (rar->cstate.window_buf == NULL)
+ return ARCHIVE_FATAL;
+
+ /* The unpacker spends most of the time in this function. It would be
+ * a good idea to introduce some optimizations here.
+ *
+ * Just remember that this loop treats buffers that overlap differently
+ * than buffers that do not overlap. This is why a simple memcpy(3)
+ * call will not be enough. */
+
+ for(i = 0; i < len; i++) {
+ const ssize_t write_idx = (write_ptr + i) & cmask;
+ const ssize_t read_idx = (write_ptr + i - dist) & cmask;
+ rar->cstate.window_buf[write_idx] =
+ rar->cstate.window_buf[read_idx];
+ }
+
+ rar->cstate.write_ptr += len;
+ return ARCHIVE_OK;
+}
+
+static int do_uncompress_block(struct archive_read* a, const uint8_t* p) {
+ struct rar5* rar = get_context(a);
+ uint16_t num;
+ int ret;
+
+ const uint64_t cmask = rar->cstate.window_mask;
+ const struct compressed_block_header* hdr = &rar->last_block_hdr;
+ const uint8_t bit_size = 1 + bf_bit_size(hdr);
+
+ while(1) {
+ if(rar->cstate.write_ptr - rar->cstate.last_write_ptr >
+ (rar->cstate.window_size >> 1)) {
+ /* Don't allow growing data by more than half of the
+ * window size at a time. In such case, break the loop;
+ * next call to this function will continue processing
+ * from this moment. */
+ break;
+ }
+
+ if(rar->bits.in_addr > rar->cstate.cur_block_size - 1 ||
+ (rar->bits.in_addr == rar->cstate.cur_block_size - 1 &&
+ rar->bits.bit_addr >= bit_size))
+ {
+ /* If the program counter is here, it means the
+ * function has finished processing the block. */
+ rar->cstate.block_parsing_finished = 1;
+ break;
+ }
+
+ /* Decode the next literal. */
+ if(ARCHIVE_OK != decode_number(a, &rar->cstate.ld, p, &num)) {
+ return ARCHIVE_EOF;
+ }
+
+ /* Num holds a decompression literal, or 'command code'.
+ *
+ * - Values lower than 256 are just bytes. Those codes
+ * can be stored in the output buffer directly.
+ *
+ * - Code 256 defines a new filter, which is later used to
+ * ransform the data block accordingly to the filter type.
+ * The data block needs to be fully uncompressed first.
+ *
+ * - Code bigger than 257 and smaller than 262 define
+ * a repetition pattern that should be copied from
+ * an already uncompressed chunk of data.
+ */
+
+ if(num < 256) {
+ /* Directly store the byte. */
+ int64_t write_idx = rar->cstate.solid_offset +
+ rar->cstate.write_ptr++;
+
+ rar->cstate.window_buf[write_idx & cmask] =
+ (uint8_t) num;
+ continue;
+ } else if(num >= 262) {
+ uint16_t dist_slot;
+ int len = decode_code_length(a, rar, p, num - 262),
+ dbits,
+ dist = 1;
+
+ if(len == -1) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Failed to decode the code length");
+
+ return ARCHIVE_FATAL;
+ }
+
+ if(ARCHIVE_OK != decode_number(a, &rar->cstate.dd, p,
+ &dist_slot))
+ {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Failed to decode the distance slot");
+
+ return ARCHIVE_FATAL;
+ }
+
+ if(dist_slot < 4) {
+ dbits = 0;
+ dist += dist_slot;
+ } else {
+ dbits = dist_slot / 2 - 1;
+
+ /* Cast to uint32_t will make sure the shift
+ * left operation won't produce undefined
+ * result. Then, the uint32_t type will
+ * be implicitly casted to int. */
+ dist += (uint32_t) (2 |
+ (dist_slot & 1)) << dbits;
+ }
+
+ if(dbits > 0) {
+ if(dbits >= 4) {
+ uint32_t add = 0;
+ uint16_t low_dist;
+
+ if(dbits > 4) {
+ if(ARCHIVE_OK != (ret = read_bits_32(
+ a, rar, p, &add))) {
+ /* Return EOF if we
+ * can't read more
+ * data. */
+ return ret;
+ }
+
+ skip_bits(rar, dbits - 4);
+ add = (add >> (
+ 36 - dbits)) << 4;
+ dist += add;
+ }
+
+ if(ARCHIVE_OK != decode_number(a,
+ &rar->cstate.ldd, p, &low_dist))
+ {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Failed to decode the "
+ "distance slot");
+
+ return ARCHIVE_FATAL;
+ }
+
+ if(dist >= INT_MAX - low_dist - 1) {
+ /* This only happens in
+ * invalid archives. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Distance pointer "
+ "overflow");
+ return ARCHIVE_FATAL;
+ }
+
+ dist += low_dist;
+ } else {
+ /* dbits is one of [0,1,2,3] */
+ int add;
+
+ if(ARCHIVE_OK != (ret = read_consume_bits(a, rar,
+ p, dbits, &add))) {
+ /* Return EOF if we can't read
+ * more data. */
+ return ret;
+ }
+
+ dist += add;
+ }
+ }
+
+ if(dist > 0x100) {
+ len++;
+
+ if(dist > 0x2000) {
+ len++;
+
+ if(dist > 0x40000) {
+ len++;
+ }
+ }
+ }
+
+ dist_cache_push(rar, dist);
+ rar->cstate.last_len = len;
+
+ if(ARCHIVE_OK != copy_string(a, len, dist))
+ return ARCHIVE_FATAL;
+
+ continue;
+ } else if(num == 256) {
+ /* Create a filter. */
+ ret = parse_filter(a, p);
+ if(ret != ARCHIVE_OK)
+ return ret;
+
+ continue;
+ } else if(num == 257) {
+ if(rar->cstate.last_len != 0) {
+ if(ARCHIVE_OK != copy_string(a,
+ rar->cstate.last_len,
+ rar->cstate.dist_cache[0]))
+ {
+ return ARCHIVE_FATAL;
+ }
+ }
+
+ continue;
+ } else {
+ /* num < 262 */
+ const int idx = num - 258;
+ const int dist = dist_cache_touch(rar, idx);
+
+ uint16_t len_slot;
+ int len;
+
+ if(ARCHIVE_OK != decode_number(a, &rar->cstate.rd, p,
+ &len_slot)) {
+ return ARCHIVE_FATAL;
+ }
+
+ len = decode_code_length(a, rar, p, len_slot);
+ if (len == -1) {
+ return ARCHIVE_FATAL;
+ }
+
+ rar->cstate.last_len = len;
+
+ if(ARCHIVE_OK != copy_string(a, len, dist))
+ return ARCHIVE_FATAL;
+
+ continue;
+ }
+ }
+
+ return ARCHIVE_OK;
+}
+
+/* Binary search for the RARv5 signature. */
+static int scan_for_signature(struct archive_read* a) {
+ const uint8_t* p;
+ const int chunk_size = 512;
+ ssize_t i;
+ char signature[sizeof(rar5_signature_xor)];
+
+ /* If we're here, it means we're on an 'unknown territory' data.
+ * There's no indication what kind of data we're reading here.
+ * It could be some text comment, any kind of binary data,
+ * digital sign, dragons, etc.
+ *
+ * We want to find a valid RARv5 magic header inside this unknown
+ * data. */
+
+ /* Is it possible in libarchive to just skip everything until the
+ * end of the file? If so, it would be a better approach than the
+ * current implementation of this function. */
+
+ rar5_signature(signature);
+
+ while(1) {
+ if(!read_ahead(a, chunk_size, &p))
+ return ARCHIVE_EOF;
+
+ for(i = 0; i < chunk_size - (int)sizeof(rar5_signature_xor);
+ i++) {
+ if(memcmp(&p[i], signature,
+ sizeof(rar5_signature_xor)) == 0) {
+ /* Consume the number of bytes we've used to
+ * search for the signature, as well as the
+ * number of bytes used by the signature
+ * itself. After this we should be standing
+ * on a valid base block header. */
+ (void) consume(a,
+ i + sizeof(rar5_signature_xor));
+ return ARCHIVE_OK;
+ }
+ }
+
+ consume(a, chunk_size);
+ }
+
+ return ARCHIVE_FATAL;
+}
+
+/* This function will switch the multivolume archive file to another file,
+ * i.e. from part03 to part 04. */
+static int advance_multivolume(struct archive_read* a) {
+ int lret;
+ struct rar5* rar = get_context(a);
+
+ /* A small state machine that will skip unnecessary data, needed to
+ * switch from one multivolume to another. Such skipping is needed if
+ * we want to be an stream-oriented (instead of file-oriented)
+ * unpacker.
+ *
+ * The state machine starts with `rar->main.endarc` == 0. It also
+ * assumes that current stream pointer points to some base block
+ * header.
+ *
+ * The `endarc` field is being set when the base block parsing
+ * function encounters the 'end of archive' marker.
+ */
+
+ while(1) {
+ if(rar->main.endarc == 1) {
+ int looping = 1;
+
+ rar->main.endarc = 0;
+
+ while(looping) {
+ lret = skip_base_block(a);
+ switch(lret) {
+ case ARCHIVE_RETRY:
+ /* Continue looping. */
+ break;
+ case ARCHIVE_OK:
+ /* Break loop. */
+ looping = 0;
+ break;
+ default:
+ /* Forward any errors to the
+ * caller. */
+ return lret;
+ }
+ }
+
+ break;
+ } else {
+ /* Skip current base block. In order to properly skip
+ * it, we really need to simply parse it and discard
+ * the results. */
+
+ lret = skip_base_block(a);
+ if(lret == ARCHIVE_FATAL || lret == ARCHIVE_FAILED)
+ return lret;
+
+ /* The `skip_base_block` function tells us if we
+ * should continue with skipping, or we should stop
+ * skipping. We're trying to skip everything up to
+ * a base FILE block. */
+
+ if(lret != ARCHIVE_RETRY) {
+ /* If there was an error during skipping, or we
+ * have just skipped a FILE base block... */
+
+ if(rar->main.endarc == 0) {
+ return lret;
+ } else {
+ continue;
+ }
+ }
+ }
+ }
+
+ return ARCHIVE_OK;
+}
+
+/* Merges the partial block from the first multivolume archive file, and
+ * partial block from the second multivolume archive file. The result is
+ * a chunk of memory containing the whole block, and the stream pointer
+ * is advanced to the next block in the second multivolume archive file. */
+static int merge_block(struct archive_read* a, ssize_t block_size,
+ const uint8_t** p)
+{
+ struct rar5* rar = get_context(a);
+ ssize_t cur_block_size, partial_offset = 0;
+ const uint8_t* lp;
+ int ret;
+
+ if(rar->merge_mode) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Recursive merge is not allowed");
+
+ return ARCHIVE_FATAL;
+ }
+
+ /* Set a flag that we're in the switching mode. */
+ rar->cstate.switch_multivolume = 1;
+
+ /* Reallocate the memory which will hold the whole block. */
+ if(rar->vol.push_buf)
+ free((void*) rar->vol.push_buf);
+
+ /* Increasing the allocation block by 8 is due to bit reading functions,
+ * which are using additional 2 or 4 bytes. Allocating the block size
+ * by exact value would make bit reader perform reads from invalid
+ * memory block when reading the last byte from the buffer. */
+ rar->vol.push_buf = malloc(block_size + 8);
+ if(!rar->vol.push_buf) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for a merge block buffer.");
+ return ARCHIVE_FATAL;
+ }
+
+ /* Valgrind complains if the extension block for bit reader is not
+ * initialized, so initialize it. */
+ memset(&rar->vol.push_buf[block_size], 0, 8);
+
+ /* A single block can span across multiple multivolume archive files,
+ * so we use a loop here. This loop will consume enough multivolume
+ * archive files until the whole block is read. */
+
+ while(1) {
+ /* Get the size of current block chunk in this multivolume
+ * archive file and read it. */
+ cur_block_size = rar5_min(rar->file.bytes_remaining,
+ block_size - partial_offset);
+
+ if(cur_block_size == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Encountered block size == 0 during block merge");
+ return ARCHIVE_FATAL;
+ }
+
+ if(!read_ahead(a, cur_block_size, &lp))
+ return ARCHIVE_EOF;
+
+ /* Sanity check; there should never be a situation where this
+ * function reads more data than the block's size. */
+ if(partial_offset + cur_block_size > block_size) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Consumed too much data when merging blocks.");
+ return ARCHIVE_FATAL;
+ }
+
+ /* Merge previous block chunk with current block chunk,
+ * or create first block chunk if this is our first
+ * iteration. */
+ memcpy(&rar->vol.push_buf[partial_offset], lp, cur_block_size);
+
+ /* Advance the stream read pointer by this block chunk size. */
+ if(ARCHIVE_OK != consume(a, cur_block_size))
+ return ARCHIVE_EOF;
+
+ /* Update the pointers. `partial_offset` contains information
+ * about the sum of merged block chunks. */
+ partial_offset += cur_block_size;
+ rar->file.bytes_remaining -= cur_block_size;
+
+ /* If `partial_offset` is the same as `block_size`, this means
+ * we've merged all block chunks and we have a valid full
+ * block. */
+ if(partial_offset == block_size) {
+ break;
+ }
+
+ /* If we don't have any bytes to read, this means we should
+ * switch to another multivolume archive file. */
+ if(rar->file.bytes_remaining == 0) {
+ rar->merge_mode++;
+ ret = advance_multivolume(a);
+ rar->merge_mode--;
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+ }
+ }
+
+ *p = rar->vol.push_buf;
+
+ /* If we're here, we can resume unpacking by processing the block
+ * pointed to by the `*p` memory pointer. */
+
+ return ARCHIVE_OK;
+}
+
+static int process_block(struct archive_read* a) {
+ const uint8_t* p;
+ struct rar5* rar = get_context(a);
+ int ret;
+
+ /* If we don't have any data to be processed, this most probably means
+ * we need to switch to the next volume. */
+ if(rar->main.volume && rar->file.bytes_remaining == 0) {
+ ret = advance_multivolume(a);
+ if(ret != ARCHIVE_OK)
+ return ret;
+ }
+
+ if(rar->cstate.block_parsing_finished) {
+ ssize_t block_size;
+ ssize_t to_skip;
+ ssize_t cur_block_size;
+
+ /* The header size won't be bigger than 6 bytes. */
+ if(!read_ahead(a, 6, &p)) {
+ /* Failed to prefetch data block header. */
+ return ARCHIVE_EOF;
+ }
+
+ /*
+ * Read block_size by parsing block header. Validate the header
+ * by calculating CRC byte stored inside the header. Size of
+ * the header is not constant (block size can be stored either
+ * in 1 or 2 bytes), that's why block size is left out from the
+ * `compressed_block_header` structure and returned by
+ * `parse_block_header` as the second argument. */
+
+ ret = parse_block_header(a, p, &block_size,
+ &rar->last_block_hdr);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+
+ /* Skip block header. Next data is huffman tables,
+ * if present. */
+ to_skip = sizeof(struct compressed_block_header) +
+ bf_byte_count(&rar->last_block_hdr) + 1;
+
+ if(ARCHIVE_OK != consume(a, to_skip))
+ return ARCHIVE_EOF;
+
+ rar->file.bytes_remaining -= to_skip;
+
+ /* The block size gives information about the whole block size,
+ * but the block could be stored in split form when using
+ * multi-volume archives. In this case, the block size will be
+ * bigger than the actual data stored in this file. Remaining
+ * part of the data will be in another file. */
+
+ cur_block_size =
+ rar5_min(rar->file.bytes_remaining, block_size);
+
+ if(block_size > rar->file.bytes_remaining) {
+ /* If current blocks' size is bigger than our data
+ * size, this means we have a multivolume archive.
+ * In this case, skip all base headers until the end
+ * of the file, proceed to next "partXXX.rar" volume,
+ * find its signature, skip all headers up to the first
+ * FILE base header, and continue from there.
+ *
+ * Note that `merge_block` will update the `rar`
+ * context structure quite extensively. */
+
+ ret = merge_block(a, block_size, &p);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+
+ cur_block_size = block_size;
+
+ /* Current stream pointer should be now directly
+ * *after* the block that spanned through multiple
+ * archive files. `p` pointer should have the data of
+ * the *whole* block (merged from partial blocks
+ * stored in multiple archives files). */
+ } else {
+ rar->cstate.switch_multivolume = 0;
+
+ /* Read the whole block size into memory. This can take
+ * up to 8 megabytes of memory in theoretical cases.
+ * Might be worth to optimize this and use a standard
+ * chunk of 4kb's. */
+ if(!read_ahead(a, 4 + cur_block_size, &p)) {
+ /* Failed to prefetch block data. */
+ return ARCHIVE_EOF;
+ }
+ }
+
+ rar->cstate.block_buf = p;
+ rar->cstate.cur_block_size = cur_block_size;
+ rar->cstate.block_parsing_finished = 0;
+
+ rar->bits.in_addr = 0;
+ rar->bits.bit_addr = 0;
+
+ if(bf_is_table_present(&rar->last_block_hdr)) {
+ /* Load Huffman tables. */
+ ret = parse_tables(a, rar, p);
+ if(ret != ARCHIVE_OK) {
+ /* Error during decompression of Huffman
+ * tables. */
+ return ret;
+ }
+ }
+ } else {
+ /* Block parsing not finished, reuse previous memory buffer. */
+ p = rar->cstate.block_buf;
+ }
+
+ /* Uncompress the block, or a part of it, depending on how many bytes
+ * will be generated by uncompressing the block.
+ *
+ * In case too many bytes will be generated, calling this function
+ * again will resume the uncompression operation. */
+ ret = do_uncompress_block(a, p);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+
+ if(rar->cstate.block_parsing_finished &&
+ rar->cstate.switch_multivolume == 0 &&
+ rar->cstate.cur_block_size > 0)
+ {
+ /* If we're processing a normal block, consume the whole
+ * block. We can do this because we've already read the whole
+ * block to memory. */
+ if(ARCHIVE_OK != consume(a, rar->cstate.cur_block_size))
+ return ARCHIVE_FATAL;
+
+ rar->file.bytes_remaining -= rar->cstate.cur_block_size;
+ } else if(rar->cstate.switch_multivolume) {
+ /* Don't consume the block if we're doing multivolume
+ * processing. The volume switching function will consume
+ * the proper count of bytes instead. */
+ rar->cstate.switch_multivolume = 0;
+ }
+
+ return ARCHIVE_OK;
+}
+
+/* Pops the `buf`, `size` and `offset` from the "data ready" stack.
+ *
+ * Returns ARCHIVE_OK when those arguments can be used, ARCHIVE_RETRY
+ * when there is no data on the stack. */
+static int use_data(struct rar5* rar, const void** buf, size_t* size,
+ int64_t* offset)
+{
+ int i;
+
+ for(i = 0; i < rar5_countof(rar->cstate.dready); i++) {
+ struct data_ready *d = &rar->cstate.dready[i];
+
+ if(d->used) {
+ if(buf) *buf = d->buf;
+ if(size) *size = d->size;
+ if(offset) *offset = d->offset;
+
+ d->used = 0;
+ return ARCHIVE_OK;
+ }
+ }
+
+ return ARCHIVE_RETRY;
+}
+
+/* Pushes the `buf`, `size` and `offset` arguments to the rar->cstate.dready
+ * FIFO stack. Those values will be popped from this stack by the `use_data`
+ * function. */
+static int push_data_ready(struct archive_read* a, struct rar5* rar,
+ const uint8_t* buf, size_t size, int64_t offset)
+{
+ int i;
+
+ /* Don't push if we're in skip mode. This is needed because solid
+ * streams need full processing even if we're skipping data. After
+ * fully processing the stream, we need to discard the generated bytes,
+ * because we're interested only in the side effect: building up the
+ * internal window circular buffer. This window buffer will be used
+ * later during unpacking of requested data. */
+ if(rar->skip_mode)
+ return ARCHIVE_OK;
+
+ /* Sanity check. */
+ if(offset != rar->file.last_offset + rar->file.last_size) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Sanity check error: output stream is not continuous");
+ return ARCHIVE_FATAL;
+ }
+
+ for(i = 0; i < rar5_countof(rar->cstate.dready); i++) {
+ struct data_ready* d = &rar->cstate.dready[i];
+ if(!d->used) {
+ d->used = 1;
+ d->buf = buf;
+ d->size = size;
+ d->offset = offset;
+
+ /* These fields are used only in sanity checking. */
+ rar->file.last_offset = offset;
+ rar->file.last_size = size;
+
+ /* Calculate the checksum of this new block before
+ * submitting data to libarchive's engine. */
+ update_crc(rar, d->buf, d->size);
+
+ return ARCHIVE_OK;
+ }
+ }
+
+ /* Program counter will reach this code if the `rar->cstate.data_ready`
+ * stack will be filled up so that no new entries will be allowed. The
+ * code shouldn't allow such situation to occur. So we treat this case
+ * as an internal error. */
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Error: premature end of data_ready stack");
+ return ARCHIVE_FATAL;
+}
+
+/* This function uncompresses the data that is stored in the <FILE> base
+ * block.
+ *
+ * The FILE base block looks like this:
+ *
+ * <header><huffman tables><block_1><block_2>...<block_n>
+ *
+ * The <header> is a block header, that is parsed in parse_block_header().
+ * It's a "compressed_block_header" structure, containing metadata needed
+ * to know when we should stop looking for more <block_n> blocks.
+ *
+ * <huffman tables> contain data needed to set up the huffman tables, needed
+ * for the actual decompression.
+ *
+ * Each <block_n> consists of series of literals:
+ *
+ * <literal><literal><literal>...<literal>
+ *
+ * Those literals generate the uncompression data. They operate on a circular
+ * buffer, sometimes writing raw data into it, sometimes referencing
+ * some previous data inside this buffer, and sometimes declaring a filter
+ * that will need to be executed on the data stored in the circular buffer.
+ * It all depends on the literal that is used.
+ *
+ * Sometimes blocks produce output data, sometimes they don't. For example, for
+ * some huge files that use lots of filters, sometimes a block is filled with
+ * only filter declaration literals. Such blocks won't produce any data in the
+ * circular buffer.
+ *
+ * Sometimes blocks will produce 4 bytes of data, and sometimes 1 megabyte,
+ * because a literal can reference previously decompressed data. For example,
+ * there can be a literal that says: 'append a byte 0xFE here', and after
+ * it another literal can say 'append 1 megabyte of data from circular buffer
+ * offset 0x12345'. This is how RAR format handles compressing repeated
+ * patterns.
+ *
+ * The RAR compressor creates those literals and the actual efficiency of
+ * compression depends on what those literals are. The literals can also
+ * be seen as a kind of a non-turing-complete virtual machine that simply
+ * tells the decompressor what it should do.
+ * */
+
+static int do_uncompress_file(struct archive_read* a) {
+ struct rar5* rar = get_context(a);
+ int ret;
+ int64_t max_end_pos;
+
+ if(!rar->cstate.initialized) {
+ /* Don't perform full context reinitialization if we're
+ * processing a solid archive. */
+ if(!rar->main.solid || !rar->cstate.window_buf) {
+ init_unpack(rar);
+ }
+
+ rar->cstate.initialized = 1;
+ }
+
+ /* Don't allow extraction if window_size is invalid. */
+ if(rar->cstate.window_size == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid window size declaration in this file");
+
+ /* This should never happen in valid files. */
+ return ARCHIVE_FATAL;
+ }
+
+ if(rar->cstate.all_filters_applied == 1) {
+ /* We use while(1) here, but standard case allows for just 1
+ * iteration. The loop will iterate if process_block() didn't
+ * generate any data at all. This can happen if the block
+ * contains only filter definitions (this is common in big
+ * files). */
+ while(1) {
+ ret = process_block(a);
+ if(ret == ARCHIVE_EOF || ret == ARCHIVE_FATAL)
+ return ret;
+
+ if(rar->cstate.last_write_ptr ==
+ rar->cstate.write_ptr) {
+ /* The block didn't generate any new data,
+ * so just process a new block. */
+ continue;
+ }
+
+ /* The block has generated some new data, so break
+ * the loop. */
+ break;
+ }
+ }
+
+ /* Try to run filters. If filters won't be applied, it means that
+ * insufficient data was generated. */
+ ret = apply_filters(a);
+ if(ret == ARCHIVE_RETRY) {
+ return ARCHIVE_OK;
+ } else if(ret == ARCHIVE_FATAL) {
+ return ARCHIVE_FATAL;
+ }
+
+ /* If apply_filters() will return ARCHIVE_OK, we can continue here. */
+
+ if(cdeque_size(&rar->cstate.filters) > 0) {
+ /* Check if we can write something before hitting first
+ * filter. */
+ struct filter_info* flt;
+
+ /* Get the block_start offset from the first filter. */
+ if(CDE_OK != cdeque_front(&rar->cstate.filters,
+ cdeque_filter_p(&flt)))
+ {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Can't read first filter");
+ return ARCHIVE_FATAL;
+ }
+
+ max_end_pos = rar5_min(flt->block_start,
+ rar->cstate.write_ptr);
+ } else {
+ /* There are no filters defined, or all filters were applied.
+ * This means we can just store the data without any
+ * postprocessing. */
+ max_end_pos = rar->cstate.write_ptr;
+ }
+
+ if(max_end_pos == rar->cstate.last_write_ptr) {
+ /* We can't write anything yet. The block uncompression
+ * function did not generate enough data, and no filter can be
+ * applied. At the same time we don't have any data that can be
+ * stored without filter postprocessing. This means we need to
+ * wait for more data to be generated, so we can apply the
+ * filters.
+ *
+ * Signal the caller that we need more data to be able to do
+ * anything.
+ */
+ return ARCHIVE_RETRY;
+ } else {
+ /* We can write the data before hitting the first filter.
+ * So let's do it. The push_window_data() function will
+ * effectively return the selected data block to the user
+ * application. */
+ push_window_data(a, rar, rar->cstate.last_write_ptr,
+ max_end_pos);
+ rar->cstate.last_write_ptr = max_end_pos;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int uncompress_file(struct archive_read* a) {
+ int ret;
+
+ while(1) {
+ /* Sometimes the uncompression function will return a
+ * 'retry' signal. If this will happen, we have to retry
+ * the function. */
+ ret = do_uncompress_file(a);
+ if(ret != ARCHIVE_RETRY)
+ return ret;
+ }
+}
+
+
+static int do_unstore_file(struct archive_read* a,
+ struct rar5* rar, const void** buf, size_t* size, int64_t* offset)
+{
+ size_t to_read;
+ const uint8_t* p;
+
+ if(rar->file.bytes_remaining == 0 && rar->main.volume > 0 &&
+ rar->generic.split_after > 0)
+ {
+ int ret;
+
+ rar->cstate.switch_multivolume = 1;
+ ret = advance_multivolume(a);
+ rar->cstate.switch_multivolume = 0;
+
+ if(ret != ARCHIVE_OK) {
+ /* Failed to advance to next multivolume archive
+ * file. */
+ return ret;
+ }
+ }
+
+ to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024);
+ if(to_read == 0) {
+ return ARCHIVE_EOF;
+ }
+
+ if(!read_ahead(a, to_read, &p)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "I/O error when unstoring file");
+ return ARCHIVE_FATAL;
+ }
+
+ if(ARCHIVE_OK != consume(a, to_read)) {
+ return ARCHIVE_EOF;
+ }
+
+ if(buf) *buf = p;
+ if(size) *size = to_read;
+ if(offset) *offset = rar->cstate.last_unstore_ptr;
+
+ rar->file.bytes_remaining -= to_read;
+ rar->cstate.last_unstore_ptr += to_read;
+
+ update_crc(rar, p, to_read);
+ return ARCHIVE_OK;
+}
+
+static int do_unpack(struct archive_read* a, struct rar5* rar,
+ const void** buf, size_t* size, int64_t* offset)
+{
+ enum COMPRESSION_METHOD {
+ STORE = 0, FASTEST = 1, FAST = 2, NORMAL = 3, GOOD = 4,
+ BEST = 5
+ };
+
+ if(rar->file.service > 0) {
+ return do_unstore_file(a, rar, buf, size, offset);
+ } else {
+ switch(rar->cstate.method) {
+ case STORE:
+ return do_unstore_file(a, rar, buf, size,
+ offset);
+ case FASTEST:
+ /* fallthrough */
+ case FAST:
+ /* fallthrough */
+ case NORMAL:
+ /* fallthrough */
+ case GOOD:
+ /* fallthrough */
+ case BEST:
+ /* No data is returned here. But because a sparse-file aware
+ * caller (like archive_read_data_into_fd) may treat zero-size
+ * as a sparse file block, we need to update the offset
+ * accordingly. At this point the decoder doesn't have any
+ * pending uncompressed data blocks, so the current position in
+ * the output file should be last_write_ptr. */
+ if (offset) *offset = rar->cstate.last_write_ptr;
+ return uncompress_file(a);
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Compression method not supported: 0x%x",
+ rar->cstate.method);
+
+ return ARCHIVE_FATAL;
+ }
+ }
+
+#if !defined WIN32
+ /* Not reached. */
+ return ARCHIVE_OK;
+#endif
+}
+
+static int verify_checksums(struct archive_read* a) {
+ int verify_crc;
+ struct rar5* rar = get_context(a);
+
+ /* Check checksums only when actually unpacking the data. There's no
+ * need to calculate checksum when we're skipping data in solid archives
+ * (skipping in solid archives is the same thing as unpacking compressed
+ * data and discarding the result). */
+
+ if(!rar->skip_mode) {
+ /* Always check checksums if we're not in skip mode */
+ verify_crc = 1;
+ } else {
+ /* We can override the logic above with a compile-time option
+ * NO_CRC_ON_SOLID_SKIP. This option is used during debugging,
+ * and it will check checksums of unpacked data even when
+ * we're skipping it. */
+
+#if defined CHECK_CRC_ON_SOLID_SKIP
+ /* Debug case */
+ verify_crc = 1;
+#else
+ /* Normal case */
+ verify_crc = 0;
+#endif
+ }
+
+ if(verify_crc) {
+ /* During unpacking, on each unpacked block we're calling the
+ * update_crc() function. Since we are here, the unpacking
+ * process is already over and we can check if calculated
+ * checksum (CRC32 or BLAKE2sp) is the same as what is stored
+ * in the archive. */
+ if(rar->file.stored_crc32 > 0) {
+ /* Check CRC32 only when the file contains a CRC32
+ * value for this file. */
+
+ if(rar->file.calculated_crc32 !=
+ rar->file.stored_crc32) {
+ /* Checksums do not match; the unpacked file
+ * is corrupted. */
+
+ DEBUG_CODE {
+ printf("Checksum error: CRC32 "
+ "(was: %08" PRIx32 ", expected: %08" PRIx32 ")\n",
+ rar->file.calculated_crc32,
+ rar->file.stored_crc32);
+ }
+
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Checksum error: CRC32");
+ return ARCHIVE_FATAL;
+#endif
+ } else {
+ DEBUG_CODE {
+ printf("Checksum OK: CRC32 "
+ "(%08" PRIx32 "/%08" PRIx32 ")\n",
+ rar->file.stored_crc32,
+ rar->file.calculated_crc32);
+ }
+ }
+ }
+
+ if(rar->file.has_blake2 > 0) {
+ /* BLAKE2sp is an optional checksum algorithm that is
+ * added to RARv5 archives when using the `-htb` switch
+ * during creation of archive.
+ *
+ * We now finalize the hash calculation by calling the
+ * `final` function. This will generate the final hash
+ * value we can use to compare it with the BLAKE2sp
+ * checksum that is stored in the archive.
+ *
+ * The return value of this `final` function is not
+ * very helpful, as it guards only against improper use.
+ * This is why we're explicitly ignoring it. */
+
+ uint8_t b2_buf[32];
+ (void) blake2sp_final(&rar->file.b2state, b2_buf, 32);
+
+ if(memcmp(&rar->file.blake2sp, b2_buf, 32) != 0) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Checksum error: BLAKE2");
+
+ return ARCHIVE_FATAL;
+#endif
+ }
+ }
+ }
+
+ /* Finalization for this file has been successfully completed. */
+ return ARCHIVE_OK;
+}
+
+static int verify_global_checksums(struct archive_read* a) {
+ return verify_checksums(a);
+}
+
+/*
+ * Decryption function for the magic signature pattern. Check the comment near
+ * the `rar5_signature_xor` symbol to read the rationale behind this.
+ */
+static void rar5_signature(char *buf) {
+ size_t i;
+
+ for(i = 0; i < sizeof(rar5_signature_xor); i++) {
+ buf[i] = rar5_signature_xor[i] ^ 0xA1;
+ }
+}
+
+static int rar5_read_data(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset) {
+ int ret;
+ struct rar5* rar = get_context(a);
+
+ if (size)
+ *size = 0;
+
+ if(rar->file.dir > 0) {
+ /* Don't process any data if this file entry was declared
+ * as a directory. This is needed, because entries marked as
+ * directory doesn't have any dictionary buffer allocated, so
+ * it's impossible to perform any decompression. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't decompress an entry marked as a directory");
+ return ARCHIVE_FAILED;
+ }
+
+ if(!rar->skip_mode && (rar->cstate.last_write_ptr > rar->file.unpacked_size)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Unpacker has written too many bytes");
+ return ARCHIVE_FATAL;
+ }
+
+ ret = use_data(rar, buff, size, offset);
+ if(ret == ARCHIVE_OK) {
+ return ret;
+ }
+
+ if(rar->file.eof == 1) {
+ return ARCHIVE_EOF;
+ }
+
+ ret = do_unpack(a, rar, buff, size, offset);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+
+ if(rar->file.bytes_remaining == 0 &&
+ rar->cstate.last_write_ptr == rar->file.unpacked_size)
+ {
+ /* If all bytes of current file were processed, run
+ * finalization.
+ *
+ * Finalization will check checksum against proper values. If
+ * some of the checksums will not match, we'll return an error
+ * value in the last `archive_read_data` call to signal an error
+ * to the user. */
+
+ rar->file.eof = 1;
+ return verify_global_checksums(a);
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int rar5_read_data_skip(struct archive_read *a) {
+ struct rar5* rar = get_context(a);
+
+ if(rar->main.solid) {
+ /* In solid archives, instead of skipping the data, we need to
+ * extract it, and dispose the result. The side effect of this
+ * operation will be setting up the initial window buffer state
+ * needed to be able to extract the selected file. */
+
+ int ret;
+
+ /* Make sure to process all blocks in the compressed stream. */
+ while(rar->file.bytes_remaining > 0) {
+ /* Setting the "skip mode" will allow us to skip
+ * checksum checks during data skipping. Checking the
+ * checksum of skipped data isn't really necessary and
+ * it's only slowing things down.
+ *
+ * This is incremented instead of setting to 1 because
+ * this data skipping function can be called
+ * recursively. */
+ rar->skip_mode++;
+
+ /* We're disposing 1 block of data, so we use triple
+ * NULLs in arguments. */
+ ret = rar5_read_data(a, NULL, NULL, NULL);
+
+ /* Turn off "skip mode". */
+ rar->skip_mode--;
+
+ if(ret < 0 || ret == ARCHIVE_EOF) {
+ /* Propagate any potential error conditions
+ * to the caller. */
+ return ret;
+ }
+ }
+ } else {
+ /* In standard archives, we can just jump over the compressed
+ * stream. Each file in non-solid archives starts from an empty
+ * window buffer. */
+
+ if(ARCHIVE_OK != consume(a, rar->file.bytes_remaining)) {
+ return ARCHIVE_FATAL;
+ }
+
+ rar->file.bytes_remaining = 0;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int64_t rar5_seek_data(struct archive_read *a, int64_t offset,
+ int whence)
+{
+ (void) a;
+ (void) offset;
+ (void) whence;
+
+ /* We're a streaming unpacker, and we don't support seeking. */
+
+ return ARCHIVE_FATAL;
+}
+
+static int rar5_cleanup(struct archive_read *a) {
+ struct rar5* rar = get_context(a);
+
+ free(rar->cstate.window_buf);
+ free(rar->cstate.filtered_buf);
+
+ free(rar->vol.push_buf);
+
+ free_filters(rar);
+ cdeque_free(&rar->cstate.filters);
+
+ free(rar);
+ a->format->data = NULL;
+
+ return ARCHIVE_OK;
+}
+
+static int rar5_capabilities(struct archive_read * a) {
+ (void) a;
+ return 0;
+}
+
+static int rar5_has_encrypted_entries(struct archive_read *_a) {
+ (void) _a;
+
+ /* Unsupported for now. */
+ return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED;
+}
+
+static int rar5_init(struct rar5* rar) {
+ memset(rar, 0, sizeof(struct rar5));
+
+ if(CDE_OK != cdeque_init(&rar->cstate.filters, 8192))
+ return ARCHIVE_FATAL;
+
+ return ARCHIVE_OK;
+}
+
+int archive_read_support_format_rar5(struct archive *_a) {
+ struct archive_read* ar;
+ int ret;
+ struct rar5* rar;
+
+ if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar)))
+ return ret;
+
+ rar = malloc(sizeof(*rar));
+ if(rar == NULL) {
+ archive_set_error(&ar->archive, ENOMEM,
+ "Can't allocate rar5 data");
+ return ARCHIVE_FATAL;
+ }
+
+ if(ARCHIVE_OK != rar5_init(rar)) {
+ archive_set_error(&ar->archive, ENOMEM,
+ "Can't allocate rar5 filter buffer");
+ free(rar);
+ return ARCHIVE_FATAL;
+ }
+
+ ret = __archive_read_register_format(ar,
+ rar,
+ "rar5",
+ rar5_bid,
+ rar5_options,
+ rar5_read_header,
+ rar5_read_data,
+ rar5_read_data_skip,
+ rar5_seek_data,
+ rar5_cleanup,
+ rar5_capabilities,
+ rar5_has_encrypted_entries);
+
+ if(ret != ARCHIVE_OK) {
+ (void) rar5_cleanup(ar);
+ }
+
+ return ret;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_raw.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_raw.c
new file mode 100644
index 00000000000..ec0520b60a6
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_raw.c
@@ -0,0 +1,192 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_raw.c 201107 2009-12-28 03:25:33Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+struct raw_info {
+ int64_t offset; /* Current position in the file. */
+ int64_t unconsumed;
+ int end_of_file;
+};
+
+static int archive_read_format_raw_bid(struct archive_read *, int);
+static int archive_read_format_raw_cleanup(struct archive_read *);
+static int archive_read_format_raw_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_raw_read_data_skip(struct archive_read *);
+static int archive_read_format_raw_read_header(struct archive_read *,
+ struct archive_entry *);
+
+int
+archive_read_support_format_raw(struct archive *_a)
+{
+ struct raw_info *info;
+ struct archive_read *a = (struct archive_read *)_a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_raw");
+
+ info = (struct raw_info *)calloc(1, sizeof(*info));
+ if (info == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate raw_info data");
+ return (ARCHIVE_FATAL);
+ }
+
+ r = __archive_read_register_format(a,
+ info,
+ "raw",
+ archive_read_format_raw_bid,
+ NULL,
+ archive_read_format_raw_read_header,
+ archive_read_format_raw_read_data,
+ archive_read_format_raw_read_data_skip,
+ NULL,
+ archive_read_format_raw_cleanup,
+ NULL,
+ NULL);
+ if (r != ARCHIVE_OK)
+ free(info);
+ return (r);
+}
+
+/*
+ * Bid 1 if this is a non-empty file. Anyone who can really support
+ * this should outbid us, so it should generally be safe to use "raw"
+ * in conjunction with other formats. But, this could really confuse
+ * folks if there are bid errors or minor file damage, so we don't
+ * include "raw" as part of support_format_all().
+ */
+static int
+archive_read_format_raw_bid(struct archive_read *a, int best_bid)
+{
+ if (best_bid < 1 && __archive_read_ahead(a, 1, NULL) != NULL)
+ return (1);
+ return (-1);
+}
+
+/*
+ * Mock up a fake header.
+ */
+static int
+archive_read_format_raw_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct raw_info *info;
+
+ info = (struct raw_info *)(a->format->data);
+ if (info->end_of_file)
+ return (ARCHIVE_EOF);
+
+ a->archive.archive_format = ARCHIVE_FORMAT_RAW;
+ a->archive.archive_format_name = "raw";
+ archive_entry_set_pathname(entry, "data");
+ archive_entry_set_filetype(entry, AE_IFREG);
+ archive_entry_set_perm(entry, 0644);
+ /* I'm deliberately leaving most fields unset here. */
+
+ /* Let the filter fill out any fields it might have. */
+ return __archive_read_header(a, entry);
+}
+
+static int
+archive_read_format_raw_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ struct raw_info *info;
+ ssize_t avail;
+
+ info = (struct raw_info *)(a->format->data);
+
+ /* Consume the bytes we read last time. */
+ if (info->unconsumed) {
+ __archive_read_consume(a, info->unconsumed);
+ info->unconsumed = 0;
+ }
+
+ if (info->end_of_file)
+ return (ARCHIVE_EOF);
+
+ /* Get whatever bytes are immediately available. */
+ *buff = __archive_read_ahead(a, 1, &avail);
+ if (avail > 0) {
+ /* Return the bytes we just read */
+ *size = avail;
+ *offset = info->offset;
+ info->offset += *size;
+ info->unconsumed = avail;
+ return (ARCHIVE_OK);
+ } else if (0 == avail) {
+ /* Record and return end-of-file. */
+ info->end_of_file = 1;
+ *size = 0;
+ *offset = info->offset;
+ return (ARCHIVE_EOF);
+ } else {
+ /* Record and return an error. */
+ *size = 0;
+ *offset = info->offset;
+ return ((int)avail);
+ }
+}
+
+static int
+archive_read_format_raw_read_data_skip(struct archive_read *a)
+{
+ struct raw_info *info = (struct raw_info *)(a->format->data);
+
+ /* Consume the bytes we read last time. */
+ if (info->unconsumed) {
+ __archive_read_consume(a, info->unconsumed);
+ info->unconsumed = 0;
+ }
+ info->end_of_file = 1;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_raw_cleanup(struct archive_read *a)
+{
+ struct raw_info *info;
+
+ info = (struct raw_info *)(a->format->data);
+ free(info);
+ a->format->data = NULL;
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_tar.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_tar.c
new file mode 100644
index 00000000000..93c3fd58573
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_tar.c
@@ -0,0 +1,2946 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2016 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_tar.c 201161 2009-12-29 05:44:39Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stddef.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_acl_private.h" /* For ACL parsing routines. */
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#define tar_min(a,b) ((a) < (b) ? (a) : (b))
+
+/*
+ * Layout of POSIX 'ustar' tar header.
+ */
+struct archive_entry_header_ustar {
+ char name[100];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char checksum[8];
+ char typeflag[1];
+ char linkname[100]; /* "old format" header ends here */
+ char magic[6]; /* For POSIX: "ustar\0" */
+ char version[2]; /* For POSIX: "00" */
+ char uname[32];
+ char gname[32];
+ char rdevmajor[8];
+ char rdevminor[8];
+ char prefix[155];
+};
+
+/*
+ * Structure of GNU tar header
+ */
+struct gnu_sparse {
+ char offset[12];
+ char numbytes[12];
+};
+
+struct archive_entry_header_gnutar {
+ char name[100];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char checksum[8];
+ char typeflag[1];
+ char linkname[100];
+ char magic[8]; /* "ustar \0" (note blank/blank/null at end) */
+ char uname[32];
+ char gname[32];
+ char rdevmajor[8];
+ char rdevminor[8];
+ char atime[12];
+ char ctime[12];
+ char offset[12];
+ char longnames[4];
+ char unused[1];
+ struct gnu_sparse sparse[4];
+ char isextended[1];
+ char realsize[12];
+ /*
+ * Old GNU format doesn't use POSIX 'prefix' field; they use
+ * the 'L' (longname) entry instead.
+ */
+};
+
+/*
+ * Data specific to this format.
+ */
+struct sparse_block {
+ struct sparse_block *next;
+ int64_t offset;
+ int64_t remaining;
+ int hole;
+};
+
+struct tar {
+ struct archive_string acl_text;
+ struct archive_string entry_pathname;
+ /* For "GNU.sparse.name" and other similar path extensions. */
+ struct archive_string entry_pathname_override;
+ struct archive_string entry_linkpath;
+ struct archive_string entry_uname;
+ struct archive_string entry_gname;
+ struct archive_string longlink;
+ struct archive_string longname;
+ struct archive_string pax_header;
+ struct archive_string pax_global;
+ struct archive_string line;
+ int pax_hdrcharset_binary;
+ int header_recursion_depth;
+ int64_t entry_bytes_remaining;
+ int64_t entry_offset;
+ int64_t entry_padding;
+ int64_t entry_bytes_unconsumed;
+ int64_t realsize;
+ int sparse_allowed;
+ struct sparse_block *sparse_list;
+ struct sparse_block *sparse_last;
+ int64_t sparse_offset;
+ int64_t sparse_numbytes;
+ int sparse_gnu_major;
+ int sparse_gnu_minor;
+ char sparse_gnu_pending;
+
+ struct archive_string localname;
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv;
+ struct archive_string_conv *sconv_acl;
+ struct archive_string_conv *sconv_default;
+ int init_default_conversion;
+ int compat_2x;
+ int process_mac_extensions;
+ int read_concatenated_archives;
+ int realsize_override;
+};
+
+static int archive_block_is_null(const char *p);
+static char *base64_decode(const char *, size_t, size_t *);
+static int gnu_add_sparse_entry(struct archive_read *, struct tar *,
+ int64_t offset, int64_t remaining);
+
+static void gnu_clear_sparse_list(struct tar *);
+static int gnu_sparse_old_read(struct archive_read *, struct tar *,
+ const struct archive_entry_header_gnutar *header, size_t *);
+static int gnu_sparse_old_parse(struct archive_read *, struct tar *,
+ const struct gnu_sparse *sparse, int length);
+static int gnu_sparse_01_parse(struct archive_read *, struct tar *,
+ const char *);
+static ssize_t gnu_sparse_10_read(struct archive_read *, struct tar *,
+ size_t *);
+static int header_Solaris_ACL(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *, size_t *);
+static int header_common(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *);
+static int header_old_tar(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *);
+static int header_pax_extensions(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *, size_t *);
+static int header_pax_global(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *h, size_t *);
+static int header_longlink(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *h, size_t *);
+static int header_longname(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *h, size_t *);
+static int read_mac_metadata_blob(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *h, size_t *);
+static int header_volume(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *h, size_t *);
+static int header_ustar(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *h);
+static int header_gnutar(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *h, size_t *);
+static int archive_read_format_tar_bid(struct archive_read *, int);
+static int archive_read_format_tar_options(struct archive_read *,
+ const char *, const char *);
+static int archive_read_format_tar_cleanup(struct archive_read *);
+static int archive_read_format_tar_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset);
+static int archive_read_format_tar_skip(struct archive_read *a);
+static int archive_read_format_tar_read_header(struct archive_read *,
+ struct archive_entry *);
+static int checksum(struct archive_read *, const void *);
+static int pax_attribute(struct archive_read *, struct tar *,
+ struct archive_entry *, const char *key, const char *value,
+ size_t value_length);
+static int pax_attribute_acl(struct archive_read *, struct tar *,
+ struct archive_entry *, const char *, int);
+static int pax_attribute_xattr(struct archive_entry *, const char *,
+ const char *);
+static int pax_header(struct archive_read *, struct tar *,
+ struct archive_entry *, struct archive_string *);
+static void pax_time(const char *, int64_t *sec, long *nanos);
+static ssize_t readline(struct archive_read *, struct tar *, const char **,
+ ssize_t limit, size_t *);
+static int read_body_to_string(struct archive_read *, struct tar *,
+ struct archive_string *, const void *h, size_t *);
+static int solaris_sparse_parse(struct archive_read *, struct tar *,
+ struct archive_entry *, const char *);
+static int64_t tar_atol(const char *, size_t);
+static int64_t tar_atol10(const char *, size_t);
+static int64_t tar_atol256(const char *, size_t);
+static int64_t tar_atol8(const char *, size_t);
+static int tar_read_header(struct archive_read *, struct tar *,
+ struct archive_entry *, size_t *);
+static int tohex(int c);
+static char *url_decode(const char *);
+static void tar_flush_unconsumed(struct archive_read *, size_t *);
+
+
+int
+archive_read_support_format_gnutar(struct archive *a)
+{
+ archive_check_magic(a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_gnutar");
+ return (archive_read_support_format_tar(a));
+}
+
+
+int
+archive_read_support_format_tar(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct tar *tar;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_tar");
+
+ tar = (struct tar *)calloc(1, sizeof(*tar));
+ if (tar == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate tar data");
+ return (ARCHIVE_FATAL);
+ }
+#ifdef HAVE_COPYFILE_H
+ /* Set this by default on Mac OS. */
+ tar->process_mac_extensions = 1;
+#endif
+
+ r = __archive_read_register_format(a, tar, "tar",
+ archive_read_format_tar_bid,
+ archive_read_format_tar_options,
+ archive_read_format_tar_read_header,
+ archive_read_format_tar_read_data,
+ archive_read_format_tar_skip,
+ NULL,
+ archive_read_format_tar_cleanup,
+ NULL,
+ NULL);
+
+ if (r != ARCHIVE_OK)
+ free(tar);
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_tar_cleanup(struct archive_read *a)
+{
+ struct tar *tar;
+
+ tar = (struct tar *)(a->format->data);
+ gnu_clear_sparse_list(tar);
+ archive_string_free(&tar->acl_text);
+ archive_string_free(&tar->entry_pathname);
+ archive_string_free(&tar->entry_pathname_override);
+ archive_string_free(&tar->entry_linkpath);
+ archive_string_free(&tar->entry_uname);
+ archive_string_free(&tar->entry_gname);
+ archive_string_free(&tar->line);
+ archive_string_free(&tar->pax_global);
+ archive_string_free(&tar->pax_header);
+ archive_string_free(&tar->longname);
+ archive_string_free(&tar->longlink);
+ archive_string_free(&tar->localname);
+ free(tar);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Validate number field
+ *
+ * This has to be pretty lenient in order to accommodate the enormous
+ * variety of tar writers in the world:
+ * = POSIX (IEEE Std 1003.1-1988) ustar requires octal values with leading
+ * zeros and allows fields to be terminated with space or null characters
+ * = Many writers use different termination (in particular, libarchive
+ * omits terminator bytes to squeeze one or two more digits)
+ * = Many writers pad with space and omit leading zeros
+ * = GNU tar and star write base-256 values if numbers are too
+ * big to be represented in octal
+ *
+ * Examples of specific tar headers that we should support:
+ * = Perl Archive::Tar terminates uid, gid, devminor and devmajor with two
+ * null bytes, pads size with spaces and other numeric fields with zeroes
+ * = plexus-archiver prior to 2.6.3 (before switching to commons-compress)
+ * may have uid and gid fields filled with spaces without any octal digits
+ * at all and pads all numeric fields with spaces
+ *
+ * This should tolerate all variants in use. It will reject a field
+ * where the writer just left garbage after a trailing NUL.
+ */
+static int
+validate_number_field(const char* p_field, size_t i_size)
+{
+ unsigned char marker = (unsigned char)p_field[0];
+ if (marker == 128 || marker == 255 || marker == 0) {
+ /* Base-256 marker, there's nothing we can check. */
+ return 1;
+ } else {
+ /* Must be octal */
+ size_t i = 0;
+ /* Skip any leading spaces */
+ while (i < i_size && p_field[i] == ' ') {
+ ++i;
+ }
+ /* Skip octal digits. */
+ while (i < i_size && p_field[i] >= '0' && p_field[i] <= '7') {
+ ++i;
+ }
+ /* Any remaining characters must be space or NUL padding. */
+ while (i < i_size) {
+ if (p_field[i] != ' ' && p_field[i] != 0) {
+ return 0;
+ }
+ ++i;
+ }
+ return 1;
+ }
+}
+
+static int
+archive_read_format_tar_bid(struct archive_read *a, int best_bid)
+{
+ int bid;
+ const char *h;
+ const struct archive_entry_header_ustar *header;
+
+ (void)best_bid; /* UNUSED */
+
+ bid = 0;
+
+ /* Now let's look at the actual header and see if it matches. */
+ h = __archive_read_ahead(a, 512, NULL);
+ if (h == NULL)
+ return (-1);
+
+ /* If it's an end-of-archive mark, we can handle it. */
+ if (h[0] == 0 && archive_block_is_null(h)) {
+ /*
+ * Usually, I bid the number of bits verified, but
+ * in this case, 4096 seems excessive so I picked 10 as
+ * an arbitrary but reasonable-seeming value.
+ */
+ return (10);
+ }
+
+ /* If it's not an end-of-archive mark, it must have a valid checksum.*/
+ if (!checksum(a, h))
+ return (0);
+ bid += 48; /* Checksum is usually 6 octal digits. */
+
+ header = (const struct archive_entry_header_ustar *)h;
+
+ /* Recognize POSIX formats. */
+ if ((memcmp(header->magic, "ustar\0", 6) == 0)
+ && (memcmp(header->version, "00", 2) == 0))
+ bid += 56;
+
+ /* Recognize GNU tar format. */
+ if ((memcmp(header->magic, "ustar ", 6) == 0)
+ && (memcmp(header->version, " \0", 2) == 0))
+ bid += 56;
+
+ /* Type flag must be null, digit or A-Z, a-z. */
+ if (header->typeflag[0] != 0 &&
+ !( header->typeflag[0] >= '0' && header->typeflag[0] <= '9') &&
+ !( header->typeflag[0] >= 'A' && header->typeflag[0] <= 'Z') &&
+ !( header->typeflag[0] >= 'a' && header->typeflag[0] <= 'z') )
+ return (0);
+ bid += 2; /* 6 bits of variation in an 8-bit field leaves 2 bits. */
+
+ /*
+ * Check format of mode/uid/gid/mtime/size/rdevmajor/rdevminor fields.
+ */
+ if (validate_number_field(header->mode, sizeof(header->mode)) == 0
+ || validate_number_field(header->uid, sizeof(header->uid)) == 0
+ || validate_number_field(header->gid, sizeof(header->gid)) == 0
+ || validate_number_field(header->mtime, sizeof(header->mtime)) == 0
+ || validate_number_field(header->size, sizeof(header->size)) == 0
+ || validate_number_field(header->rdevmajor, sizeof(header->rdevmajor)) == 0
+ || validate_number_field(header->rdevminor, sizeof(header->rdevminor)) == 0) {
+ bid = 0;
+ }
+
+ return (bid);
+}
+
+static int
+archive_read_format_tar_options(struct archive_read *a,
+ const char *key, const char *val)
+{
+ struct tar *tar;
+ int ret = ARCHIVE_FAILED;
+
+ tar = (struct tar *)(a->format->data);
+ if (strcmp(key, "compat-2x") == 0) {
+ /* Handle UTF-8 filenames as libarchive 2.x */
+ tar->compat_2x = (val != NULL && val[0] != 0);
+ tar->init_default_conversion = tar->compat_2x;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "tar: hdrcharset option needs a character-set name");
+ else {
+ tar->opt_sconv =
+ archive_string_conversion_from_charset(
+ &a->archive, val, 0);
+ if (tar->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ } else if (strcmp(key, "mac-ext") == 0) {
+ tar->process_mac_extensions = (val != NULL && val[0] != 0);
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "read_concatenated_archives") == 0) {
+ tar->read_concatenated_archives = (val != NULL && val[0] != 0);
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+/* utility function- this exists to centralize the logic of tracking
+ * how much unconsumed data we have floating around, and to consume
+ * anything outstanding since we're going to do read_aheads
+ */
+static void
+tar_flush_unconsumed(struct archive_read *a, size_t *unconsumed)
+{
+ if (*unconsumed) {
+/*
+ void *data = (void *)__archive_read_ahead(a, *unconsumed, NULL);
+ * this block of code is to poison claimed unconsumed space, ensuring
+ * things break if it is in use still.
+ * currently it WILL break things, so enable it only for debugging this issue
+ if (data) {
+ memset(data, 0xff, *unconsumed);
+ }
+*/
+ __archive_read_consume(a, *unconsumed);
+ *unconsumed = 0;
+ }
+}
+
+/*
+ * The function invoked by archive_read_next_header(). This
+ * just sets up a few things and then calls the internal
+ * tar_read_header() function below.
+ */
+static int
+archive_read_format_tar_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ /*
+ * When converting tar archives to cpio archives, it is
+ * essential that each distinct file have a distinct inode
+ * number. To simplify this, we keep a static count here to
+ * assign fake dev/inode numbers to each tar entry. Note that
+ * pax format archives may overwrite this with something more
+ * useful.
+ *
+ * Ideally, we would track every file read from the archive so
+ * that we could assign the same dev/ino pair to hardlinks,
+ * but the memory required to store a complete lookup table is
+ * probably not worthwhile just to support the relatively
+ * obscure tar->cpio conversion case.
+ */
+ static int default_inode;
+ static int default_dev;
+ struct tar *tar;
+ const char *p;
+ const wchar_t *wp;
+ int r;
+ size_t l, unconsumed = 0;
+
+ /* Assign default device/inode values. */
+ archive_entry_set_dev(entry, 1 + default_dev); /* Don't use zero. */
+ archive_entry_set_ino(entry, ++default_inode); /* Don't use zero. */
+ /* Limit generated st_ino number to 16 bits. */
+ if (default_inode >= 0xffff) {
+ ++default_dev;
+ default_inode = 0;
+ }
+
+ tar = (struct tar *)(a->format->data);
+ tar->entry_offset = 0;
+ gnu_clear_sparse_list(tar);
+ tar->realsize = -1; /* Mark this as "unset" */
+ tar->realsize_override = 0;
+
+ /* Setup default string conversion. */
+ tar->sconv = tar->opt_sconv;
+ if (tar->sconv == NULL) {
+ if (!tar->init_default_conversion) {
+ tar->sconv_default =
+ archive_string_default_conversion_for_read(&(a->archive));
+ tar->init_default_conversion = 1;
+ }
+ tar->sconv = tar->sconv_default;
+ }
+
+ r = tar_read_header(a, tar, entry, &unconsumed);
+
+ tar_flush_unconsumed(a, &unconsumed);
+
+ /*
+ * "non-sparse" files are really just sparse files with
+ * a single block.
+ */
+ if (tar->sparse_list == NULL) {
+ if (gnu_add_sparse_entry(a, tar, 0, tar->entry_bytes_remaining)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } else {
+ struct sparse_block *sb;
+
+ for (sb = tar->sparse_list; sb != NULL; sb = sb->next) {
+ if (!sb->hole)
+ archive_entry_sparse_add_entry(entry,
+ sb->offset, sb->remaining);
+ }
+ }
+
+ if (r == ARCHIVE_OK && archive_entry_filetype(entry) == AE_IFREG) {
+ /*
+ * "Regular" entry with trailing '/' is really
+ * directory: This is needed for certain old tar
+ * variants and even for some broken newer ones.
+ */
+ if ((wp = archive_entry_pathname_w(entry)) != NULL) {
+ l = wcslen(wp);
+ if (l > 0 && wp[l - 1] == L'/') {
+ archive_entry_set_filetype(entry, AE_IFDIR);
+ tar->entry_bytes_remaining = 0;
+ tar->entry_padding = 0;
+ }
+ } else if ((p = archive_entry_pathname(entry)) != NULL) {
+ l = strlen(p);
+ if (l > 0 && p[l - 1] == '/') {
+ archive_entry_set_filetype(entry, AE_IFDIR);
+ tar->entry_bytes_remaining = 0;
+ tar->entry_padding = 0;
+ }
+ }
+ }
+ return (r);
+}
+
+static int
+archive_read_format_tar_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ ssize_t bytes_read;
+ struct tar *tar;
+ struct sparse_block *p;
+
+ tar = (struct tar *)(a->format->data);
+
+ for (;;) {
+ /* Remove exhausted entries from sparse list. */
+ while (tar->sparse_list != NULL &&
+ tar->sparse_list->remaining == 0) {
+ p = tar->sparse_list;
+ tar->sparse_list = p->next;
+ free(p);
+ }
+
+ if (tar->entry_bytes_unconsumed) {
+ __archive_read_consume(a, tar->entry_bytes_unconsumed);
+ tar->entry_bytes_unconsumed = 0;
+ }
+
+ /* If we're at end of file, return EOF. */
+ if (tar->sparse_list == NULL ||
+ tar->entry_bytes_remaining == 0) {
+ if (__archive_read_consume(a, tar->entry_padding) < 0)
+ return (ARCHIVE_FATAL);
+ tar->entry_padding = 0;
+ *buff = NULL;
+ *size = 0;
+ *offset = tar->realsize;
+ return (ARCHIVE_EOF);
+ }
+
+ *buff = __archive_read_ahead(a, 1, &bytes_read);
+ if (bytes_read < 0)
+ return (ARCHIVE_FATAL);
+ if (*buff == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Truncated tar archive");
+ return (ARCHIVE_FATAL);
+ }
+ if (bytes_read > tar->entry_bytes_remaining)
+ bytes_read = (ssize_t)tar->entry_bytes_remaining;
+ /* Don't read more than is available in the
+ * current sparse block. */
+ if (tar->sparse_list->remaining < bytes_read)
+ bytes_read = (ssize_t)tar->sparse_list->remaining;
+ *size = bytes_read;
+ *offset = tar->sparse_list->offset;
+ tar->sparse_list->remaining -= bytes_read;
+ tar->sparse_list->offset += bytes_read;
+ tar->entry_bytes_remaining -= bytes_read;
+ tar->entry_bytes_unconsumed = bytes_read;
+
+ if (!tar->sparse_list->hole)
+ return (ARCHIVE_OK);
+ /* Current is hole data and skip this. */
+ }
+}
+
+static int
+archive_read_format_tar_skip(struct archive_read *a)
+{
+ int64_t bytes_skipped;
+ int64_t request;
+ struct sparse_block *p;
+ struct tar* tar;
+
+ tar = (struct tar *)(a->format->data);
+
+ /* Do not consume the hole of a sparse file. */
+ request = 0;
+ for (p = tar->sparse_list; p != NULL; p = p->next) {
+ if (!p->hole) {
+ if (p->remaining >= INT64_MAX - request) {
+ return ARCHIVE_FATAL;
+ }
+ request += p->remaining;
+ }
+ }
+ if (request > tar->entry_bytes_remaining)
+ request = tar->entry_bytes_remaining;
+ request += tar->entry_padding + tar->entry_bytes_unconsumed;
+
+ bytes_skipped = __archive_read_consume(a, request);
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+
+ tar->entry_bytes_remaining = 0;
+ tar->entry_bytes_unconsumed = 0;
+ tar->entry_padding = 0;
+
+ /* Free the sparse list. */
+ gnu_clear_sparse_list(tar);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * This function recursively interprets all of the headers associated
+ * with a single entry.
+ */
+static int
+tar_read_header(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, size_t *unconsumed)
+{
+ ssize_t bytes;
+ int err, eof_vol_header;
+ const char *h;
+ const struct archive_entry_header_ustar *header;
+ const struct archive_entry_header_gnutar *gnuheader;
+
+ eof_vol_header = 0;
+
+ /* Loop until we find a workable header record. */
+ for (;;) {
+ tar_flush_unconsumed(a, unconsumed);
+
+ /* Read 512-byte header record */
+ h = __archive_read_ahead(a, 512, &bytes);
+ if (bytes < 0)
+ return ((int)bytes);
+ if (bytes == 0) { /* EOF at a block boundary. */
+ /* Some writers do omit the block of nulls. <sigh> */
+ return (ARCHIVE_EOF);
+ }
+ if (bytes < 512) { /* Short block at EOF; this is bad. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated tar archive");
+ return (ARCHIVE_FATAL);
+ }
+ *unconsumed = 512;
+
+ /* Header is workable if it's not an end-of-archive mark. */
+ if (h[0] != 0 || !archive_block_is_null(h))
+ break;
+
+ /* Ensure format is set for archives with only null blocks. */
+ if (a->archive.archive_format_name == NULL) {
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR;
+ a->archive.archive_format_name = "tar";
+ }
+
+ if (!tar->read_concatenated_archives) {
+ /* Try to consume a second all-null record, as well. */
+ tar_flush_unconsumed(a, unconsumed);
+ h = __archive_read_ahead(a, 512, NULL);
+ if (h != NULL && h[0] == 0 && archive_block_is_null(h))
+ __archive_read_consume(a, 512);
+ archive_clear_error(&a->archive);
+ return (ARCHIVE_EOF);
+ }
+
+ /*
+ * We're reading concatenated archives, ignore this block and
+ * loop to get the next.
+ */
+ }
+
+ /*
+ * Note: If the checksum fails and we return ARCHIVE_RETRY,
+ * then the client is likely to just retry. This is a very
+ * crude way to search for the next valid header!
+ *
+ * TODO: Improve this by implementing a real header scan.
+ */
+ if (!checksum(a, h)) {
+ tar_flush_unconsumed(a, unconsumed);
+ archive_set_error(&a->archive, EINVAL, "Damaged tar archive");
+ return (ARCHIVE_RETRY); /* Retryable: Invalid header */
+ }
+
+ if (++tar->header_recursion_depth > 32) {
+ tar_flush_unconsumed(a, unconsumed);
+ archive_set_error(&a->archive, EINVAL, "Too many special headers");
+ return (ARCHIVE_WARN);
+ }
+
+ /* Determine the format variant. */
+ header = (const struct archive_entry_header_ustar *)h;
+
+ switch(header->typeflag[0]) {
+ case 'A': /* Solaris tar ACL */
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+ a->archive.archive_format_name = "Solaris tar";
+ err = header_Solaris_ACL(a, tar, entry, h, unconsumed);
+ break;
+ case 'g': /* POSIX-standard 'g' header. */
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+ a->archive.archive_format_name = "POSIX pax interchange format";
+ err = header_pax_global(a, tar, entry, h, unconsumed);
+ if (err == ARCHIVE_EOF)
+ return (err);
+ break;
+ case 'K': /* Long link name (GNU tar, others) */
+ err = header_longlink(a, tar, entry, h, unconsumed);
+ break;
+ case 'L': /* Long filename (GNU tar, others) */
+ err = header_longname(a, tar, entry, h, unconsumed);
+ break;
+ case 'V': /* GNU volume header */
+ err = header_volume(a, tar, entry, h, unconsumed);
+ if (err == ARCHIVE_EOF)
+ eof_vol_header = 1;
+ break;
+ case 'X': /* Used by SUN tar; same as 'x'. */
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+ a->archive.archive_format_name =
+ "POSIX pax interchange format (Sun variant)";
+ err = header_pax_extensions(a, tar, entry, h, unconsumed);
+ break;
+ case 'x': /* POSIX-standard 'x' header. */
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+ a->archive.archive_format_name = "POSIX pax interchange format";
+ err = header_pax_extensions(a, tar, entry, h, unconsumed);
+ break;
+ default:
+ gnuheader = (const struct archive_entry_header_gnutar *)h;
+ if (memcmp(gnuheader->magic, "ustar \0", 8) == 0) {
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR;
+ a->archive.archive_format_name = "GNU tar format";
+ err = header_gnutar(a, tar, entry, h, unconsumed);
+ } else if (memcmp(header->magic, "ustar", 5) == 0) {
+ if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) {
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR;
+ a->archive.archive_format_name = "POSIX ustar format";
+ }
+ err = header_ustar(a, tar, entry, h);
+ } else {
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR;
+ a->archive.archive_format_name = "tar (non-POSIX)";
+ err = header_old_tar(a, tar, entry, h);
+ }
+ }
+ if (err == ARCHIVE_FATAL)
+ return (err);
+
+ tar_flush_unconsumed(a, unconsumed);
+
+ h = NULL;
+ header = NULL;
+
+ --tar->header_recursion_depth;
+ /* Yuck. Apple's design here ends up storing long pathname
+ * extensions for both the AppleDouble extension entry and the
+ * regular entry.
+ */
+ if ((err == ARCHIVE_WARN || err == ARCHIVE_OK) &&
+ tar->header_recursion_depth == 0 &&
+ tar->process_mac_extensions) {
+ int err2 = read_mac_metadata_blob(a, tar, entry, h, unconsumed);
+ if (err2 < err)
+ err = err2;
+ }
+
+ /* We return warnings or success as-is. Anything else is fatal. */
+ if (err == ARCHIVE_WARN || err == ARCHIVE_OK) {
+ if (tar->sparse_gnu_pending) {
+ if (tar->sparse_gnu_major == 1 &&
+ tar->sparse_gnu_minor == 0) {
+ ssize_t bytes_read;
+
+ tar->sparse_gnu_pending = 0;
+ /* Read initial sparse map. */
+ bytes_read = gnu_sparse_10_read(a, tar, unconsumed);
+ if (bytes_read < 0)
+ return ((int)bytes_read);
+ tar->entry_bytes_remaining -= bytes_read;
+ } else {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Unrecognized GNU sparse file format");
+ return (ARCHIVE_WARN);
+ }
+ tar->sparse_gnu_pending = 0;
+ }
+ return (err);
+ }
+ if (err == ARCHIVE_EOF) {
+ if (!eof_vol_header) {
+ /* EOF when recursively reading a header is bad. */
+ archive_set_error(&a->archive, EINVAL,
+ "Damaged tar archive");
+ } else {
+ /* If we encounter just a GNU volume header treat
+ * this situation as an empty archive */
+ return (ARCHIVE_EOF);
+ }
+ }
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Return true if block checksum is correct.
+ */
+static int
+checksum(struct archive_read *a, const void *h)
+{
+ const unsigned char *bytes;
+ const struct archive_entry_header_ustar *header;
+ int check, sum;
+ size_t i;
+
+ (void)a; /* UNUSED */
+ bytes = (const unsigned char *)h;
+ header = (const struct archive_entry_header_ustar *)h;
+
+ /* Checksum field must hold an octal number */
+ for (i = 0; i < sizeof(header->checksum); ++i) {
+ char c = header->checksum[i];
+ if (c != ' ' && c != '\0' && (c < '0' || c > '7'))
+ return 0;
+ }
+
+ /*
+ * Test the checksum. Note that POSIX specifies _unsigned_
+ * bytes for this calculation.
+ */
+ sum = (int)tar_atol(header->checksum, sizeof(header->checksum));
+ check = 0;
+ for (i = 0; i < 148; i++)
+ check += (unsigned char)bytes[i];
+ for (; i < 156; i++)
+ check += 32;
+ for (; i < 512; i++)
+ check += (unsigned char)bytes[i];
+ if (sum == check)
+ return (1);
+
+ /*
+ * Repeat test with _signed_ bytes, just in case this archive
+ * was created by an old BSD, Solaris, or HP-UX tar with a
+ * broken checksum calculation.
+ */
+ check = 0;
+ for (i = 0; i < 148; i++)
+ check += (signed char)bytes[i];
+ for (; i < 156; i++)
+ check += 32;
+ for (; i < 512; i++)
+ check += (signed char)bytes[i];
+ if (sum == check)
+ return (1);
+
+ return (0);
+}
+
+/*
+ * Return true if this block contains only nulls.
+ */
+static int
+archive_block_is_null(const char *p)
+{
+ unsigned i;
+
+ for (i = 0; i < 512; i++)
+ if (*p++)
+ return (0);
+ return (1);
+}
+
+/*
+ * Interpret 'A' Solaris ACL header
+ */
+static int
+header_Solaris_ACL(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+ const struct archive_entry_header_ustar *header;
+ size_t size;
+ int err, acl_type;
+ int64_t type;
+ char *acl, *p;
+
+ /*
+ * read_body_to_string adds a NUL terminator, but we need a little
+ * more to make sure that we don't overrun acl_text later.
+ */
+ header = (const struct archive_entry_header_ustar *)h;
+ size = (size_t)tar_atol(header->size, sizeof(header->size));
+ err = read_body_to_string(a, tar, &(tar->acl_text), h, unconsumed);
+ if (err != ARCHIVE_OK)
+ return (err);
+
+ /* Recursively read next header */
+ err = tar_read_header(a, tar, entry, unconsumed);
+ if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
+ return (err);
+
+ /* TODO: Examine the first characters to see if this
+ * is an AIX ACL descriptor. We'll likely never support
+ * them, but it would be polite to recognize and warn when
+ * we do see them. */
+
+ /* Leading octal number indicates ACL type and number of entries. */
+ p = acl = tar->acl_text.s;
+ type = 0;
+ while (*p != '\0' && p < acl + size) {
+ if (*p < '0' || *p > '7') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Malformed Solaris ACL attribute (invalid digit)");
+ return(ARCHIVE_WARN);
+ }
+ type <<= 3;
+ type += *p - '0';
+ if (type > 077777777) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Malformed Solaris ACL attribute (count too large)");
+ return (ARCHIVE_WARN);
+ }
+ p++;
+ }
+ switch ((int)type & ~0777777) {
+ case 01000000:
+ /* POSIX.1e ACL */
+ acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+ break;
+ case 03000000:
+ /* NFSv4 ACL */
+ acl_type = ARCHIVE_ENTRY_ACL_TYPE_NFS4;
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Malformed Solaris ACL attribute (unsupported type %o)",
+ (int)type);
+ return (ARCHIVE_WARN);
+ }
+ p++;
+
+ if (p >= acl + size) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Malformed Solaris ACL attribute (body overflow)");
+ return(ARCHIVE_WARN);
+ }
+
+ /* ACL text is null-terminated; find the end. */
+ size -= (p - acl);
+ acl = p;
+
+ while (*p != '\0' && p < acl + size)
+ p++;
+
+ if (tar->sconv_acl == NULL) {
+ tar->sconv_acl = archive_string_conversion_from_charset(
+ &(a->archive), "UTF-8", 1);
+ if (tar->sconv_acl == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ archive_strncpy(&(tar->localname), acl, p - acl);
+ err = archive_acl_from_text_l(archive_entry_acl(entry),
+ tar->localname.s, acl_type, tar->sconv_acl);
+ if (err != ARCHIVE_OK) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for ACL");
+ } else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Malformed Solaris ACL attribute (unparsable)");
+ }
+ return (err);
+}
+
+/*
+ * Interpret 'K' long linkname header.
+ */
+static int
+header_longlink(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+ int err;
+
+ err = read_body_to_string(a, tar, &(tar->longlink), h, unconsumed);
+ if (err != ARCHIVE_OK)
+ return (err);
+ err = tar_read_header(a, tar, entry, unconsumed);
+ if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
+ return (err);
+ /* Set symlink if symlink already set, else hardlink. */
+ archive_entry_copy_link(entry, tar->longlink.s);
+ return (ARCHIVE_OK);
+}
+
+static int
+set_conversion_failed_error(struct archive_read *a,
+ struct archive_string_conv *sconv, const char *name)
+{
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for %s", name);
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "%s can't be converted from %s to current locale.",
+ name, archive_string_conversion_charset_name(sconv));
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Interpret 'L' long filename header.
+ */
+static int
+header_longname(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+ int err;
+
+ err = read_body_to_string(a, tar, &(tar->longname), h, unconsumed);
+ if (err != ARCHIVE_OK)
+ return (err);
+ /* Read and parse "real" header, then override name. */
+ err = tar_read_header(a, tar, entry, unconsumed);
+ if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
+ return (err);
+ if (archive_entry_copy_pathname_l(entry, tar->longname.s,
+ archive_strlen(&(tar->longname)), tar->sconv) != 0)
+ err = set_conversion_failed_error(a, tar->sconv, "Pathname");
+ return (err);
+}
+
+
+/*
+ * Interpret 'V' GNU tar volume header.
+ */
+static int
+header_volume(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+ (void)h;
+
+ /* Just skip this and read the next header. */
+ return (tar_read_header(a, tar, entry, unconsumed));
+}
+
+/*
+ * Read body of an archive entry into an archive_string object.
+ */
+static int
+read_body_to_string(struct archive_read *a, struct tar *tar,
+ struct archive_string *as, const void *h, size_t *unconsumed)
+{
+ int64_t size;
+ const struct archive_entry_header_ustar *header;
+ const void *src;
+
+ (void)tar; /* UNUSED */
+ header = (const struct archive_entry_header_ustar *)h;
+ size = tar_atol(header->size, sizeof(header->size));
+ if ((size > 1048576) || (size < 0)) {
+ archive_set_error(&a->archive, EINVAL,
+ "Special header too large");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Fail if we can't make our buffer big enough. */
+ if (archive_string_ensure(as, (size_t)size+1) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ tar_flush_unconsumed(a, unconsumed);
+
+ /* Read the body into the string. */
+ *unconsumed = (size_t)((size + 511) & ~ 511);
+ src = __archive_read_ahead(a, *unconsumed, NULL);
+ if (src == NULL) {
+ *unconsumed = 0;
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(as->s, src, (size_t)size);
+ as->s[size] = '\0';
+ as->length = (size_t)size;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Parse out common header elements.
+ *
+ * This would be the same as header_old_tar, except that the
+ * filename is handled slightly differently for old and POSIX
+ * entries (POSIX entries support a 'prefix'). This factoring
+ * allows header_old_tar and header_ustar
+ * to handle filenames differently, while still putting most of the
+ * common parsing into one place.
+ */
+static int
+header_common(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h)
+{
+ const struct archive_entry_header_ustar *header;
+ char tartype;
+ int err = ARCHIVE_OK;
+
+ header = (const struct archive_entry_header_ustar *)h;
+ if (header->linkname[0])
+ archive_strncpy(&(tar->entry_linkpath),
+ header->linkname, sizeof(header->linkname));
+ else
+ archive_string_empty(&(tar->entry_linkpath));
+
+ /* Parse out the numeric fields (all are octal) */
+ archive_entry_set_mode(entry,
+ (mode_t)tar_atol(header->mode, sizeof(header->mode)));
+ archive_entry_set_uid(entry, tar_atol(header->uid, sizeof(header->uid)));
+ archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid)));
+ tar->entry_bytes_remaining = tar_atol(header->size, sizeof(header->size));
+ if (tar->entry_bytes_remaining < 0) {
+ tar->entry_bytes_remaining = 0;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Tar entry has negative size");
+ return (ARCHIVE_FATAL);
+ }
+ if (tar->entry_bytes_remaining == INT64_MAX) {
+ /* Note: tar_atol returns INT64_MAX on overflow */
+ tar->entry_bytes_remaining = 0;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Tar entry size overflow");
+ return (ARCHIVE_FATAL);
+ }
+ tar->realsize = tar->entry_bytes_remaining;
+ archive_entry_set_size(entry, tar->entry_bytes_remaining);
+ archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0);
+
+ /* Handle the tar type flag appropriately. */
+ tartype = header->typeflag[0];
+
+ switch (tartype) {
+ case '1': /* Hard link */
+ if (archive_entry_copy_hardlink_l(entry, tar->entry_linkpath.s,
+ archive_strlen(&(tar->entry_linkpath)), tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv,
+ "Linkname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+ /*
+ * The following may seem odd, but: Technically, tar
+ * does not store the file type for a "hard link"
+ * entry, only the fact that it is a hard link. So, I
+ * leave the type zero normally. But, pax interchange
+ * format allows hard links to have data, which
+ * implies that the underlying entry is a regular
+ * file.
+ */
+ if (archive_entry_size(entry) > 0)
+ archive_entry_set_filetype(entry, AE_IFREG);
+
+ /*
+ * A tricky point: Traditionally, tar readers have
+ * ignored the size field when reading hardlink
+ * entries, and some writers put non-zero sizes even
+ * though the body is empty. POSIX blessed this
+ * convention in the 1988 standard, but broke with
+ * this tradition in 2001 by permitting hardlink
+ * entries to store valid bodies in pax interchange
+ * format, but not in ustar format. Since there is no
+ * hard and fast way to distinguish pax interchange
+ * from earlier archives (the 'x' and 'g' entries are
+ * optional, after all), we need a heuristic.
+ */
+ if (archive_entry_size(entry) == 0) {
+ /* If the size is already zero, we're done. */
+ } else if (a->archive.archive_format
+ == ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) {
+ /* Definitely pax extended; must obey hardlink size. */
+ } else if (a->archive.archive_format == ARCHIVE_FORMAT_TAR
+ || a->archive.archive_format == ARCHIVE_FORMAT_TAR_GNUTAR)
+ {
+ /* Old-style or GNU tar: we must ignore the size. */
+ archive_entry_set_size(entry, 0);
+ tar->entry_bytes_remaining = 0;
+ } else if (archive_read_format_tar_bid(a, 50) > 50) {
+ /*
+ * We don't know if it's pax: If the bid
+ * function sees a valid ustar header
+ * immediately following, then let's ignore
+ * the hardlink size.
+ */
+ archive_entry_set_size(entry, 0);
+ tar->entry_bytes_remaining = 0;
+ }
+ /*
+ * TODO: There are still two cases I'd like to handle:
+ * = a ustar non-pax archive with a hardlink entry at
+ * end-of-archive. (Look for block of nulls following?)
+ * = a pax archive that has not seen any pax headers
+ * and has an entry which is a hardlink entry storing
+ * a body containing an uncompressed tar archive.
+ * The first is worth addressing; I don't see any reliable
+ * way to deal with the second possibility.
+ */
+ break;
+ case '2': /* Symlink */
+ archive_entry_set_filetype(entry, AE_IFLNK);
+ archive_entry_set_size(entry, 0);
+ tar->entry_bytes_remaining = 0;
+ if (archive_entry_copy_symlink_l(entry, tar->entry_linkpath.s,
+ archive_strlen(&(tar->entry_linkpath)), tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv,
+ "Linkname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+ break;
+ case '3': /* Character device */
+ archive_entry_set_filetype(entry, AE_IFCHR);
+ archive_entry_set_size(entry, 0);
+ tar->entry_bytes_remaining = 0;
+ break;
+ case '4': /* Block device */
+ archive_entry_set_filetype(entry, AE_IFBLK);
+ archive_entry_set_size(entry, 0);
+ tar->entry_bytes_remaining = 0;
+ break;
+ case '5': /* Dir */
+ archive_entry_set_filetype(entry, AE_IFDIR);
+ archive_entry_set_size(entry, 0);
+ tar->entry_bytes_remaining = 0;
+ break;
+ case '6': /* FIFO device */
+ archive_entry_set_filetype(entry, AE_IFIFO);
+ archive_entry_set_size(entry, 0);
+ tar->entry_bytes_remaining = 0;
+ break;
+ case 'D': /* GNU incremental directory type */
+ /*
+ * No special handling is actually required here.
+ * It might be nice someday to preprocess the file list and
+ * provide it to the client, though.
+ */
+ archive_entry_set_filetype(entry, AE_IFDIR);
+ break;
+ case 'M': /* GNU "Multi-volume" (remainder of file from last archive)*/
+ /*
+ * As far as I can tell, this is just like a regular file
+ * entry, except that the contents should be _appended_ to
+ * the indicated file at the indicated offset. This may
+ * require some API work to fully support.
+ */
+ break;
+ case 'N': /* Old GNU "long filename" entry. */
+ /* The body of this entry is a script for renaming
+ * previously-extracted entries. Ugh. It will never
+ * be supported by libarchive. */
+ archive_entry_set_filetype(entry, AE_IFREG);
+ break;
+ case 'S': /* GNU sparse files */
+ /*
+ * Sparse files are really just regular files with
+ * sparse information in the extended area.
+ */
+ /* FALLTHROUGH */
+ case '0':
+ /*
+ * Enable sparse file "read" support only for regular
+ * files and explicit GNU sparse files. However, we
+ * don't allow non-standard file types to be sparse.
+ */
+ tar->sparse_allowed = 1;
+ /* FALLTHROUGH */
+ default: /* Regular file and non-standard types */
+ /*
+ * Per POSIX: non-recognized types should always be
+ * treated as regular files.
+ */
+ archive_entry_set_filetype(entry, AE_IFREG);
+ break;
+ }
+ return (err);
+}
+
+/*
+ * Parse out header elements for "old-style" tar archives.
+ */
+static int
+header_old_tar(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h)
+{
+ const struct archive_entry_header_ustar *header;
+ int err = ARCHIVE_OK, err2;
+
+ /* Copy filename over (to ensure null termination). */
+ header = (const struct archive_entry_header_ustar *)h;
+ if (archive_entry_copy_pathname_l(entry,
+ header->name, sizeof(header->name), tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv, "Pathname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+
+ /* Grab rest of common fields */
+ err2 = header_common(a, tar, entry, h);
+ if (err > err2)
+ err = err2;
+
+ tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
+ return (err);
+}
+
+/*
+ * Read a Mac AppleDouble-encoded blob of file metadata,
+ * if there is one.
+ */
+static int
+read_mac_metadata_blob(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+ int64_t size;
+ size_t msize;
+ const void *data;
+ const char *p, *name;
+ const wchar_t *wp, *wname;
+
+ (void)h; /* UNUSED */
+
+ wname = wp = archive_entry_pathname_w(entry);
+ if (wp != NULL) {
+ /* Find the last path element. */
+ for (; *wp != L'\0'; ++wp) {
+ if (wp[0] == '/' && wp[1] != L'\0')
+ wname = wp + 1;
+ }
+ /*
+ * If last path element starts with "._", then
+ * this is a Mac extension.
+ */
+ if (wname[0] != L'.' || wname[1] != L'_' || wname[2] == L'\0')
+ return ARCHIVE_OK;
+ } else {
+ /* Find the last path element. */
+ name = p = archive_entry_pathname(entry);
+ if (p == NULL)
+ return (ARCHIVE_FAILED);
+ for (; *p != '\0'; ++p) {
+ if (p[0] == '/' && p[1] != '\0')
+ name = p + 1;
+ }
+ /*
+ * If last path element starts with "._", then
+ * this is a Mac extension.
+ */
+ if (name[0] != '.' || name[1] != '_' || name[2] == '\0')
+ return ARCHIVE_OK;
+ }
+
+ /* Read the body as a Mac OS metadata blob. */
+ size = archive_entry_size(entry);
+ msize = (size_t)size;
+ if (size < 0 || (uintmax_t)msize != (uintmax_t)size) {
+ *unconsumed = 0;
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * TODO: Look beyond the body here to peek at the next header.
+ * If it's a regular header (not an extension header)
+ * that has the wrong name, just return the current
+ * entry as-is, without consuming the body here.
+ * That would reduce the risk of us mis-identifying
+ * an ordinary file that just happened to have
+ * a name starting with "._".
+ *
+ * Q: Is the above idea really possible? Even
+ * when there are GNU or pax extension entries?
+ */
+ data = __archive_read_ahead(a, msize, NULL);
+ if (data == NULL) {
+ *unconsumed = 0;
+ return (ARCHIVE_FATAL);
+ }
+ archive_entry_copy_mac_metadata(entry, data, msize);
+ *unconsumed = (msize + 511) & ~ 511;
+ tar_flush_unconsumed(a, unconsumed);
+ return (tar_read_header(a, tar, entry, unconsumed));
+}
+
+/*
+ * Parse a file header for a pax extended archive entry.
+ */
+static int
+header_pax_global(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+ int err;
+
+ err = read_body_to_string(a, tar, &(tar->pax_global), h, unconsumed);
+ if (err != ARCHIVE_OK)
+ return (err);
+ err = tar_read_header(a, tar, entry, unconsumed);
+ return (err);
+}
+
+static int
+header_pax_extensions(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+ int err, err2;
+
+ err = read_body_to_string(a, tar, &(tar->pax_header), h, unconsumed);
+ if (err != ARCHIVE_OK)
+ return (err);
+
+ /* Parse the next header. */
+ err = tar_read_header(a, tar, entry, unconsumed);
+ if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
+ return (err);
+
+ /*
+ * TODO: Parse global/default options into 'entry' struct here
+ * before handling file-specific options.
+ *
+ * This design (parse standard header, then overwrite with pax
+ * extended attribute data) usually works well, but isn't ideal;
+ * it would be better to parse the pax extended attributes first
+ * and then skip any fields in the standard header that were
+ * defined in the pax header.
+ */
+ err2 = pax_header(a, tar, entry, &tar->pax_header);
+ err = err_combine(err, err2);
+ tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
+ return (err);
+}
+
+
+/*
+ * Parse a file header for a Posix "ustar" archive entry. This also
+ * handles "pax" or "extended ustar" entries.
+ */
+static int
+header_ustar(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h)
+{
+ const struct archive_entry_header_ustar *header;
+ struct archive_string *as;
+ int err = ARCHIVE_OK, r;
+
+ header = (const struct archive_entry_header_ustar *)h;
+
+ /* Copy name into an internal buffer to ensure null-termination. */
+ as = &(tar->entry_pathname);
+ if (header->prefix[0]) {
+ archive_strncpy(as, header->prefix, sizeof(header->prefix));
+ if (as->s[archive_strlen(as) - 1] != '/')
+ archive_strappend_char(as, '/');
+ archive_strncat(as, header->name, sizeof(header->name));
+ } else {
+ archive_strncpy(as, header->name, sizeof(header->name));
+ }
+ if (archive_entry_copy_pathname_l(entry, as->s, archive_strlen(as),
+ tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv, "Pathname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+
+ /* Handle rest of common fields. */
+ r = header_common(a, tar, entry, h);
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ if (r < err)
+ err = r;
+
+ /* Handle POSIX ustar fields. */
+ if (archive_entry_copy_uname_l(entry,
+ header->uname, sizeof(header->uname), tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv, "Uname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+
+ if (archive_entry_copy_gname_l(entry,
+ header->gname, sizeof(header->gname), tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv, "Gname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+
+ /* Parse out device numbers only for char and block specials. */
+ if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {
+ archive_entry_set_rdevmajor(entry, (dev_t)
+ tar_atol(header->rdevmajor, sizeof(header->rdevmajor)));
+ archive_entry_set_rdevminor(entry, (dev_t)
+ tar_atol(header->rdevminor, sizeof(header->rdevminor)));
+ }
+
+ tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
+
+ return (err);
+}
+
+
+/*
+ * Parse the pax extended attributes record.
+ *
+ * Returns non-zero if there's an error in the data.
+ */
+static int
+pax_header(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, struct archive_string *in_as)
+{
+ size_t attr_length, l, line_length, value_length;
+ char *p;
+ char *key, *value;
+ struct archive_string *as;
+ struct archive_string_conv *sconv;
+ int err, err2;
+ char *attr = in_as->s;
+
+ attr_length = in_as->length;
+ tar->pax_hdrcharset_binary = 0;
+ archive_string_empty(&(tar->entry_gname));
+ archive_string_empty(&(tar->entry_linkpath));
+ archive_string_empty(&(tar->entry_pathname));
+ archive_string_empty(&(tar->entry_pathname_override));
+ archive_string_empty(&(tar->entry_uname));
+ err = ARCHIVE_OK;
+ while (attr_length > 0) {
+ /* Parse decimal length field at start of line. */
+ line_length = 0;
+ l = attr_length;
+ p = attr; /* Record start of line. */
+ while (l>0) {
+ if (*p == ' ') {
+ p++;
+ l--;
+ break;
+ }
+ if (*p < '0' || *p > '9') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Ignoring malformed pax extended attributes");
+ return (ARCHIVE_WARN);
+ }
+ line_length *= 10;
+ line_length += *p - '0';
+ if (line_length > 999999) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Rejecting pax extended attribute > 1MB");
+ return (ARCHIVE_WARN);
+ }
+ p++;
+ l--;
+ }
+
+ /*
+ * Parsed length must be no bigger than available data,
+ * at least 1, and the last character of the line must
+ * be '\n'.
+ */
+ if (line_length > attr_length
+ || line_length < 1
+ || attr[line_length - 1] != '\n')
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Ignoring malformed pax extended attribute");
+ return (ARCHIVE_WARN);
+ }
+
+ /* Null-terminate the line. */
+ attr[line_length - 1] = '\0';
+
+ /* Find end of key and null terminate it. */
+ key = p;
+ if (key[0] == '=')
+ return (-1);
+ while (*p && *p != '=')
+ ++p;
+ if (*p == '\0') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid pax extended attributes");
+ return (ARCHIVE_WARN);
+ }
+ *p = '\0';
+
+ value = p + 1;
+
+ /* Some values may be binary data */
+ value_length = attr + line_length - 1 - value;
+
+ /* Identify this attribute and set it in the entry. */
+ err2 = pax_attribute(a, tar, entry, key, value, value_length);
+ if (err2 == ARCHIVE_FATAL)
+ return (err2);
+ err = err_combine(err, err2);
+
+ /* Skip to next line */
+ attr += line_length;
+ attr_length -= line_length;
+ }
+
+ /*
+ * PAX format uses UTF-8 as default charset for its metadata
+ * unless hdrcharset=BINARY is present in its header.
+ * We apply the charset specified by the hdrcharset option only
+ * when the hdrcharset attribute(in PAX header) is BINARY because
+ * we respect the charset described in PAX header and BINARY also
+ * means that metadata(filename,uname and gname) character-set
+ * is unknown.
+ */
+ if (tar->pax_hdrcharset_binary)
+ sconv = tar->opt_sconv;
+ else {
+ sconv = archive_string_conversion_from_charset(
+ &(a->archive), "UTF-8", 1);
+ if (sconv == NULL)
+ return (ARCHIVE_FATAL);
+ if (tar->compat_2x)
+ archive_string_conversion_set_opt(sconv,
+ SCONV_SET_OPT_UTF8_LIBARCHIVE2X);
+ }
+
+ if (archive_strlen(&(tar->entry_gname)) > 0) {
+ if (archive_entry_copy_gname_l(entry, tar->entry_gname.s,
+ archive_strlen(&(tar->entry_gname)), sconv) != 0) {
+ err = set_conversion_failed_error(a, sconv, "Gname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ /* Use a converted an original name. */
+ archive_entry_copy_gname(entry, tar->entry_gname.s);
+ }
+ }
+ if (archive_strlen(&(tar->entry_linkpath)) > 0) {
+ if (archive_entry_copy_link_l(entry, tar->entry_linkpath.s,
+ archive_strlen(&(tar->entry_linkpath)), sconv) != 0) {
+ err = set_conversion_failed_error(a, sconv, "Linkname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ /* Use a converted an original name. */
+ archive_entry_copy_link(entry, tar->entry_linkpath.s);
+ }
+ }
+ /*
+ * Some extensions (such as the GNU sparse file extensions)
+ * deliberately store a synthetic name under the regular 'path'
+ * attribute and the real file name under a different attribute.
+ * Since we're supposed to not care about the order, we
+ * have no choice but to store all of the various filenames
+ * we find and figure it all out afterwards. This is the
+ * figuring out part.
+ */
+ as = NULL;
+ if (archive_strlen(&(tar->entry_pathname_override)) > 0)
+ as = &(tar->entry_pathname_override);
+ else if (archive_strlen(&(tar->entry_pathname)) > 0)
+ as = &(tar->entry_pathname);
+ if (as != NULL) {
+ if (archive_entry_copy_pathname_l(entry, as->s,
+ archive_strlen(as), sconv) != 0) {
+ err = set_conversion_failed_error(a, sconv, "Pathname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ /* Use a converted an original name. */
+ archive_entry_copy_pathname(entry, as->s);
+ }
+ }
+ if (archive_strlen(&(tar->entry_uname)) > 0) {
+ if (archive_entry_copy_uname_l(entry, tar->entry_uname.s,
+ archive_strlen(&(tar->entry_uname)), sconv) != 0) {
+ err = set_conversion_failed_error(a, sconv, "Uname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ /* Use a converted an original name. */
+ archive_entry_copy_uname(entry, tar->entry_uname.s);
+ }
+ }
+ return (err);
+}
+
+static int
+pax_attribute_xattr(struct archive_entry *entry,
+ const char *name, const char *value)
+{
+ char *name_decoded;
+ void *value_decoded;
+ size_t value_len;
+
+ if (strlen(name) < 18 || (memcmp(name, "LIBARCHIVE.xattr.", 17)) != 0)
+ return 3;
+
+ name += 17;
+
+ /* URL-decode name */
+ name_decoded = url_decode(name);
+ if (name_decoded == NULL)
+ return 2;
+
+ /* Base-64 decode value */
+ value_decoded = base64_decode(value, strlen(value), &value_len);
+ if (value_decoded == NULL) {
+ free(name_decoded);
+ return 1;
+ }
+
+ archive_entry_xattr_add_entry(entry, name_decoded,
+ value_decoded, value_len);
+
+ free(name_decoded);
+ free(value_decoded);
+ return 0;
+}
+
+static int
+pax_attribute_schily_xattr(struct archive_entry *entry,
+ const char *name, const char *value, size_t value_length)
+{
+ if (strlen(name) < 14 || (memcmp(name, "SCHILY.xattr.", 13)) != 0)
+ return 1;
+
+ name += 13;
+
+ archive_entry_xattr_add_entry(entry, name, value, value_length);
+
+ return 0;
+}
+
+static int
+pax_attribute_rht_security_selinux(struct archive_entry *entry,
+ const char *value, size_t value_length)
+{
+ archive_entry_xattr_add_entry(entry, "security.selinux",
+ value, value_length);
+
+ return 0;
+}
+
+static int
+pax_attribute_acl(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const char *value, int type)
+{
+ int r;
+ const char* errstr;
+
+ switch (type) {
+ case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+ errstr = "SCHILY.acl.access";
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+ errstr = "SCHILY.acl.default";
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+ errstr = "SCHILY.acl.ace";
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Unknown ACL type: %d", type);
+ return(ARCHIVE_FATAL);
+ }
+
+ if (tar->sconv_acl == NULL) {
+ tar->sconv_acl =
+ archive_string_conversion_from_charset(
+ &(a->archive), "UTF-8", 1);
+ if (tar->sconv_acl == NULL)
+ return (ARCHIVE_FATAL);
+ }
+
+ r = archive_acl_from_text_l(archive_entry_acl(entry), value, type,
+ tar->sconv_acl);
+ if (r != ARCHIVE_OK) {
+ if (r == ARCHIVE_FATAL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "%s %s", "Can't allocate memory for ",
+ errstr);
+ return (r);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC, "%s %s", "Parse error: ", errstr);
+ }
+ return (r);
+}
+
+/*
+ * Parse a single key=value attribute. key/value pointers are
+ * assumed to point into reasonably long-lived storage.
+ *
+ * Note that POSIX reserves all-lowercase keywords. Vendor-specific
+ * extensions should always have keywords of the form "VENDOR.attribute"
+ * In particular, it's quite feasible to support many different
+ * vendor extensions here. I'm using "LIBARCHIVE" for extensions
+ * unique to this library.
+ *
+ * Investigate other vendor-specific extensions and see if
+ * any of them look useful.
+ */
+static int
+pax_attribute(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const char *key, const char *value, size_t value_length)
+{
+ int64_t s;
+ long n;
+ int err = ARCHIVE_OK, r;
+
+ if (value == NULL)
+ value = ""; /* Disable compiler warning; do not pass
+ * NULL pointer to strlen(). */
+ switch (key[0]) {
+ case 'G':
+ /* Reject GNU.sparse.* headers on non-regular files. */
+ if (strncmp(key, "GNU.sparse", 10) == 0 &&
+ !tar->sparse_allowed) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Non-regular file cannot be sparse");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* GNU "0.0" sparse pax format. */
+ if (strcmp(key, "GNU.sparse.numblocks") == 0) {
+ tar->sparse_offset = -1;
+ tar->sparse_numbytes = -1;
+ tar->sparse_gnu_major = 0;
+ tar->sparse_gnu_minor = 0;
+ }
+ if (strcmp(key, "GNU.sparse.offset") == 0) {
+ tar->sparse_offset = tar_atol10(value, strlen(value));
+ if (tar->sparse_numbytes != -1) {
+ if (gnu_add_sparse_entry(a, tar,
+ tar->sparse_offset, tar->sparse_numbytes)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ tar->sparse_offset = -1;
+ tar->sparse_numbytes = -1;
+ }
+ }
+ if (strcmp(key, "GNU.sparse.numbytes") == 0) {
+ tar->sparse_numbytes = tar_atol10(value, strlen(value));
+ if (tar->sparse_offset != -1) {
+ if (gnu_add_sparse_entry(a, tar,
+ tar->sparse_offset, tar->sparse_numbytes)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ tar->sparse_offset = -1;
+ tar->sparse_numbytes = -1;
+ }
+ }
+ if (strcmp(key, "GNU.sparse.size") == 0) {
+ tar->realsize = tar_atol10(value, strlen(value));
+ archive_entry_set_size(entry, tar->realsize);
+ tar->realsize_override = 1;
+ }
+
+ /* GNU "0.1" sparse pax format. */
+ if (strcmp(key, "GNU.sparse.map") == 0) {
+ tar->sparse_gnu_major = 0;
+ tar->sparse_gnu_minor = 1;
+ if (gnu_sparse_01_parse(a, tar, value) != ARCHIVE_OK)
+ return (ARCHIVE_WARN);
+ }
+
+ /* GNU "1.0" sparse pax format */
+ if (strcmp(key, "GNU.sparse.major") == 0) {
+ tar->sparse_gnu_major = (int)tar_atol10(value, strlen(value));
+ tar->sparse_gnu_pending = 1;
+ }
+ if (strcmp(key, "GNU.sparse.minor") == 0) {
+ tar->sparse_gnu_minor = (int)tar_atol10(value, strlen(value));
+ tar->sparse_gnu_pending = 1;
+ }
+ if (strcmp(key, "GNU.sparse.name") == 0) {
+ /*
+ * The real filename; when storing sparse
+ * files, GNU tar puts a synthesized name into
+ * the regular 'path' attribute in an attempt
+ * to limit confusion. ;-)
+ */
+ archive_strcpy(&(tar->entry_pathname_override), value);
+ }
+ if (strcmp(key, "GNU.sparse.realsize") == 0) {
+ tar->realsize = tar_atol10(value, strlen(value));
+ archive_entry_set_size(entry, tar->realsize);
+ tar->realsize_override = 1;
+ }
+ break;
+ case 'L':
+ /* Our extensions */
+/* TODO: Handle arbitrary extended attributes... */
+/*
+ if (strcmp(key, "LIBARCHIVE.xxxxxxx") == 0)
+ archive_entry_set_xxxxxx(entry, value);
+*/
+ if (strcmp(key, "LIBARCHIVE.creationtime") == 0) {
+ pax_time(value, &s, &n);
+ archive_entry_set_birthtime(entry, s, n);
+ }
+ if (strcmp(key, "LIBARCHIVE.symlinktype") == 0) {
+ if (strcmp(value, "file") == 0) {
+ archive_entry_set_symlink_type(entry,
+ AE_SYMLINK_TYPE_FILE);
+ } else if (strcmp(value, "dir") == 0) {
+ archive_entry_set_symlink_type(entry,
+ AE_SYMLINK_TYPE_DIRECTORY);
+ }
+ }
+ if (memcmp(key, "LIBARCHIVE.xattr.", 17) == 0)
+ pax_attribute_xattr(entry, key, value);
+ break;
+ case 'R':
+ /* GNU tar uses RHT.security header to store SELinux xattrs
+ * SCHILY.xattr.security.selinux == RHT.security.selinux */
+ if (strcmp(key, "RHT.security.selinux") == 0) {
+ pax_attribute_rht_security_selinux(entry, value,
+ value_length);
+ }
+ break;
+ case 'S':
+ /* We support some keys used by the "star" archiver */
+ if (strcmp(key, "SCHILY.acl.access") == 0) {
+ r = pax_attribute_acl(a, tar, entry, value,
+ ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ } else if (strcmp(key, "SCHILY.acl.default") == 0) {
+ r = pax_attribute_acl(a, tar, entry, value,
+ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ } else if (strcmp(key, "SCHILY.acl.ace") == 0) {
+ r = pax_attribute_acl(a, tar, entry, value,
+ ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ } else if (strcmp(key, "SCHILY.devmajor") == 0) {
+ archive_entry_set_rdevmajor(entry,
+ (dev_t)tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "SCHILY.devminor") == 0) {
+ archive_entry_set_rdevminor(entry,
+ (dev_t)tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "SCHILY.fflags") == 0) {
+ archive_entry_copy_fflags_text(entry, value);
+ } else if (strcmp(key, "SCHILY.dev") == 0) {
+ archive_entry_set_dev(entry,
+ (dev_t)tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "SCHILY.ino") == 0) {
+ archive_entry_set_ino(entry,
+ tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "SCHILY.nlink") == 0) {
+ archive_entry_set_nlink(entry, (unsigned)
+ tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "SCHILY.realsize") == 0) {
+ tar->realsize = tar_atol10(value, strlen(value));
+ tar->realsize_override = 1;
+ archive_entry_set_size(entry, tar->realsize);
+ } else if (strncmp(key, "SCHILY.xattr.", 13) == 0) {
+ pax_attribute_schily_xattr(entry, key, value,
+ value_length);
+ } else if (strcmp(key, "SUN.holesdata") == 0) {
+ /* A Solaris extension for sparse. */
+ r = solaris_sparse_parse(a, tar, entry, value);
+ if (r < err) {
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ err = r;
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Parse error: SUN.holesdata");
+ }
+ }
+ break;
+ case 'a':
+ if (strcmp(key, "atime") == 0) {
+ pax_time(value, &s, &n);
+ archive_entry_set_atime(entry, s, n);
+ }
+ break;
+ case 'c':
+ if (strcmp(key, "ctime") == 0) {
+ pax_time(value, &s, &n);
+ archive_entry_set_ctime(entry, s, n);
+ } else if (strcmp(key, "charset") == 0) {
+ /* TODO: Publish charset information in entry. */
+ } else if (strcmp(key, "comment") == 0) {
+ /* TODO: Publish comment in entry. */
+ }
+ break;
+ case 'g':
+ if (strcmp(key, "gid") == 0) {
+ archive_entry_set_gid(entry,
+ tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "gname") == 0) {
+ archive_strcpy(&(tar->entry_gname), value);
+ }
+ break;
+ case 'h':
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (strcmp(value, "BINARY") == 0)
+ /* Binary mode. */
+ tar->pax_hdrcharset_binary = 1;
+ else if (strcmp(value, "ISO-IR 10646 2000 UTF-8") == 0)
+ tar->pax_hdrcharset_binary = 0;
+ }
+ break;
+ case 'l':
+ /* pax interchange doesn't distinguish hardlink vs. symlink. */
+ if (strcmp(key, "linkpath") == 0) {
+ archive_strcpy(&(tar->entry_linkpath), value);
+ }
+ break;
+ case 'm':
+ if (strcmp(key, "mtime") == 0) {
+ pax_time(value, &s, &n);
+ archive_entry_set_mtime(entry, s, n);
+ }
+ break;
+ case 'p':
+ if (strcmp(key, "path") == 0) {
+ archive_strcpy(&(tar->entry_pathname), value);
+ }
+ break;
+ case 'r':
+ /* POSIX has reserved 'realtime.*' */
+ break;
+ case 's':
+ /* POSIX has reserved 'security.*' */
+ /* Someday: if (strcmp(key, "security.acl") == 0) { ... } */
+ if (strcmp(key, "size") == 0) {
+ /* "size" is the size of the data in the entry. */
+ tar->entry_bytes_remaining
+ = tar_atol10(value, strlen(value));
+ if (tar->entry_bytes_remaining < 0) {
+ tar->entry_bytes_remaining = 0;
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Tar size attribute is negative");
+ return (ARCHIVE_FATAL);
+ }
+ if (tar->entry_bytes_remaining == INT64_MAX) {
+ /* Note: tar_atol returns INT64_MAX on overflow */
+ tar->entry_bytes_remaining = 0;
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Tar size attribute overflow");
+ return (ARCHIVE_FATAL);
+ }
+ /*
+ * The "size" pax header keyword always overrides the
+ * "size" field in the tar header.
+ * GNU.sparse.realsize, GNU.sparse.size and
+ * SCHILY.realsize override this value.
+ */
+ if (!tar->realsize_override) {
+ archive_entry_set_size(entry,
+ tar->entry_bytes_remaining);
+ tar->realsize
+ = tar->entry_bytes_remaining;
+ }
+ }
+ break;
+ case 'u':
+ if (strcmp(key, "uid") == 0) {
+ archive_entry_set_uid(entry,
+ tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "uname") == 0) {
+ archive_strcpy(&(tar->entry_uname), value);
+ }
+ break;
+ }
+ return (err);
+}
+
+
+
+/*
+ * parse a decimal time value, which may include a fractional portion
+ */
+static void
+pax_time(const char *p, int64_t *ps, long *pn)
+{
+ char digit;
+ int64_t s;
+ unsigned long l;
+ int sign;
+ int64_t limit, last_digit_limit;
+
+ limit = INT64_MAX / 10;
+ last_digit_limit = INT64_MAX % 10;
+
+ s = 0;
+ sign = 1;
+ if (*p == '-') {
+ sign = -1;
+ p++;
+ }
+ while (*p >= '0' && *p <= '9') {
+ digit = *p - '0';
+ if (s > limit ||
+ (s == limit && digit > last_digit_limit)) {
+ s = INT64_MAX;
+ break;
+ }
+ s = (s * 10) + digit;
+ ++p;
+ }
+
+ *ps = s * sign;
+
+ /* Calculate nanoseconds. */
+ *pn = 0;
+
+ if (*p != '.')
+ return;
+
+ l = 100000000UL;
+ do {
+ ++p;
+ if (*p >= '0' && *p <= '9')
+ *pn += (*p - '0') * l;
+ else
+ break;
+ } while (l /= 10);
+}
+
+/*
+ * Parse GNU tar header
+ */
+static int
+header_gnutar(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+ const struct archive_entry_header_gnutar *header;
+ int64_t t;
+ int err = ARCHIVE_OK;
+
+ /*
+ * GNU header is like POSIX ustar, except 'prefix' is
+ * replaced with some other fields. This also means the
+ * filename is stored as in old-style archives.
+ */
+
+ /* Grab fields common to all tar variants. */
+ err = header_common(a, tar, entry, h);
+ if (err == ARCHIVE_FATAL)
+ return (err);
+
+ /* Copy filename over (to ensure null termination). */
+ header = (const struct archive_entry_header_gnutar *)h;
+ if (archive_entry_copy_pathname_l(entry,
+ header->name, sizeof(header->name), tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv, "Pathname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+
+ /* Fields common to ustar and GNU */
+ /* XXX Can the following be factored out since it's common
+ * to ustar and gnu tar? Is it okay to move it down into
+ * header_common, perhaps? */
+ if (archive_entry_copy_uname_l(entry,
+ header->uname, sizeof(header->uname), tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv, "Uname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+
+ if (archive_entry_copy_gname_l(entry,
+ header->gname, sizeof(header->gname), tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv, "Gname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+
+ /* Parse out device numbers only for char and block specials */
+ if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {
+ archive_entry_set_rdevmajor(entry, (dev_t)
+ tar_atol(header->rdevmajor, sizeof(header->rdevmajor)));
+ archive_entry_set_rdevminor(entry, (dev_t)
+ tar_atol(header->rdevminor, sizeof(header->rdevminor)));
+ } else
+ archive_entry_set_rdev(entry, 0);
+
+ tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
+
+ /* Grab GNU-specific fields. */
+ t = tar_atol(header->atime, sizeof(header->atime));
+ if (t > 0)
+ archive_entry_set_atime(entry, t, 0);
+ t = tar_atol(header->ctime, sizeof(header->ctime));
+ if (t > 0)
+ archive_entry_set_ctime(entry, t, 0);
+
+ if (header->realsize[0] != 0) {
+ tar->realsize
+ = tar_atol(header->realsize, sizeof(header->realsize));
+ archive_entry_set_size(entry, tar->realsize);
+ tar->realsize_override = 1;
+ }
+
+ if (header->sparse[0].offset[0] != 0) {
+ if (gnu_sparse_old_read(a, tar, header, unconsumed)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } else {
+ if (header->isextended[0] != 0) {
+ /* XXX WTF? XXX */
+ }
+ }
+
+ return (err);
+}
+
+static int
+gnu_add_sparse_entry(struct archive_read *a, struct tar *tar,
+ int64_t offset, int64_t remaining)
+{
+ struct sparse_block *p;
+
+ p = (struct sparse_block *)calloc(1, sizeof(*p));
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ if (tar->sparse_last != NULL)
+ tar->sparse_last->next = p;
+ else
+ tar->sparse_list = p;
+ tar->sparse_last = p;
+ if (remaining < 0 || offset < 0 || offset > INT64_MAX - remaining) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed sparse map data");
+ return (ARCHIVE_FATAL);
+ }
+ p->offset = offset;
+ p->remaining = remaining;
+ return (ARCHIVE_OK);
+}
+
+static void
+gnu_clear_sparse_list(struct tar *tar)
+{
+ struct sparse_block *p;
+
+ while (tar->sparse_list != NULL) {
+ p = tar->sparse_list;
+ tar->sparse_list = p->next;
+ free(p);
+ }
+ tar->sparse_last = NULL;
+}
+
+/*
+ * GNU tar old-format sparse data.
+ *
+ * GNU old-format sparse data is stored in a fixed-field
+ * format. Offset/size values are 11-byte octal fields (same
+ * format as 'size' field in ustart header). These are
+ * stored in the header, allocating subsequent header blocks
+ * as needed. Extending the header in this way is a pretty
+ * severe POSIX violation; this design has earned GNU tar a
+ * lot of criticism.
+ */
+
+static int
+gnu_sparse_old_read(struct archive_read *a, struct tar *tar,
+ const struct archive_entry_header_gnutar *header, size_t *unconsumed)
+{
+ ssize_t bytes_read;
+ const void *data;
+ struct extended {
+ struct gnu_sparse sparse[21];
+ char isextended[1];
+ char padding[7];
+ };
+ const struct extended *ext;
+
+ if (gnu_sparse_old_parse(a, tar, header->sparse, 4) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ if (header->isextended[0] == 0)
+ return (ARCHIVE_OK);
+
+ do {
+ tar_flush_unconsumed(a, unconsumed);
+ data = __archive_read_ahead(a, 512, &bytes_read);
+ if (bytes_read < 0)
+ return (ARCHIVE_FATAL);
+ if (bytes_read < 512) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated tar archive "
+ "detected while reading sparse file data");
+ return (ARCHIVE_FATAL);
+ }
+ *unconsumed = 512;
+ ext = (const struct extended *)data;
+ if (gnu_sparse_old_parse(a, tar, ext->sparse, 21) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } while (ext->isextended[0] != 0);
+ if (tar->sparse_list != NULL)
+ tar->entry_offset = tar->sparse_list->offset;
+ return (ARCHIVE_OK);
+}
+
+static int
+gnu_sparse_old_parse(struct archive_read *a, struct tar *tar,
+ const struct gnu_sparse *sparse, int length)
+{
+ while (length > 0 && sparse->offset[0] != 0) {
+ if (gnu_add_sparse_entry(a, tar,
+ tar_atol(sparse->offset, sizeof(sparse->offset)),
+ tar_atol(sparse->numbytes, sizeof(sparse->numbytes)))
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ sparse++;
+ length--;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * GNU tar sparse format 0.0
+ *
+ * Beginning with GNU tar 1.15, sparse files are stored using
+ * information in the pax extended header. The GNU tar maintainers
+ * have gone through a number of variations in the process of working
+ * out this scheme; fortunately, they're all numbered.
+ *
+ * Sparse format 0.0 uses attribute GNU.sparse.numblocks to store the
+ * number of blocks, and GNU.sparse.offset/GNU.sparse.numbytes to
+ * store offset/size for each block. The repeated instances of these
+ * latter fields violate the pax specification (which frowns on
+ * duplicate keys), so this format was quickly replaced.
+ */
+
+/*
+ * GNU tar sparse format 0.1
+ *
+ * This version replaced the offset/numbytes attributes with
+ * a single "map" attribute that stored a list of integers. This
+ * format had two problems: First, the "map" attribute could be very
+ * long, which caused problems for some implementations. More
+ * importantly, the sparse data was lost when extracted by archivers
+ * that didn't recognize this extension.
+ */
+
+static int
+gnu_sparse_01_parse(struct archive_read *a, struct tar *tar, const char *p)
+{
+ const char *e;
+ int64_t offset = -1, size = -1;
+
+ for (;;) {
+ e = p;
+ while (*e != '\0' && *e != ',') {
+ if (*e < '0' || *e > '9')
+ return (ARCHIVE_WARN);
+ e++;
+ }
+ if (offset < 0) {
+ offset = tar_atol10(p, e - p);
+ if (offset < 0)
+ return (ARCHIVE_WARN);
+ } else {
+ size = tar_atol10(p, e - p);
+ if (size < 0)
+ return (ARCHIVE_WARN);
+ if (gnu_add_sparse_entry(a, tar, offset, size)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ offset = -1;
+ }
+ if (*e == '\0')
+ return (ARCHIVE_OK);
+ p = e + 1;
+ }
+}
+
+/*
+ * GNU tar sparse format 1.0
+ *
+ * The idea: The offset/size data is stored as a series of base-10
+ * ASCII numbers prepended to the file data, so that dearchivers that
+ * don't support this format will extract the block map along with the
+ * data and a separate post-process can restore the sparseness.
+ *
+ * Unfortunately, GNU tar 1.16 had a bug that added unnecessary
+ * padding to the body of the file when using this format. GNU tar
+ * 1.17 corrected this bug without bumping the version number, so
+ * it's not possible to support both variants. This code supports
+ * the later variant at the expense of not supporting the former.
+ *
+ * This variant also replaced GNU.sparse.size with GNU.sparse.realsize
+ * and introduced the GNU.sparse.major/GNU.sparse.minor attributes.
+ */
+
+/*
+ * Read the next line from the input, and parse it as a decimal
+ * integer followed by '\n'. Returns positive integer value or
+ * negative on error.
+ */
+static int64_t
+gnu_sparse_10_atol(struct archive_read *a, struct tar *tar,
+ int64_t *remaining, size_t *unconsumed)
+{
+ int64_t l, limit, last_digit_limit;
+ const char *p;
+ ssize_t bytes_read;
+ int base, digit;
+
+ base = 10;
+ limit = INT64_MAX / base;
+ last_digit_limit = INT64_MAX % base;
+
+ /*
+ * Skip any lines starting with '#'; GNU tar specs
+ * don't require this, but they should.
+ */
+ do {
+ bytes_read = readline(a, tar, &p,
+ (ssize_t)tar_min(*remaining, 100), unconsumed);
+ if (bytes_read <= 0)
+ return (ARCHIVE_FATAL);
+ *remaining -= bytes_read;
+ } while (p[0] == '#');
+
+ l = 0;
+ while (bytes_read > 0) {
+ if (*p == '\n')
+ return (l);
+ if (*p < '0' || *p >= '0' + base)
+ return (ARCHIVE_WARN);
+ digit = *p - '0';
+ if (l > limit || (l == limit && digit > last_digit_limit))
+ l = INT64_MAX; /* Truncate on overflow. */
+ else
+ l = (l * base) + digit;
+ p++;
+ bytes_read--;
+ }
+ /* TODO: Error message. */
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Returns length (in bytes) of the sparse data description
+ * that was read.
+ */
+static ssize_t
+gnu_sparse_10_read(struct archive_read *a, struct tar *tar, size_t *unconsumed)
+{
+ ssize_t bytes_read;
+ int entries;
+ int64_t offset, size, to_skip, remaining;
+
+ /* Clear out the existing sparse list. */
+ gnu_clear_sparse_list(tar);
+
+ remaining = tar->entry_bytes_remaining;
+
+ /* Parse entries. */
+ entries = (int)gnu_sparse_10_atol(a, tar, &remaining, unconsumed);
+ if (entries < 0)
+ return (ARCHIVE_FATAL);
+ /* Parse the individual entries. */
+ while (entries-- > 0) {
+ /* Parse offset/size */
+ offset = gnu_sparse_10_atol(a, tar, &remaining, unconsumed);
+ if (offset < 0)
+ return (ARCHIVE_FATAL);
+ size = gnu_sparse_10_atol(a, tar, &remaining, unconsumed);
+ if (size < 0)
+ return (ARCHIVE_FATAL);
+ /* Add a new sparse entry. */
+ if (gnu_add_sparse_entry(a, tar, offset, size) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ /* Skip rest of block... */
+ tar_flush_unconsumed(a, unconsumed);
+ bytes_read = (ssize_t)(tar->entry_bytes_remaining - remaining);
+ to_skip = 0x1ff & -bytes_read;
+ /* Fail if tar->entry_bytes_remaing would get negative */
+ if (to_skip > remaining)
+ return (ARCHIVE_FATAL);
+ if (to_skip != __archive_read_consume(a, to_skip))
+ return (ARCHIVE_FATAL);
+ return ((ssize_t)(bytes_read + to_skip));
+}
+
+/*
+ * Solaris pax extension for a sparse file. This is recorded with the
+ * data and hole pairs. The way recording sparse information by Solaris'
+ * pax simply indicates where data and sparse are, so the stored contents
+ * consist of both data and hole.
+ */
+static int
+solaris_sparse_parse(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const char *p)
+{
+ const char *e;
+ int64_t start, end;
+ int hole = 1;
+
+ (void)entry; /* UNUSED */
+
+ end = 0;
+ if (*p == ' ')
+ p++;
+ else
+ return (ARCHIVE_WARN);
+ for (;;) {
+ e = p;
+ while (*e != '\0' && *e != ' ') {
+ if (*e < '0' || *e > '9')
+ return (ARCHIVE_WARN);
+ e++;
+ }
+ start = end;
+ end = tar_atol10(p, e - p);
+ if (end < 0)
+ return (ARCHIVE_WARN);
+ if (start < end) {
+ if (gnu_add_sparse_entry(a, tar, start,
+ end - start) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ tar->sparse_last->hole = hole;
+ }
+ if (*e == '\0')
+ return (ARCHIVE_OK);
+ p = e + 1;
+ hole = hole == 0;
+ }
+}
+
+/*-
+ * Convert text->integer.
+ *
+ * Traditional tar formats (including POSIX) specify base-8 for
+ * all of the standard numeric fields. This is a significant limitation
+ * in practice:
+ * = file size is limited to 8GB
+ * = rdevmajor and rdevminor are limited to 21 bits
+ * = uid/gid are limited to 21 bits
+ *
+ * There are two workarounds for this:
+ * = pax extended headers, which use variable-length string fields
+ * = GNU tar and STAR both allow either base-8 or base-256 in
+ * most fields. The high bit is set to indicate base-256.
+ *
+ * On read, this implementation supports both extensions.
+ */
+static int64_t
+tar_atol(const char *p, size_t char_cnt)
+{
+ /*
+ * Technically, GNU tar considers a field to be in base-256
+ * only if the first byte is 0xff or 0x80.
+ */
+ if (*p & 0x80)
+ return (tar_atol256(p, char_cnt));
+ return (tar_atol8(p, char_cnt));
+}
+
+/*
+ * Note that this implementation does not (and should not!) obey
+ * locale settings; you cannot simply substitute strtol here, since
+ * it does obey locale.
+ */
+static int64_t
+tar_atol_base_n(const char *p, size_t char_cnt, int base)
+{
+ int64_t l, maxval, limit, last_digit_limit;
+ int digit, sign;
+
+ maxval = INT64_MAX;
+ limit = INT64_MAX / base;
+ last_digit_limit = INT64_MAX % base;
+
+ /* the pointer will not be dereferenced if char_cnt is zero
+ * due to the way the && operator is evaluated.
+ */
+ while (char_cnt != 0 && (*p == ' ' || *p == '\t')) {
+ p++;
+ char_cnt--;
+ }
+
+ sign = 1;
+ if (char_cnt != 0 && *p == '-') {
+ sign = -1;
+ p++;
+ char_cnt--;
+
+ maxval = INT64_MIN;
+ limit = -(INT64_MIN / base);
+ last_digit_limit = -(INT64_MIN % base);
+ }
+
+ l = 0;
+ if (char_cnt != 0) {
+ digit = *p - '0';
+ while (digit >= 0 && digit < base && char_cnt != 0) {
+ if (l>limit || (l == limit && digit >= last_digit_limit)) {
+ return maxval; /* Truncate on overflow. */
+ }
+ l = (l * base) + digit;
+ digit = *++p - '0';
+ char_cnt--;
+ }
+ }
+ return (sign < 0) ? -l : l;
+}
+
+static int64_t
+tar_atol8(const char *p, size_t char_cnt)
+{
+ return tar_atol_base_n(p, char_cnt, 8);
+}
+
+static int64_t
+tar_atol10(const char *p, size_t char_cnt)
+{
+ return tar_atol_base_n(p, char_cnt, 10);
+}
+
+/*
+ * Parse a base-256 integer. This is just a variable-length
+ * twos-complement signed binary value in big-endian order, except
+ * that the high-order bit is ignored. The values here can be up to
+ * 12 bytes, so we need to be careful about overflowing 64-bit
+ * (8-byte) integers.
+ *
+ * This code unashamedly assumes that the local machine uses 8-bit
+ * bytes and twos-complement arithmetic.
+ */
+static int64_t
+tar_atol256(const char *_p, size_t char_cnt)
+{
+ uint64_t l;
+ const unsigned char *p = (const unsigned char *)_p;
+ unsigned char c, neg;
+
+ /* Extend 7-bit 2s-comp to 8-bit 2s-comp, decide sign. */
+ c = *p;
+ if (c & 0x40) {
+ neg = 0xff;
+ c |= 0x80;
+ l = ~ARCHIVE_LITERAL_ULL(0);
+ } else {
+ neg = 0;
+ c &= 0x7f;
+ l = 0;
+ }
+
+ /* If more than 8 bytes, check that we can ignore
+ * high-order bits without overflow. */
+ while (char_cnt > sizeof(int64_t)) {
+ --char_cnt;
+ if (c != neg)
+ return neg ? INT64_MIN : INT64_MAX;
+ c = *++p;
+ }
+
+ /* c is first byte that fits; if sign mismatch, return overflow */
+ if ((c ^ neg) & 0x80) {
+ return neg ? INT64_MIN : INT64_MAX;
+ }
+
+ /* Accumulate remaining bytes. */
+ while (--char_cnt > 0) {
+ l = (l << 8) | c;
+ c = *++p;
+ }
+ l = (l << 8) | c;
+ /* Return signed twos-complement value. */
+ return (int64_t)(l);
+}
+
+/*
+ * Returns length of line (including trailing newline)
+ * or negative on error. 'start' argument is updated to
+ * point to first character of line. This avoids copying
+ * when possible.
+ */
+static ssize_t
+readline(struct archive_read *a, struct tar *tar, const char **start,
+ ssize_t limit, size_t *unconsumed)
+{
+ ssize_t bytes_read;
+ ssize_t total_size = 0;
+ const void *t;
+ const char *s;
+ void *p;
+
+ tar_flush_unconsumed(a, unconsumed);
+
+ t = __archive_read_ahead(a, 1, &bytes_read);
+ if (bytes_read <= 0)
+ return (ARCHIVE_FATAL);
+ s = t; /* Start of line? */
+ p = memchr(t, '\n', bytes_read);
+ /* If we found '\n' in the read buffer, return pointer to that. */
+ if (p != NULL) {
+ bytes_read = 1 + ((const char *)p) - s;
+ if (bytes_read > limit) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Line too long");
+ return (ARCHIVE_FATAL);
+ }
+ *unconsumed = bytes_read;
+ *start = s;
+ return (bytes_read);
+ }
+ *unconsumed = bytes_read;
+ /* Otherwise, we need to accumulate in a line buffer. */
+ for (;;) {
+ if (total_size + bytes_read > limit) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Line too long");
+ return (ARCHIVE_FATAL);
+ }
+ if (archive_string_ensure(&tar->line, total_size + bytes_read) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate working buffer");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(tar->line.s + total_size, t, bytes_read);
+ tar_flush_unconsumed(a, unconsumed);
+ total_size += bytes_read;
+ /* If we found '\n', clean up and return. */
+ if (p != NULL) {
+ *start = tar->line.s;
+ return (total_size);
+ }
+ /* Read some more. */
+ t = __archive_read_ahead(a, 1, &bytes_read);
+ if (bytes_read <= 0)
+ return (ARCHIVE_FATAL);
+ s = t; /* Start of line? */
+ p = memchr(t, '\n', bytes_read);
+ /* If we found '\n', trim the read. */
+ if (p != NULL) {
+ bytes_read = 1 + ((const char *)p) - s;
+ }
+ *unconsumed = bytes_read;
+ }
+}
+
+/*
+ * base64_decode - Base64 decode
+ *
+ * This accepts most variations of base-64 encoding, including:
+ * * with or without line breaks
+ * * with or without the final group padded with '=' or '_' characters
+ * (The most economical Base-64 variant does not pad the last group and
+ * omits line breaks; RFC1341 used for MIME requires both.)
+ */
+static char *
+base64_decode(const char *s, size_t len, size_t *out_len)
+{
+ static const unsigned char digits[64] = {
+ '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','+','/' };
+ static unsigned char decode_table[128];
+ char *out, *d;
+ const unsigned char *src = (const unsigned char *)s;
+
+ /* If the decode table is not yet initialized, prepare it. */
+ if (decode_table[digits[1]] != 1) {
+ unsigned i;
+ memset(decode_table, 0xff, sizeof(decode_table));
+ for (i = 0; i < sizeof(digits); i++)
+ decode_table[digits[i]] = i;
+ }
+
+ /* Allocate enough space to hold the entire output. */
+ /* Note that we may not use all of this... */
+ out = (char *)malloc(len - len / 4 + 1);
+ if (out == NULL) {
+ *out_len = 0;
+ return (NULL);
+ }
+ d = out;
+
+ while (len > 0) {
+ /* Collect the next group of (up to) four characters. */
+ int v = 0;
+ int group_size = 0;
+ while (group_size < 4 && len > 0) {
+ /* '=' or '_' padding indicates final group. */
+ if (*src == '=' || *src == '_') {
+ len = 0;
+ break;
+ }
+ /* Skip illegal characters (including line breaks) */
+ if (*src > 127 || *src < 32
+ || decode_table[*src] == 0xff) {
+ len--;
+ src++;
+ continue;
+ }
+ v <<= 6;
+ v |= decode_table[*src++];
+ len --;
+ group_size++;
+ }
+ /* Align a short group properly. */
+ v <<= 6 * (4 - group_size);
+ /* Unpack the group we just collected. */
+ switch (group_size) {
+ case 4: d[2] = v & 0xff;
+ /* FALLTHROUGH */
+ case 3: d[1] = (v >> 8) & 0xff;
+ /* FALLTHROUGH */
+ case 2: d[0] = (v >> 16) & 0xff;
+ break;
+ case 1: /* this is invalid! */
+ break;
+ }
+ d += group_size * 3 / 4;
+ }
+
+ *out_len = d - out;
+ return (out);
+}
+
+static char *
+url_decode(const char *in)
+{
+ char *out, *d;
+ const char *s;
+
+ out = (char *)malloc(strlen(in) + 1);
+ if (out == NULL)
+ return (NULL);
+ for (s = in, d = out; *s != '\0'; ) {
+ if (s[0] == '%' && s[1] != '\0' && s[2] != '\0') {
+ /* Try to convert % escape */
+ int digit1 = tohex(s[1]);
+ int digit2 = tohex(s[2]);
+ if (digit1 >= 0 && digit2 >= 0) {
+ /* Looks good, consume three chars */
+ s += 3;
+ /* Convert output */
+ *d++ = ((digit1 << 4) | digit2);
+ continue;
+ }
+ /* Else fall through and treat '%' as normal char */
+ }
+ *d++ = *s++;
+ }
+ *d = '\0';
+ return (out);
+}
+
+static int
+tohex(int c)
+{
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ else if (c >= 'A' && c <= 'F')
+ return (c - 'A' + 10);
+ else if (c >= 'a' && c <= 'f')
+ return (c - 'a' + 10);
+ else
+ return (-1);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_warc.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_warc.c
new file mode 100644
index 00000000000..61ab29ea145
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_warc.c
@@ -0,0 +1,848 @@
+/*-
+ * Copyright (c) 2014 Sebastian Freundt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+/**
+ * WARC is standardised by ISO TC46/SC4/WG12 and currently available as
+ * ISO 28500:2009.
+ * For the purposes of this file we used the final draft from:
+ * http://bibnum.bnf.fr/warc/WARC_ISO_28500_version1_latestdraft.pdf
+ *
+ * Todo:
+ * [ ] real-world warcs can contain resources at endpoints ending in /
+ * e.g. http://bibnum.bnf.fr/warc/
+ * if you're lucky their response contains a Content-Location: header
+ * pointing to a unix-compliant filename, in the example above it's
+ * Content-Location: http://bibnum.bnf.fr/warc/index.html
+ * however, that's not mandated and github for example doesn't follow
+ * this convention.
+ * We need a set of archive options to control what to do with
+ * entries like these, at the moment care is taken to skip them.
+ *
+ **/
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+typedef enum {
+ WT_NONE,
+ /* warcinfo */
+ WT_INFO,
+ /* metadata */
+ WT_META,
+ /* resource */
+ WT_RSRC,
+ /* request, unsupported */
+ WT_REQ,
+ /* response, unsupported */
+ WT_RSP,
+ /* revisit, unsupported */
+ WT_RVIS,
+ /* conversion, unsupported */
+ WT_CONV,
+ /* continuation, unsupported at the moment */
+ WT_CONT,
+ /* invalid type */
+ LAST_WT
+} warc_type_t;
+
+typedef struct {
+ size_t len;
+ const char *str;
+} warc_string_t;
+
+typedef struct {
+ size_t len;
+ char *str;
+} warc_strbuf_t;
+
+struct warc_s {
+ /* content length ahead */
+ size_t cntlen;
+ /* and how much we've processed so far */
+ size_t cntoff;
+ /* and how much we need to consume between calls */
+ size_t unconsumed;
+
+ /* string pool */
+ warc_strbuf_t pool;
+ /* previous version */
+ unsigned int pver;
+ /* stringified format name */
+ struct archive_string sver;
+};
+
+static int _warc_bid(struct archive_read *a, int);
+static int _warc_cleanup(struct archive_read *a);
+static int _warc_read(struct archive_read*, const void**, size_t*, int64_t*);
+static int _warc_skip(struct archive_read *a);
+static int _warc_rdhdr(struct archive_read *a, struct archive_entry *e);
+
+/* private routines */
+static unsigned int _warc_rdver(const char *buf, size_t bsz);
+static unsigned int _warc_rdtyp(const char *buf, size_t bsz);
+static warc_string_t _warc_rduri(const char *buf, size_t bsz);
+static ssize_t _warc_rdlen(const char *buf, size_t bsz);
+static time_t _warc_rdrtm(const char *buf, size_t bsz);
+static time_t _warc_rdmtm(const char *buf, size_t bsz);
+static const char *_warc_find_eoh(const char *buf, size_t bsz);
+static const char *_warc_find_eol(const char *buf, size_t bsz);
+
+int
+archive_read_support_format_warc(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct warc_s *w;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_warc");
+
+ if ((w = calloc(1, sizeof(*w))) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate warc data");
+ return (ARCHIVE_FATAL);
+ }
+
+ r = __archive_read_register_format(
+ a, w, "warc",
+ _warc_bid, NULL, _warc_rdhdr, _warc_read,
+ _warc_skip, NULL, _warc_cleanup, NULL, NULL);
+
+ if (r != ARCHIVE_OK) {
+ free(w);
+ return (r);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+_warc_cleanup(struct archive_read *a)
+{
+ struct warc_s *w = a->format->data;
+
+ if (w->pool.len > 0U) {
+ free(w->pool.str);
+ }
+ archive_string_free(&w->sver);
+ free(w);
+ a->format->data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+_warc_bid(struct archive_read *a, int best_bid)
+{
+ const char *hdr;
+ ssize_t nrd;
+ unsigned int ver;
+
+ (void)best_bid; /* UNUSED */
+
+ /* check first line of file, it should be a record already */
+ if ((hdr = __archive_read_ahead(a, 12U, &nrd)) == NULL) {
+ /* no idea what to do */
+ return -1;
+ } else if (nrd < 12) {
+ /* nah, not for us, our magic cookie is at least 12 bytes */
+ return -1;
+ }
+
+ /* otherwise snarf the record's version number */
+ ver = _warc_rdver(hdr, nrd);
+ if (ver < 1200U || ver > 10000U) {
+ /* we only support WARC 0.12 to 1.0 */
+ return -1;
+ }
+
+ /* otherwise be confident */
+ return (64);
+}
+
+static int
+_warc_rdhdr(struct archive_read *a, struct archive_entry *entry)
+{
+#define HDR_PROBE_LEN (12U)
+ struct warc_s *w = a->format->data;
+ unsigned int ver;
+ const char *buf;
+ ssize_t nrd;
+ const char *eoh;
+ /* for the file name, saves some strndup()'ing */
+ warc_string_t fnam;
+ /* warc record type, not that we really use it a lot */
+ warc_type_t ftyp;
+ /* content-length+error monad */
+ ssize_t cntlen;
+ /* record time is the WARC-Date time we reinterpret it as ctime */
+ time_t rtime;
+ /* mtime is the Last-Modified time which will be the entry's mtime */
+ time_t mtime;
+
+start_over:
+ /* just use read_ahead() they keep track of unconsumed
+ * bits and bobs for us; no need to put an extra shift in
+ * and reproduce that functionality here */
+ buf = __archive_read_ahead(a, HDR_PROBE_LEN, &nrd);
+
+ if (nrd < 0) {
+ /* no good */
+ archive_set_error(
+ &a->archive, ARCHIVE_ERRNO_MISC,
+ "Bad record header");
+ return (ARCHIVE_FATAL);
+ } else if (buf == NULL) {
+ /* there should be room for at least WARC/bla\r\n
+ * must be EOF therefore */
+ return (ARCHIVE_EOF);
+ }
+ /* looks good so far, try and find the end of the header now */
+ eoh = _warc_find_eoh(buf, nrd);
+ if (eoh == NULL) {
+ /* still no good, the header end might be beyond the
+ * probe we've requested, but then again who'd cram
+ * so much stuff into the header *and* be 28500-compliant */
+ archive_set_error(
+ &a->archive, ARCHIVE_ERRNO_MISC,
+ "Bad record header");
+ return (ARCHIVE_FATAL);
+ }
+ ver = _warc_rdver(buf, eoh - buf);
+ /* we currently support WARC 0.12 to 1.0 */
+ if (ver == 0U) {
+ archive_set_error(
+ &a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid record version");
+ return (ARCHIVE_FATAL);
+ } else if (ver < 1200U || ver > 10000U) {
+ archive_set_error(
+ &a->archive, ARCHIVE_ERRNO_MISC,
+ "Unsupported record version: %u.%u",
+ ver / 10000, (ver % 10000) / 100);
+ return (ARCHIVE_FATAL);
+ }
+ cntlen = _warc_rdlen(buf, eoh - buf);
+ if (cntlen < 0) {
+ /* nightmare! the specs say content-length is mandatory
+ * so I don't feel overly bad stopping the reader here */
+ archive_set_error(
+ &a->archive, EINVAL,
+ "Bad content length");
+ return (ARCHIVE_FATAL);
+ }
+ rtime = _warc_rdrtm(buf, eoh - buf);
+ if (rtime == (time_t)-1) {
+ /* record time is mandatory as per WARC/1.0,
+ * so just barf here, fast and loud */
+ archive_set_error(
+ &a->archive, EINVAL,
+ "Bad record time");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* let the world know we're a WARC archive */
+ a->archive.archive_format = ARCHIVE_FORMAT_WARC;
+ if (ver != w->pver) {
+ /* stringify this entry's version */
+ archive_string_sprintf(&w->sver,
+ "WARC/%u.%u", ver / 10000, (ver % 10000) / 100);
+ /* remember the version */
+ w->pver = ver;
+ }
+ /* start off with the type */
+ ftyp = _warc_rdtyp(buf, eoh - buf);
+ /* and let future calls know about the content */
+ w->cntlen = cntlen;
+ w->cntoff = 0U;
+ mtime = 0;/* Avoid compiling error on some platform. */
+
+ switch (ftyp) {
+ case WT_RSRC:
+ case WT_RSP:
+ /* only try and read the filename in the cases that are
+ * guaranteed to have one */
+ fnam = _warc_rduri(buf, eoh - buf);
+ /* check the last character in the URI to avoid creating
+ * directory endpoints as files, see Todo above */
+ if (fnam.len == 0 || fnam.str[fnam.len - 1] == '/') {
+ /* break here for now */
+ fnam.len = 0U;
+ fnam.str = NULL;
+ break;
+ }
+ /* bang to our string pool, so we save a
+ * malloc()+free() roundtrip */
+ if (fnam.len + 1U > w->pool.len) {
+ w->pool.len = ((fnam.len + 64U) / 64U) * 64U;
+ w->pool.str = realloc(w->pool.str, w->pool.len);
+ }
+ memcpy(w->pool.str, fnam.str, fnam.len);
+ w->pool.str[fnam.len] = '\0';
+ /* let no one else know about the pool, it's a secret, shhh */
+ fnam.str = w->pool.str;
+
+ /* snarf mtime or deduce from rtime
+ * this is a custom header added by our writer, it's quite
+ * hard to believe anyone else would go through with it
+ * (apart from being part of some http responses of course) */
+ if ((mtime = _warc_rdmtm(buf, eoh - buf)) == (time_t)-1) {
+ mtime = rtime;
+ }
+ break;
+ case WT_NONE:
+ case WT_INFO:
+ case WT_META:
+ case WT_REQ:
+ case WT_RVIS:
+ case WT_CONV:
+ case WT_CONT:
+ case LAST_WT:
+ default:
+ fnam.len = 0U;
+ fnam.str = NULL;
+ break;
+ }
+
+ /* now eat some of those delicious buffer bits */
+ __archive_read_consume(a, eoh - buf);
+
+ switch (ftyp) {
+ case WT_RSRC:
+ case WT_RSP:
+ if (fnam.len > 0U) {
+ /* populate entry object */
+ archive_entry_set_filetype(entry, AE_IFREG);
+ archive_entry_copy_pathname(entry, fnam.str);
+ archive_entry_set_size(entry, cntlen);
+ archive_entry_set_perm(entry, 0644);
+ /* rtime is the new ctime, mtime stays mtime */
+ archive_entry_set_ctime(entry, rtime, 0L);
+ archive_entry_set_mtime(entry, mtime, 0L);
+ break;
+ }
+ /* FALLTHROUGH */
+ case WT_NONE:
+ case WT_INFO:
+ case WT_META:
+ case WT_REQ:
+ case WT_RVIS:
+ case WT_CONV:
+ case WT_CONT:
+ case LAST_WT:
+ default:
+ /* consume the content and start over */
+ _warc_skip(a);
+ goto start_over;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+_warc_read(struct archive_read *a, const void **buf, size_t *bsz, int64_t *off)
+{
+ struct warc_s *w = a->format->data;
+ const char *rab;
+ ssize_t nrd;
+
+ if (w->cntoff >= w->cntlen) {
+ eof:
+ /* it's our lucky day, no work, we can leave early */
+ *buf = NULL;
+ *bsz = 0U;
+ *off = w->cntoff + 4U/*for \r\n\r\n separator*/;
+ w->unconsumed = 0U;
+ return (ARCHIVE_EOF);
+ }
+
+ if (w->unconsumed) {
+ __archive_read_consume(a, w->unconsumed);
+ w->unconsumed = 0U;
+ }
+
+ rab = __archive_read_ahead(a, 1U, &nrd);
+ if (nrd < 0) {
+ *bsz = 0U;
+ /* big catastrophe */
+ return (int)nrd;
+ } else if (nrd == 0) {
+ goto eof;
+ } else if ((size_t)nrd > w->cntlen - w->cntoff) {
+ /* clamp to content-length */
+ nrd = w->cntlen - w->cntoff;
+ }
+ *off = w->cntoff;
+ *bsz = nrd;
+ *buf = rab;
+
+ w->cntoff += nrd;
+ w->unconsumed = (size_t)nrd;
+ return (ARCHIVE_OK);
+}
+
+static int
+_warc_skip(struct archive_read *a)
+{
+ struct warc_s *w = a->format->data;
+
+ __archive_read_consume(a, w->cntlen + 4U/*\r\n\r\n separator*/);
+ w->cntlen = 0U;
+ w->cntoff = 0U;
+ return (ARCHIVE_OK);
+}
+
+
+/* private routines */
+static void*
+deconst(const void *c)
+{
+ return (void *)(uintptr_t)c;
+}
+
+static char*
+xmemmem(const char *hay, const size_t haysize,
+ const char *needle, const size_t needlesize)
+{
+ const char *const eoh = hay + haysize;
+ const char *const eon = needle + needlesize;
+ const char *hp;
+ const char *np;
+ const char *cand;
+ unsigned int hsum;
+ unsigned int nsum;
+ unsigned int eqp;
+
+ /* trivial checks first
+ * a 0-sized needle is defined to be found anywhere in haystack
+ * then run strchr() to find a candidate in HAYSTACK (i.e. a portion
+ * that happens to begin with *NEEDLE) */
+ if (needlesize == 0UL) {
+ return deconst(hay);
+ } else if ((hay = memchr(hay, *needle, haysize)) == NULL) {
+ /* trivial */
+ return NULL;
+ }
+
+ /* First characters of haystack and needle are the same now. Both are
+ * guaranteed to be at least one character long. Now computes the sum
+ * of characters values of needle together with the sum of the first
+ * needle_len characters of haystack. */
+ for (hp = hay + 1U, np = needle + 1U, hsum = *hay, nsum = *hay, eqp = 1U;
+ hp < eoh && np < eon;
+ hsum ^= *hp, nsum ^= *np, eqp &= *hp == *np, hp++, np++);
+
+ /* HP now references the (NEEDLESIZE + 1)-th character. */
+ if (np < eon) {
+ /* haystack is smaller than needle, :O */
+ return NULL;
+ } else if (eqp) {
+ /* found a match */
+ return deconst(hay);
+ }
+
+ /* now loop through the rest of haystack,
+ * updating the sum iteratively */
+ for (cand = hay; hp < eoh; hp++) {
+ hsum ^= *cand++;
+ hsum ^= *hp;
+
+ /* Since the sum of the characters is already known to be
+ * equal at that point, it is enough to check just NEEDLESIZE - 1
+ * characters for equality,
+ * also CAND is by design < HP, so no need for range checks */
+ if (hsum == nsum && memcmp(cand, needle, needlesize - 1U) == 0) {
+ return deconst(cand);
+ }
+ }
+ return NULL;
+}
+
+static int
+strtoi_lim(const char *str, const char **ep, int llim, int ulim)
+{
+ int res = 0;
+ const char *sp;
+ /* we keep track of the number of digits via rulim */
+ int rulim;
+
+ for (sp = str, rulim = ulim > 10 ? ulim : 10;
+ res * 10 <= ulim && rulim && *sp >= '0' && *sp <= '9';
+ sp++, rulim /= 10) {
+ res *= 10;
+ res += *sp - '0';
+ }
+ if (sp == str) {
+ res = -1;
+ } else if (res < llim || res > ulim) {
+ res = -2;
+ }
+ *ep = (const char*)sp;
+ return res;
+}
+
+static time_t
+time_from_tm(struct tm *t)
+{
+#if HAVE__MKGMTIME
+ return _mkgmtime(t);
+#elif HAVE_TIMEGM
+ /* Use platform timegm() if available. */
+ return (timegm(t));
+#else
+ /* Else use direct calculation using POSIX assumptions. */
+ /* First, fix up tm_yday based on the year/month/day. */
+ if (mktime(t) == (time_t)-1)
+ return ((time_t)-1);
+ /* Then we can compute timegm() from first principles. */
+ return (t->tm_sec
+ + t->tm_min * 60
+ + t->tm_hour * 3600
+ + t->tm_yday * 86400
+ + (t->tm_year - 70) * 31536000
+ + ((t->tm_year - 69) / 4) * 86400
+ - ((t->tm_year - 1) / 100) * 86400
+ + ((t->tm_year + 299) / 400) * 86400);
+#endif
+}
+
+static time_t
+xstrpisotime(const char *s, char **endptr)
+{
+/** like strptime() but strictly for ISO 8601 Zulu strings */
+ struct tm tm;
+ time_t res = (time_t)-1;
+
+ /* make sure tm is clean */
+ memset(&tm, 0, sizeof(tm));
+
+ /* as a courtesy to our callers, and since this is a non-standard
+ * routine, we skip leading whitespace */
+ while (*s == ' ' || *s == '\t')
+ ++s;
+
+ /* read year */
+ if ((tm.tm_year = strtoi_lim(s, &s, 1583, 4095)) < 0 || *s++ != '-') {
+ goto out;
+ }
+ /* read month */
+ if ((tm.tm_mon = strtoi_lim(s, &s, 1, 12)) < 0 || *s++ != '-') {
+ goto out;
+ }
+ /* read day-of-month */
+ if ((tm.tm_mday = strtoi_lim(s, &s, 1, 31)) < 0 || *s++ != 'T') {
+ goto out;
+ }
+ /* read hour */
+ if ((tm.tm_hour = strtoi_lim(s, &s, 0, 23)) < 0 || *s++ != ':') {
+ goto out;
+ }
+ /* read minute */
+ if ((tm.tm_min = strtoi_lim(s, &s, 0, 59)) < 0 || *s++ != ':') {
+ goto out;
+ }
+ /* read second */
+ if ((tm.tm_sec = strtoi_lim(s, &s, 0, 60)) < 0 || *s++ != 'Z') {
+ goto out;
+ }
+
+ /* massage TM to fulfill some of POSIX' constraints */
+ tm.tm_year -= 1900;
+ tm.tm_mon--;
+
+ /* now convert our custom tm struct to a unix stamp using UTC */
+ res = time_from_tm(&tm);
+
+out:
+ if (endptr != NULL) {
+ *endptr = deconst(s);
+ }
+ return res;
+}
+
+static unsigned int
+_warc_rdver(const char *buf, size_t bsz)
+{
+ static const char magic[] = "WARC/";
+ const char *c;
+ unsigned int ver = 0U;
+ unsigned int end = 0U;
+
+ if (bsz < 12 || memcmp(buf, magic, sizeof(magic) - 1U) != 0) {
+ /* buffer too small or invalid magic */
+ return ver;
+ }
+ /* looks good so far, read the version number for a laugh */
+ buf += sizeof(magic) - 1U;
+
+ if (isdigit((unsigned char)buf[0U]) && (buf[1U] == '.') &&
+ isdigit((unsigned char)buf[2U])) {
+ /* we support a maximum of 2 digits in the minor version */
+ if (isdigit((unsigned char)buf[3U]))
+ end = 1U;
+ /* set up major version */
+ ver = (buf[0U] - '0') * 10000U;
+ /* set up minor version */
+ if (end == 1U) {
+ ver += (buf[2U] - '0') * 1000U;
+ ver += (buf[3U] - '0') * 100U;
+ } else
+ ver += (buf[2U] - '0') * 100U;
+ /*
+ * WARC below version 0.12 has a space-separated header
+ * WARC 0.12 and above terminates the version with a CRLF
+ */
+ c = buf + 3U + end;
+ if (ver >= 1200U) {
+ if (memcmp(c, "\r\n", 2U) != 0)
+ ver = 0U;
+ } else {
+ /* ver < 1200U */
+ if (*c != ' ' && *c != '\t')
+ ver = 0U;
+ }
+ }
+ return ver;
+}
+
+static unsigned int
+_warc_rdtyp(const char *buf, size_t bsz)
+{
+ static const char _key[] = "\r\nWARC-Type:";
+ const char *val, *eol;
+
+ if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
+ /* no bother */
+ return WT_NONE;
+ }
+ val += sizeof(_key) - 1U;
+ if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
+ /* no end of line */
+ return WT_NONE;
+ }
+
+ /* overread whitespace */
+ while (val < eol && (*val == ' ' || *val == '\t'))
+ ++val;
+
+ if (val + 8U == eol) {
+ if (memcmp(val, "resource", 8U) == 0)
+ return WT_RSRC;
+ else if (memcmp(val, "response", 8U) == 0)
+ return WT_RSP;
+ }
+ return WT_NONE;
+}
+
+static warc_string_t
+_warc_rduri(const char *buf, size_t bsz)
+{
+ static const char _key[] = "\r\nWARC-Target-URI:";
+ const char *val, *uri, *eol, *p;
+ warc_string_t res = {0U, NULL};
+
+ if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
+ /* no bother */
+ return res;
+ }
+ /* overread whitespace */
+ val += sizeof(_key) - 1U;
+ if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
+ /* no end of line */
+ return res;
+ }
+
+ while (val < eol && (*val == ' ' || *val == '\t'))
+ ++val;
+
+ /* overread URL designators */
+ if ((uri = xmemmem(val, eol - val, "://", 3U)) == NULL) {
+ /* not touching that! */
+ return res;
+ }
+
+ /* spaces inside uri are not allowed, CRLF should follow */
+ for (p = val; p < eol; p++) {
+ if (isspace((unsigned char)*p))
+ return res;
+ }
+
+ /* there must be at least space for ftp */
+ if (uri < (val + 3U))
+ return res;
+
+ /* move uri to point to after :// */
+ uri += 3U;
+
+ /* now then, inspect the URI */
+ if (memcmp(val, "file", 4U) == 0) {
+ /* perfect, nothing left to do here */
+
+ } else if (memcmp(val, "http", 4U) == 0 ||
+ memcmp(val, "ftp", 3U) == 0) {
+ /* overread domain, and the first / */
+ while (uri < eol && *uri++ != '/');
+ } else {
+ /* not sure what to do? best to bugger off */
+ return res;
+ }
+ res.str = uri;
+ res.len = eol - uri;
+ return res;
+}
+
+static ssize_t
+_warc_rdlen(const char *buf, size_t bsz)
+{
+ static const char _key[] = "\r\nContent-Length:";
+ const char *val, *eol;
+ char *on = NULL;
+ long int len;
+
+ if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
+ /* no bother */
+ return -1;
+ }
+ val += sizeof(_key) - 1U;
+ if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
+ /* no end of line */
+ return -1;
+ }
+
+ /* skip leading whitespace */
+ while (val < eol && (*val == ' ' || *val == '\t'))
+ val++;
+ /* there must be at least one digit */
+ if (!isdigit((unsigned char)*val))
+ return -1;
+ errno = 0;
+ len = strtol(val, &on, 10);
+ if (errno != 0 || on != eol) {
+ /* line must end here */
+ return -1;
+ }
+
+ return (size_t)len;
+}
+
+static time_t
+_warc_rdrtm(const char *buf, size_t bsz)
+{
+ static const char _key[] = "\r\nWARC-Date:";
+ const char *val, *eol;
+ char *on = NULL;
+ time_t res;
+
+ if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
+ /* no bother */
+ return (time_t)-1;
+ }
+ val += sizeof(_key) - 1U;
+ if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) {
+ /* no end of line */
+ return -1;
+ }
+
+ /* xstrpisotime() kindly overreads whitespace for us, so use that */
+ res = xstrpisotime(val, &on);
+ if (on != eol) {
+ /* line must end here */
+ return -1;
+ }
+ return res;
+}
+
+static time_t
+_warc_rdmtm(const char *buf, size_t bsz)
+{
+ static const char _key[] = "\r\nLast-Modified:";
+ const char *val, *eol;
+ char *on = NULL;
+ time_t res;
+
+ if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
+ /* no bother */
+ return (time_t)-1;
+ }
+ val += sizeof(_key) - 1U;
+ if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) {
+ /* no end of line */
+ return -1;
+ }
+
+ /* xstrpisotime() kindly overreads whitespace for us, so use that */
+ res = xstrpisotime(val, &on);
+ if (on != eol) {
+ /* line must end here */
+ return -1;
+ }
+ return res;
+}
+
+static const char*
+_warc_find_eoh(const char *buf, size_t bsz)
+{
+ static const char _marker[] = "\r\n\r\n";
+ const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U);
+
+ if (hit != NULL) {
+ hit += sizeof(_marker) - 1U;
+ }
+ return hit;
+}
+
+static const char*
+_warc_find_eol(const char *buf, size_t bsz)
+{
+ static const char _marker[] = "\r\n";
+ const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U);
+
+ return hit;
+}
+/* archive_read_support_format_warc.c ends here */
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_xar.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_xar.c
new file mode 100644
index 00000000000..312a1db2d1e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_xar.c
@@ -0,0 +1,3332 @@
+/*-
+ * Copyright (c) 2009 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_LIBXML_XMLREADER_H
+#error #include <libxml/xmlreader.h>
+#elif HAVE_BSDXML_H
+#include <bsdxml.h>
+#elif HAVE_EXPAT_H
+#error #include <expat.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#if HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_digest_private.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#if (!defined(HAVE_LIBXML_XMLREADER_H) && \
+ !defined(HAVE_BSDXML_H) && !defined(HAVE_EXPAT_H)) ||\
+ !defined(HAVE_ZLIB_H) || \
+ !defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1)
+/*
+ * xar needs several external libraries.
+ * o libxml2 or expat --- XML parser
+ * o openssl or MD5/SHA1 hash function
+ * o zlib
+ * o bzlib2 (option)
+ * o liblzma (option)
+ */
+int
+archive_read_support_format_xar(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_xar");
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Xar not supported on this platform");
+ return (ARCHIVE_WARN);
+}
+
+#else /* Support xar format */
+
+/* #define DEBUG 1 */
+/* #define DEBUG_PRINT_TOC 1 */
+#if DEBUG_PRINT_TOC
+#define PRINT_TOC(d, outbytes) do { \
+ unsigned char *x = (unsigned char *)(uintptr_t)d; \
+ unsigned char c = x[outbytes-1]; \
+ x[outbytes - 1] = 0; \
+ fprintf(stderr, "%s", x); \
+ fprintf(stderr, "%c", c); \
+ x[outbytes - 1] = c; \
+} while (0)
+#else
+#define PRINT_TOC(d, outbytes)
+#endif
+
+#define HEADER_MAGIC 0x78617221
+#define HEADER_SIZE 28
+#define HEADER_VERSION 1
+#define CKSUM_NONE 0
+#define CKSUM_SHA1 1
+#define CKSUM_MD5 2
+
+#define MD5_SIZE 16
+#define SHA1_SIZE 20
+#define MAX_SUM_SIZE 20
+
+enum enctype {
+ NONE,
+ GZIP,
+ BZIP2,
+ LZMA,
+ XZ,
+};
+
+struct chksumval {
+ int alg;
+ size_t len;
+ unsigned char val[MAX_SUM_SIZE];
+};
+
+struct chksumwork {
+ int alg;
+#ifdef ARCHIVE_HAS_MD5
+ archive_md5_ctx md5ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+ archive_sha1_ctx sha1ctx;
+#endif
+};
+
+struct xattr {
+ struct xattr *next;
+ struct archive_string name;
+ uint64_t id;
+ uint64_t length;
+ uint64_t offset;
+ uint64_t size;
+ enum enctype encoding;
+ struct chksumval a_sum;
+ struct chksumval e_sum;
+ struct archive_string fstype;
+};
+
+struct xar_file {
+ struct xar_file *next;
+ struct xar_file *hdnext;
+ struct xar_file *parent;
+ int subdirs;
+
+ unsigned int has;
+#define HAS_DATA 0x00001
+#define HAS_PATHNAME 0x00002
+#define HAS_SYMLINK 0x00004
+#define HAS_TIME 0x00008
+#define HAS_UID 0x00010
+#define HAS_GID 0x00020
+#define HAS_MODE 0x00040
+#define HAS_TYPE 0x00080
+#define HAS_DEV 0x00100
+#define HAS_DEVMAJOR 0x00200
+#define HAS_DEVMINOR 0x00400
+#define HAS_INO 0x00800
+#define HAS_FFLAGS 0x01000
+#define HAS_XATTR 0x02000
+#define HAS_ACL 0x04000
+#define HAS_CTIME 0x08000
+#define HAS_MTIME 0x10000
+#define HAS_ATIME 0x20000
+
+ uint64_t id;
+ uint64_t length;
+ uint64_t offset;
+ uint64_t size;
+ enum enctype encoding;
+ struct chksumval a_sum;
+ struct chksumval e_sum;
+ struct archive_string pathname;
+ struct archive_string symlink;
+ time_t ctime;
+ time_t mtime;
+ time_t atime;
+ struct archive_string uname;
+ int64_t uid;
+ struct archive_string gname;
+ int64_t gid;
+ mode_t mode;
+ dev_t dev;
+ dev_t devmajor;
+ dev_t devminor;
+ int64_t ino64;
+ struct archive_string fflags_text;
+ unsigned int link;
+ unsigned int nlink;
+ struct archive_string hardlink;
+ struct xattr *xattr_list;
+};
+
+struct hdlink {
+ struct hdlink *next;
+
+ unsigned int id;
+ int cnt;
+ struct xar_file *files;
+};
+
+struct heap_queue {
+ struct xar_file **files;
+ int allocated;
+ int used;
+};
+
+enum xmlstatus {
+ INIT,
+ XAR,
+ TOC,
+ TOC_CREATION_TIME,
+ TOC_CHECKSUM,
+ TOC_CHECKSUM_OFFSET,
+ TOC_CHECKSUM_SIZE,
+ TOC_FILE,
+ FILE_DATA,
+ FILE_DATA_LENGTH,
+ FILE_DATA_OFFSET,
+ FILE_DATA_SIZE,
+ FILE_DATA_ENCODING,
+ FILE_DATA_A_CHECKSUM,
+ FILE_DATA_E_CHECKSUM,
+ FILE_DATA_CONTENT,
+ FILE_EA,
+ FILE_EA_LENGTH,
+ FILE_EA_OFFSET,
+ FILE_EA_SIZE,
+ FILE_EA_ENCODING,
+ FILE_EA_A_CHECKSUM,
+ FILE_EA_E_CHECKSUM,
+ FILE_EA_NAME,
+ FILE_EA_FSTYPE,
+ FILE_CTIME,
+ FILE_MTIME,
+ FILE_ATIME,
+ FILE_GROUP,
+ FILE_GID,
+ FILE_USER,
+ FILE_UID,
+ FILE_MODE,
+ FILE_DEVICE,
+ FILE_DEVICE_MAJOR,
+ FILE_DEVICE_MINOR,
+ FILE_DEVICENO,
+ FILE_INODE,
+ FILE_LINK,
+ FILE_TYPE,
+ FILE_NAME,
+ FILE_ACL,
+ FILE_ACL_DEFAULT,
+ FILE_ACL_ACCESS,
+ FILE_ACL_APPLEEXTENDED,
+ /* BSD file flags. */
+ FILE_FLAGS,
+ FILE_FLAGS_USER_NODUMP,
+ FILE_FLAGS_USER_IMMUTABLE,
+ FILE_FLAGS_USER_APPEND,
+ FILE_FLAGS_USER_OPAQUE,
+ FILE_FLAGS_USER_NOUNLINK,
+ FILE_FLAGS_SYS_ARCHIVED,
+ FILE_FLAGS_SYS_IMMUTABLE,
+ FILE_FLAGS_SYS_APPEND,
+ FILE_FLAGS_SYS_NOUNLINK,
+ FILE_FLAGS_SYS_SNAPSHOT,
+ /* Linux file flags. */
+ FILE_EXT2,
+ FILE_EXT2_SecureDeletion,
+ FILE_EXT2_Undelete,
+ FILE_EXT2_Compress,
+ FILE_EXT2_Synchronous,
+ FILE_EXT2_Immutable,
+ FILE_EXT2_AppendOnly,
+ FILE_EXT2_NoDump,
+ FILE_EXT2_NoAtime,
+ FILE_EXT2_CompDirty,
+ FILE_EXT2_CompBlock,
+ FILE_EXT2_NoCompBlock,
+ FILE_EXT2_CompError,
+ FILE_EXT2_BTree,
+ FILE_EXT2_HashIndexed,
+ FILE_EXT2_iMagic,
+ FILE_EXT2_Journaled,
+ FILE_EXT2_NoTail,
+ FILE_EXT2_DirSync,
+ FILE_EXT2_TopDir,
+ FILE_EXT2_Reserved,
+ UNKNOWN,
+};
+
+struct unknown_tag {
+ struct unknown_tag *next;
+ struct archive_string name;
+};
+
+struct xar {
+ uint64_t offset; /* Current position in the file. */
+ int64_t total;
+ uint64_t h_base;
+ int end_of_file;
+#define OUTBUFF_SIZE (1024 * 64)
+ unsigned char *outbuff;
+
+ enum xmlstatus xmlsts;
+ enum xmlstatus xmlsts_unknown;
+ struct unknown_tag *unknowntags;
+ int base64text;
+
+ /*
+ * TOC
+ */
+ uint64_t toc_remaining;
+ uint64_t toc_total;
+ uint64_t toc_chksum_offset;
+ uint64_t toc_chksum_size;
+
+ /*
+ * For Decoding data.
+ */
+ enum enctype rd_encoding;
+ z_stream stream;
+ int stream_valid;
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ bz_stream bzstream;
+ int bzstream_valid;
+#endif
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+ lzma_stream lzstream;
+ int lzstream_valid;
+#endif
+ /*
+ * For Checksum data.
+ */
+ struct chksumwork a_sumwrk;
+ struct chksumwork e_sumwrk;
+
+ struct xar_file *file; /* current reading file. */
+ struct xattr *xattr; /* current reading extended attribute. */
+ struct heap_queue file_queue;
+ struct xar_file *hdlink_orgs;
+ struct hdlink *hdlink_list;
+
+ int entry_init;
+ uint64_t entry_total;
+ uint64_t entry_remaining;
+ size_t entry_unconsumed;
+ uint64_t entry_size;
+ enum enctype entry_encoding;
+ struct chksumval entry_a_sum;
+ struct chksumval entry_e_sum;
+
+ struct archive_string_conv *sconv;
+};
+
+struct xmlattr {
+ struct xmlattr *next;
+ char *name;
+ char *value;
+};
+
+struct xmlattr_list {
+ struct xmlattr *first;
+ struct xmlattr **last;
+};
+
+static int xar_bid(struct archive_read *, int);
+static int xar_read_header(struct archive_read *,
+ struct archive_entry *);
+static int xar_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int xar_read_data_skip(struct archive_read *);
+static int xar_cleanup(struct archive_read *);
+static int move_reading_point(struct archive_read *, uint64_t);
+static int rd_contents_init(struct archive_read *,
+ enum enctype, int, int);
+static int rd_contents(struct archive_read *, const void **,
+ size_t *, size_t *, uint64_t);
+static uint64_t atol10(const char *, size_t);
+static int64_t atol8(const char *, size_t);
+static size_t atohex(unsigned char *, size_t, const char *, size_t);
+static time_t parse_time(const char *p, size_t n);
+static int heap_add_entry(struct archive_read *a,
+ struct heap_queue *, struct xar_file *);
+static struct xar_file *heap_get_entry(struct heap_queue *);
+static int add_link(struct archive_read *,
+ struct xar *, struct xar_file *);
+static void checksum_init(struct archive_read *, int, int);
+static void checksum_update(struct archive_read *, const void *,
+ size_t, const void *, size_t);
+static int checksum_final(struct archive_read *, const void *,
+ size_t, const void *, size_t);
+static void checksum_cleanup(struct archive_read *);
+static int decompression_init(struct archive_read *, enum enctype);
+static int decompress(struct archive_read *, const void **,
+ size_t *, const void *, size_t *);
+static int decompression_cleanup(struct archive_read *);
+static void xmlattr_cleanup(struct xmlattr_list *);
+static int file_new(struct archive_read *,
+ struct xar *, struct xmlattr_list *);
+static void file_free(struct xar_file *);
+static int xattr_new(struct archive_read *,
+ struct xar *, struct xmlattr_list *);
+static void xattr_free(struct xattr *);
+static int getencoding(struct xmlattr_list *);
+static int getsumalgorithm(struct xmlattr_list *);
+static int unknowntag_start(struct archive_read *,
+ struct xar *, const char *);
+static void unknowntag_end(struct xar *, const char *);
+static int xml_start(struct archive_read *,
+ const char *, struct xmlattr_list *);
+static void xml_end(void *, const char *);
+static void xml_data(void *, const char *, int);
+static int xml_parse_file_flags(struct xar *, const char *);
+static int xml_parse_file_ext2(struct xar *, const char *);
+#if defined(HAVE_LIBXML_XMLREADER_H)
+static int xml2_xmlattr_setup(struct archive_read *,
+ struct xmlattr_list *, xmlTextReaderPtr);
+static int xml2_read_cb(void *, char *, int);
+static int xml2_close_cb(void *);
+static void xml2_error_hdr(void *, const char *, xmlParserSeverities,
+ xmlTextReaderLocatorPtr);
+static int xml2_read_toc(struct archive_read *);
+#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H)
+struct expat_userData {
+ int state;
+ struct archive_read *archive;
+};
+static int expat_xmlattr_setup(struct archive_read *,
+ struct xmlattr_list *, const XML_Char **);
+static void expat_start_cb(void *, const XML_Char *, const XML_Char **);
+static void expat_end_cb(void *, const XML_Char *);
+static void expat_data_cb(void *, const XML_Char *, int);
+static int expat_read_toc(struct archive_read *);
+#endif
+
+int
+archive_read_support_format_xar(struct archive *_a)
+{
+ struct xar *xar;
+ struct archive_read *a = (struct archive_read *)_a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_xar");
+
+ xar = (struct xar *)calloc(1, sizeof(*xar));
+ if (xar == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate xar data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* initialize xar->file_queue */
+ xar->file_queue.allocated = 0;
+ xar->file_queue.used = 0;
+ xar->file_queue.files = NULL;
+
+ r = __archive_read_register_format(a,
+ xar,
+ "xar",
+ xar_bid,
+ NULL,
+ xar_read_header,
+ xar_read_data,
+ xar_read_data_skip,
+ NULL,
+ xar_cleanup,
+ NULL,
+ NULL);
+ if (r != ARCHIVE_OK)
+ free(xar);
+ return (r);
+}
+
+static int
+xar_bid(struct archive_read *a, int best_bid)
+{
+ const unsigned char *b;
+ int bid;
+
+ (void)best_bid; /* UNUSED */
+
+ b = __archive_read_ahead(a, HEADER_SIZE, NULL);
+ if (b == NULL)
+ return (-1);
+
+ bid = 0;
+ /*
+ * Verify magic code
+ */
+ if (archive_be32dec(b) != HEADER_MAGIC)
+ return (0);
+ bid += 32;
+ /*
+ * Verify header size
+ */
+ if (archive_be16dec(b+4) != HEADER_SIZE)
+ return (0);
+ bid += 16;
+ /*
+ * Verify header version
+ */
+ if (archive_be16dec(b+6) != HEADER_VERSION)
+ return (0);
+ bid += 16;
+ /*
+ * Verify type of checksum
+ */
+ switch (archive_be32dec(b+24)) {
+ case CKSUM_NONE:
+ case CKSUM_SHA1:
+ case CKSUM_MD5:
+ bid += 32;
+ break;
+ default:
+ return (0);
+ }
+
+ return (bid);
+}
+
+static int
+read_toc(struct archive_read *a)
+{
+ struct xar *xar;
+ struct xar_file *file;
+ const unsigned char *b;
+ uint64_t toc_compressed_size;
+ uint64_t toc_uncompressed_size;
+ uint32_t toc_chksum_alg;
+ ssize_t bytes;
+ int r;
+
+ xar = (struct xar *)(a->format->data);
+
+ /*
+ * Read xar header.
+ */
+ b = __archive_read_ahead(a, HEADER_SIZE, &bytes);
+ if (bytes < 0)
+ return ((int)bytes);
+ if (bytes < HEADER_SIZE) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated archive header");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (archive_be32dec(b) != HEADER_MAGIC) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid header magic");
+ return (ARCHIVE_FATAL);
+ }
+ if (archive_be16dec(b+6) != HEADER_VERSION) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported header version(%d)",
+ archive_be16dec(b+6));
+ return (ARCHIVE_FATAL);
+ }
+ toc_compressed_size = archive_be64dec(b+8);
+ xar->toc_remaining = toc_compressed_size;
+ toc_uncompressed_size = archive_be64dec(b+16);
+ toc_chksum_alg = archive_be32dec(b+24);
+ __archive_read_consume(a, HEADER_SIZE);
+ xar->offset += HEADER_SIZE;
+ xar->toc_total = 0;
+
+ /*
+ * Read TOC(Table of Contents).
+ */
+ /* Initialize reading contents. */
+ r = move_reading_point(a, HEADER_SIZE);
+ if (r != ARCHIVE_OK)
+ return (r);
+ r = rd_contents_init(a, GZIP, toc_chksum_alg, CKSUM_NONE);
+ if (r != ARCHIVE_OK)
+ return (r);
+
+#ifdef HAVE_LIBXML_XMLREADER_H
+ r = xml2_read_toc(a);
+#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H)
+ r = expat_read_toc(a);
+#endif
+ if (r != ARCHIVE_OK)
+ return (r);
+
+ /* Set 'The HEAP' base. */
+ xar->h_base = xar->offset;
+ if (xar->toc_total != toc_uncompressed_size) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "TOC uncompressed size error");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Checksum TOC
+ */
+ if (toc_chksum_alg != CKSUM_NONE) {
+ r = move_reading_point(a, xar->toc_chksum_offset);
+ if (r != ARCHIVE_OK)
+ return (r);
+ b = __archive_read_ahead(a,
+ (size_t)xar->toc_chksum_size, &bytes);
+ if (bytes < 0)
+ return ((int)bytes);
+ if ((uint64_t)bytes < xar->toc_chksum_size) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated archive file");
+ return (ARCHIVE_FATAL);
+ }
+ r = checksum_final(a, b,
+ (size_t)xar->toc_chksum_size, NULL, 0);
+ __archive_read_consume(a, xar->toc_chksum_size);
+ xar->offset += xar->toc_chksum_size;
+ if (r != ARCHIVE_OK)
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ return (ARCHIVE_FATAL);
+#endif
+ }
+
+ /*
+ * Connect hardlinked files.
+ */
+ for (file = xar->hdlink_orgs; file != NULL; file = file->hdnext) {
+ struct hdlink **hdlink;
+
+ for (hdlink = &(xar->hdlink_list); *hdlink != NULL;
+ hdlink = &((*hdlink)->next)) {
+ if ((*hdlink)->id == file->id) {
+ struct hdlink *hltmp;
+ struct xar_file *f2;
+ int nlink = (*hdlink)->cnt + 1;
+
+ file->nlink = nlink;
+ for (f2 = (*hdlink)->files; f2 != NULL;
+ f2 = f2->hdnext) {
+ f2->nlink = nlink;
+ archive_string_copy(
+ &(f2->hardlink), &(file->pathname));
+ }
+ /* Remove resolved files from hdlist_list. */
+ hltmp = *hdlink;
+ *hdlink = hltmp->next;
+ free(hltmp);
+ break;
+ }
+ }
+ }
+ a->archive.archive_format = ARCHIVE_FORMAT_XAR;
+ a->archive.archive_format_name = "xar";
+
+ return (ARCHIVE_OK);
+}
+
+static int
+xar_read_header(struct archive_read *a, struct archive_entry *entry)
+{
+ struct xar *xar;
+ struct xar_file *file;
+ struct xattr *xattr;
+ int r;
+
+ xar = (struct xar *)(a->format->data);
+ r = ARCHIVE_OK;
+
+ if (xar->offset == 0) {
+ /* Create a character conversion object. */
+ if (xar->sconv == NULL) {
+ xar->sconv = archive_string_conversion_from_charset(
+ &(a->archive), "UTF-8", 1);
+ if (xar->sconv == NULL)
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Read TOC. */
+ r = read_toc(a);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ for (;;) {
+ file = xar->file = heap_get_entry(&(xar->file_queue));
+ if (file == NULL) {
+ xar->end_of_file = 1;
+ return (ARCHIVE_EOF);
+ }
+ if ((file->mode & AE_IFMT) != AE_IFDIR)
+ break;
+ if (file->has != (HAS_PATHNAME | HAS_TYPE))
+ break;
+ /*
+ * If a file type is a directory and it does not have
+ * any metadata, do not export.
+ */
+ file_free(file);
+ }
+ if (file->has & HAS_ATIME) {
+ archive_entry_set_atime(entry, file->atime, 0);
+ }
+ if (file->has & HAS_CTIME) {
+ archive_entry_set_ctime(entry, file->ctime, 0);
+ }
+ if (file->has & HAS_MTIME) {
+ archive_entry_set_mtime(entry, file->mtime, 0);
+ }
+ archive_entry_set_gid(entry, file->gid);
+ if (file->gname.length > 0 &&
+ archive_entry_copy_gname_l(entry, file->gname.s,
+ archive_strlen(&(file->gname)), xar->sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Gname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Gname cannot be converted from %s to current locale.",
+ archive_string_conversion_charset_name(xar->sconv));
+ r = ARCHIVE_WARN;
+ }
+ archive_entry_set_uid(entry, file->uid);
+ if (file->uname.length > 0 &&
+ archive_entry_copy_uname_l(entry, file->uname.s,
+ archive_strlen(&(file->uname)), xar->sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Uname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Uname cannot be converted from %s to current locale.",
+ archive_string_conversion_charset_name(xar->sconv));
+ r = ARCHIVE_WARN;
+ }
+ archive_entry_set_mode(entry, file->mode);
+ if (archive_entry_copy_pathname_l(entry, file->pathname.s,
+ archive_strlen(&(file->pathname)), xar->sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname cannot be converted from %s to current locale.",
+ archive_string_conversion_charset_name(xar->sconv));
+ r = ARCHIVE_WARN;
+ }
+
+
+ if (file->symlink.length > 0 &&
+ archive_entry_copy_symlink_l(entry, file->symlink.s,
+ archive_strlen(&(file->symlink)), xar->sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Linkname cannot be converted from %s to current locale.",
+ archive_string_conversion_charset_name(xar->sconv));
+ r = ARCHIVE_WARN;
+ }
+ /* Set proper nlink. */
+ if ((file->mode & AE_IFMT) == AE_IFDIR)
+ archive_entry_set_nlink(entry, file->subdirs + 2);
+ else
+ archive_entry_set_nlink(entry, file->nlink);
+ archive_entry_set_size(entry, file->size);
+ if (archive_strlen(&(file->hardlink)) > 0)
+ archive_entry_set_hardlink(entry, file->hardlink.s);
+ archive_entry_set_ino64(entry, file->ino64);
+ if (file->has & HAS_DEV)
+ archive_entry_set_dev(entry, file->dev);
+ if (file->has & HAS_DEVMAJOR)
+ archive_entry_set_devmajor(entry, file->devmajor);
+ if (file->has & HAS_DEVMINOR)
+ archive_entry_set_devminor(entry, file->devminor);
+ if (archive_strlen(&(file->fflags_text)) > 0)
+ archive_entry_copy_fflags_text(entry, file->fflags_text.s);
+
+ xar->entry_init = 1;
+ xar->entry_total = 0;
+ xar->entry_remaining = file->length;
+ xar->entry_size = file->size;
+ xar->entry_encoding = file->encoding;
+ xar->entry_a_sum = file->a_sum;
+ xar->entry_e_sum = file->e_sum;
+ /*
+ * Read extended attributes.
+ */
+ xattr = file->xattr_list;
+ while (xattr != NULL) {
+ const void *d;
+ size_t outbytes = 0;
+ size_t used = 0;
+
+ r = move_reading_point(a, xattr->offset);
+ if (r != ARCHIVE_OK)
+ break;
+ r = rd_contents_init(a, xattr->encoding,
+ xattr->a_sum.alg, xattr->e_sum.alg);
+ if (r != ARCHIVE_OK)
+ break;
+ d = NULL;
+ r = rd_contents(a, &d, &outbytes, &used, xattr->length);
+ if (r != ARCHIVE_OK)
+ break;
+ if (outbytes != xattr->size) {
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "Decompressed size error");
+ r = ARCHIVE_FATAL;
+ break;
+ }
+ r = checksum_final(a,
+ xattr->a_sum.val, xattr->a_sum.len,
+ xattr->e_sum.val, xattr->e_sum.len);
+ if (r != ARCHIVE_OK) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "Xattr checksum error");
+ r = ARCHIVE_WARN;
+ break;
+#endif
+ }
+ if (xattr->name.s == NULL) {
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "Xattr name error");
+ r = ARCHIVE_WARN;
+ break;
+ }
+ archive_entry_xattr_add_entry(entry,
+ xattr->name.s, d, outbytes);
+ xattr = xattr->next;
+ }
+ if (r != ARCHIVE_OK) {
+ file_free(file);
+ return (r);
+ }
+
+ if (xar->entry_remaining > 0)
+ /* Move reading point to the beginning of current
+ * file contents. */
+ r = move_reading_point(a, file->offset);
+ else
+ r = ARCHIVE_OK;
+
+ file_free(file);
+ return (r);
+}
+
+static int
+xar_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ struct xar *xar;
+ size_t used = 0;
+ int r;
+
+ xar = (struct xar *)(a->format->data);
+
+ if (xar->entry_unconsumed) {
+ __archive_read_consume(a, xar->entry_unconsumed);
+ xar->entry_unconsumed = 0;
+ }
+
+ if (xar->end_of_file || xar->entry_remaining <= 0) {
+ r = ARCHIVE_EOF;
+ goto abort_read_data;
+ }
+
+ if (xar->entry_init) {
+ r = rd_contents_init(a, xar->entry_encoding,
+ xar->entry_a_sum.alg, xar->entry_e_sum.alg);
+ if (r != ARCHIVE_OK) {
+ xar->entry_remaining = 0;
+ return (r);
+ }
+ xar->entry_init = 0;
+ }
+
+ *buff = NULL;
+ r = rd_contents(a, buff, size, &used, xar->entry_remaining);
+ if (r != ARCHIVE_OK)
+ goto abort_read_data;
+
+ *offset = xar->entry_total;
+ xar->entry_total += *size;
+ xar->total += *size;
+ xar->offset += used;
+ xar->entry_remaining -= used;
+ xar->entry_unconsumed = used;
+
+ if (xar->entry_remaining == 0) {
+ if (xar->entry_total != xar->entry_size) {
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "Decompressed size error");
+ r = ARCHIVE_FATAL;
+ goto abort_read_data;
+ }
+ r = checksum_final(a,
+ xar->entry_a_sum.val, xar->entry_a_sum.len,
+ xar->entry_e_sum.val, xar->entry_e_sum.len);
+ if (r != ARCHIVE_OK)
+ goto abort_read_data;
+ }
+
+ return (ARCHIVE_OK);
+abort_read_data:
+ *buff = NULL;
+ *size = 0;
+ *offset = xar->total;
+ return (r);
+}
+
+static int
+xar_read_data_skip(struct archive_read *a)
+{
+ struct xar *xar;
+ int64_t bytes_skipped;
+
+ xar = (struct xar *)(a->format->data);
+ if (xar->end_of_file)
+ return (ARCHIVE_EOF);
+ bytes_skipped = __archive_read_consume(a, xar->entry_remaining +
+ xar->entry_unconsumed);
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+ xar->offset += bytes_skipped;
+ xar->entry_unconsumed = 0;
+ return (ARCHIVE_OK);
+}
+
+static int
+xar_cleanup(struct archive_read *a)
+{
+ struct xar *xar;
+ struct hdlink *hdlink;
+ int i;
+ int r;
+
+ xar = (struct xar *)(a->format->data);
+ checksum_cleanup(a);
+ r = decompression_cleanup(a);
+ hdlink = xar->hdlink_list;
+ while (hdlink != NULL) {
+ struct hdlink *next = hdlink->next;
+
+ free(hdlink);
+ hdlink = next;
+ }
+ for (i = 0; i < xar->file_queue.used; i++)
+ file_free(xar->file_queue.files[i]);
+ free(xar->file_queue.files);
+ while (xar->unknowntags != NULL) {
+ struct unknown_tag *tag;
+
+ tag = xar->unknowntags;
+ xar->unknowntags = tag->next;
+ archive_string_free(&(tag->name));
+ free(tag);
+ }
+ free(xar->outbuff);
+ free(xar);
+ a->format->data = NULL;
+ return (r);
+}
+
+static int
+move_reading_point(struct archive_read *a, uint64_t offset)
+{
+ struct xar *xar;
+
+ xar = (struct xar *)(a->format->data);
+ if (xar->offset - xar->h_base != offset) {
+ /* Seek forward to the start of file contents. */
+ int64_t step;
+
+ step = offset - (xar->offset - xar->h_base);
+ if (step > 0) {
+ step = __archive_read_consume(a, step);
+ if (step < 0)
+ return ((int)step);
+ xar->offset += step;
+ } else {
+ int64_t pos = __archive_read_seek(a, xar->h_base + offset, SEEK_SET);
+ if (pos == ARCHIVE_FAILED) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Cannot seek.");
+ return (ARCHIVE_FAILED);
+ }
+ xar->offset = pos;
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+rd_contents_init(struct archive_read *a, enum enctype encoding,
+ int a_sum_alg, int e_sum_alg)
+{
+ int r;
+
+ /* Init decompress library. */
+ if ((r = decompression_init(a, encoding)) != ARCHIVE_OK)
+ return (r);
+ /* Init checksum library. */
+ checksum_init(a, a_sum_alg, e_sum_alg);
+ return (ARCHIVE_OK);
+}
+
+static int
+rd_contents(struct archive_read *a, const void **buff, size_t *size,
+ size_t *used, uint64_t remaining)
+{
+ const unsigned char *b;
+ ssize_t bytes;
+
+ /* Get whatever bytes are immediately available. */
+ b = __archive_read_ahead(a, 1, &bytes);
+ if (bytes < 0)
+ return ((int)bytes);
+ if (bytes == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Truncated archive file");
+ return (ARCHIVE_FATAL);
+ }
+ if ((uint64_t)bytes > remaining)
+ bytes = (ssize_t)remaining;
+
+ /*
+ * Decompress contents of file.
+ */
+ *used = bytes;
+ if (decompress(a, buff, size, b, used) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /*
+ * Update checksum of a compressed data and a extracted data.
+ */
+ checksum_update(a, b, *used, *buff, *size);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Note that this implementation does not (and should not!) obey
+ * locale settings; you cannot simply substitute strtol here, since
+ * it does obey locale.
+ */
+
+static uint64_t
+atol10(const char *p, size_t char_cnt)
+{
+ uint64_t l;
+ int digit;
+
+ if (char_cnt == 0)
+ return (0);
+
+ l = 0;
+ digit = *p - '0';
+ while (digit >= 0 && digit < 10 && char_cnt-- > 0) {
+ l = (l * 10) + digit;
+ digit = *++p - '0';
+ }
+ return (l);
+}
+
+static int64_t
+atol8(const char *p, size_t char_cnt)
+{
+ int64_t l;
+ int digit;
+
+ if (char_cnt == 0)
+ return (0);
+
+ l = 0;
+ while (char_cnt-- > 0) {
+ if (*p >= '0' && *p <= '7')
+ digit = *p - '0';
+ else
+ break;
+ p++;
+ l <<= 3;
+ l |= digit;
+ }
+ return (l);
+}
+
+static size_t
+atohex(unsigned char *b, size_t bsize, const char *p, size_t psize)
+{
+ size_t fbsize = bsize;
+
+ while (bsize && psize > 1) {
+ unsigned char x;
+
+ if (p[0] >= 'a' && p[0] <= 'z')
+ x = (p[0] - 'a' + 0x0a) << 4;
+ else if (p[0] >= 'A' && p[0] <= 'Z')
+ x = (p[0] - 'A' + 0x0a) << 4;
+ else if (p[0] >= '0' && p[0] <= '9')
+ x = (p[0] - '0') << 4;
+ else
+ return (-1);
+ if (p[1] >= 'a' && p[1] <= 'z')
+ x |= p[1] - 'a' + 0x0a;
+ else if (p[1] >= 'A' && p[1] <= 'Z')
+ x |= p[1] - 'A' + 0x0a;
+ else if (p[1] >= '0' && p[1] <= '9')
+ x |= p[1] - '0';
+ else
+ return (-1);
+
+ *b++ = x;
+ bsize--;
+ p += 2;
+ psize -= 2;
+ }
+ return (fbsize - bsize);
+}
+
+static time_t
+time_from_tm(struct tm *t)
+{
+#if HAVE__MKGMTIME
+ return _mkgmtime(t);
+#elif HAVE_TIMEGM
+ /* Use platform timegm() if available. */
+ return (timegm(t));
+#else
+ /* Else use direct calculation using POSIX assumptions. */
+ /* First, fix up tm_yday based on the year/month/day. */
+ mktime(t);
+ /* Then we can compute timegm() from first principles. */
+ return (t->tm_sec
+ + t->tm_min * 60
+ + t->tm_hour * 3600
+ + t->tm_yday * 86400
+ + (t->tm_year - 70) * 31536000
+ + ((t->tm_year - 69) / 4) * 86400
+ - ((t->tm_year - 1) / 100) * 86400
+ + ((t->tm_year + 299) / 400) * 86400);
+#endif
+}
+
+static time_t
+parse_time(const char *p, size_t n)
+{
+ struct tm tm;
+ time_t t = 0;
+ int64_t data;
+
+ memset(&tm, 0, sizeof(tm));
+ if (n != 20)
+ return (t);
+ data = atol10(p, 4);
+ if (data < 1900)
+ return (t);
+ tm.tm_year = (int)data - 1900;
+ p += 4;
+ if (*p++ != '-')
+ return (t);
+ data = atol10(p, 2);
+ if (data < 1 || data > 12)
+ return (t);
+ tm.tm_mon = (int)data -1;
+ p += 2;
+ if (*p++ != '-')
+ return (t);
+ data = atol10(p, 2);
+ if (data < 1 || data > 31)
+ return (t);
+ tm.tm_mday = (int)data;
+ p += 2;
+ if (*p++ != 'T')
+ return (t);
+ data = atol10(p, 2);
+ if (data < 0 || data > 23)
+ return (t);
+ tm.tm_hour = (int)data;
+ p += 2;
+ if (*p++ != ':')
+ return (t);
+ data = atol10(p, 2);
+ if (data < 0 || data > 59)
+ return (t);
+ tm.tm_min = (int)data;
+ p += 2;
+ if (*p++ != ':')
+ return (t);
+ data = atol10(p, 2);
+ if (data < 0 || data > 60)
+ return (t);
+ tm.tm_sec = (int)data;
+#if 0
+ p += 2;
+ if (*p != 'Z')
+ return (t);
+#endif
+
+ t = time_from_tm(&tm);
+
+ return (t);
+}
+
+static int
+heap_add_entry(struct archive_read *a,
+ struct heap_queue *heap, struct xar_file *file)
+{
+ uint64_t file_id, parent_id;
+ int hole, parent;
+
+ /* Expand our pending files list as necessary. */
+ if (heap->used >= heap->allocated) {
+ struct xar_file **new_pending_files;
+ int new_size;
+
+ if (heap->allocated < 1024)
+ new_size = 1024;
+ else
+ new_size = heap->allocated * 2;
+ /* Overflow might keep us from growing the list. */
+ if (new_size <= heap->allocated) {
+ archive_set_error(&a->archive,
+ ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ new_pending_files = (struct xar_file **)
+ malloc(new_size * sizeof(new_pending_files[0]));
+ if (new_pending_files == NULL) {
+ archive_set_error(&a->archive,
+ ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ if (heap->allocated) {
+ memcpy(new_pending_files, heap->files,
+ heap->allocated * sizeof(new_pending_files[0]));
+ free(heap->files);
+ }
+ heap->files = new_pending_files;
+ heap->allocated = new_size;
+ }
+
+ file_id = file->id;
+
+ /*
+ * Start with hole at end, walk it up tree to find insertion point.
+ */
+ hole = heap->used++;
+ while (hole > 0) {
+ parent = (hole - 1)/2;
+ parent_id = heap->files[parent]->id;
+ if (file_id >= parent_id) {
+ heap->files[hole] = file;
+ return (ARCHIVE_OK);
+ }
+ /* Move parent into hole <==> move hole up tree. */
+ heap->files[hole] = heap->files[parent];
+ hole = parent;
+ }
+ heap->files[0] = file;
+
+ return (ARCHIVE_OK);
+}
+
+static struct xar_file *
+heap_get_entry(struct heap_queue *heap)
+{
+ uint64_t a_id, b_id, c_id;
+ int a, b, c;
+ struct xar_file *r, *tmp;
+
+ if (heap->used < 1)
+ return (NULL);
+
+ /*
+ * The first file in the list is the earliest; we'll return this.
+ */
+ r = heap->files[0];
+
+ /*
+ * Move the last item in the heap to the root of the tree
+ */
+ heap->files[0] = heap->files[--(heap->used)];
+
+ /*
+ * Rebalance the heap.
+ */
+ a = 0; /* Starting element and its heap key */
+ a_id = heap->files[a]->id;
+ for (;;) {
+ b = a + a + 1; /* First child */
+ if (b >= heap->used)
+ return (r);
+ b_id = heap->files[b]->id;
+ c = b + 1; /* Use second child if it is smaller. */
+ if (c < heap->used) {
+ c_id = heap->files[c]->id;
+ if (c_id < b_id) {
+ b = c;
+ b_id = c_id;
+ }
+ }
+ if (a_id <= b_id)
+ return (r);
+ tmp = heap->files[a];
+ heap->files[a] = heap->files[b];
+ heap->files[b] = tmp;
+ a = b;
+ }
+}
+
+static int
+add_link(struct archive_read *a, struct xar *xar, struct xar_file *file)
+{
+ struct hdlink *hdlink;
+
+ for (hdlink = xar->hdlink_list; hdlink != NULL; hdlink = hdlink->next) {
+ if (hdlink->id == file->link) {
+ file->hdnext = hdlink->files;
+ hdlink->cnt++;
+ hdlink->files = file;
+ return (ARCHIVE_OK);
+ }
+ }
+ hdlink = malloc(sizeof(*hdlink));
+ if (hdlink == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ file->hdnext = NULL;
+ hdlink->id = file->link;
+ hdlink->cnt = 1;
+ hdlink->files = file;
+ hdlink->next = xar->hdlink_list;
+ xar->hdlink_list = hdlink;
+ return (ARCHIVE_OK);
+}
+
+static void
+_checksum_init(struct chksumwork *sumwrk, int sum_alg)
+{
+ sumwrk->alg = sum_alg;
+ switch (sum_alg) {
+ case CKSUM_NONE:
+ break;
+ case CKSUM_SHA1:
+ archive_sha1_init(&(sumwrk->sha1ctx));
+ break;
+ case CKSUM_MD5:
+ archive_md5_init(&(sumwrk->md5ctx));
+ break;
+ }
+}
+
+static void
+_checksum_update(struct chksumwork *sumwrk, const void *buff, size_t size)
+{
+
+ switch (sumwrk->alg) {
+ case CKSUM_NONE:
+ break;
+ case CKSUM_SHA1:
+ archive_sha1_update(&(sumwrk->sha1ctx), buff, size);
+ break;
+ case CKSUM_MD5:
+ archive_md5_update(&(sumwrk->md5ctx), buff, size);
+ break;
+ }
+}
+
+static int
+_checksum_final(struct chksumwork *sumwrk, const void *val, size_t len)
+{
+ unsigned char sum[MAX_SUM_SIZE];
+ int r = ARCHIVE_OK;
+
+ switch (sumwrk->alg) {
+ case CKSUM_NONE:
+ break;
+ case CKSUM_SHA1:
+ archive_sha1_final(&(sumwrk->sha1ctx), sum);
+ if (len != SHA1_SIZE ||
+ memcmp(val, sum, SHA1_SIZE) != 0)
+ r = ARCHIVE_FAILED;
+ break;
+ case CKSUM_MD5:
+ archive_md5_final(&(sumwrk->md5ctx), sum);
+ if (len != MD5_SIZE ||
+ memcmp(val, sum, MD5_SIZE) != 0)
+ r = ARCHIVE_FAILED;
+ break;
+ }
+ return (r);
+}
+
+static void
+checksum_init(struct archive_read *a, int a_sum_alg, int e_sum_alg)
+{
+ struct xar *xar;
+
+ xar = (struct xar *)(a->format->data);
+ _checksum_init(&(xar->a_sumwrk), a_sum_alg);
+ _checksum_init(&(xar->e_sumwrk), e_sum_alg);
+}
+
+static void
+checksum_update(struct archive_read *a, const void *abuff, size_t asize,
+ const void *ebuff, size_t esize)
+{
+ struct xar *xar;
+
+ xar = (struct xar *)(a->format->data);
+ _checksum_update(&(xar->a_sumwrk), abuff, asize);
+ _checksum_update(&(xar->e_sumwrk), ebuff, esize);
+}
+
+static int
+checksum_final(struct archive_read *a, const void *a_sum_val,
+ size_t a_sum_len, const void *e_sum_val, size_t e_sum_len)
+{
+ struct xar *xar;
+ int r;
+
+ xar = (struct xar *)(a->format->data);
+ r = _checksum_final(&(xar->a_sumwrk), a_sum_val, a_sum_len);
+ if (r == ARCHIVE_OK)
+ r = _checksum_final(&(xar->e_sumwrk), e_sum_val, e_sum_len);
+ if (r != ARCHIVE_OK)
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "Sumcheck error");
+ return (r);
+}
+
+static int
+decompression_init(struct archive_read *a, enum enctype encoding)
+{
+ struct xar *xar;
+ const char *detail;
+ int r;
+
+ xar = (struct xar *)(a->format->data);
+ xar->rd_encoding = encoding;
+ switch (encoding) {
+ case NONE:
+ break;
+ case GZIP:
+ if (xar->stream_valid)
+ r = inflateReset(&(xar->stream));
+ else
+ r = inflateInit(&(xar->stream));
+ if (r != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Couldn't initialize zlib stream.");
+ return (ARCHIVE_FATAL);
+ }
+ xar->stream_valid = 1;
+ xar->stream.total_in = 0;
+ xar->stream.total_out = 0;
+ break;
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ case BZIP2:
+ if (xar->bzstream_valid) {
+ BZ2_bzDecompressEnd(&(xar->bzstream));
+ xar->bzstream_valid = 0;
+ }
+ r = BZ2_bzDecompressInit(&(xar->bzstream), 0, 0);
+ if (r == BZ_MEM_ERROR)
+ r = BZ2_bzDecompressInit(&(xar->bzstream), 0, 1);
+ if (r != BZ_OK) {
+ int err = ARCHIVE_ERRNO_MISC;
+ detail = NULL;
+ switch (r) {
+ case BZ_PARAM_ERROR:
+ detail = "invalid setup parameter";
+ break;
+ case BZ_MEM_ERROR:
+ err = ENOMEM;
+ detail = "out of memory";
+ break;
+ case BZ_CONFIG_ERROR:
+ detail = "mis-compiled library";
+ break;
+ }
+ archive_set_error(&a->archive, err,
+ "Internal error initializing decompressor: %s",
+ detail == NULL ? "??" : detail);
+ xar->bzstream_valid = 0;
+ return (ARCHIVE_FATAL);
+ }
+ xar->bzstream_valid = 1;
+ xar->bzstream.total_in_lo32 = 0;
+ xar->bzstream.total_in_hi32 = 0;
+ xar->bzstream.total_out_lo32 = 0;
+ xar->bzstream.total_out_hi32 = 0;
+ break;
+#endif
+#if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)
+#if LZMA_VERSION_MAJOR >= 5
+/* Effectively disable the limiter. */
+#define LZMA_MEMLIMIT UINT64_MAX
+#else
+/* NOTE: This needs to check memory size which running system has. */
+#define LZMA_MEMLIMIT (1U << 30)
+#endif
+ case XZ:
+ case LZMA:
+ if (xar->lzstream_valid) {
+ lzma_end(&(xar->lzstream));
+ xar->lzstream_valid = 0;
+ }
+ if (xar->entry_encoding == XZ)
+ r = lzma_stream_decoder(&(xar->lzstream),
+ LZMA_MEMLIMIT,/* memlimit */
+ LZMA_CONCATENATED);
+ else
+ r = lzma_alone_decoder(&(xar->lzstream),
+ LZMA_MEMLIMIT);/* memlimit */
+ if (r != LZMA_OK) {
+ switch (r) {
+ case LZMA_MEM_ERROR:
+ archive_set_error(&a->archive,
+ ENOMEM,
+ "Internal error initializing "
+ "compression library: "
+ "Cannot allocate memory");
+ break;
+ case LZMA_OPTIONS_ERROR:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Internal error initializing "
+ "compression library: "
+ "Invalid or unsupported options");
+ break;
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Internal error initializing "
+ "lzma library");
+ break;
+ }
+ return (ARCHIVE_FATAL);
+ }
+ xar->lzstream_valid = 1;
+ xar->lzstream.total_in = 0;
+ xar->lzstream.total_out = 0;
+ break;
+#endif
+ /*
+ * Unsupported compression.
+ */
+ default:
+#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR)
+ case BZIP2:
+#endif
+#if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA)
+ case LZMA:
+ case XZ:
+#endif
+ switch (xar->entry_encoding) {
+ case BZIP2: detail = "bzip2"; break;
+ case LZMA: detail = "lzma"; break;
+ case XZ: detail = "xz"; break;
+ default: detail = "??"; break;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s compression not supported on this platform",
+ detail);
+ return (ARCHIVE_FAILED);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+decompress(struct archive_read *a, const void **buff, size_t *outbytes,
+ const void *b, size_t *used)
+{
+ struct xar *xar;
+ void *outbuff;
+ size_t avail_in, avail_out;
+ int r;
+
+ xar = (struct xar *)(a->format->data);
+ avail_in = *used;
+ outbuff = (void *)(uintptr_t)*buff;
+ if (outbuff == NULL) {
+ if (xar->outbuff == NULL) {
+ xar->outbuff = malloc(OUTBUFF_SIZE);
+ if (xar->outbuff == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory for out buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ outbuff = xar->outbuff;
+ *buff = outbuff;
+ avail_out = OUTBUFF_SIZE;
+ } else
+ avail_out = *outbytes;
+ switch (xar->rd_encoding) {
+ case GZIP:
+ xar->stream.next_in = (Bytef *)(uintptr_t)b;
+ xar->stream.avail_in = avail_in;
+ xar->stream.next_out = (unsigned char *)outbuff;
+ xar->stream.avail_out = avail_out;
+ r = inflate(&(xar->stream), 0);
+ switch (r) {
+ case Z_OK: /* Decompressor made some progress.*/
+ case Z_STREAM_END: /* Found end of stream. */
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "File decompression failed (%d)", r);
+ return (ARCHIVE_FATAL);
+ }
+ *used = avail_in - xar->stream.avail_in;
+ *outbytes = avail_out - xar->stream.avail_out;
+ break;
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ case BZIP2:
+ xar->bzstream.next_in = (char *)(uintptr_t)b;
+ xar->bzstream.avail_in = avail_in;
+ xar->bzstream.next_out = (char *)outbuff;
+ xar->bzstream.avail_out = avail_out;
+ r = BZ2_bzDecompress(&(xar->bzstream));
+ switch (r) {
+ case BZ_STREAM_END: /* Found end of stream. */
+ switch (BZ2_bzDecompressEnd(&(xar->bzstream))) {
+ case BZ_OK:
+ break;
+ default:
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up decompressor");
+ return (ARCHIVE_FATAL);
+ }
+ xar->bzstream_valid = 0;
+ /* FALLTHROUGH */
+ case BZ_OK: /* Decompressor made some progress. */
+ break;
+ default:
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "bzip decompression failed");
+ return (ARCHIVE_FATAL);
+ }
+ *used = avail_in - xar->bzstream.avail_in;
+ *outbytes = avail_out - xar->bzstream.avail_out;
+ break;
+#endif
+#if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)
+ case LZMA:
+ case XZ:
+ xar->lzstream.next_in = b;
+ xar->lzstream.avail_in = avail_in;
+ xar->lzstream.next_out = (unsigned char *)outbuff;
+ xar->lzstream.avail_out = avail_out;
+ r = lzma_code(&(xar->lzstream), LZMA_RUN);
+ switch (r) {
+ case LZMA_STREAM_END: /* Found end of stream. */
+ lzma_end(&(xar->lzstream));
+ xar->lzstream_valid = 0;
+ /* FALLTHROUGH */
+ case LZMA_OK: /* Decompressor made some progress. */
+ break;
+ default:
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "%s decompression failed(%d)",
+ (xar->entry_encoding == XZ)?"xz":"lzma",
+ r);
+ return (ARCHIVE_FATAL);
+ }
+ *used = avail_in - xar->lzstream.avail_in;
+ *outbytes = avail_out - xar->lzstream.avail_out;
+ break;
+#endif
+#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR)
+ case BZIP2:
+#endif
+#if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA)
+ case LZMA:
+ case XZ:
+#endif
+ case NONE:
+ default:
+ if (outbuff == xar->outbuff) {
+ *buff = b;
+ *used = avail_in;
+ *outbytes = avail_in;
+ } else {
+ if (avail_out > avail_in)
+ avail_out = avail_in;
+ memcpy(outbuff, b, avail_out);
+ *used = avail_out;
+ *outbytes = avail_out;
+ }
+ break;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+decompression_cleanup(struct archive_read *a)
+{
+ struct xar *xar;
+ int r;
+
+ xar = (struct xar *)(a->format->data);
+ r = ARCHIVE_OK;
+ if (xar->stream_valid) {
+ if (inflateEnd(&(xar->stream)) != Z_OK) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up zlib decompressor");
+ r = ARCHIVE_FATAL;
+ }
+ }
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ if (xar->bzstream_valid) {
+ if (BZ2_bzDecompressEnd(&(xar->bzstream)) != BZ_OK) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up bzip2 decompressor");
+ r = ARCHIVE_FATAL;
+ }
+ }
+#endif
+#if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)
+ if (xar->lzstream_valid)
+ lzma_end(&(xar->lzstream));
+#elif defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)
+ if (xar->lzstream_valid) {
+ if (lzmadec_end(&(xar->lzstream)) != LZMADEC_OK) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up lzmadec decompressor");
+ r = ARCHIVE_FATAL;
+ }
+ }
+#endif
+ return (r);
+}
+
+static void
+checksum_cleanup(struct archive_read *a) {
+ struct xar *xar;
+
+ xar = (struct xar *)(a->format->data);
+
+ _checksum_final(&(xar->a_sumwrk), NULL, 0);
+ _checksum_final(&(xar->e_sumwrk), NULL, 0);
+}
+
+static void
+xmlattr_cleanup(struct xmlattr_list *list)
+{
+ struct xmlattr *attr, *next;
+
+ attr = list->first;
+ while (attr != NULL) {
+ next = attr->next;
+ free(attr->name);
+ free(attr->value);
+ free(attr);
+ attr = next;
+ }
+ list->first = NULL;
+ list->last = &(list->first);
+}
+
+static int
+file_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list)
+{
+ struct xar_file *file;
+ struct xmlattr *attr;
+
+ file = calloc(1, sizeof(*file));
+ if (file == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ file->parent = xar->file;
+ file->mode = 0777 | AE_IFREG;
+ file->atime = 0;
+ file->mtime = 0;
+ xar->file = file;
+ xar->xattr = NULL;
+ for (attr = list->first; attr != NULL; attr = attr->next) {
+ if (strcmp(attr->name, "id") == 0)
+ file->id = atol10(attr->value, strlen(attr->value));
+ }
+ file->nlink = 1;
+ if (heap_add_entry(a, &(xar->file_queue), file) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ return (ARCHIVE_OK);
+}
+
+static void
+file_free(struct xar_file *file)
+{
+ struct xattr *xattr;
+
+ archive_string_free(&(file->pathname));
+ archive_string_free(&(file->symlink));
+ archive_string_free(&(file->uname));
+ archive_string_free(&(file->gname));
+ archive_string_free(&(file->hardlink));
+ xattr = file->xattr_list;
+ while (xattr != NULL) {
+ struct xattr *next;
+
+ next = xattr->next;
+ xattr_free(xattr);
+ xattr = next;
+ }
+
+ free(file);
+}
+
+static int
+xattr_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list)
+{
+ struct xattr *xattr, **nx;
+ struct xmlattr *attr;
+
+ xattr = calloc(1, sizeof(*xattr));
+ if (xattr == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ xar->xattr = xattr;
+ for (attr = list->first; attr != NULL; attr = attr->next) {
+ if (strcmp(attr->name, "id") == 0)
+ xattr->id = atol10(attr->value, strlen(attr->value));
+ }
+ /* Chain to xattr list. */
+ for (nx = &(xar->file->xattr_list);
+ *nx != NULL; nx = &((*nx)->next)) {
+ if (xattr->id < (*nx)->id)
+ break;
+ }
+ xattr->next = *nx;
+ *nx = xattr;
+
+ return (ARCHIVE_OK);
+}
+
+static void
+xattr_free(struct xattr *xattr)
+{
+ archive_string_free(&(xattr->name));
+ free(xattr);
+}
+
+static int
+getencoding(struct xmlattr_list *list)
+{
+ struct xmlattr *attr;
+ enum enctype encoding = NONE;
+
+ for (attr = list->first; attr != NULL; attr = attr->next) {
+ if (strcmp(attr->name, "style") == 0) {
+ if (strcmp(attr->value, "application/octet-stream") == 0)
+ encoding = NONE;
+ else if (strcmp(attr->value, "application/x-gzip") == 0)
+ encoding = GZIP;
+ else if (strcmp(attr->value, "application/x-bzip2") == 0)
+ encoding = BZIP2;
+ else if (strcmp(attr->value, "application/x-lzma") == 0)
+ encoding = LZMA;
+ else if (strcmp(attr->value, "application/x-xz") == 0)
+ encoding = XZ;
+ }
+ }
+ return (encoding);
+}
+
+static int
+getsumalgorithm(struct xmlattr_list *list)
+{
+ struct xmlattr *attr;
+ int alg = CKSUM_NONE;
+
+ for (attr = list->first; attr != NULL; attr = attr->next) {
+ if (strcmp(attr->name, "style") == 0) {
+ const char *v = attr->value;
+ if ((v[0] == 'S' || v[0] == 's') &&
+ (v[1] == 'H' || v[1] == 'h') &&
+ (v[2] == 'A' || v[2] == 'a') &&
+ v[3] == '1' && v[4] == '\0')
+ alg = CKSUM_SHA1;
+ if ((v[0] == 'M' || v[0] == 'm') &&
+ (v[1] == 'D' || v[1] == 'd') &&
+ v[2] == '5' && v[3] == '\0')
+ alg = CKSUM_MD5;
+ }
+ }
+ return (alg);
+}
+
+static int
+unknowntag_start(struct archive_read *a, struct xar *xar, const char *name)
+{
+ struct unknown_tag *tag;
+
+ tag = malloc(sizeof(*tag));
+ if (tag == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ tag->next = xar->unknowntags;
+ archive_string_init(&(tag->name));
+ archive_strcpy(&(tag->name), name);
+ if (xar->unknowntags == NULL) {
+#if DEBUG
+ fprintf(stderr, "UNKNOWNTAG_START:%s\n", name);
+#endif
+ xar->xmlsts_unknown = xar->xmlsts;
+ xar->xmlsts = UNKNOWN;
+ }
+ xar->unknowntags = tag;
+ return (ARCHIVE_OK);
+}
+
+static void
+unknowntag_end(struct xar *xar, const char *name)
+{
+ struct unknown_tag *tag;
+
+ tag = xar->unknowntags;
+ if (tag == NULL || name == NULL)
+ return;
+ if (strcmp(tag->name.s, name) == 0) {
+ xar->unknowntags = tag->next;
+ archive_string_free(&(tag->name));
+ free(tag);
+ if (xar->unknowntags == NULL) {
+#if DEBUG
+ fprintf(stderr, "UNKNOWNTAG_END:%s\n", name);
+#endif
+ xar->xmlsts = xar->xmlsts_unknown;
+ }
+ }
+}
+
+static int
+xml_start(struct archive_read *a, const char *name, struct xmlattr_list *list)
+{
+ struct xar *xar;
+ struct xmlattr *attr;
+
+ xar = (struct xar *)(a->format->data);
+
+#if DEBUG
+ fprintf(stderr, "xml_sta:[%s]\n", name);
+ for (attr = list->first; attr != NULL; attr = attr->next)
+ fprintf(stderr, " attr:\"%s\"=\"%s\"\n",
+ attr->name, attr->value);
+#endif
+ xar->base64text = 0;
+ switch (xar->xmlsts) {
+ case INIT:
+ if (strcmp(name, "xar") == 0)
+ xar->xmlsts = XAR;
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case XAR:
+ if (strcmp(name, "toc") == 0)
+ xar->xmlsts = TOC;
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case TOC:
+ if (strcmp(name, "creation-time") == 0)
+ xar->xmlsts = TOC_CREATION_TIME;
+ else if (strcmp(name, "checksum") == 0)
+ xar->xmlsts = TOC_CHECKSUM;
+ else if (strcmp(name, "file") == 0) {
+ if (file_new(a, xar, list) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ xar->xmlsts = TOC_FILE;
+ }
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case TOC_CHECKSUM:
+ if (strcmp(name, "offset") == 0)
+ xar->xmlsts = TOC_CHECKSUM_OFFSET;
+ else if (strcmp(name, "size") == 0)
+ xar->xmlsts = TOC_CHECKSUM_SIZE;
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case TOC_FILE:
+ if (strcmp(name, "file") == 0) {
+ if (file_new(a, xar, list) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ else if (strcmp(name, "data") == 0)
+ xar->xmlsts = FILE_DATA;
+ else if (strcmp(name, "ea") == 0) {
+ if (xattr_new(a, xar, list) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ xar->xmlsts = FILE_EA;
+ }
+ else if (strcmp(name, "ctime") == 0)
+ xar->xmlsts = FILE_CTIME;
+ else if (strcmp(name, "mtime") == 0)
+ xar->xmlsts = FILE_MTIME;
+ else if (strcmp(name, "atime") == 0)
+ xar->xmlsts = FILE_ATIME;
+ else if (strcmp(name, "group") == 0)
+ xar->xmlsts = FILE_GROUP;
+ else if (strcmp(name, "gid") == 0)
+ xar->xmlsts = FILE_GID;
+ else if (strcmp(name, "user") == 0)
+ xar->xmlsts = FILE_USER;
+ else if (strcmp(name, "uid") == 0)
+ xar->xmlsts = FILE_UID;
+ else if (strcmp(name, "mode") == 0)
+ xar->xmlsts = FILE_MODE;
+ else if (strcmp(name, "device") == 0)
+ xar->xmlsts = FILE_DEVICE;
+ else if (strcmp(name, "deviceno") == 0)
+ xar->xmlsts = FILE_DEVICENO;
+ else if (strcmp(name, "inode") == 0)
+ xar->xmlsts = FILE_INODE;
+ else if (strcmp(name, "link") == 0)
+ xar->xmlsts = FILE_LINK;
+ else if (strcmp(name, "type") == 0) {
+ xar->xmlsts = FILE_TYPE;
+ for (attr = list->first; attr != NULL;
+ attr = attr->next) {
+ if (strcmp(attr->name, "link") != 0)
+ continue;
+ if (strcmp(attr->value, "original") == 0) {
+ xar->file->hdnext = xar->hdlink_orgs;
+ xar->hdlink_orgs = xar->file;
+ } else {
+ xar->file->link = (unsigned)atol10(attr->value,
+ strlen(attr->value));
+ if (xar->file->link > 0)
+ if (add_link(a, xar, xar->file) != ARCHIVE_OK) {
+ return (ARCHIVE_FATAL);
+ };
+ }
+ }
+ }
+ else if (strcmp(name, "name") == 0) {
+ xar->xmlsts = FILE_NAME;
+ for (attr = list->first; attr != NULL;
+ attr = attr->next) {
+ if (strcmp(attr->name, "enctype") == 0 &&
+ strcmp(attr->value, "base64") == 0)
+ xar->base64text = 1;
+ }
+ }
+ else if (strcmp(name, "acl") == 0)
+ xar->xmlsts = FILE_ACL;
+ else if (strcmp(name, "flags") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ else if (strcmp(name, "ext2") == 0)
+ xar->xmlsts = FILE_EXT2;
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case FILE_DATA:
+ if (strcmp(name, "length") == 0)
+ xar->xmlsts = FILE_DATA_LENGTH;
+ else if (strcmp(name, "offset") == 0)
+ xar->xmlsts = FILE_DATA_OFFSET;
+ else if (strcmp(name, "size") == 0)
+ xar->xmlsts = FILE_DATA_SIZE;
+ else if (strcmp(name, "encoding") == 0) {
+ xar->xmlsts = FILE_DATA_ENCODING;
+ xar->file->encoding = getencoding(list);
+ }
+ else if (strcmp(name, "archived-checksum") == 0) {
+ xar->xmlsts = FILE_DATA_A_CHECKSUM;
+ xar->file->a_sum.alg = getsumalgorithm(list);
+ }
+ else if (strcmp(name, "extracted-checksum") == 0) {
+ xar->xmlsts = FILE_DATA_E_CHECKSUM;
+ xar->file->e_sum.alg = getsumalgorithm(list);
+ }
+ else if (strcmp(name, "content") == 0)
+ xar->xmlsts = FILE_DATA_CONTENT;
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case FILE_DEVICE:
+ if (strcmp(name, "major") == 0)
+ xar->xmlsts = FILE_DEVICE_MAJOR;
+ else if (strcmp(name, "minor") == 0)
+ xar->xmlsts = FILE_DEVICE_MINOR;
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case FILE_DATA_CONTENT:
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case FILE_EA:
+ if (strcmp(name, "length") == 0)
+ xar->xmlsts = FILE_EA_LENGTH;
+ else if (strcmp(name, "offset") == 0)
+ xar->xmlsts = FILE_EA_OFFSET;
+ else if (strcmp(name, "size") == 0)
+ xar->xmlsts = FILE_EA_SIZE;
+ else if (strcmp(name, "encoding") == 0) {
+ xar->xmlsts = FILE_EA_ENCODING;
+ xar->xattr->encoding = getencoding(list);
+ } else if (strcmp(name, "archived-checksum") == 0)
+ xar->xmlsts = FILE_EA_A_CHECKSUM;
+ else if (strcmp(name, "extracted-checksum") == 0)
+ xar->xmlsts = FILE_EA_E_CHECKSUM;
+ else if (strcmp(name, "name") == 0)
+ xar->xmlsts = FILE_EA_NAME;
+ else if (strcmp(name, "fstype") == 0)
+ xar->xmlsts = FILE_EA_FSTYPE;
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case FILE_ACL:
+ if (strcmp(name, "appleextended") == 0)
+ xar->xmlsts = FILE_ACL_APPLEEXTENDED;
+ else if (strcmp(name, "default") == 0)
+ xar->xmlsts = FILE_ACL_DEFAULT;
+ else if (strcmp(name, "access") == 0)
+ xar->xmlsts = FILE_ACL_ACCESS;
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case FILE_FLAGS:
+ if (!xml_parse_file_flags(xar, name))
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case FILE_EXT2:
+ if (!xml_parse_file_ext2(xar, name))
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case TOC_CREATION_TIME:
+ case TOC_CHECKSUM_OFFSET:
+ case TOC_CHECKSUM_SIZE:
+ case FILE_DATA_LENGTH:
+ case FILE_DATA_OFFSET:
+ case FILE_DATA_SIZE:
+ case FILE_DATA_ENCODING:
+ case FILE_DATA_A_CHECKSUM:
+ case FILE_DATA_E_CHECKSUM:
+ case FILE_EA_LENGTH:
+ case FILE_EA_OFFSET:
+ case FILE_EA_SIZE:
+ case FILE_EA_ENCODING:
+ case FILE_EA_A_CHECKSUM:
+ case FILE_EA_E_CHECKSUM:
+ case FILE_EA_NAME:
+ case FILE_EA_FSTYPE:
+ case FILE_CTIME:
+ case FILE_MTIME:
+ case FILE_ATIME:
+ case FILE_GROUP:
+ case FILE_GID:
+ case FILE_USER:
+ case FILE_UID:
+ case FILE_INODE:
+ case FILE_DEVICE_MAJOR:
+ case FILE_DEVICE_MINOR:
+ case FILE_DEVICENO:
+ case FILE_MODE:
+ case FILE_TYPE:
+ case FILE_LINK:
+ case FILE_NAME:
+ case FILE_ACL_DEFAULT:
+ case FILE_ACL_ACCESS:
+ case FILE_ACL_APPLEEXTENDED:
+ case FILE_FLAGS_USER_NODUMP:
+ case FILE_FLAGS_USER_IMMUTABLE:
+ case FILE_FLAGS_USER_APPEND:
+ case FILE_FLAGS_USER_OPAQUE:
+ case FILE_FLAGS_USER_NOUNLINK:
+ case FILE_FLAGS_SYS_ARCHIVED:
+ case FILE_FLAGS_SYS_IMMUTABLE:
+ case FILE_FLAGS_SYS_APPEND:
+ case FILE_FLAGS_SYS_NOUNLINK:
+ case FILE_FLAGS_SYS_SNAPSHOT:
+ case FILE_EXT2_SecureDeletion:
+ case FILE_EXT2_Undelete:
+ case FILE_EXT2_Compress:
+ case FILE_EXT2_Synchronous:
+ case FILE_EXT2_Immutable:
+ case FILE_EXT2_AppendOnly:
+ case FILE_EXT2_NoDump:
+ case FILE_EXT2_NoAtime:
+ case FILE_EXT2_CompDirty:
+ case FILE_EXT2_CompBlock:
+ case FILE_EXT2_NoCompBlock:
+ case FILE_EXT2_CompError:
+ case FILE_EXT2_BTree:
+ case FILE_EXT2_HashIndexed:
+ case FILE_EXT2_iMagic:
+ case FILE_EXT2_Journaled:
+ case FILE_EXT2_NoTail:
+ case FILE_EXT2_DirSync:
+ case FILE_EXT2_TopDir:
+ case FILE_EXT2_Reserved:
+ case UNKNOWN:
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ }
+ return (ARCHIVE_OK);
+}
+
+static void
+xml_end(void *userData, const char *name)
+{
+ struct archive_read *a;
+ struct xar *xar;
+
+ a = (struct archive_read *)userData;
+ xar = (struct xar *)(a->format->data);
+
+#if DEBUG
+ fprintf(stderr, "xml_end:[%s]\n", name);
+#endif
+ switch (xar->xmlsts) {
+ case INIT:
+ break;
+ case XAR:
+ if (strcmp(name, "xar") == 0)
+ xar->xmlsts = INIT;
+ break;
+ case TOC:
+ if (strcmp(name, "toc") == 0)
+ xar->xmlsts = XAR;
+ break;
+ case TOC_CREATION_TIME:
+ if (strcmp(name, "creation-time") == 0)
+ xar->xmlsts = TOC;
+ break;
+ case TOC_CHECKSUM:
+ if (strcmp(name, "checksum") == 0)
+ xar->xmlsts = TOC;
+ break;
+ case TOC_CHECKSUM_OFFSET:
+ if (strcmp(name, "offset") == 0)
+ xar->xmlsts = TOC_CHECKSUM;
+ break;
+ case TOC_CHECKSUM_SIZE:
+ if (strcmp(name, "size") == 0)
+ xar->xmlsts = TOC_CHECKSUM;
+ break;
+ case TOC_FILE:
+ if (strcmp(name, "file") == 0) {
+ if (xar->file->parent != NULL &&
+ ((xar->file->mode & AE_IFMT) == AE_IFDIR))
+ xar->file->parent->subdirs++;
+ xar->file = xar->file->parent;
+ if (xar->file == NULL)
+ xar->xmlsts = TOC;
+ }
+ break;
+ case FILE_DATA:
+ if (strcmp(name, "data") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_DATA_LENGTH:
+ if (strcmp(name, "length") == 0)
+ xar->xmlsts = FILE_DATA;
+ break;
+ case FILE_DATA_OFFSET:
+ if (strcmp(name, "offset") == 0)
+ xar->xmlsts = FILE_DATA;
+ break;
+ case FILE_DATA_SIZE:
+ if (strcmp(name, "size") == 0)
+ xar->xmlsts = FILE_DATA;
+ break;
+ case FILE_DATA_ENCODING:
+ if (strcmp(name, "encoding") == 0)
+ xar->xmlsts = FILE_DATA;
+ break;
+ case FILE_DATA_A_CHECKSUM:
+ if (strcmp(name, "archived-checksum") == 0)
+ xar->xmlsts = FILE_DATA;
+ break;
+ case FILE_DATA_E_CHECKSUM:
+ if (strcmp(name, "extracted-checksum") == 0)
+ xar->xmlsts = FILE_DATA;
+ break;
+ case FILE_DATA_CONTENT:
+ if (strcmp(name, "content") == 0)
+ xar->xmlsts = FILE_DATA;
+ break;
+ case FILE_EA:
+ if (strcmp(name, "ea") == 0) {
+ xar->xmlsts = TOC_FILE;
+ xar->xattr = NULL;
+ }
+ break;
+ case FILE_EA_LENGTH:
+ if (strcmp(name, "length") == 0)
+ xar->xmlsts = FILE_EA;
+ break;
+ case FILE_EA_OFFSET:
+ if (strcmp(name, "offset") == 0)
+ xar->xmlsts = FILE_EA;
+ break;
+ case FILE_EA_SIZE:
+ if (strcmp(name, "size") == 0)
+ xar->xmlsts = FILE_EA;
+ break;
+ case FILE_EA_ENCODING:
+ if (strcmp(name, "encoding") == 0)
+ xar->xmlsts = FILE_EA;
+ break;
+ case FILE_EA_A_CHECKSUM:
+ if (strcmp(name, "archived-checksum") == 0)
+ xar->xmlsts = FILE_EA;
+ break;
+ case FILE_EA_E_CHECKSUM:
+ if (strcmp(name, "extracted-checksum") == 0)
+ xar->xmlsts = FILE_EA;
+ break;
+ case FILE_EA_NAME:
+ if (strcmp(name, "name") == 0)
+ xar->xmlsts = FILE_EA;
+ break;
+ case FILE_EA_FSTYPE:
+ if (strcmp(name, "fstype") == 0)
+ xar->xmlsts = FILE_EA;
+ break;
+ case FILE_CTIME:
+ if (strcmp(name, "ctime") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_MTIME:
+ if (strcmp(name, "mtime") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_ATIME:
+ if (strcmp(name, "atime") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_GROUP:
+ if (strcmp(name, "group") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_GID:
+ if (strcmp(name, "gid") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_USER:
+ if (strcmp(name, "user") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_UID:
+ if (strcmp(name, "uid") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_MODE:
+ if (strcmp(name, "mode") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_DEVICE:
+ if (strcmp(name, "device") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_DEVICE_MAJOR:
+ if (strcmp(name, "major") == 0)
+ xar->xmlsts = FILE_DEVICE;
+ break;
+ case FILE_DEVICE_MINOR:
+ if (strcmp(name, "minor") == 0)
+ xar->xmlsts = FILE_DEVICE;
+ break;
+ case FILE_DEVICENO:
+ if (strcmp(name, "deviceno") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_INODE:
+ if (strcmp(name, "inode") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_LINK:
+ if (strcmp(name, "link") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_TYPE:
+ if (strcmp(name, "type") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_NAME:
+ if (strcmp(name, "name") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_ACL:
+ if (strcmp(name, "acl") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_ACL_DEFAULT:
+ if (strcmp(name, "default") == 0)
+ xar->xmlsts = FILE_ACL;
+ break;
+ case FILE_ACL_ACCESS:
+ if (strcmp(name, "access") == 0)
+ xar->xmlsts = FILE_ACL;
+ break;
+ case FILE_ACL_APPLEEXTENDED:
+ if (strcmp(name, "appleextended") == 0)
+ xar->xmlsts = FILE_ACL;
+ break;
+ case FILE_FLAGS:
+ if (strcmp(name, "flags") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_FLAGS_USER_NODUMP:
+ if (strcmp(name, "UserNoDump") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_USER_IMMUTABLE:
+ if (strcmp(name, "UserImmutable") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_USER_APPEND:
+ if (strcmp(name, "UserAppend") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_USER_OPAQUE:
+ if (strcmp(name, "UserOpaque") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_USER_NOUNLINK:
+ if (strcmp(name, "UserNoUnlink") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_SYS_ARCHIVED:
+ if (strcmp(name, "SystemArchived") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_SYS_IMMUTABLE:
+ if (strcmp(name, "SystemImmutable") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_SYS_APPEND:
+ if (strcmp(name, "SystemAppend") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_SYS_NOUNLINK:
+ if (strcmp(name, "SystemNoUnlink") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_SYS_SNAPSHOT:
+ if (strcmp(name, "SystemSnapshot") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_EXT2:
+ if (strcmp(name, "ext2") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_EXT2_SecureDeletion:
+ if (strcmp(name, "SecureDeletion") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_Undelete:
+ if (strcmp(name, "Undelete") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_Compress:
+ if (strcmp(name, "Compress") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_Synchronous:
+ if (strcmp(name, "Synchronous") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_Immutable:
+ if (strcmp(name, "Immutable") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_AppendOnly:
+ if (strcmp(name, "AppendOnly") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_NoDump:
+ if (strcmp(name, "NoDump") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_NoAtime:
+ if (strcmp(name, "NoAtime") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_CompDirty:
+ if (strcmp(name, "CompDirty") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_CompBlock:
+ if (strcmp(name, "CompBlock") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_NoCompBlock:
+ if (strcmp(name, "NoCompBlock") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_CompError:
+ if (strcmp(name, "CompError") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_BTree:
+ if (strcmp(name, "BTree") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_HashIndexed:
+ if (strcmp(name, "HashIndexed") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_iMagic:
+ if (strcmp(name, "iMagic") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_Journaled:
+ if (strcmp(name, "Journaled") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_NoTail:
+ if (strcmp(name, "NoTail") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_DirSync:
+ if (strcmp(name, "DirSync") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_TopDir:
+ if (strcmp(name, "TopDir") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_Reserved:
+ if (strcmp(name, "Reserved") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case UNKNOWN:
+ unknowntag_end(xar, name);
+ break;
+ }
+}
+
+static const int base64[256] = {
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* 00 - 0F */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* 10 - 1F */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 62, -1, -1, -1, 63, /* 20 - 2F */
+ 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, -1, -1, -1, -1, -1, -1, /* 30 - 3F */
+ -1, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, /* 40 - 4F */
+ 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, -1, -1, -1, -1, -1, /* 50 - 5F */
+ -1, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */
+ 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, /* 70 - 7F */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 8F */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* 90 - 9F */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* A0 - AF */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* B0 - BF */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* C0 - CF */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* D0 - DF */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* E0 - EF */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* F0 - FF */
+};
+
+static void
+strappend_base64(struct xar *xar,
+ struct archive_string *as, const char *s, size_t l)
+{
+ unsigned char buff[256];
+ unsigned char *out;
+ const unsigned char *b;
+ size_t len;
+
+ (void)xar; /* UNUSED */
+ len = 0;
+ out = buff;
+ b = (const unsigned char *)s;
+ while (l > 0) {
+ int n = 0;
+
+ if (base64[b[0]] < 0 || base64[b[1]] < 0)
+ break;
+ n = base64[*b++] << 18;
+ n |= base64[*b++] << 12;
+ *out++ = n >> 16;
+ len++;
+ l -= 2;
+
+ if (l > 0) {
+ if (base64[*b] < 0)
+ break;
+ n |= base64[*b++] << 6;
+ *out++ = (n >> 8) & 0xFF;
+ len++;
+ --l;
+ }
+ if (l > 0) {
+ if (base64[*b] < 0)
+ break;
+ n |= base64[*b++];
+ *out++ = n & 0xFF;
+ len++;
+ --l;
+ }
+ if (len+3 >= sizeof(buff)) {
+ archive_strncat(as, (const char *)buff, len);
+ len = 0;
+ out = buff;
+ }
+ }
+ if (len > 0)
+ archive_strncat(as, (const char *)buff, len);
+}
+
+static int
+is_string(const char *known, const char *data, size_t len)
+{
+ if (strlen(known) != len)
+ return -1;
+ return memcmp(data, known, len);
+}
+
+static void
+xml_data(void *userData, const char *s, int len)
+{
+ struct archive_read *a;
+ struct xar *xar;
+
+ a = (struct archive_read *)userData;
+ xar = (struct xar *)(a->format->data);
+
+#if DEBUG
+ {
+ char buff[1024];
+ if (len > (int)(sizeof(buff)-1))
+ len = (int)(sizeof(buff)-1);
+ strncpy(buff, s, len);
+ buff[len] = 0;
+ fprintf(stderr, "\tlen=%d:\"%s\"\n", len, buff);
+ }
+#endif
+ switch (xar->xmlsts) {
+ case TOC_CHECKSUM_OFFSET:
+ xar->toc_chksum_offset = atol10(s, len);
+ break;
+ case TOC_CHECKSUM_SIZE:
+ xar->toc_chksum_size = atol10(s, len);
+ break;
+ default:
+ break;
+ }
+ if (xar->file == NULL)
+ return;
+
+ switch (xar->xmlsts) {
+ case FILE_NAME:
+ if (xar->file->parent != NULL) {
+ archive_string_concat(&(xar->file->pathname),
+ &(xar->file->parent->pathname));
+ archive_strappend_char(&(xar->file->pathname), '/');
+ }
+ xar->file->has |= HAS_PATHNAME;
+ if (xar->base64text) {
+ strappend_base64(xar,
+ &(xar->file->pathname), s, len);
+ } else
+ archive_strncat(&(xar->file->pathname), s, len);
+ break;
+ case FILE_LINK:
+ xar->file->has |= HAS_SYMLINK;
+ archive_strncpy(&(xar->file->symlink), s, len);
+ break;
+ case FILE_TYPE:
+ if (is_string("file", s, len) == 0 ||
+ is_string("hardlink", s, len) == 0)
+ xar->file->mode =
+ (xar->file->mode & ~AE_IFMT) | AE_IFREG;
+ if (is_string("directory", s, len) == 0)
+ xar->file->mode =
+ (xar->file->mode & ~AE_IFMT) | AE_IFDIR;
+ if (is_string("symlink", s, len) == 0)
+ xar->file->mode =
+ (xar->file->mode & ~AE_IFMT) | AE_IFLNK;
+ if (is_string("character special", s, len) == 0)
+ xar->file->mode =
+ (xar->file->mode & ~AE_IFMT) | AE_IFCHR;
+ if (is_string("block special", s, len) == 0)
+ xar->file->mode =
+ (xar->file->mode & ~AE_IFMT) | AE_IFBLK;
+ if (is_string("socket", s, len) == 0)
+ xar->file->mode =
+ (xar->file->mode & ~AE_IFMT) | AE_IFSOCK;
+ if (is_string("fifo", s, len) == 0)
+ xar->file->mode =
+ (xar->file->mode & ~AE_IFMT) | AE_IFIFO;
+ xar->file->has |= HAS_TYPE;
+ break;
+ case FILE_INODE:
+ xar->file->has |= HAS_INO;
+ xar->file->ino64 = atol10(s, len);
+ break;
+ case FILE_DEVICE_MAJOR:
+ xar->file->has |= HAS_DEVMAJOR;
+ xar->file->devmajor = (dev_t)atol10(s, len);
+ break;
+ case FILE_DEVICE_MINOR:
+ xar->file->has |= HAS_DEVMINOR;
+ xar->file->devminor = (dev_t)atol10(s, len);
+ break;
+ case FILE_DEVICENO:
+ xar->file->has |= HAS_DEV;
+ xar->file->dev = (dev_t)atol10(s, len);
+ break;
+ case FILE_MODE:
+ xar->file->has |= HAS_MODE;
+ xar->file->mode =
+ (xar->file->mode & AE_IFMT) |
+ ((mode_t)(atol8(s, len)) & ~AE_IFMT);
+ break;
+ case FILE_GROUP:
+ xar->file->has |= HAS_GID;
+ archive_strncpy(&(xar->file->gname), s, len);
+ break;
+ case FILE_GID:
+ xar->file->has |= HAS_GID;
+ xar->file->gid = atol10(s, len);
+ break;
+ case FILE_USER:
+ xar->file->has |= HAS_UID;
+ archive_strncpy(&(xar->file->uname), s, len);
+ break;
+ case FILE_UID:
+ xar->file->has |= HAS_UID;
+ xar->file->uid = atol10(s, len);
+ break;
+ case FILE_CTIME:
+ xar->file->has |= HAS_TIME | HAS_CTIME;
+ xar->file->ctime = parse_time(s, len);
+ break;
+ case FILE_MTIME:
+ xar->file->has |= HAS_TIME | HAS_MTIME;
+ xar->file->mtime = parse_time(s, len);
+ break;
+ case FILE_ATIME:
+ xar->file->has |= HAS_TIME | HAS_ATIME;
+ xar->file->atime = parse_time(s, len);
+ break;
+ case FILE_DATA_LENGTH:
+ xar->file->has |= HAS_DATA;
+ xar->file->length = atol10(s, len);
+ break;
+ case FILE_DATA_OFFSET:
+ xar->file->has |= HAS_DATA;
+ xar->file->offset = atol10(s, len);
+ break;
+ case FILE_DATA_SIZE:
+ xar->file->has |= HAS_DATA;
+ xar->file->size = atol10(s, len);
+ break;
+ case FILE_DATA_A_CHECKSUM:
+ xar->file->a_sum.len = atohex(xar->file->a_sum.val,
+ sizeof(xar->file->a_sum.val), s, len);
+ break;
+ case FILE_DATA_E_CHECKSUM:
+ xar->file->e_sum.len = atohex(xar->file->e_sum.val,
+ sizeof(xar->file->e_sum.val), s, len);
+ break;
+ case FILE_EA_LENGTH:
+ xar->file->has |= HAS_XATTR;
+ xar->xattr->length = atol10(s, len);
+ break;
+ case FILE_EA_OFFSET:
+ xar->file->has |= HAS_XATTR;
+ xar->xattr->offset = atol10(s, len);
+ break;
+ case FILE_EA_SIZE:
+ xar->file->has |= HAS_XATTR;
+ xar->xattr->size = atol10(s, len);
+ break;
+ case FILE_EA_A_CHECKSUM:
+ xar->file->has |= HAS_XATTR;
+ xar->xattr->a_sum.len = atohex(xar->xattr->a_sum.val,
+ sizeof(xar->xattr->a_sum.val), s, len);
+ break;
+ case FILE_EA_E_CHECKSUM:
+ xar->file->has |= HAS_XATTR;
+ xar->xattr->e_sum.len = atohex(xar->xattr->e_sum.val,
+ sizeof(xar->xattr->e_sum.val), s, len);
+ break;
+ case FILE_EA_NAME:
+ xar->file->has |= HAS_XATTR;
+ archive_strncpy(&(xar->xattr->name), s, len);
+ break;
+ case FILE_EA_FSTYPE:
+ xar->file->has |= HAS_XATTR;
+ archive_strncpy(&(xar->xattr->fstype), s, len);
+ break;
+ break;
+ case FILE_ACL_DEFAULT:
+ case FILE_ACL_ACCESS:
+ case FILE_ACL_APPLEEXTENDED:
+ xar->file->has |= HAS_ACL;
+ /* TODO */
+ break;
+ case INIT:
+ case XAR:
+ case TOC:
+ case TOC_CREATION_TIME:
+ case TOC_CHECKSUM:
+ case TOC_CHECKSUM_OFFSET:
+ case TOC_CHECKSUM_SIZE:
+ case TOC_FILE:
+ case FILE_DATA:
+ case FILE_DATA_ENCODING:
+ case FILE_DATA_CONTENT:
+ case FILE_DEVICE:
+ case FILE_EA:
+ case FILE_EA_ENCODING:
+ case FILE_ACL:
+ case FILE_FLAGS:
+ case FILE_FLAGS_USER_NODUMP:
+ case FILE_FLAGS_USER_IMMUTABLE:
+ case FILE_FLAGS_USER_APPEND:
+ case FILE_FLAGS_USER_OPAQUE:
+ case FILE_FLAGS_USER_NOUNLINK:
+ case FILE_FLAGS_SYS_ARCHIVED:
+ case FILE_FLAGS_SYS_IMMUTABLE:
+ case FILE_FLAGS_SYS_APPEND:
+ case FILE_FLAGS_SYS_NOUNLINK:
+ case FILE_FLAGS_SYS_SNAPSHOT:
+ case FILE_EXT2:
+ case FILE_EXT2_SecureDeletion:
+ case FILE_EXT2_Undelete:
+ case FILE_EXT2_Compress:
+ case FILE_EXT2_Synchronous:
+ case FILE_EXT2_Immutable:
+ case FILE_EXT2_AppendOnly:
+ case FILE_EXT2_NoDump:
+ case FILE_EXT2_NoAtime:
+ case FILE_EXT2_CompDirty:
+ case FILE_EXT2_CompBlock:
+ case FILE_EXT2_NoCompBlock:
+ case FILE_EXT2_CompError:
+ case FILE_EXT2_BTree:
+ case FILE_EXT2_HashIndexed:
+ case FILE_EXT2_iMagic:
+ case FILE_EXT2_Journaled:
+ case FILE_EXT2_NoTail:
+ case FILE_EXT2_DirSync:
+ case FILE_EXT2_TopDir:
+ case FILE_EXT2_Reserved:
+ case UNKNOWN:
+ break;
+ }
+}
+
+/*
+ * BSD file flags.
+ */
+static int
+xml_parse_file_flags(struct xar *xar, const char *name)
+{
+ const char *flag = NULL;
+
+ if (strcmp(name, "UserNoDump") == 0) {
+ xar->xmlsts = FILE_FLAGS_USER_NODUMP;
+ flag = "nodump";
+ }
+ else if (strcmp(name, "UserImmutable") == 0) {
+ xar->xmlsts = FILE_FLAGS_USER_IMMUTABLE;
+ flag = "uimmutable";
+ }
+ else if (strcmp(name, "UserAppend") == 0) {
+ xar->xmlsts = FILE_FLAGS_USER_APPEND;
+ flag = "uappend";
+ }
+ else if (strcmp(name, "UserOpaque") == 0) {
+ xar->xmlsts = FILE_FLAGS_USER_OPAQUE;
+ flag = "opaque";
+ }
+ else if (strcmp(name, "UserNoUnlink") == 0) {
+ xar->xmlsts = FILE_FLAGS_USER_NOUNLINK;
+ flag = "nouunlink";
+ }
+ else if (strcmp(name, "SystemArchived") == 0) {
+ xar->xmlsts = FILE_FLAGS_SYS_ARCHIVED;
+ flag = "archived";
+ }
+ else if (strcmp(name, "SystemImmutable") == 0) {
+ xar->xmlsts = FILE_FLAGS_SYS_IMMUTABLE;
+ flag = "simmutable";
+ }
+ else if (strcmp(name, "SystemAppend") == 0) {
+ xar->xmlsts = FILE_FLAGS_SYS_APPEND;
+ flag = "sappend";
+ }
+ else if (strcmp(name, "SystemNoUnlink") == 0) {
+ xar->xmlsts = FILE_FLAGS_SYS_NOUNLINK;
+ flag = "nosunlink";
+ }
+ else if (strcmp(name, "SystemSnapshot") == 0) {
+ xar->xmlsts = FILE_FLAGS_SYS_SNAPSHOT;
+ flag = "snapshot";
+ }
+
+ if (flag == NULL)
+ return (0);
+ xar->file->has |= HAS_FFLAGS;
+ if (archive_strlen(&(xar->file->fflags_text)) > 0)
+ archive_strappend_char(&(xar->file->fflags_text), ',');
+ archive_strcat(&(xar->file->fflags_text), flag);
+ return (1);
+}
+
+/*
+ * Linux file flags.
+ */
+static int
+xml_parse_file_ext2(struct xar *xar, const char *name)
+{
+ const char *flag = NULL;
+
+ if (strcmp(name, "SecureDeletion") == 0) {
+ xar->xmlsts = FILE_EXT2_SecureDeletion;
+ flag = "securedeletion";
+ }
+ else if (strcmp(name, "Undelete") == 0) {
+ xar->xmlsts = FILE_EXT2_Undelete;
+ flag = "nouunlink";
+ }
+ else if (strcmp(name, "Compress") == 0) {
+ xar->xmlsts = FILE_EXT2_Compress;
+ flag = "compress";
+ }
+ else if (strcmp(name, "Synchronous") == 0) {
+ xar->xmlsts = FILE_EXT2_Synchronous;
+ flag = "sync";
+ }
+ else if (strcmp(name, "Immutable") == 0) {
+ xar->xmlsts = FILE_EXT2_Immutable;
+ flag = "simmutable";
+ }
+ else if (strcmp(name, "AppendOnly") == 0) {
+ xar->xmlsts = FILE_EXT2_AppendOnly;
+ flag = "sappend";
+ }
+ else if (strcmp(name, "NoDump") == 0) {
+ xar->xmlsts = FILE_EXT2_NoDump;
+ flag = "nodump";
+ }
+ else if (strcmp(name, "NoAtime") == 0) {
+ xar->xmlsts = FILE_EXT2_NoAtime;
+ flag = "noatime";
+ }
+ else if (strcmp(name, "CompDirty") == 0) {
+ xar->xmlsts = FILE_EXT2_CompDirty;
+ flag = "compdirty";
+ }
+ else if (strcmp(name, "CompBlock") == 0) {
+ xar->xmlsts = FILE_EXT2_CompBlock;
+ flag = "comprblk";
+ }
+ else if (strcmp(name, "NoCompBlock") == 0) {
+ xar->xmlsts = FILE_EXT2_NoCompBlock;
+ flag = "nocomprblk";
+ }
+ else if (strcmp(name, "CompError") == 0) {
+ xar->xmlsts = FILE_EXT2_CompError;
+ flag = "comperr";
+ }
+ else if (strcmp(name, "BTree") == 0) {
+ xar->xmlsts = FILE_EXT2_BTree;
+ flag = "btree";
+ }
+ else if (strcmp(name, "HashIndexed") == 0) {
+ xar->xmlsts = FILE_EXT2_HashIndexed;
+ flag = "hashidx";
+ }
+ else if (strcmp(name, "iMagic") == 0) {
+ xar->xmlsts = FILE_EXT2_iMagic;
+ flag = "imagic";
+ }
+ else if (strcmp(name, "Journaled") == 0) {
+ xar->xmlsts = FILE_EXT2_Journaled;
+ flag = "journal";
+ }
+ else if (strcmp(name, "NoTail") == 0) {
+ xar->xmlsts = FILE_EXT2_NoTail;
+ flag = "notail";
+ }
+ else if (strcmp(name, "DirSync") == 0) {
+ xar->xmlsts = FILE_EXT2_DirSync;
+ flag = "dirsync";
+ }
+ else if (strcmp(name, "TopDir") == 0) {
+ xar->xmlsts = FILE_EXT2_TopDir;
+ flag = "topdir";
+ }
+ else if (strcmp(name, "Reserved") == 0) {
+ xar->xmlsts = FILE_EXT2_Reserved;
+ flag = "reserved";
+ }
+
+ if (flag == NULL)
+ return (0);
+ if (archive_strlen(&(xar->file->fflags_text)) > 0)
+ archive_strappend_char(&(xar->file->fflags_text), ',');
+ archive_strcat(&(xar->file->fflags_text), flag);
+ return (1);
+}
+
+#ifdef HAVE_LIBXML_XMLREADER_H
+
+static int
+xml2_xmlattr_setup(struct archive_read *a,
+ struct xmlattr_list *list, xmlTextReaderPtr reader)
+{
+ struct xmlattr *attr;
+ int r;
+
+ list->first = NULL;
+ list->last = &(list->first);
+ r = xmlTextReaderMoveToFirstAttribute(reader);
+ while (r == 1) {
+ attr = malloc(sizeof*(attr));
+ if (attr == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ attr->name = strdup(
+ (const char *)xmlTextReaderConstLocalName(reader));
+ if (attr->name == NULL) {
+ free(attr);
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ attr->value = strdup(
+ (const char *)xmlTextReaderConstValue(reader));
+ if (attr->value == NULL) {
+ free(attr->name);
+ free(attr);
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ attr->next = NULL;
+ *list->last = attr;
+ list->last = &(attr->next);
+ r = xmlTextReaderMoveToNextAttribute(reader);
+ }
+ return (r);
+}
+
+static int
+xml2_read_cb(void *context, char *buffer, int len)
+{
+ struct archive_read *a;
+ struct xar *xar;
+ const void *d;
+ size_t outbytes;
+ size_t used = 0;
+ int r;
+
+ a = (struct archive_read *)context;
+ xar = (struct xar *)(a->format->data);
+
+ if (xar->toc_remaining <= 0)
+ return (0);
+ d = buffer;
+ outbytes = len;
+ r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining);
+ if (r != ARCHIVE_OK)
+ return (r);
+ __archive_read_consume(a, used);
+ xar->toc_remaining -= used;
+ xar->offset += used;
+ xar->toc_total += outbytes;
+ PRINT_TOC(buffer, len);
+
+ return ((int)outbytes);
+}
+
+static int
+xml2_close_cb(void *context)
+{
+
+ (void)context; /* UNUSED */
+ return (0);
+}
+
+static void
+xml2_error_hdr(void *arg, const char *msg, xmlParserSeverities severity,
+ xmlTextReaderLocatorPtr locator)
+{
+ struct archive_read *a;
+
+ (void)locator; /* UNUSED */
+ a = (struct archive_read *)arg;
+ switch (severity) {
+ case XML_PARSER_SEVERITY_VALIDITY_WARNING:
+ case XML_PARSER_SEVERITY_WARNING:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "XML Parsing error: %s", msg);
+ break;
+ case XML_PARSER_SEVERITY_VALIDITY_ERROR:
+ case XML_PARSER_SEVERITY_ERROR:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "XML Parsing error: %s", msg);
+ break;
+ }
+}
+
+static int
+xml2_read_toc(struct archive_read *a)
+{
+ xmlTextReaderPtr reader;
+ struct xmlattr_list list;
+ int r;
+
+ reader = xmlReaderForIO(xml2_read_cb, xml2_close_cb, a, NULL, NULL, 0);
+ if (reader == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory for xml parser");
+ return (ARCHIVE_FATAL);
+ }
+ xmlTextReaderSetErrorHandler(reader, xml2_error_hdr, a);
+
+ while ((r = xmlTextReaderRead(reader)) == 1) {
+ const char *name, *value;
+ int type, empty;
+
+ type = xmlTextReaderNodeType(reader);
+ name = (const char *)xmlTextReaderConstLocalName(reader);
+ switch (type) {
+ case XML_READER_TYPE_ELEMENT:
+ empty = xmlTextReaderIsEmptyElement(reader);
+ r = xml2_xmlattr_setup(a, &list, reader);
+ if (r == ARCHIVE_OK)
+ r = xml_start(a, name, &list);
+ xmlattr_cleanup(&list);
+ if (r != ARCHIVE_OK)
+ return (r);
+ if (empty)
+ xml_end(a, name);
+ break;
+ case XML_READER_TYPE_END_ELEMENT:
+ xml_end(a, name);
+ break;
+ case XML_READER_TYPE_TEXT:
+ value = (const char *)xmlTextReaderConstValue(reader);
+ xml_data(a, value, strlen(value));
+ break;
+ case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
+ default:
+ break;
+ }
+ if (r < 0)
+ break;
+ }
+ xmlFreeTextReader(reader);
+ xmlCleanupParser();
+
+ return ((r == 0)?ARCHIVE_OK:ARCHIVE_FATAL);
+}
+
+#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H)
+
+static int
+expat_xmlattr_setup(struct archive_read *a,
+ struct xmlattr_list *list, const XML_Char **atts)
+{
+ struct xmlattr *attr;
+ char *name, *value;
+
+ list->first = NULL;
+ list->last = &(list->first);
+ if (atts == NULL)
+ return (ARCHIVE_OK);
+ while (atts[0] != NULL && atts[1] != NULL) {
+ attr = malloc(sizeof*(attr));
+ name = strdup(atts[0]);
+ value = strdup(atts[1]);
+ if (attr == NULL || name == NULL || value == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ free(attr);
+ free(name);
+ free(value);
+ return (ARCHIVE_FATAL);
+ }
+ attr->name = name;
+ attr->value = value;
+ attr->next = NULL;
+ *list->last = attr;
+ list->last = &(attr->next);
+ atts += 2;
+ }
+ return (ARCHIVE_OK);
+}
+
+static void
+expat_start_cb(void *userData, const XML_Char *name, const XML_Char **atts)
+{
+ struct expat_userData *ud = (struct expat_userData *)userData;
+ struct archive_read *a = ud->archive;
+ struct xmlattr_list list;
+ int r;
+
+ r = expat_xmlattr_setup(a, &list, atts);
+ if (r == ARCHIVE_OK)
+ r = xml_start(a, (const char *)name, &list);
+ xmlattr_cleanup(&list);
+ ud->state = r;
+}
+
+static void
+expat_end_cb(void *userData, const XML_Char *name)
+{
+ struct expat_userData *ud = (struct expat_userData *)userData;
+
+ xml_end(ud->archive, (const char *)name);
+}
+
+static void
+expat_data_cb(void *userData, const XML_Char *s, int len)
+{
+ struct expat_userData *ud = (struct expat_userData *)userData;
+
+ xml_data(ud->archive, s, len);
+}
+
+static int
+expat_read_toc(struct archive_read *a)
+{
+ struct xar *xar;
+ XML_Parser parser;
+ struct expat_userData ud;
+
+ ud.state = ARCHIVE_OK;
+ ud.archive = a;
+
+ xar = (struct xar *)(a->format->data);
+
+ /* Initialize XML Parser library. */
+ parser = XML_ParserCreate(NULL);
+ if (parser == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory for xml parser");
+ return (ARCHIVE_FATAL);
+ }
+ XML_SetUserData(parser, &ud);
+ XML_SetElementHandler(parser, expat_start_cb, expat_end_cb);
+ XML_SetCharacterDataHandler(parser, expat_data_cb);
+ xar->xmlsts = INIT;
+
+ while (xar->toc_remaining && ud.state == ARCHIVE_OK) {
+ enum XML_Status xr;
+ const void *d;
+ size_t outbytes;
+ size_t used;
+ int r;
+
+ d = NULL;
+ r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining);
+ if (r != ARCHIVE_OK)
+ return (r);
+ xar->toc_remaining -= used;
+ xar->offset += used;
+ xar->toc_total += outbytes;
+ PRINT_TOC(d, outbytes);
+
+ xr = XML_Parse(parser, d, outbytes, xar->toc_remaining == 0);
+ __archive_read_consume(a, used);
+ if (xr == XML_STATUS_ERROR) {
+ XML_ParserFree(parser);
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "XML Parsing failed");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ XML_ParserFree(parser);
+ return (ud.state);
+}
+#endif /* defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H) */
+
+#endif /* Support xar format */
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_zip.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_zip.c
new file mode 100644
index 00000000000..cca4a3af7e9
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_zip.c
@@ -0,0 +1,4270 @@
+/*-
+ * Copyright (c) 2004-2013 Tim Kientzle
+ * Copyright (c) 2011-2012,2014 Michihiro NAKAJIMA
+ * Copyright (c) 2013 Konrad Kleine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102 2009-12-28 03:11:36Z kientzle $");
+
+/*
+ * The definitive documentation of the Zip file format is:
+ * http://www.pkware.com/documents/casestudies/APPNOTE.TXT
+ *
+ * The Info-Zip project has pioneered various extensions to better
+ * support Zip on Unix, including the 0x5455 "UT", 0x5855 "UX", 0x7855
+ * "Ux", and 0x7875 "ux" extensions for time and ownership
+ * information.
+ *
+ * History of this code: The streaming Zip reader was first added to
+ * libarchive in January 2005. Support for seekable input sources was
+ * added in Nov 2011. Zip64 support (including a significant code
+ * refactoring) was added in 2014.
+ */
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#ifdef HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+#ifdef HAVE_ZSTD_H
+#include <zstd.h>
+#endif
+
+#include "archive.h"
+#include "archive_digest_private.h"
+#include "archive_cryptor_private.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_hmac_private.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_read_private.h"
+#include "archive_ppmd8_private.h"
+
+#ifndef HAVE_ZLIB_H
+#error #include "archive_crc32.h"
+#endif
+
+struct zip_entry {
+ struct archive_rb_node node;
+ struct zip_entry *next;
+ int64_t local_header_offset;
+ int64_t compressed_size;
+ int64_t uncompressed_size;
+ int64_t gid;
+ int64_t uid;
+ struct archive_string rsrcname;
+ time_t mtime;
+ time_t atime;
+ time_t ctime;
+ uint32_t crc32;
+ uint16_t mode;
+ uint16_t zip_flags; /* From GP Flags Field */
+ unsigned char compression;
+ unsigned char system; /* From "version written by" */
+ unsigned char flags; /* Our extra markers. */
+ unsigned char decdat;/* Used for Decryption check */
+
+ /* WinZip AES encryption extra field should be available
+ * when compression is 99. */
+ struct {
+ /* Vendor version: AE-1 - 0x0001, AE-2 - 0x0002 */
+ unsigned vendor;
+#define AES_VENDOR_AE_1 0x0001
+#define AES_VENDOR_AE_2 0x0002
+ /* AES encryption strength:
+ * 1 - 128 bits, 2 - 192 bits, 2 - 256 bits. */
+ unsigned strength;
+ /* Actual compression method. */
+ unsigned char compression;
+ } aes_extra;
+};
+
+struct trad_enc_ctx {
+ uint32_t keys[3];
+};
+
+/* Bits used in zip_flags. */
+#define ZIP_ENCRYPTED (1 << 0)
+#define ZIP_LENGTH_AT_END (1 << 3)
+#define ZIP_STRONG_ENCRYPTED (1 << 6)
+#define ZIP_UTF8_NAME (1 << 11)
+/* See "7.2 Single Password Symmetric Encryption Method"
+ in http://www.pkware.com/documents/casestudies/APPNOTE.TXT */
+#define ZIP_CENTRAL_DIRECTORY_ENCRYPTED (1 << 13)
+
+/* Bits used in flags. */
+#define LA_USED_ZIP64 (1 << 0)
+#define LA_FROM_CENTRAL_DIRECTORY (1 << 1)
+
+/*
+ * See "WinZip - AES Encryption Information"
+ * http://www.winzip.com/aes_info.htm
+ */
+/* Value used in compression method. */
+#define WINZIP_AES_ENCRYPTION 99
+/* Authentication code size. */
+#define AUTH_CODE_SIZE 10
+/**/
+#define MAX_DERIVED_KEY_BUF_SIZE (AES_MAX_KEY_SIZE * 2 + 2)
+
+struct zip {
+ /* Structural information about the archive. */
+ struct archive_string format_name;
+ int64_t central_directory_offset;
+ int64_t central_directory_offset_adjusted;
+ size_t central_directory_entries_total;
+ size_t central_directory_entries_on_this_disk;
+ int has_encrypted_entries;
+
+ /* List of entries (seekable Zip only) */
+ struct zip_entry *zip_entries;
+ struct archive_rb_tree tree;
+ struct archive_rb_tree tree_rsrc;
+
+ /* Bytes read but not yet consumed via __archive_read_consume() */
+ size_t unconsumed;
+
+ /* Information about entry we're currently reading. */
+ struct zip_entry *entry;
+ int64_t entry_bytes_remaining;
+
+ /* These count the number of bytes actually read for the entry. */
+ int64_t entry_compressed_bytes_read;
+ int64_t entry_uncompressed_bytes_read;
+
+ /* Running CRC32 of the decompressed data */
+ unsigned long entry_crc32;
+ unsigned long (*crc32func)(unsigned long, const void *,
+ size_t);
+ char ignore_crc32;
+
+ /* Flags to mark progress of decompression. */
+ char decompress_init;
+ char end_of_entry;
+
+ unsigned char *uncompressed_buffer;
+ size_t uncompressed_buffer_size;
+
+#ifdef HAVE_ZLIB_H
+ z_stream stream;
+ char stream_valid;
+#endif
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+ lzma_stream zipx_lzma_stream;
+ char zipx_lzma_valid;
+#endif
+
+#ifdef HAVE_BZLIB_H
+ bz_stream bzstream;
+ char bzstream_valid;
+#endif
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+ ZSTD_DStream *zstdstream;
+ char zstdstream_valid;
+#endif
+
+ IByteIn zipx_ppmd_stream;
+ ssize_t zipx_ppmd_read_compressed;
+ CPpmd8 ppmd8;
+ char ppmd8_valid;
+ char ppmd8_stream_failed;
+
+ struct archive_string_conv *sconv;
+ struct archive_string_conv *sconv_default;
+ struct archive_string_conv *sconv_utf8;
+ int init_default_conversion;
+ int process_mac_extensions;
+
+ char init_decryption;
+
+ /* Decryption buffer. */
+ /*
+ * The decrypted data starts at decrypted_ptr and
+ * extends for decrypted_bytes_remaining. Decryption
+ * adds new data to the end of this block, data is returned
+ * to clients from the beginning. When the block hits the
+ * end of decrypted_buffer, it has to be shuffled back to
+ * the beginning of the buffer.
+ */
+ unsigned char *decrypted_buffer;
+ unsigned char *decrypted_ptr;
+ size_t decrypted_buffer_size;
+ size_t decrypted_bytes_remaining;
+ size_t decrypted_unconsumed_bytes;
+
+ /* Traditional PKWARE decryption. */
+ struct trad_enc_ctx tctx;
+ char tctx_valid;
+
+ /* WinZip AES decryption. */
+ /* Contexts used for AES decryption. */
+ archive_crypto_ctx cctx;
+ char cctx_valid;
+ archive_hmac_sha1_ctx hctx;
+ char hctx_valid;
+
+ /* Strong encryption's decryption header information. */
+ unsigned iv_size;
+ unsigned alg_id;
+ unsigned bit_len;
+ unsigned flags;
+ unsigned erd_size;
+ unsigned v_size;
+ unsigned v_crc32;
+ uint8_t *iv;
+ uint8_t *erd;
+ uint8_t *v_data;
+};
+
+/* Many systems define min or MIN, but not all. */
+#define zipmin(a,b) ((a) < (b) ? (a) : (b))
+
+#ifdef HAVE_ZLIB_H
+static int
+zip_read_data_deflate(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset);
+#endif
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+static int
+zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset);
+#endif
+
+/* This function is used by Ppmd8_DecodeSymbol during decompression of Ppmd8
+ * streams inside ZIP files. It has 2 purposes: one is to fetch the next
+ * compressed byte from the stream, second one is to increase the counter how
+ * many compressed bytes were read. */
+static Byte
+ppmd_read(void* p) {
+ /* Get the handle to current decompression context. */
+ struct archive_read *a = ((IByteIn*)p)->a;
+ struct zip *zip = (struct zip*) a->format->data;
+ ssize_t bytes_avail = 0;
+
+ /* Fetch next byte. */
+ const uint8_t* data = __archive_read_ahead(a, 1, &bytes_avail);
+ if(bytes_avail < 1) {
+ zip->ppmd8_stream_failed = 1;
+ return 0;
+ }
+
+ __archive_read_consume(a, 1);
+
+ /* Increment the counter. */
+ ++zip->zipx_ppmd_read_compressed;
+
+ /* Return the next compressed byte. */
+ return data[0];
+}
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ Traditional PKWARE Decryption functions.
+ */
+
+static void
+trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c)
+{
+ uint8_t t;
+#define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL)
+
+ ctx->keys[0] = CRC32(ctx->keys[0], c);
+ ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1;
+ t = (ctx->keys[1] >> 24) & 0xff;
+ ctx->keys[2] = CRC32(ctx->keys[2], t);
+#undef CRC32
+}
+
+static uint8_t
+trad_enc_decrypt_byte(struct trad_enc_ctx *ctx)
+{
+ unsigned temp = ctx->keys[2] | 2;
+ return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff;
+}
+
+static void
+trad_enc_decrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out, size_t out_len)
+{
+ unsigned i, max;
+
+ max = (unsigned)((in_len < out_len)? in_len: out_len);
+
+ for (i = 0; i < max; i++) {
+ uint8_t t = in[i] ^ trad_enc_decrypt_byte(ctx);
+ out[i] = t;
+ trad_enc_update_keys(ctx, t);
+ }
+}
+
+static int
+trad_enc_init(struct trad_enc_ctx *ctx, const char *pw, size_t pw_len,
+ const uint8_t *key, size_t key_len, uint8_t *crcchk)
+{
+ uint8_t header[12];
+
+ if (key_len < 12) {
+ *crcchk = 0xff;
+ return -1;
+ }
+
+ ctx->keys[0] = 305419896L;
+ ctx->keys[1] = 591751049L;
+ ctx->keys[2] = 878082192L;
+
+ for (;pw_len; --pw_len)
+ trad_enc_update_keys(ctx, *pw++);
+
+ trad_enc_decrypt_update(ctx, key, 12, header, 12);
+ /* Return the last byte for CRC check. */
+ *crcchk = header[11];
+ return 0;
+}
+
+#if 0
+static void
+crypt_derive_key_sha1(const void *p, int size, unsigned char *key,
+ int key_size)
+{
+#define MD_SIZE 20
+ archive_sha1_ctx ctx;
+ unsigned char md1[MD_SIZE];
+ unsigned char md2[MD_SIZE * 2];
+ unsigned char mkb[64];
+ int i;
+
+ archive_sha1_init(&ctx);
+ archive_sha1_update(&ctx, p, size);
+ archive_sha1_final(&ctx, md1);
+
+ memset(mkb, 0x36, sizeof(mkb));
+ for (i = 0; i < MD_SIZE; i++)
+ mkb[i] ^= md1[i];
+ archive_sha1_init(&ctx);
+ archive_sha1_update(&ctx, mkb, sizeof(mkb));
+ archive_sha1_final(&ctx, md2);
+
+ memset(mkb, 0x5C, sizeof(mkb));
+ for (i = 0; i < MD_SIZE; i++)
+ mkb[i] ^= md1[i];
+ archive_sha1_init(&ctx);
+ archive_sha1_update(&ctx, mkb, sizeof(mkb));
+ archive_sha1_final(&ctx, md2 + MD_SIZE);
+
+ if (key_size > 32)
+ key_size = 32;
+ memcpy(key, md2, key_size);
+#undef MD_SIZE
+}
+#endif
+
+/*
+ * Common code for streaming or seeking modes.
+ *
+ * Includes code to read local file headers, decompress data
+ * from entry bodies, and common API.
+ */
+
+static unsigned long
+real_crc32(unsigned long crc, const void *buff, size_t len)
+{
+ return crc32(crc, buff, (unsigned int)len);
+}
+
+/* Used by "ignorecrc32" option to speed up tests. */
+static unsigned long
+fake_crc32(unsigned long crc, const void *buff, size_t len)
+{
+ (void)crc; /* UNUSED */
+ (void)buff; /* UNUSED */
+ (void)len; /* UNUSED */
+ return 0;
+}
+
+static const struct {
+ int id;
+ const char * name;
+} compression_methods[] = {
+ {0, "uncompressed"}, /* The file is stored (no compression) */
+ {1, "shrinking"}, /* The file is Shrunk */
+ {2, "reduced-1"}, /* The file is Reduced with compression factor 1 */
+ {3, "reduced-2"}, /* The file is Reduced with compression factor 2 */
+ {4, "reduced-3"}, /* The file is Reduced with compression factor 3 */
+ {5, "reduced-4"}, /* The file is Reduced with compression factor 4 */
+ {6, "imploded"}, /* The file is Imploded */
+ {7, "reserved"}, /* Reserved for Tokenizing compression algorithm */
+ {8, "deflation"}, /* The file is Deflated */
+ {9, "deflation-64-bit"}, /* Enhanced Deflating using Deflate64(tm) */
+ {10, "ibm-terse"},/* PKWARE Data Compression Library Imploding
+ * (old IBM TERSE) */
+ {11, "reserved"}, /* Reserved by PKWARE */
+ {12, "bzip"}, /* File is compressed using BZIP2 algorithm */
+ {13, "reserved"}, /* Reserved by PKWARE */
+ {14, "lzma"}, /* LZMA (EFS) */
+ {15, "reserved"}, /* Reserved by PKWARE */
+ {16, "reserved"}, /* Reserved by PKWARE */
+ {17, "reserved"}, /* Reserved by PKWARE */
+ {18, "ibm-terse-new"}, /* File is compressed using IBM TERSE (new) */
+ {19, "ibm-lz777"},/* IBM LZ77 z Architecture (PFS) */
+ {93, "zstd"}, /* Zstandard (zstd) Compression */
+ {95, "xz"}, /* XZ compressed data */
+ {96, "jpeg"}, /* JPEG compressed data */
+ {97, "wav-pack"}, /* WavPack compressed data */
+ {98, "ppmd-1"}, /* PPMd version I, Rev 1 */
+ {99, "aes"} /* WinZip AES encryption */
+};
+
+static const char *
+compression_name(const int compression)
+{
+ static const int num_compression_methods =
+ sizeof(compression_methods)/sizeof(compression_methods[0]);
+ int i=0;
+
+ while(compression >= 0 && i < num_compression_methods) {
+ if (compression_methods[i].id == compression)
+ return compression_methods[i].name;
+ i++;
+ }
+ return "??";
+}
+
+/* Convert an MSDOS-style date/time into Unix-style time. */
+static time_t
+zip_time(const char *p)
+{
+ int msTime, msDate;
+ struct tm ts;
+
+ msTime = (0xff & (unsigned)p[0]) + 256 * (0xff & (unsigned)p[1]);
+ msDate = (0xff & (unsigned)p[2]) + 256 * (0xff & (unsigned)p[3]);
+
+ memset(&ts, 0, sizeof(ts));
+ ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */
+ ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */
+ ts.tm_mday = msDate & 0x1f; /* Day of month. */
+ ts.tm_hour = (msTime >> 11) & 0x1f;
+ ts.tm_min = (msTime >> 5) & 0x3f;
+ ts.tm_sec = (msTime << 1) & 0x3e;
+ ts.tm_isdst = -1;
+ return mktime(&ts);
+}
+
+/*
+ * The extra data is stored as a list of
+ * id1+size1+data1 + id2+size2+data2 ...
+ * triplets. id and size are 2 bytes each.
+ */
+static int
+process_extra(struct archive_read *a, struct archive_entry *entry,
+ const char *p, size_t extra_length, struct zip_entry* zip_entry)
+{
+ unsigned offset = 0;
+ struct zip *zip = (struct zip *)(a->format->data);
+
+ if (extra_length == 0) {
+ return ARCHIVE_OK;
+ }
+
+ if (extra_length < 4) {
+ size_t i = 0;
+ /* Some ZIP files may have trailing 0 bytes. Let's check they
+ * are all 0 and ignore them instead of returning an error.
+ *
+ * This is not technically correct, but some ZIP files look
+ * like this and other tools support those files - so let's
+ * also support them.
+ */
+ for (; i < extra_length; i++) {
+ if (p[i] != 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Too-small extra data: "
+ "Need at least 4 bytes, "
+ "but only found %d bytes",
+ (int)extra_length);
+ return ARCHIVE_FAILED;
+ }
+ }
+
+ return ARCHIVE_OK;
+ }
+
+ while (offset <= extra_length - 4) {
+ unsigned short headerid = archive_le16dec(p + offset);
+ unsigned short datasize = archive_le16dec(p + offset + 2);
+
+ offset += 4;
+ if (offset + datasize > extra_length) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT, "Extra data overflow: "
+ "Need %d bytes but only found %d bytes",
+ (int)datasize, (int)(extra_length - offset));
+ return ARCHIVE_FAILED;
+ }
+#ifdef DEBUG
+ fprintf(stderr, "Header id 0x%04x, length %d\n",
+ headerid, datasize);
+#endif
+ switch (headerid) {
+ case 0x0001:
+ /* Zip64 extended information extra field. */
+ zip_entry->flags |= LA_USED_ZIP64;
+ if (zip_entry->uncompressed_size == 0xffffffff) {
+ uint64_t t = 0;
+ if (datasize < 8
+ || (t = archive_le64dec(p + offset)) >
+ INT64_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Malformed 64-bit "
+ "uncompressed size");
+ return ARCHIVE_FAILED;
+ }
+ zip_entry->uncompressed_size = t;
+ offset += 8;
+ datasize -= 8;
+ }
+ if (zip_entry->compressed_size == 0xffffffff) {
+ uint64_t t = 0;
+ if (datasize < 8
+ || (t = archive_le64dec(p + offset)) >
+ INT64_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Malformed 64-bit "
+ "compressed size");
+ return ARCHIVE_FAILED;
+ }
+ zip_entry->compressed_size = t;
+ offset += 8;
+ datasize -= 8;
+ }
+ if (zip_entry->local_header_offset == 0xffffffff) {
+ uint64_t t = 0;
+ if (datasize < 8
+ || (t = archive_le64dec(p + offset)) >
+ INT64_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Malformed 64-bit "
+ "local header offset");
+ return ARCHIVE_FAILED;
+ }
+ zip_entry->local_header_offset = t;
+ offset += 8;
+ datasize -= 8;
+ }
+ /* archive_le32dec(p + offset) gives disk
+ * on which file starts, but we don't handle
+ * multi-volume Zip files. */
+ break;
+#ifdef DEBUG
+ case 0x0017:
+ {
+ /* Strong encryption field. */
+ if (archive_le16dec(p + offset) == 2) {
+ unsigned algId =
+ archive_le16dec(p + offset + 2);
+ unsigned bitLen =
+ archive_le16dec(p + offset + 4);
+ int flags =
+ archive_le16dec(p + offset + 6);
+ fprintf(stderr, "algId=0x%04x, bitLen=%u, "
+ "flgas=%d\n", algId, bitLen,flags);
+ }
+ break;
+ }
+#endif
+ case 0x5455:
+ {
+ /* Extended time field "UT". */
+ int flags;
+ if (datasize == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Incomplete extended time field");
+ return ARCHIVE_FAILED;
+ }
+ flags = p[offset];
+ offset++;
+ datasize--;
+ /* Flag bits indicate which dates are present. */
+ if (flags & 0x01)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "mtime: %lld -> %d\n",
+ (long long)zip_entry->mtime,
+ archive_le32dec(p + offset));
+#endif
+ if (datasize < 4)
+ break;
+ zip_entry->mtime = archive_le32dec(p + offset);
+ offset += 4;
+ datasize -= 4;
+ }
+ if (flags & 0x02)
+ {
+ if (datasize < 4)
+ break;
+ zip_entry->atime = archive_le32dec(p + offset);
+ offset += 4;
+ datasize -= 4;
+ }
+ if (flags & 0x04)
+ {
+ if (datasize < 4)
+ break;
+ zip_entry->ctime = archive_le32dec(p + offset);
+ offset += 4;
+ datasize -= 4;
+ }
+ break;
+ }
+ case 0x5855:
+ {
+ /* Info-ZIP Unix Extra Field (old version) "UX". */
+ if (datasize >= 8) {
+ zip_entry->atime = archive_le32dec(p + offset);
+ zip_entry->mtime =
+ archive_le32dec(p + offset + 4);
+ }
+ if (datasize >= 12) {
+ zip_entry->uid =
+ archive_le16dec(p + offset + 8);
+ zip_entry->gid =
+ archive_le16dec(p + offset + 10);
+ }
+ break;
+ }
+ case 0x6c78:
+ {
+ /* Experimental 'xl' field */
+ /*
+ * Introduced Dec 2013 to provide a way to
+ * include external file attributes (and other
+ * fields that ordinarily appear only in
+ * central directory) in local file header.
+ * This provides file type and permission
+ * information necessary to support full
+ * streaming extraction. Currently being
+ * discussed with other Zip developers
+ * ... subject to change.
+ *
+ * Format:
+ * The field starts with a bitmap that specifies
+ * which additional fields are included. The
+ * bitmap is variable length and can be extended in
+ * the future.
+ *
+ * n bytes - feature bitmap: first byte has low-order
+ * 7 bits. If high-order bit is set, a subsequent
+ * byte holds the next 7 bits, etc.
+ *
+ * if bitmap & 1, 2 byte "version made by"
+ * if bitmap & 2, 2 byte "internal file attributes"
+ * if bitmap & 4, 4 byte "external file attributes"
+ * if bitmap & 8, 2 byte comment length + n byte
+ * comment
+ */
+ int bitmap, bitmap_last;
+
+ if (datasize < 1)
+ break;
+ bitmap_last = bitmap = 0xff & p[offset];
+ offset += 1;
+ datasize -= 1;
+
+ /* We only support first 7 bits of bitmap; skip rest. */
+ while ((bitmap_last & 0x80) != 0
+ && datasize >= 1) {
+ bitmap_last = p[offset];
+ offset += 1;
+ datasize -= 1;
+ }
+
+ if (bitmap & 1) {
+ /* 2 byte "version made by" */
+ if (datasize < 2)
+ break;
+ zip_entry->system
+ = archive_le16dec(p + offset) >> 8;
+ offset += 2;
+ datasize -= 2;
+ }
+ if (bitmap & 2) {
+ /* 2 byte "internal file attributes" */
+ uint32_t internal_attributes;
+ if (datasize < 2)
+ break;
+ internal_attributes
+ = archive_le16dec(p + offset);
+ /* Not used by libarchive at present. */
+ (void)internal_attributes; /* UNUSED */
+ offset += 2;
+ datasize -= 2;
+ }
+ if (bitmap & 4) {
+ /* 4 byte "external file attributes" */
+ uint32_t external_attributes;
+ if (datasize < 4)
+ break;
+ external_attributes
+ = archive_le32dec(p + offset);
+ if (zip_entry->system == 3) {
+ zip_entry->mode
+ = external_attributes >> 16;
+ } else if (zip_entry->system == 0) {
+ // Interpret MSDOS directory bit
+ if (0x10 == (external_attributes &
+ 0x10)) {
+ zip_entry->mode =
+ AE_IFDIR | 0775;
+ } else {
+ zip_entry->mode =
+ AE_IFREG | 0664;
+ }
+ if (0x01 == (external_attributes &
+ 0x01)) {
+ /* Read-only bit;
+ * strip write permissions */
+ zip_entry->mode &= 0555;
+ }
+ } else {
+ zip_entry->mode = 0;
+ }
+ offset += 4;
+ datasize -= 4;
+ }
+ if (bitmap & 8) {
+ /* 2 byte comment length + comment */
+ uint32_t comment_length;
+ if (datasize < 2)
+ break;
+ comment_length
+ = archive_le16dec(p + offset);
+ offset += 2;
+ datasize -= 2;
+
+ if (datasize < comment_length)
+ break;
+ /* Comment is not supported by libarchive */
+ offset += comment_length;
+ datasize -= comment_length;
+ }
+ break;
+ }
+ case 0x7075:
+ {
+ /* Info-ZIP Unicode Path Extra Field. */
+ if (datasize < 5 || entry == NULL)
+ break;
+ offset += 5;
+ datasize -= 5;
+
+ /* The path name in this field is always encoded
+ * in UTF-8. */
+ if (zip->sconv_utf8 == NULL) {
+ zip->sconv_utf8 =
+ archive_string_conversion_from_charset(
+ &a->archive, "UTF-8", 1);
+ /* If the converter from UTF-8 is not
+ * available, then the path name from the main
+ * field will more likely be correct. */
+ if (zip->sconv_utf8 == NULL)
+ break;
+ }
+
+ /* Make sure the CRC32 of the filename matches. */
+ if (!zip->ignore_crc32) {
+ const char *cp = archive_entry_pathname(entry);
+ if (cp) {
+ unsigned long file_crc =
+ zip->crc32func(0, cp, strlen(cp));
+ unsigned long utf_crc =
+ archive_le32dec(p + offset - 4);
+ if (file_crc != utf_crc) {
+#ifdef DEBUG
+ fprintf(stderr,
+ "CRC filename mismatch; "
+ "CDE is %lx, but UTF8 "
+ "is outdated with %lx\n",
+ file_crc, utf_crc);
+#endif
+ break;
+ }
+ }
+ }
+
+ if (archive_entry_copy_pathname_l(entry,
+ p + offset, datasize, zip->sconv_utf8) != 0) {
+ /* Ignore the error, and fallback to the path
+ * name from the main field. */
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read the ZIP "
+ "0x7075 extra field path.\n");
+#endif
+ }
+ break;
+ }
+ case 0x7855:
+ /* Info-ZIP Unix Extra Field (type 2) "Ux". */
+#ifdef DEBUG
+ fprintf(stderr, "uid %d gid %d\n",
+ archive_le16dec(p + offset),
+ archive_le16dec(p + offset + 2));
+#endif
+ if (datasize >= 2)
+ zip_entry->uid = archive_le16dec(p + offset);
+ if (datasize >= 4)
+ zip_entry->gid =
+ archive_le16dec(p + offset + 2);
+ break;
+ case 0x7875:
+ {
+ /* Info-Zip Unix Extra Field (type 3) "ux". */
+ int uidsize = 0, gidsize = 0;
+
+ /* TODO: support arbitrary uidsize/gidsize. */
+ if (datasize >= 1 && p[offset] == 1) {/* version=1 */
+ if (datasize >= 4) {
+ /* get a uid size. */
+ uidsize = 0xff & (int)p[offset+1];
+ if (uidsize == 2)
+ zip_entry->uid =
+ archive_le16dec(
+ p + offset + 2);
+ else if (uidsize == 4 && datasize >= 6)
+ zip_entry->uid =
+ archive_le32dec(
+ p + offset + 2);
+ }
+ if (datasize >= (2 + uidsize + 3)) {
+ /* get a gid size. */
+ gidsize = 0xff &
+ (int)p[offset+2+uidsize];
+ if (gidsize == 2)
+ zip_entry->gid =
+ archive_le16dec(
+ p+offset+2+uidsize+1);
+ else if (gidsize == 4 &&
+ datasize >= (2 + uidsize + 5))
+ zip_entry->gid =
+ archive_le32dec(
+ p+offset+2+uidsize+1);
+ }
+ }
+ break;
+ }
+ case 0x9901:
+ /* WinZip AES extra data field. */
+ if (datasize < 6) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Incomplete AES field");
+ return ARCHIVE_FAILED;
+ }
+ if (p[offset + 2] == 'A' && p[offset + 3] == 'E') {
+ /* Vendor version. */
+ zip_entry->aes_extra.vendor =
+ archive_le16dec(p + offset);
+ /* AES encryption strength. */
+ zip_entry->aes_extra.strength = p[offset + 4];
+ /* Actual compression method. */
+ zip_entry->aes_extra.compression =
+ p[offset + 5];
+ }
+ break;
+ default:
+ break;
+ }
+ offset += datasize;
+ }
+ return ARCHIVE_OK;
+}
+
+/*
+ * Assumes file pointer is at beginning of local file header.
+ */
+static int
+zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
+ struct zip *zip)
+{
+ const char *p;
+ const void *h;
+ const wchar_t *wp;
+ const char *cp;
+ size_t len, filename_length, extra_length;
+ struct archive_string_conv *sconv;
+ struct zip_entry *zip_entry = zip->entry;
+ struct zip_entry zip_entry_central_dir;
+ int ret = ARCHIVE_OK;
+ char version;
+
+ /* Save a copy of the original for consistency checks. */
+ zip_entry_central_dir = *zip_entry;
+
+ zip->decompress_init = 0;
+ zip->end_of_entry = 0;
+ zip->entry_uncompressed_bytes_read = 0;
+ zip->entry_compressed_bytes_read = 0;
+ zip->entry_crc32 = zip->crc32func(0, NULL, 0);
+
+ /* Setup default conversion. */
+ if (zip->sconv == NULL && !zip->init_default_conversion) {
+ zip->sconv_default =
+ archive_string_default_conversion_for_read(&(a->archive));
+ zip->init_default_conversion = 1;
+ }
+
+ if ((p = __archive_read_ahead(a, 30, NULL)) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file header");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (memcmp(p, "PK\003\004", 4) != 0) {
+ archive_set_error(&a->archive, -1, "Damaged Zip archive");
+ return ARCHIVE_FATAL;
+ }
+ version = p[4];
+ zip_entry->system = p[5];
+ zip_entry->zip_flags = archive_le16dec(p + 6);
+ if (zip_entry->zip_flags & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)) {
+ zip->has_encrypted_entries = 1;
+ archive_entry_set_is_data_encrypted(entry, 1);
+ if (zip_entry->zip_flags & ZIP_CENTRAL_DIRECTORY_ENCRYPTED &&
+ zip_entry->zip_flags & ZIP_ENCRYPTED &&
+ zip_entry->zip_flags & ZIP_STRONG_ENCRYPTED) {
+ archive_entry_set_is_metadata_encrypted(entry, 1);
+ return ARCHIVE_FATAL;
+ }
+ }
+ zip->init_decryption = (zip_entry->zip_flags & ZIP_ENCRYPTED);
+ zip_entry->compression = (char)archive_le16dec(p + 8);
+ zip_entry->mtime = zip_time(p + 10);
+ zip_entry->crc32 = archive_le32dec(p + 14);
+ if (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
+ zip_entry->decdat = p[11];
+ else
+ zip_entry->decdat = p[17];
+ zip_entry->compressed_size = archive_le32dec(p + 18);
+ zip_entry->uncompressed_size = archive_le32dec(p + 22);
+ filename_length = archive_le16dec(p + 26);
+ extra_length = archive_le16dec(p + 28);
+
+ __archive_read_consume(a, 30);
+
+ /* Read the filename. */
+ if ((h = __archive_read_ahead(a, filename_length, NULL)) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file header");
+ return (ARCHIVE_FATAL);
+ }
+ if (zip_entry->zip_flags & ZIP_UTF8_NAME) {
+ /* The filename is stored to be UTF-8. */
+ if (zip->sconv_utf8 == NULL) {
+ zip->sconv_utf8 =
+ archive_string_conversion_from_charset(
+ &a->archive, "UTF-8", 1);
+ if (zip->sconv_utf8 == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ sconv = zip->sconv_utf8;
+ } else if (zip->sconv != NULL)
+ sconv = zip->sconv;
+ else
+ sconv = zip->sconv_default;
+
+ if (archive_entry_copy_pathname_l(entry,
+ h, filename_length, sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname cannot be converted "
+ "from %s to current locale.",
+ archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ __archive_read_consume(a, filename_length);
+
+ /* Read the extra data. */
+ if ((h = __archive_read_ahead(a, extra_length, NULL)) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file header");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (ARCHIVE_OK != process_extra(a, entry, h, extra_length,
+ zip_entry)) {
+ return ARCHIVE_FATAL;
+ }
+ __archive_read_consume(a, extra_length);
+
+ /* Work around a bug in Info-Zip: When reading from a pipe, it
+ * stats the pipe instead of synthesizing a file entry. */
+ if ((zip_entry->mode & AE_IFMT) == AE_IFIFO) {
+ zip_entry->mode &= ~ AE_IFMT;
+ zip_entry->mode |= AE_IFREG;
+ }
+
+ /* If the mode is totally empty, set some sane default. */
+ if (zip_entry->mode == 0) {
+ zip_entry->mode |= 0664;
+ }
+
+ /* Windows archivers sometimes use backslash as the directory
+ * separator. Normalize to slash. */
+ if (zip_entry->system == 0 &&
+ (wp = archive_entry_pathname_w(entry)) != NULL) {
+ if (wcschr(wp, L'/') == NULL && wcschr(wp, L'\\') != NULL) {
+ size_t i;
+ struct archive_wstring s;
+ archive_string_init(&s);
+ archive_wstrcpy(&s, wp);
+ for (i = 0; i < archive_strlen(&s); i++) {
+ if (s.s[i] == '\\')
+ s.s[i] = '/';
+ }
+ archive_entry_copy_pathname_w(entry, s.s);
+ archive_wstring_free(&s);
+ }
+ }
+
+ /* Make sure that entries with a trailing '/' are marked as directories
+ * even if the External File Attributes contains bogus values. If this
+ * is not a directory and there is no type, assume a regular file. */
+ if ((zip_entry->mode & AE_IFMT) != AE_IFDIR) {
+ int has_slash;
+
+ wp = archive_entry_pathname_w(entry);
+ if (wp != NULL) {
+ len = wcslen(wp);
+ has_slash = len > 0 && wp[len - 1] == L'/';
+ } else {
+ cp = archive_entry_pathname(entry);
+ len = (cp != NULL)?strlen(cp):0;
+ has_slash = len > 0 && cp[len - 1] == '/';
+ }
+ /* Correct file type as needed. */
+ if (has_slash) {
+ zip_entry->mode &= ~AE_IFMT;
+ zip_entry->mode |= AE_IFDIR;
+ zip_entry->mode |= 0111;
+ } else if ((zip_entry->mode & AE_IFMT) == 0) {
+ zip_entry->mode |= AE_IFREG;
+ }
+ }
+
+ /* Make sure directories end in '/' */
+ if ((zip_entry->mode & AE_IFMT) == AE_IFDIR) {
+ wp = archive_entry_pathname_w(entry);
+ if (wp != NULL) {
+ len = wcslen(wp);
+ if (len > 0 && wp[len - 1] != L'/') {
+ struct archive_wstring s;
+ archive_string_init(&s);
+ archive_wstrcat(&s, wp);
+ archive_wstrappend_wchar(&s, L'/');
+ archive_entry_copy_pathname_w(entry, s.s);
+ archive_wstring_free(&s);
+ }
+ } else {
+ cp = archive_entry_pathname(entry);
+ len = (cp != NULL)?strlen(cp):0;
+ if (len > 0 && cp[len - 1] != '/') {
+ struct archive_string s;
+ archive_string_init(&s);
+ archive_strcat(&s, cp);
+ archive_strappend_char(&s, '/');
+ archive_entry_set_pathname(entry, s.s);
+ archive_string_free(&s);
+ }
+ }
+ }
+
+ if (zip_entry->flags & LA_FROM_CENTRAL_DIRECTORY) {
+ /* If this came from the central dir, its size info
+ * is definitive, so ignore the length-at-end flag. */
+ zip_entry->zip_flags &= ~ZIP_LENGTH_AT_END;
+ /* If local header is missing a value, use the one from
+ the central directory. If both have it, warn about
+ mismatches. */
+ if (zip_entry->crc32 == 0) {
+ zip_entry->crc32 = zip_entry_central_dir.crc32;
+ } else if (!zip->ignore_crc32
+ && zip_entry->crc32 != zip_entry_central_dir.crc32) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Inconsistent CRC32 values");
+ ret = ARCHIVE_WARN;
+ }
+ if (zip_entry->compressed_size == 0) {
+ zip_entry->compressed_size
+ = zip_entry_central_dir.compressed_size;
+ } else if (zip_entry->compressed_size
+ != zip_entry_central_dir.compressed_size) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Inconsistent compressed size: "
+ "%jd in central directory, %jd in local header",
+ (intmax_t)zip_entry_central_dir.compressed_size,
+ (intmax_t)zip_entry->compressed_size);
+ ret = ARCHIVE_WARN;
+ }
+ if (zip_entry->uncompressed_size == 0 ||
+ zip_entry->uncompressed_size == 0xffffffff) {
+ zip_entry->uncompressed_size
+ = zip_entry_central_dir.uncompressed_size;
+ } else if (zip_entry->uncompressed_size
+ != zip_entry_central_dir.uncompressed_size) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Inconsistent uncompressed size: "
+ "%jd in central directory, %jd in local header",
+ (intmax_t)zip_entry_central_dir.uncompressed_size,
+ (intmax_t)zip_entry->uncompressed_size);
+ ret = ARCHIVE_WARN;
+ }
+ }
+
+ /* Populate some additional entry fields: */
+ archive_entry_set_mode(entry, zip_entry->mode);
+ archive_entry_set_uid(entry, zip_entry->uid);
+ archive_entry_set_gid(entry, zip_entry->gid);
+ archive_entry_set_mtime(entry, zip_entry->mtime, 0);
+ archive_entry_set_ctime(entry, zip_entry->ctime, 0);
+ archive_entry_set_atime(entry, zip_entry->atime, 0);
+
+ if ((zip->entry->mode & AE_IFMT) == AE_IFLNK) {
+ size_t linkname_length;
+
+ if (zip_entry->compressed_size > 64 * 1024) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Zip file with oversized link entry");
+ return ARCHIVE_FATAL;
+ }
+
+ linkname_length = (size_t)zip_entry->compressed_size;
+
+ archive_entry_set_size(entry, 0);
+
+ // take into account link compression if any
+ size_t linkname_full_length = linkname_length;
+ if (zip->entry->compression != 0)
+ {
+ // symlink target string appeared to be compressed
+ int status = ARCHIVE_FATAL;
+ const void *uncompressed_buffer = NULL;
+
+ switch (zip->entry->compression)
+ {
+#if HAVE_ZLIB_H
+ case 8: /* Deflate compression. */
+ zip->entry_bytes_remaining = zip_entry->compressed_size;
+ status = zip_read_data_deflate(a, &uncompressed_buffer,
+ &linkname_full_length, NULL);
+ break;
+#endif
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+ case 14: /* ZIPx LZMA compression. */
+ /*(see zip file format specification, section 4.4.5)*/
+ zip->entry_bytes_remaining = zip_entry->compressed_size;
+ status = zip_read_data_zipx_lzma_alone(a, &uncompressed_buffer,
+ &linkname_full_length, NULL);
+ break;
+#endif
+ default: /* Unsupported compression. */
+ break;
+ }
+ if (status == ARCHIVE_OK)
+ {
+ p = uncompressed_buffer;
+ }
+ else
+ {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported ZIP compression method "
+ "during decompression of link entry (%d: %s)",
+ zip->entry->compression,
+ compression_name(zip->entry->compression));
+ return ARCHIVE_FAILED;
+ }
+ }
+ else
+ {
+ p = __archive_read_ahead(a, linkname_length, NULL);
+ }
+
+ if (p == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Truncated Zip file");
+ return ARCHIVE_FATAL;
+ }
+
+ sconv = zip->sconv;
+ if (sconv == NULL && (zip->entry->zip_flags & ZIP_UTF8_NAME))
+ sconv = zip->sconv_utf8;
+ if (sconv == NULL)
+ sconv = zip->sconv_default;
+ if (archive_entry_copy_symlink_l(entry, p, linkname_full_length,
+ sconv) != 0) {
+ if (errno != ENOMEM && sconv == zip->sconv_utf8 &&
+ (zip->entry->zip_flags & ZIP_UTF8_NAME))
+ archive_entry_copy_symlink_l(entry, p,
+ linkname_full_length, NULL);
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Symlink");
+ return (ARCHIVE_FATAL);
+ }
+ /*
+ * Since there is no character-set regulation for
+ * symlink name, do not report the conversion error
+ * in an automatic conversion.
+ */
+ if (sconv != zip->sconv_utf8 ||
+ (zip->entry->zip_flags & ZIP_UTF8_NAME) == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Symlink cannot be converted "
+ "from %s to current locale.",
+ archive_string_conversion_charset_name(
+ sconv));
+ ret = ARCHIVE_WARN;
+ }
+ }
+ zip_entry->uncompressed_size = zip_entry->compressed_size = 0;
+
+ if (__archive_read_consume(a, linkname_length) < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Read error skipping symlink target name");
+ return ARCHIVE_FATAL;
+ }
+ } else if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
+ || zip_entry->uncompressed_size > 0) {
+ /* Set the size only if it's meaningful. */
+ archive_entry_set_size(entry, zip_entry->uncompressed_size);
+ }
+ zip->entry_bytes_remaining = zip_entry->compressed_size;
+
+ /* If there's no body, force read_data() to return EOF immediately. */
+ if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
+ && zip->entry_bytes_remaining < 1)
+ zip->end_of_entry = 1;
+
+ /* Set up a more descriptive format name. */
+ archive_string_empty(&zip->format_name);
+ archive_string_sprintf(&zip->format_name, "ZIP %d.%d (%s)",
+ version / 10, version % 10,
+ compression_name(zip->entry->compression));
+ a->archive.archive_format_name = zip->format_name.s;
+
+ return (ret);
+}
+
+static int
+check_authentication_code(struct archive_read *a, const void *_p)
+{
+ struct zip *zip = (struct zip *)(a->format->data);
+
+ /* Check authentication code. */
+ if (zip->hctx_valid) {
+ const void *p;
+ uint8_t hmac[20];
+ size_t hmac_len = 20;
+ int cmp;
+
+ archive_hmac_sha1_final(&zip->hctx, hmac, &hmac_len);
+ if (_p == NULL) {
+ /* Read authentication code. */
+ p = __archive_read_ahead(a, AUTH_CODE_SIZE, NULL);
+ if (p == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file data");
+ return (ARCHIVE_FATAL);
+ }
+ } else {
+ p = _p;
+ }
+ cmp = memcmp(hmac, p, AUTH_CODE_SIZE);
+ __archive_read_consume(a, AUTH_CODE_SIZE);
+ if (cmp != 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "ZIP bad Authentication code");
+ return (ARCHIVE_WARN);
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Read "uncompressed" data. There are three cases:
+ * 1) We know the size of the data. This is always true for the
+ * seeking reader (we've examined the Central Directory already).
+ * 2) ZIP_LENGTH_AT_END was set, but only the CRC was deferred.
+ * Info-ZIP seems to do this; we know the size but have to grab
+ * the CRC from the data descriptor afterwards.
+ * 3) We're streaming and ZIP_LENGTH_AT_END was specified and
+ * we have no size information. In this case, we can do pretty
+ * well by watching for the data descriptor record. The data
+ * descriptor is 16 bytes and includes a computed CRC that should
+ * provide a strong check.
+ *
+ * TODO: Technically, the PK\007\010 signature is optional.
+ * In the original spec, the data descriptor contained CRC
+ * and size fields but had no leading signature. In practice,
+ * newer writers seem to provide the signature pretty consistently.
+ *
+ * For uncompressed data, the PK\007\010 marker seems essential
+ * to be sure we've actually seen the end of the entry.
+ *
+ * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets
+ * zip->end_of_entry if it consumes all of the data.
+ */
+static int
+zip_read_data_none(struct archive_read *a, const void **_buff,
+ size_t *size, int64_t *offset)
+{
+ struct zip *zip;
+ const char *buff;
+ ssize_t bytes_avail;
+ int r;
+
+ (void)offset; /* UNUSED */
+
+ zip = (struct zip *)(a->format->data);
+
+ if (zip->entry->zip_flags & ZIP_LENGTH_AT_END) {
+ const char *p;
+ ssize_t grabbing_bytes = 24;
+
+ if (zip->hctx_valid)
+ grabbing_bytes += AUTH_CODE_SIZE;
+ /* Grab at least 24 bytes. */
+ buff = __archive_read_ahead(a, grabbing_bytes, &bytes_avail);
+ if (bytes_avail < grabbing_bytes) {
+ /* Zip archives have end-of-archive markers
+ that are longer than this, so a failure to get at
+ least 24 bytes really does indicate a truncated
+ file. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file data");
+ return (ARCHIVE_FATAL);
+ }
+ /* Check for a complete PK\007\010 signature, followed
+ * by the correct 4-byte CRC. */
+ p = buff;
+ if (zip->hctx_valid)
+ p += AUTH_CODE_SIZE;
+ if (p[0] == 'P' && p[1] == 'K'
+ && p[2] == '\007' && p[3] == '\010'
+ && (archive_le32dec(p + 4) == zip->entry_crc32
+ || zip->ignore_crc32
+ || (zip->hctx_valid
+ && zip->entry->aes_extra.vendor == AES_VENDOR_AE_2))) {
+ if (zip->entry->flags & LA_USED_ZIP64) {
+ uint64_t compressed, uncompressed;
+ zip->entry->crc32 = archive_le32dec(p + 4);
+ compressed = archive_le64dec(p + 8);
+ uncompressed = archive_le64dec(p + 16);
+ if (compressed > INT64_MAX || uncompressed >
+ INT64_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Overflow of 64-bit file sizes");
+ return ARCHIVE_FAILED;
+ }
+ zip->entry->compressed_size = compressed;
+ zip->entry->uncompressed_size = uncompressed;
+ zip->unconsumed = 24;
+ } else {
+ zip->entry->crc32 = archive_le32dec(p + 4);
+ zip->entry->compressed_size =
+ archive_le32dec(p + 8);
+ zip->entry->uncompressed_size =
+ archive_le32dec(p + 12);
+ zip->unconsumed = 16;
+ }
+ if (zip->hctx_valid) {
+ r = check_authentication_code(a, buff);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ zip->end_of_entry = 1;
+ return (ARCHIVE_OK);
+ }
+ /* If not at EOF, ensure we consume at least one byte. */
+ ++p;
+
+ /* Scan forward until we see where a PK\007\010 signature
+ * might be. */
+ /* Return bytes up until that point. On the next call,
+ * the code above will verify the data descriptor. */
+ while (p < buff + bytes_avail - 4) {
+ if (p[3] == 'P') { p += 3; }
+ else if (p[3] == 'K') { p += 2; }
+ else if (p[3] == '\007') { p += 1; }
+ else if (p[3] == '\010' && p[2] == '\007'
+ && p[1] == 'K' && p[0] == 'P') {
+ if (zip->hctx_valid)
+ p -= AUTH_CODE_SIZE;
+ break;
+ } else { p += 4; }
+ }
+ bytes_avail = p - buff;
+ } else {
+ if (zip->entry_bytes_remaining == 0) {
+ zip->end_of_entry = 1;
+ if (zip->hctx_valid) {
+ r = check_authentication_code(a, NULL);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ return (ARCHIVE_OK);
+ }
+ /* Grab a bunch of bytes. */
+ buff = __archive_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail <= 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file data");
+ return (ARCHIVE_FATAL);
+ }
+ if (bytes_avail > zip->entry_bytes_remaining)
+ bytes_avail = (ssize_t)zip->entry_bytes_remaining;
+ }
+ if (zip->tctx_valid || zip->cctx_valid) {
+ size_t dec_size = bytes_avail;
+
+ if (dec_size > zip->decrypted_buffer_size)
+ dec_size = zip->decrypted_buffer_size;
+ if (zip->tctx_valid) {
+ trad_enc_decrypt_update(&zip->tctx,
+ (const uint8_t *)buff, dec_size,
+ zip->decrypted_buffer, dec_size);
+ } else {
+ size_t dsize = dec_size;
+ archive_hmac_sha1_update(&zip->hctx,
+ (const uint8_t *)buff, dec_size);
+ archive_decrypto_aes_ctr_update(&zip->cctx,
+ (const uint8_t *)buff, dec_size,
+ zip->decrypted_buffer, &dsize);
+ }
+ bytes_avail = dec_size;
+ buff = (const char *)zip->decrypted_buffer;
+ }
+ *size = bytes_avail;
+ zip->entry_bytes_remaining -= bytes_avail;
+ zip->entry_uncompressed_bytes_read += bytes_avail;
+ zip->entry_compressed_bytes_read += bytes_avail;
+ zip->unconsumed += bytes_avail;
+ *_buff = buff;
+ return (ARCHIVE_OK);
+}
+
+static int
+consume_optional_marker(struct archive_read *a, struct zip *zip)
+{
+ if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) {
+ const char *p;
+
+ if (NULL == (p = __archive_read_ahead(a, 24, NULL))) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP end-of-file record");
+ return (ARCHIVE_FATAL);
+ }
+ /* Consume the optional PK\007\010 marker. */
+ if (p[0] == 'P' && p[1] == 'K' &&
+ p[2] == '\007' && p[3] == '\010') {
+ p += 4;
+ zip->unconsumed = 4;
+ }
+ if (zip->entry->flags & LA_USED_ZIP64) {
+ uint64_t compressed, uncompressed;
+ zip->entry->crc32 = archive_le32dec(p);
+ compressed = archive_le64dec(p + 4);
+ uncompressed = archive_le64dec(p + 12);
+ if (compressed > INT64_MAX ||
+ uncompressed > INT64_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Overflow of 64-bit file sizes");
+ return ARCHIVE_FAILED;
+ }
+ zip->entry->compressed_size = compressed;
+ zip->entry->uncompressed_size = uncompressed;
+ zip->unconsumed += 20;
+ } else {
+ zip->entry->crc32 = archive_le32dec(p);
+ zip->entry->compressed_size = archive_le32dec(p + 4);
+ zip->entry->uncompressed_size = archive_le32dec(p + 8);
+ zip->unconsumed += 12;
+ }
+ }
+
+ return (ARCHIVE_OK);
+}
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+static int
+zipx_xz_init(struct archive_read *a, struct zip *zip)
+{
+ lzma_ret r;
+
+ if(zip->zipx_lzma_valid) {
+ lzma_end(&zip->zipx_lzma_stream);
+ zip->zipx_lzma_valid = 0;
+ }
+
+ memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream));
+ r = lzma_stream_decoder(&zip->zipx_lzma_stream, UINT64_MAX, 0);
+ if (r != LZMA_OK) {
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "xz initialization failed(%d)",
+ r);
+
+ return (ARCHIVE_FAILED);
+ }
+
+ zip->zipx_lzma_valid = 1;
+
+ free(zip->uncompressed_buffer);
+
+ zip->uncompressed_buffer_size = 256 * 1024;
+ zip->uncompressed_buffer =
+ (uint8_t*) malloc(zip->uncompressed_buffer_size);
+ if (zip->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for xz decompression");
+ return (ARCHIVE_FATAL);
+ }
+
+ zip->decompress_init = 1;
+ return (ARCHIVE_OK);
+}
+
+static int
+zipx_lzma_alone_init(struct archive_read *a, struct zip *zip)
+{
+ lzma_ret r;
+ const uint8_t* p;
+
+#pragma pack(push)
+#pragma pack(1)
+ struct _alone_header {
+ uint8_t bytes[5];
+ uint64_t uncompressed_size;
+ } alone_header;
+#pragma pack(pop)
+
+ if(zip->zipx_lzma_valid) {
+ lzma_end(&zip->zipx_lzma_stream);
+ zip->zipx_lzma_valid = 0;
+ }
+
+ /* To unpack ZIPX's "LZMA" (id 14) stream we can use standard liblzma
+ * that is a part of XZ Utils. The stream format stored inside ZIPX
+ * file is a modified "lzma alone" file format, that was used by the
+ * `lzma` utility which was later deprecated in favour of `xz` utility.
+ * Since those formats are nearly the same, we can use a standard
+ * "lzma alone" decoder from XZ Utils. */
+
+ memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream));
+ r = lzma_alone_decoder(&zip->zipx_lzma_stream, UINT64_MAX);
+ if (r != LZMA_OK) {
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "lzma initialization failed(%d)", r);
+
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Flag the cleanup function that we want our lzma-related structures
+ * to be freed later. */
+ zip->zipx_lzma_valid = 1;
+
+ /* The "lzma alone" file format and the stream format inside ZIPx are
+ * almost the same. Here's an example of a structure of "lzma alone"
+ * format:
+ *
+ * $ cat /bin/ls | lzma | xxd | head -n 1
+ * 00000000: 5d00 0080 00ff ffff ffff ffff ff00 2814
+ *
+ * 5 bytes 8 bytes n bytes
+ * <lzma_params><uncompressed_size><data...>
+ *
+ * lzma_params is a 5-byte blob that has to be decoded to extract
+ * parameters of this LZMA stream. The uncompressed_size field is an
+ * uint64_t value that contains information about the size of the
+ * uncompressed file, or UINT64_MAX if this value is unknown.
+ * The <data...> part is the actual lzma-compressed data stream.
+ *
+ * Now here's the structure of the stream inside the ZIPX file:
+ *
+ * $ cat stream_inside_zipx | xxd | head -n 1
+ * 00000000: 0914 0500 5d00 8000 0000 2814 .... ....
+ *
+ * 2byte 2byte 5 bytes n bytes
+ * <magic1><magic2><lzma_params><data...>
+ *
+ * This means that the ZIPX file contains an additional magic1 and
+ * magic2 headers, the lzma_params field contains the same parameter
+ * set as in the "lzma alone" format, and the <data...> field is the
+ * same as in the "lzma alone" format as well. Note that also the zipx
+ * format is missing the uncompressed_size field.
+ *
+ * So, in order to use the "lzma alone" decoder for the zipx lzma
+ * stream, we simply need to shuffle around some fields, prepare a new
+ * lzma alone header, feed it into lzma alone decoder so it will
+ * initialize itself properly, and then we can start feeding normal
+ * zipx lzma stream into the decoder.
+ */
+
+ /* Read magic1,magic2,lzma_params from the ZIPX stream. */
+ if(zip->entry_bytes_remaining < 9 || (p = __archive_read_ahead(a, 9, NULL)) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated lzma data");
+ return (ARCHIVE_FATAL);
+ }
+
+ if(p[2] != 0x05 || p[3] != 0x00) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid lzma data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Prepare an lzma alone header: copy the lzma_params blob into
+ * a proper place into the lzma alone header. */
+ memcpy(&alone_header.bytes[0], p + 4, 5);
+
+ /* Initialize the 'uncompressed size' field to unknown; we'll manually
+ * monitor how many bytes there are still to be uncompressed. */
+ alone_header.uncompressed_size = UINT64_MAX;
+
+ if(!zip->uncompressed_buffer) {
+ zip->uncompressed_buffer_size = 256 * 1024;
+ zip->uncompressed_buffer =
+ (uint8_t*) malloc(zip->uncompressed_buffer_size);
+
+ if (zip->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for lzma decompression");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ zip->zipx_lzma_stream.next_in = (void*) &alone_header;
+ zip->zipx_lzma_stream.avail_in = sizeof(alone_header);
+ zip->zipx_lzma_stream.total_in = 0;
+ zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer;
+ zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size;
+ zip->zipx_lzma_stream.total_out = 0;
+
+ /* Feed only the header into the lzma alone decoder. This will
+ * effectively initialize the decoder, and will not produce any
+ * output bytes yet. */
+ r = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN);
+ if (r != LZMA_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "lzma stream initialization error");
+ return ARCHIVE_FATAL;
+ }
+
+ /* We've already consumed some bytes, so take this into account. */
+ __archive_read_consume(a, 9);
+ zip->entry_bytes_remaining -= 9;
+ zip->entry_compressed_bytes_read += 9;
+
+ zip->decompress_init = 1;
+ return (ARCHIVE_OK);
+}
+
+static int
+zip_read_data_zipx_xz(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct zip* zip = (struct zip *)(a->format->data);
+ int ret;
+ lzma_ret lz_ret;
+ const void* compressed_buf;
+ ssize_t bytes_avail, in_bytes, to_consume = 0;
+
+ (void) offset; /* UNUSED */
+
+ /* Initialize decompressor if not yet initialized. */
+ if (!zip->decompress_init) {
+ ret = zipx_xz_init(a, zip);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ }
+
+ compressed_buf = __archive_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated xz file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);
+ zip->zipx_lzma_stream.next_in = compressed_buf;
+ zip->zipx_lzma_stream.avail_in = in_bytes;
+ zip->zipx_lzma_stream.total_in = 0;
+ zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer;
+ zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size;
+ zip->zipx_lzma_stream.total_out = 0;
+
+ /* Perform the decompression. */
+ lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN);
+ switch(lz_ret) {
+ case LZMA_DATA_ERROR:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "xz data error (error %d)", (int) lz_ret);
+ return (ARCHIVE_FATAL);
+
+ case LZMA_NO_CHECK:
+ case LZMA_OK:
+ break;
+
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "xz unknown error %d", (int) lz_ret);
+ return (ARCHIVE_FATAL);
+
+ case LZMA_STREAM_END:
+ lzma_end(&zip->zipx_lzma_stream);
+ zip->zipx_lzma_valid = 0;
+
+ if((int64_t) zip->zipx_lzma_stream.total_in !=
+ zip->entry_bytes_remaining)
+ {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xz premature end of stream");
+ return (ARCHIVE_FATAL);
+ }
+
+ zip->end_of_entry = 1;
+ break;
+ }
+
+ to_consume = zip->zipx_lzma_stream.total_in;
+
+ __archive_read_consume(a, to_consume);
+ zip->entry_bytes_remaining -= to_consume;
+ zip->entry_compressed_bytes_read += to_consume;
+ zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out;
+
+ *size = zip->zipx_lzma_stream.total_out;
+ *buff = zip->uncompressed_buffer;
+
+ ret = consume_optional_marker(a, zip);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct zip* zip = (struct zip *)(a->format->data);
+ int ret;
+ lzma_ret lz_ret;
+ const void* compressed_buf;
+ ssize_t bytes_avail, in_bytes, to_consume;
+
+ (void) offset; /* UNUSED */
+
+ /* Initialize decompressor if not yet initialized. */
+ if (!zip->decompress_init) {
+ ret = zipx_lzma_alone_init(a, zip);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ }
+
+ /* Fetch more compressed data. The same note as in deflate handler
+ * applies here as well:
+ *
+ * Note: '1' here is a performance optimization. Recall that the
+ * decompression layer returns a count of available bytes; asking for
+ * more than that forces the decompressor to combine reads by copying
+ * data.
+ */
+ compressed_buf = __archive_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated lzma file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Set decompressor parameters. */
+ in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);
+
+ zip->zipx_lzma_stream.next_in = compressed_buf;
+ zip->zipx_lzma_stream.avail_in = in_bytes;
+ zip->zipx_lzma_stream.total_in = 0;
+ zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer;
+ zip->zipx_lzma_stream.avail_out =
+ /* These lzma_alone streams lack end of stream marker, so let's
+ * make sure the unpacker won't try to unpack more than it's
+ * supposed to. */
+ zipmin((int64_t) zip->uncompressed_buffer_size,
+ zip->entry->uncompressed_size -
+ zip->entry_uncompressed_bytes_read);
+ zip->zipx_lzma_stream.total_out = 0;
+
+ /* Perform the decompression. */
+ lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN);
+ switch(lz_ret) {
+ case LZMA_DATA_ERROR:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "lzma data error (error %d)", (int) lz_ret);
+ return (ARCHIVE_FATAL);
+
+ /* This case is optional in lzma alone format. It can happen,
+ * but most of the files don't have it. (GitHub #1257) */
+ case LZMA_STREAM_END:
+ lzma_end(&zip->zipx_lzma_stream);
+ zip->zipx_lzma_valid = 0;
+ if((int64_t) zip->zipx_lzma_stream.total_in !=
+ zip->entry_bytes_remaining)
+ {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "lzma alone premature end of stream");
+ return (ARCHIVE_FATAL);
+ }
+
+ zip->end_of_entry = 1;
+ break;
+
+ case LZMA_OK:
+ break;
+
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "lzma unknown error %d", (int) lz_ret);
+ return (ARCHIVE_FATAL);
+ }
+
+ to_consume = zip->zipx_lzma_stream.total_in;
+
+ /* Update pointers. */
+ __archive_read_consume(a, to_consume);
+ zip->entry_bytes_remaining -= to_consume;
+ zip->entry_compressed_bytes_read += to_consume;
+ zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out;
+
+ if(zip->entry_bytes_remaining == 0) {
+ zip->end_of_entry = 1;
+ }
+
+ /* Return values. */
+ *size = zip->zipx_lzma_stream.total_out;
+ *buff = zip->uncompressed_buffer;
+
+ /* Behave the same way as during deflate decompression. */
+ ret = consume_optional_marker(a, zip);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+
+ /* Free lzma decoder handle because we'll no longer need it. */
+ if(zip->end_of_entry) {
+ lzma_end(&zip->zipx_lzma_stream);
+ zip->zipx_lzma_valid = 0;
+ }
+
+ /* If we're here, then we're good! */
+ return (ARCHIVE_OK);
+}
+#endif /* HAVE_LZMA_H && HAVE_LIBLZMA */
+
+static int
+zipx_ppmd8_init(struct archive_read *a, struct zip *zip)
+{
+ const void* p;
+ uint32_t val;
+ uint32_t order;
+ uint32_t mem;
+ uint32_t restore_method;
+
+ /* Remove previous decompression context if it exists. */
+ if(zip->ppmd8_valid) {
+ __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8);
+ zip->ppmd8_valid = 0;
+ }
+
+ /* Create a new decompression context. */
+ __archive_ppmd8_functions.Ppmd8_Construct(&zip->ppmd8);
+ zip->ppmd8_stream_failed = 0;
+
+ /* Setup function pointers required by Ppmd8 decompressor. The
+ * 'ppmd_read' function will feed new bytes to the decompressor,
+ * and will increment the 'zip->zipx_ppmd_read_compressed' counter. */
+ zip->ppmd8.Stream.In = &zip->zipx_ppmd_stream;
+ zip->zipx_ppmd_stream.a = a;
+ zip->zipx_ppmd_stream.Read = &ppmd_read;
+
+ /* Reset number of read bytes to 0. */
+ zip->zipx_ppmd_read_compressed = 0;
+
+ /* Read Ppmd8 header (2 bytes). */
+ p = __archive_read_ahead(a, 2, NULL);
+ if(!p) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated file data in PPMd8 stream");
+ return (ARCHIVE_FATAL);
+ }
+ __archive_read_consume(a, 2);
+
+ /* Decode the stream's compression parameters. */
+ val = archive_le16dec(p);
+ order = (val & 15) + 1;
+ mem = ((val >> 4) & 0xff) + 1;
+ restore_method = (val >> 12);
+
+ if(order < 2 || restore_method > 2) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid parameter set in PPMd8 stream (order=%" PRId32 ", "
+ "restore=%" PRId32 ")", order, restore_method);
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Allocate the memory needed to properly decompress the file. */
+ if(!__archive_ppmd8_functions.Ppmd8_Alloc(&zip->ppmd8, mem << 20)) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Unable to allocate memory for PPMd8 stream: %" PRId32 " bytes",
+ mem << 20);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Signal the cleanup function to release Ppmd8 context in the
+ * cleanup phase. */
+ zip->ppmd8_valid = 1;
+
+ /* Perform further Ppmd8 initialization. */
+ if(!__archive_ppmd8_functions.Ppmd8_RangeDec_Init(&zip->ppmd8)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "PPMd8 stream range decoder initialization error");
+ return (ARCHIVE_FATAL);
+ }
+
+ __archive_ppmd8_functions.Ppmd8_Init(&zip->ppmd8, order,
+ restore_method);
+
+ /* Allocate the buffer that will hold uncompressed data. */
+ free(zip->uncompressed_buffer);
+
+ zip->uncompressed_buffer_size = 256 * 1024;
+ zip->uncompressed_buffer =
+ (uint8_t*) malloc(zip->uncompressed_buffer_size);
+
+ if(zip->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for PPMd8 decompression");
+ return ARCHIVE_FATAL;
+ }
+
+ /* Ppmd8 initialization is done. */
+ zip->decompress_init = 1;
+
+ /* We've already read 2 bytes in the output stream. Additionally,
+ * Ppmd8 initialization code could read some data as well. So we
+ * are advancing the stream by 2 bytes plus whatever number of
+ * bytes Ppmd8 init function used. */
+ zip->entry_compressed_bytes_read += 2 + zip->zipx_ppmd_read_compressed;
+
+ return ARCHIVE_OK;
+}
+
+static int
+zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct zip* zip = (struct zip *)(a->format->data);
+ int ret;
+ size_t consumed_bytes = 0;
+ ssize_t bytes_avail = 0;
+
+ (void) offset; /* UNUSED */
+
+ /* If we're here for the first time, initialize Ppmd8 decompression
+ * context first. */
+ if(!zip->decompress_init) {
+ ret = zipx_ppmd8_init(a, zip);
+ if(ret != ARCHIVE_OK)
+ return ret;
+ }
+
+ /* Fetch for more data. We're reading 1 byte here, but libarchive
+ * should prefetch more bytes. */
+ (void) __archive_read_ahead(a, 1, &bytes_avail);
+ if(bytes_avail < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated PPMd8 file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* This counter will be updated inside ppmd_read(), which at one
+ * point will be called by Ppmd8_DecodeSymbol. */
+ zip->zipx_ppmd_read_compressed = 0;
+
+ /* Decompression loop. */
+ do {
+ int sym = __archive_ppmd8_functions.Ppmd8_DecodeSymbol(
+ &zip->ppmd8);
+ if(sym < 0) {
+ zip->end_of_entry = 1;
+ break;
+ }
+
+ /* This field is set by ppmd_read() when there was no more data
+ * to be read. */
+ if(zip->ppmd8_stream_failed) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated PPMd8 file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ zip->uncompressed_buffer[consumed_bytes] = (uint8_t) sym;
+ ++consumed_bytes;
+ } while(consumed_bytes < zip->uncompressed_buffer_size);
+
+ /* Update pointers for libarchive. */
+ *buff = zip->uncompressed_buffer;
+ *size = consumed_bytes;
+
+ /* Update pointers so we can continue decompression in another call. */
+ zip->entry_bytes_remaining -= zip->zipx_ppmd_read_compressed;
+ zip->entry_compressed_bytes_read += zip->zipx_ppmd_read_compressed;
+ zip->entry_uncompressed_bytes_read += consumed_bytes;
+
+ /* If we're at the end of stream, deinitialize Ppmd8 context. */
+ if(zip->end_of_entry) {
+ __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8);
+ zip->ppmd8_valid = 0;
+ }
+
+ /* Seek for optional marker, same way as in each zip entry. */
+ ret = consume_optional_marker(a, zip);
+ if (ret != ARCHIVE_OK)
+ return ret;
+
+ return ARCHIVE_OK;
+}
+
+#ifdef HAVE_BZLIB_H
+static int
+zipx_bzip2_init(struct archive_read *a, struct zip *zip)
+{
+ int r;
+
+ /* Deallocate already existing BZ2 decompression context if it
+ * exists. */
+ if(zip->bzstream_valid) {
+ BZ2_bzDecompressEnd(&zip->bzstream);
+ zip->bzstream_valid = 0;
+ }
+
+ /* Allocate a new BZ2 decompression context. */
+ memset(&zip->bzstream, 0, sizeof(bz_stream));
+ r = BZ2_bzDecompressInit(&zip->bzstream, 0, 1);
+ if(r != BZ_OK) {
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "bzip2 initialization failed(%d)",
+ r);
+
+ return ARCHIVE_FAILED;
+ }
+
+ /* Mark the bzstream field to be released in cleanup phase. */
+ zip->bzstream_valid = 1;
+
+ /* (Re)allocate the buffer that will contain decompressed bytes. */
+ free(zip->uncompressed_buffer);
+
+ zip->uncompressed_buffer_size = 256 * 1024;
+ zip->uncompressed_buffer =
+ (uint8_t*) malloc(zip->uncompressed_buffer_size);
+ if (zip->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for bzip2 decompression");
+ return ARCHIVE_FATAL;
+ }
+
+ /* Initialization done. */
+ zip->decompress_init = 1;
+ return ARCHIVE_OK;
+}
+
+static int
+zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct zip *zip = (struct zip *)(a->format->data);
+ ssize_t bytes_avail = 0, in_bytes, to_consume;
+ const void *compressed_buff;
+ int r;
+ uint64_t total_out;
+
+ (void) offset; /* UNUSED */
+
+ /* Initialize decompression context if we're here for the first time. */
+ if(!zip->decompress_init) {
+ r = zipx_bzip2_init(a, zip);
+ if(r != ARCHIVE_OK)
+ return r;
+ }
+
+ /* Fetch more compressed bytes. */
+ compressed_buff = __archive_read_ahead(a, 1, &bytes_avail);
+ if(bytes_avail < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated bzip2 file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);
+ if(in_bytes < 1) {
+ /* libbz2 doesn't complain when caller feeds avail_in == 0.
+ * It will actually return success in this case, which is
+ * undesirable. This is why we need to make this check
+ * manually. */
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated bzip2 file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Setup buffer boundaries. */
+ zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff;
+ zip->bzstream.avail_in = (uint32_t)in_bytes;
+ zip->bzstream.total_in_hi32 = 0;
+ zip->bzstream.total_in_lo32 = 0;
+ zip->bzstream.next_out = (char*) zip->uncompressed_buffer;
+ zip->bzstream.avail_out = (uint32_t)zip->uncompressed_buffer_size;
+ zip->bzstream.total_out_hi32 = 0;
+ zip->bzstream.total_out_lo32 = 0;
+
+ /* Perform the decompression. */
+ r = BZ2_bzDecompress(&zip->bzstream);
+ switch(r) {
+ case BZ_STREAM_END:
+ /* If we're at the end of the stream, deinitialize the
+ * decompression context now. */
+ switch(BZ2_bzDecompressEnd(&zip->bzstream)) {
+ case BZ_OK:
+ break;
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up bzip2 "
+ "decompressor");
+ return ARCHIVE_FATAL;
+ }
+
+ zip->end_of_entry = 1;
+ break;
+ case BZ_OK:
+ /* The decompressor has successfully decoded this
+ * chunk of data, but more data is still in queue. */
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "bzip2 decompression failed");
+ return ARCHIVE_FATAL;
+ }
+
+ /* Update the pointers so decompressor can continue decoding. */
+ to_consume = zip->bzstream.total_in_lo32;
+ __archive_read_consume(a, to_consume);
+
+ total_out = ((uint64_t) zip->bzstream.total_out_hi32 << 32) |
+ zip->bzstream.total_out_lo32;
+
+ zip->entry_bytes_remaining -= to_consume;
+ zip->entry_compressed_bytes_read += to_consume;
+ zip->entry_uncompressed_bytes_read += total_out;
+
+ /* Give libarchive its due. */
+ *size = total_out;
+ *buff = zip->uncompressed_buffer;
+
+ /* Seek for optional marker, like in other entries. */
+ r = consume_optional_marker(a, zip);
+ if(r != ARCHIVE_OK)
+ return r;
+
+ return ARCHIVE_OK;
+}
+
+#endif
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+static int
+zipx_zstd_init(struct archive_read *a, struct zip *zip)
+{
+ size_t r;
+
+ /* Deallocate already existing Zstd decompression context if it
+ * exists. */
+ if(zip->zstdstream_valid) {
+ ZSTD_freeDStream(zip->zstdstream);
+ zip->zstdstream_valid = 0;
+ }
+
+ /* Allocate a new Zstd decompression context. */
+ zip->zstdstream = ZSTD_createDStream();
+
+ r = ZSTD_initDStream(zip->zstdstream);
+ if (ZSTD_isError(r)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Error initializing zstd decompressor: %s",
+ ZSTD_getErrorName(r));
+
+ return ARCHIVE_FAILED;
+ }
+
+ /* Mark the zstdstream field to be released in cleanup phase. */
+ zip->zstdstream_valid = 1;
+
+ /* (Re)allocate the buffer that will contain decompressed bytes. */
+ free(zip->uncompressed_buffer);
+
+ zip->uncompressed_buffer_size = ZSTD_DStreamOutSize();
+ zip->uncompressed_buffer =
+ (uint8_t*) malloc(zip->uncompressed_buffer_size);
+ if (zip->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for Zstd decompression");
+
+ return ARCHIVE_FATAL;
+ }
+
+ /* Initialization done. */
+ zip->decompress_init = 1;
+ return ARCHIVE_OK;
+}
+
+static int
+zip_read_data_zipx_zstd(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct zip *zip = (struct zip *)(a->format->data);
+ ssize_t bytes_avail = 0, in_bytes, to_consume;
+ const void *compressed_buff;
+ int r;
+ size_t ret;
+ uint64_t total_out;
+ ZSTD_outBuffer out;
+ ZSTD_inBuffer in;
+
+ (void) offset; /* UNUSED */
+
+ /* Initialize decompression context if we're here for the first time. */
+ if(!zip->decompress_init) {
+ r = zipx_zstd_init(a, zip);
+ if(r != ARCHIVE_OK)
+ return r;
+ }
+
+ /* Fetch more compressed bytes */
+ compressed_buff = __archive_read_ahead(a, 1, &bytes_avail);
+ if(bytes_avail < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated zstd file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);
+ if(in_bytes < 1) {
+ /* zstd doesn't complain when caller feeds avail_in == 0.
+ * It will actually return success in this case, which is
+ * undesirable. This is why we need to make this check
+ * manually. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated zstd file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Setup buffer boundaries */
+ in.src = compressed_buff;
+ in.size = in_bytes;
+ in.pos = 0;
+ out = (ZSTD_outBuffer) { zip->uncompressed_buffer, zip->uncompressed_buffer_size, 0 };
+
+ /* Perform the decompression. */
+ ret = ZSTD_decompressStream(zip->zstdstream, &out, &in);
+ if (ZSTD_isError(ret)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Error during zstd decompression: %s",
+ ZSTD_getErrorName(ret));
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Check end of the stream. */
+ if (ret == 0) {
+ if ((in.pos == in.size) && (out.pos < out.size)) {
+ zip->end_of_entry = 1;
+ ZSTD_freeDStream(zip->zstdstream);
+ zip->zstdstream_valid = 0;
+ }
+ }
+
+ /* Update the pointers so decompressor can continue decoding. */
+ to_consume = in.pos;
+ __archive_read_consume(a, to_consume);
+
+ total_out = out.pos;
+
+ zip->entry_bytes_remaining -= to_consume;
+ zip->entry_compressed_bytes_read += to_consume;
+ zip->entry_uncompressed_bytes_read += total_out;
+
+ /* Give libarchive its due. */
+ *size = total_out;
+ *buff = zip->uncompressed_buffer;
+
+ /* Seek for optional marker, like in other entries. */
+ r = consume_optional_marker(a, zip);
+ if(r != ARCHIVE_OK)
+ return r;
+
+ return ARCHIVE_OK;
+}
+#endif
+
+#ifdef HAVE_ZLIB_H
+static int
+zip_deflate_init(struct archive_read *a, struct zip *zip)
+{
+ int r;
+
+ /* If we haven't yet read any data, initialize the decompressor. */
+ if (!zip->decompress_init) {
+ if (zip->stream_valid)
+ r = inflateReset(&zip->stream);
+ else
+ r = inflateInit2(&zip->stream,
+ -15 /* Don't check for zlib header */);
+ if (r != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't initialize ZIP decompression.");
+ return (ARCHIVE_FATAL);
+ }
+ /* Stream structure has been set up. */
+ zip->stream_valid = 1;
+ /* We've initialized decompression for this stream. */
+ zip->decompress_init = 1;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+zip_read_data_deflate(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct zip *zip;
+ ssize_t bytes_avail;
+ const void *compressed_buff, *sp;
+ int r;
+
+ (void)offset; /* UNUSED */
+
+ zip = (struct zip *)(a->format->data);
+
+ /* If the buffer hasn't been allocated, allocate it now. */
+ if (zip->uncompressed_buffer == NULL) {
+ zip->uncompressed_buffer_size = 256 * 1024;
+ zip->uncompressed_buffer
+ = (unsigned char *)malloc(zip->uncompressed_buffer_size);
+ if (zip->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for ZIP decompression");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ r = zip_deflate_init(a, zip);
+ if (r != ARCHIVE_OK)
+ return (r);
+
+ /*
+ * Note: '1' here is a performance optimization.
+ * Recall that the decompression layer returns a count of
+ * available bytes; asking for more than that forces the
+ * decompressor to combine reads by copying data.
+ */
+ compressed_buff = sp = __archive_read_ahead(a, 1, &bytes_avail);
+ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+ && bytes_avail > zip->entry_bytes_remaining) {
+ bytes_avail = (ssize_t)zip->entry_bytes_remaining;
+ }
+ if (bytes_avail < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (zip->tctx_valid || zip->cctx_valid) {
+ if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) {
+ size_t buff_remaining =
+ (zip->decrypted_buffer +
+ zip->decrypted_buffer_size)
+ - (zip->decrypted_ptr +
+ zip->decrypted_bytes_remaining);
+
+ if (buff_remaining > (size_t)bytes_avail)
+ buff_remaining = (size_t)bytes_avail;
+
+ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) &&
+ zip->entry_bytes_remaining > 0) {
+ if ((int64_t)(zip->decrypted_bytes_remaining
+ + buff_remaining)
+ > zip->entry_bytes_remaining) {
+ if (zip->entry_bytes_remaining <
+ (int64_t)zip->decrypted_bytes_remaining)
+ buff_remaining = 0;
+ else
+ buff_remaining =
+ (size_t)zip->entry_bytes_remaining
+ - zip->decrypted_bytes_remaining;
+ }
+ }
+ if (buff_remaining > 0) {
+ if (zip->tctx_valid) {
+ trad_enc_decrypt_update(&zip->tctx,
+ compressed_buff, buff_remaining,
+ zip->decrypted_ptr
+ + zip->decrypted_bytes_remaining,
+ buff_remaining);
+ } else {
+ size_t dsize = buff_remaining;
+ archive_decrypto_aes_ctr_update(
+ &zip->cctx,
+ compressed_buff, buff_remaining,
+ zip->decrypted_ptr
+ + zip->decrypted_bytes_remaining,
+ &dsize);
+ }
+ zip->decrypted_bytes_remaining +=
+ buff_remaining;
+ }
+ }
+ bytes_avail = zip->decrypted_bytes_remaining;
+ compressed_buff = (const char *)zip->decrypted_ptr;
+ }
+
+ /*
+ * A bug in zlib.h: stream.next_in should be marked 'const'
+ * but isn't (the library never alters data through the
+ * next_in pointer, only reads it). The result: this ugly
+ * cast to remove 'const'.
+ */
+ zip->stream.next_in = (Bytef *)(uintptr_t)(const void *)compressed_buff;
+ zip->stream.avail_in = (uInt)bytes_avail;
+ zip->stream.total_in = 0;
+ zip->stream.next_out = zip->uncompressed_buffer;
+ zip->stream.avail_out = (uInt)zip->uncompressed_buffer_size;
+ zip->stream.total_out = 0;
+
+ r = inflate(&zip->stream, 0);
+ switch (r) {
+ case Z_OK:
+ break;
+ case Z_STREAM_END:
+ zip->end_of_entry = 1;
+ break;
+ case Z_MEM_ERROR:
+ archive_set_error(&a->archive, ENOMEM,
+ "Out of memory for ZIP decompression");
+ return (ARCHIVE_FATAL);
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "ZIP decompression failed (%d)", r);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Consume as much as the compressor actually used. */
+ bytes_avail = zip->stream.total_in;
+ if (zip->tctx_valid || zip->cctx_valid) {
+ zip->decrypted_bytes_remaining -= bytes_avail;
+ if (zip->decrypted_bytes_remaining == 0)
+ zip->decrypted_ptr = zip->decrypted_buffer;
+ else
+ zip->decrypted_ptr += bytes_avail;
+ }
+ /* Calculate compressed data as much as we used.*/
+ if (zip->hctx_valid)
+ archive_hmac_sha1_update(&zip->hctx, sp, bytes_avail);
+ __archive_read_consume(a, bytes_avail);
+ zip->entry_bytes_remaining -= bytes_avail;
+ zip->entry_compressed_bytes_read += bytes_avail;
+
+ *size = zip->stream.total_out;
+ zip->entry_uncompressed_bytes_read += zip->stream.total_out;
+ *buff = zip->uncompressed_buffer;
+
+ if (zip->end_of_entry && zip->hctx_valid) {
+ r = check_authentication_code(a, NULL);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ r = consume_optional_marker(a, zip);
+ if (r != ARCHIVE_OK)
+ return (r);
+
+ return (ARCHIVE_OK);
+}
+#endif
+
+static int
+read_decryption_header(struct archive_read *a)
+{
+ struct zip *zip = (struct zip *)(a->format->data);
+ const char *p;
+ unsigned int remaining_size;
+ unsigned int ts;
+
+ /*
+ * Read an initialization vector data field.
+ */
+ p = __archive_read_ahead(a, 2, NULL);
+ if (p == NULL)
+ goto truncated;
+ ts = zip->iv_size;
+ zip->iv_size = archive_le16dec(p);
+ __archive_read_consume(a, 2);
+ if (ts < zip->iv_size) {
+ free(zip->iv);
+ zip->iv = NULL;
+ }
+ p = __archive_read_ahead(a, zip->iv_size, NULL);
+ if (p == NULL)
+ goto truncated;
+ if (zip->iv == NULL) {
+ zip->iv = malloc(zip->iv_size);
+ if (zip->iv == NULL)
+ goto nomem;
+ }
+ memcpy(zip->iv, p, zip->iv_size);
+ __archive_read_consume(a, zip->iv_size);
+
+ /*
+ * Read a size of remaining decryption header field.
+ */
+ p = __archive_read_ahead(a, 14, NULL);
+ if (p == NULL)
+ goto truncated;
+ remaining_size = archive_le32dec(p);
+ if (remaining_size < 16 || remaining_size > (1 << 18))
+ goto corrupted;
+
+ /* Check if format version is supported. */
+ if (archive_le16dec(p+4) != 3) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported encryption format version: %u",
+ archive_le16dec(p+4));
+ return (ARCHIVE_FAILED);
+ }
+
+ /*
+ * Read an encryption algorithm field.
+ */
+ zip->alg_id = archive_le16dec(p+6);
+ switch (zip->alg_id) {
+ case 0x6601:/* DES */
+ case 0x6602:/* RC2 */
+ case 0x6603:/* 3DES 168 */
+ case 0x6609:/* 3DES 112 */
+ case 0x660E:/* AES 128 */
+ case 0x660F:/* AES 192 */
+ case 0x6610:/* AES 256 */
+ case 0x6702:/* RC2 (version >= 5.2) */
+ case 0x6720:/* Blowfish */
+ case 0x6721:/* Twofish */
+ case 0x6801:/* RC4 */
+ /* Supported encryption algorithm. */
+ break;
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unknown encryption algorithm: %u", zip->alg_id);
+ return (ARCHIVE_FAILED);
+ }
+
+ /*
+ * Read a bit length field.
+ */
+ zip->bit_len = archive_le16dec(p+8);
+
+ /*
+ * Read a flags field.
+ */
+ zip->flags = archive_le16dec(p+10);
+ switch (zip->flags & 0xf000) {
+ case 0x0001: /* Password is required to decrypt. */
+ case 0x0002: /* Certificates only. */
+ case 0x0003: /* Password or certificate required to decrypt. */
+ break;
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unknown encryption flag: %u", zip->flags);
+ return (ARCHIVE_FAILED);
+ }
+ if ((zip->flags & 0xf000) == 0 ||
+ (zip->flags & 0xf000) == 0x4000) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unknown encryption flag: %u", zip->flags);
+ return (ARCHIVE_FAILED);
+ }
+
+ /*
+ * Read an encrypted random data field.
+ */
+ ts = zip->erd_size;
+ zip->erd_size = archive_le16dec(p+12);
+ __archive_read_consume(a, 14);
+ if ((zip->erd_size & 0xf) != 0 ||
+ (zip->erd_size + 16) > remaining_size ||
+ (zip->erd_size + 16) < zip->erd_size)
+ goto corrupted;
+
+ if (ts < zip->erd_size) {
+ free(zip->erd);
+ zip->erd = NULL;
+ }
+ p = __archive_read_ahead(a, zip->erd_size, NULL);
+ if (p == NULL)
+ goto truncated;
+ if (zip->erd == NULL) {
+ zip->erd = malloc(zip->erd_size);
+ if (zip->erd == NULL)
+ goto nomem;
+ }
+ memcpy(zip->erd, p, zip->erd_size);
+ __archive_read_consume(a, zip->erd_size);
+
+ /*
+ * Read a reserved data field.
+ */
+ p = __archive_read_ahead(a, 4, NULL);
+ if (p == NULL)
+ goto truncated;
+ /* Reserved data size should be zero. */
+ if (archive_le32dec(p) != 0)
+ goto corrupted;
+ __archive_read_consume(a, 4);
+
+ /*
+ * Read a password validation data field.
+ */
+ p = __archive_read_ahead(a, 2, NULL);
+ if (p == NULL)
+ goto truncated;
+ ts = zip->v_size;
+ zip->v_size = archive_le16dec(p);
+ __archive_read_consume(a, 2);
+ if ((zip->v_size & 0x0f) != 0 ||
+ (zip->erd_size + zip->v_size + 16) > remaining_size ||
+ (zip->erd_size + zip->v_size + 16) < (zip->erd_size + zip->v_size))
+ goto corrupted;
+ if (ts < zip->v_size) {
+ free(zip->v_data);
+ zip->v_data = NULL;
+ }
+ p = __archive_read_ahead(a, zip->v_size, NULL);
+ if (p == NULL)
+ goto truncated;
+ if (zip->v_data == NULL) {
+ zip->v_data = malloc(zip->v_size);
+ if (zip->v_data == NULL)
+ goto nomem;
+ }
+ memcpy(zip->v_data, p, zip->v_size);
+ __archive_read_consume(a, zip->v_size);
+
+ p = __archive_read_ahead(a, 4, NULL);
+ if (p == NULL)
+ goto truncated;
+ zip->v_crc32 = archive_le32dec(p);
+ __archive_read_consume(a, 4);
+
+ /*return (ARCHIVE_OK);
+ * This is not fully implemented yet.*/
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Encrypted file is unsupported");
+ return (ARCHIVE_FAILED);
+truncated:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file data");
+ return (ARCHIVE_FATAL);
+corrupted:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Corrupted ZIP file data");
+ return (ARCHIVE_FATAL);
+nomem:
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for ZIP decryption");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+zip_alloc_decryption_buffer(struct archive_read *a)
+{
+ struct zip *zip = (struct zip *)(a->format->data);
+ size_t bs = 256 * 1024;
+
+ if (zip->decrypted_buffer == NULL) {
+ zip->decrypted_buffer_size = bs;
+ zip->decrypted_buffer = malloc(bs);
+ if (zip->decrypted_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for ZIP decryption");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ zip->decrypted_ptr = zip->decrypted_buffer;
+ return (ARCHIVE_OK);
+}
+
+static int
+init_traditional_PKWARE_decryption(struct archive_read *a)
+{
+ struct zip *zip = (struct zip *)(a->format->data);
+ const void *p;
+ int retry;
+ int r;
+
+ if (zip->tctx_valid)
+ return (ARCHIVE_OK);
+
+ /*
+ Read the 12 bytes encryption header stored at
+ the start of the data area.
+ */
+#define ENC_HEADER_SIZE 12
+ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+ && zip->entry_bytes_remaining < ENC_HEADER_SIZE) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated Zip encrypted body: only %jd bytes available",
+ (intmax_t)zip->entry_bytes_remaining);
+ return (ARCHIVE_FATAL);
+ }
+
+ p = __archive_read_ahead(a, ENC_HEADER_SIZE, NULL);
+ if (p == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file data");
+ return (ARCHIVE_FATAL);
+ }
+
+ for (retry = 0;; retry++) {
+ const char *passphrase;
+ uint8_t crcchk;
+
+ passphrase = __archive_read_next_passphrase(a);
+ if (passphrase == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ (retry > 0)?
+ "Incorrect passphrase":
+ "Passphrase required for this entry");
+ return (ARCHIVE_FAILED);
+ }
+
+ /*
+ * Initialize ctx for Traditional PKWARE Decryption.
+ */
+ r = trad_enc_init(&zip->tctx, passphrase, strlen(passphrase),
+ p, ENC_HEADER_SIZE, &crcchk);
+ if (r == 0 && crcchk == zip->entry->decdat)
+ break;/* The passphrase is OK. */
+ if (retry > 10000) {
+ /* Avoid infinity loop. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Too many incorrect passphrases");
+ return (ARCHIVE_FAILED);
+ }
+ }
+
+ __archive_read_consume(a, ENC_HEADER_SIZE);
+ zip->tctx_valid = 1;
+ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) {
+ zip->entry_bytes_remaining -= ENC_HEADER_SIZE;
+ }
+ /*zip->entry_uncompressed_bytes_read += ENC_HEADER_SIZE;*/
+ zip->entry_compressed_bytes_read += ENC_HEADER_SIZE;
+ zip->decrypted_bytes_remaining = 0;
+
+ return (zip_alloc_decryption_buffer(a));
+#undef ENC_HEADER_SIZE
+}
+
+static int
+init_WinZip_AES_decryption(struct archive_read *a)
+{
+ struct zip *zip = (struct zip *)(a->format->data);
+ const void *p;
+ const uint8_t *pv;
+ size_t key_len, salt_len;
+ uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE];
+ int retry;
+ int r;
+
+ if (zip->cctx_valid || zip->hctx_valid)
+ return (ARCHIVE_OK);
+
+ switch (zip->entry->aes_extra.strength) {
+ case 1: salt_len = 8; key_len = 16; break;
+ case 2: salt_len = 12; key_len = 24; break;
+ case 3: salt_len = 16; key_len = 32; break;
+ default: goto corrupted;
+ }
+ p = __archive_read_ahead(a, salt_len + 2, NULL);
+ if (p == NULL)
+ goto truncated;
+
+ for (retry = 0;; retry++) {
+ const char *passphrase;
+
+ passphrase = __archive_read_next_passphrase(a);
+ if (passphrase == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ (retry > 0)?
+ "Incorrect passphrase":
+ "Passphrase required for this entry");
+ return (ARCHIVE_FAILED);
+ }
+ memset(derived_key, 0, sizeof(derived_key));
+ r = archive_pbkdf2_sha1(passphrase, strlen(passphrase),
+ p, salt_len, 1000, derived_key, key_len * 2 + 2);
+ if (r != 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Decryption is unsupported due to lack of "
+ "crypto library");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Check password verification value. */
+ pv = ((const uint8_t *)p) + salt_len;
+ if (derived_key[key_len * 2] == pv[0] &&
+ derived_key[key_len * 2 + 1] == pv[1])
+ break;/* The passphrase is OK. */
+ if (retry > 10000) {
+ /* Avoid infinity loop. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Too many incorrect passphrases");
+ return (ARCHIVE_FAILED);
+ }
+ }
+
+ r = archive_decrypto_aes_ctr_init(&zip->cctx, derived_key, key_len);
+ if (r != 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Decryption is unsupported due to lack of crypto library");
+ return (ARCHIVE_FAILED);
+ }
+ r = archive_hmac_sha1_init(&zip->hctx, derived_key + key_len, key_len);
+ if (r != 0) {
+ archive_decrypto_aes_ctr_release(&zip->cctx);
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to initialize HMAC-SHA1");
+ return (ARCHIVE_FAILED);
+ }
+ zip->cctx_valid = zip->hctx_valid = 1;
+ __archive_read_consume(a, salt_len + 2);
+ zip->entry_bytes_remaining -= salt_len + 2 + AUTH_CODE_SIZE;
+ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+ && zip->entry_bytes_remaining < 0)
+ goto corrupted;
+ zip->entry_compressed_bytes_read += salt_len + 2 + AUTH_CODE_SIZE;
+ zip->decrypted_bytes_remaining = 0;
+
+ zip->entry->compression = zip->entry->aes_extra.compression;
+ return (zip_alloc_decryption_buffer(a));
+
+truncated:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file data");
+ return (ARCHIVE_FATAL);
+corrupted:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Corrupted ZIP file data");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+archive_read_format_zip_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ int r;
+ struct zip *zip = (struct zip *)(a->format->data);
+
+ if (zip->has_encrypted_entries ==
+ ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+ zip->has_encrypted_entries = 0;
+ }
+
+ *offset = zip->entry_uncompressed_bytes_read;
+ *size = 0;
+ *buff = NULL;
+
+ /* If we hit end-of-entry last time, return ARCHIVE_EOF. */
+ if (zip->end_of_entry)
+ return (ARCHIVE_EOF);
+
+ /* Return EOF immediately if this is a non-regular file. */
+ if (AE_IFREG != (zip->entry->mode & AE_IFMT))
+ return (ARCHIVE_EOF);
+
+ __archive_read_consume(a, zip->unconsumed);
+ zip->unconsumed = 0;
+
+ if (zip->init_decryption) {
+ zip->has_encrypted_entries = 1;
+ if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED)
+ r = read_decryption_header(a);
+ else if (zip->entry->compression == WINZIP_AES_ENCRYPTION)
+ r = init_WinZip_AES_decryption(a);
+ else
+ r = init_traditional_PKWARE_decryption(a);
+ if (r != ARCHIVE_OK)
+ return (r);
+ zip->init_decryption = 0;
+ }
+
+ switch(zip->entry->compression) {
+ case 0: /* No compression. */
+ r = zip_read_data_none(a, buff, size, offset);
+ break;
+#ifdef HAVE_BZLIB_H
+ case 12: /* ZIPx bzip2 compression. */
+ r = zip_read_data_zipx_bzip2(a, buff, size, offset);
+ break;
+#endif
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+ case 14: /* ZIPx LZMA compression. */
+ r = zip_read_data_zipx_lzma_alone(a, buff, size, offset);
+ break;
+ case 95: /* ZIPx XZ compression. */
+ r = zip_read_data_zipx_xz(a, buff, size, offset);
+ break;
+#endif
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+ case 93: /* ZIPx Zstd compression. */
+ r = zip_read_data_zipx_zstd(a, buff, size, offset);
+ break;
+#endif
+ /* PPMd support is built-in, so we don't need any #if guards. */
+ case 98: /* ZIPx PPMd compression. */
+ r = zip_read_data_zipx_ppmd(a, buff, size, offset);
+ break;
+
+#ifdef HAVE_ZLIB_H
+ case 8: /* Deflate compression. */
+ r = zip_read_data_deflate(a, buff, size, offset);
+ break;
+#endif
+ default: /* Unsupported compression. */
+ /* Return a warning. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported ZIP compression method (%d: %s)",
+ zip->entry->compression, compression_name(zip->entry->compression));
+ /* We can't decompress this entry, but we will
+ * be able to skip() it and try the next entry. */
+ return (ARCHIVE_FAILED);
+ break;
+ }
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Update checksum */
+ if (*size)
+ zip->entry_crc32 = zip->crc32func(zip->entry_crc32, *buff,
+ (unsigned)*size);
+ /* If we hit the end, swallow any end-of-data marker. */
+ if (zip->end_of_entry) {
+ /* Check file size, CRC against these values. */
+ if (zip->entry->compressed_size !=
+ zip->entry_compressed_bytes_read) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "ZIP compressed data is wrong size "
+ "(read %jd, expected %jd)",
+ (intmax_t)zip->entry_compressed_bytes_read,
+ (intmax_t)zip->entry->compressed_size);
+ return (ARCHIVE_WARN);
+ }
+ /* Size field only stores the lower 32 bits of the actual
+ * size. */
+ if ((zip->entry->uncompressed_size & UINT32_MAX)
+ != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "ZIP uncompressed data is wrong size "
+ "(read %jd, expected %jd)\n",
+ (intmax_t)zip->entry_uncompressed_bytes_read,
+ (intmax_t)zip->entry->uncompressed_size);
+ return (ARCHIVE_WARN);
+ }
+ /* Check computed CRC against header */
+ if ((!zip->hctx_valid ||
+ zip->entry->aes_extra.vendor != AES_VENDOR_AE_2) &&
+ zip->entry->crc32 != zip->entry_crc32
+ && !zip->ignore_crc32) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "ZIP bad CRC: 0x%lx should be 0x%lx",
+ (unsigned long)zip->entry_crc32,
+ (unsigned long)zip->entry->crc32);
+ return (ARCHIVE_WARN);
+ }
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_zip_cleanup(struct archive_read *a)
+{
+ struct zip *zip;
+ struct zip_entry *zip_entry, *next_zip_entry;
+
+ zip = (struct zip *)(a->format->data);
+
+#ifdef HAVE_ZLIB_H
+ if (zip->stream_valid)
+ inflateEnd(&zip->stream);
+#endif
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+ if (zip->zipx_lzma_valid) {
+ lzma_end(&zip->zipx_lzma_stream);
+ }
+#endif
+
+#ifdef HAVE_BZLIB_H
+ if (zip->bzstream_valid) {
+ BZ2_bzDecompressEnd(&zip->bzstream);
+ }
+#endif
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+ if (zip->zstdstream_valid) {
+ ZSTD_freeDStream(zip->zstdstream);
+ }
+#endif
+
+ free(zip->uncompressed_buffer);
+
+ if (zip->ppmd8_valid)
+ __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8);
+
+ if (zip->zip_entries) {
+ zip_entry = zip->zip_entries;
+ while (zip_entry != NULL) {
+ next_zip_entry = zip_entry->next;
+ archive_string_free(&zip_entry->rsrcname);
+ free(zip_entry);
+ zip_entry = next_zip_entry;
+ }
+ }
+ free(zip->decrypted_buffer);
+ if (zip->cctx_valid)
+ archive_decrypto_aes_ctr_release(&zip->cctx);
+ if (zip->hctx_valid)
+ archive_hmac_sha1_cleanup(&zip->hctx);
+ free(zip->iv);
+ free(zip->erd);
+ free(zip->v_data);
+ archive_string_free(&zip->format_name);
+ free(zip);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_zip_has_encrypted_entries(struct archive_read *_a)
+{
+ if (_a && _a->format) {
+ struct zip * zip = (struct zip *)_a->format->data;
+ if (zip) {
+ return zip->has_encrypted_entries;
+ }
+ }
+ return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+}
+
+static int
+archive_read_format_zip_options(struct archive_read *a,
+ const char *key, const char *val)
+{
+ struct zip *zip;
+ int ret = ARCHIVE_FAILED;
+
+ zip = (struct zip *)(a->format->data);
+ if (strcmp(key, "compat-2x") == 0) {
+ /* Handle filenames as libarchive 2.x */
+ zip->init_default_conversion = (val != NULL) ? 1 : 0;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "zip: hdrcharset option needs a character-set name"
+ );
+ else {
+ zip->sconv = archive_string_conversion_from_charset(
+ &a->archive, val, 0);
+ if (zip->sconv != NULL) {
+ if (strcmp(val, "UTF-8") == 0)
+ zip->sconv_utf8 = zip->sconv;
+ ret = ARCHIVE_OK;
+ } else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ } else if (strcmp(key, "ignorecrc32") == 0) {
+ /* Mostly useful for testing. */
+ if (val == NULL || val[0] == 0) {
+ zip->crc32func = real_crc32;
+ zip->ignore_crc32 = 0;
+ } else {
+ zip->crc32func = fake_crc32;
+ zip->ignore_crc32 = 1;
+ }
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "mac-ext") == 0) {
+ zip->process_mac_extensions = (val != NULL && val[0] != 0);
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+int
+archive_read_support_format_zip(struct archive *a)
+{
+ int r;
+ r = archive_read_support_format_zip_streamable(a);
+ if (r != ARCHIVE_OK)
+ return r;
+ return (archive_read_support_format_zip_seekable(a));
+}
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Streaming-mode support
+ */
+
+
+static int
+archive_read_support_format_zip_capabilities_streamable(struct archive_read * a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA |
+ ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
+}
+
+static int
+archive_read_format_zip_streamable_bid(struct archive_read *a, int best_bid)
+{
+ const char *p;
+
+ (void)best_bid; /* UNUSED */
+
+ if ((p = __archive_read_ahead(a, 4, NULL)) == NULL)
+ return (-1);
+
+ /*
+ * Bid of 29 here comes from:
+ * + 16 bits for "PK",
+ * + next 16-bit field has 6 options so contributes
+ * about 16 - log_2(6) ~= 16 - 2.6 ~= 13 bits
+ *
+ * So we've effectively verified ~29 total bits of check data.
+ */
+ if (p[0] == 'P' && p[1] == 'K') {
+ if ((p[2] == '\001' && p[3] == '\002')
+ || (p[2] == '\003' && p[3] == '\004')
+ || (p[2] == '\005' && p[3] == '\006')
+ || (p[2] == '\006' && p[3] == '\006')
+ || (p[2] == '\007' && p[3] == '\010')
+ || (p[2] == '0' && p[3] == '0'))
+ return (29);
+ }
+
+ /* TODO: It's worth looking ahead a little bit for a valid
+ * PK signature. In particular, that would make it possible
+ * to read some UUEncoded SFX files or SFX files coming from
+ * a network socket. */
+
+ return (0);
+}
+
+static int
+archive_read_format_zip_streamable_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct zip *zip;
+
+ a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
+ if (a->archive.archive_format_name == NULL)
+ a->archive.archive_format_name = "ZIP";
+
+ zip = (struct zip *)(a->format->data);
+
+ /*
+ * It should be sufficient to call archive_read_next_header() for
+ * a reader to determine if an entry is encrypted or not. If the
+ * encryption of an entry is only detectable when calling
+ * archive_read_data(), so be it. We'll do the same check there
+ * as well.
+ */
+ if (zip->has_encrypted_entries ==
+ ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW)
+ zip->has_encrypted_entries = 0;
+
+ /* Make sure we have a zip_entry structure to use. */
+ if (zip->zip_entries == NULL) {
+ zip->zip_entries = malloc(sizeof(struct zip_entry));
+ if (zip->zip_entries == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Out of memory");
+ return ARCHIVE_FATAL;
+ }
+ }
+ zip->entry = zip->zip_entries;
+ memset(zip->entry, 0, sizeof(struct zip_entry));
+
+ if (zip->cctx_valid)
+ archive_decrypto_aes_ctr_release(&zip->cctx);
+ if (zip->hctx_valid)
+ archive_hmac_sha1_cleanup(&zip->hctx);
+ zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0;
+ __archive_read_reset_passphrase(a);
+
+ /* Search ahead for the next local file header. */
+ __archive_read_consume(a, zip->unconsumed);
+ zip->unconsumed = 0;
+ for (;;) {
+ int64_t skipped = 0;
+ const char *p, *end;
+ ssize_t bytes;
+
+ p = __archive_read_ahead(a, 4, &bytes);
+ if (p == NULL)
+ return (ARCHIVE_FATAL);
+ end = p + bytes;
+
+ while (p + 4 <= end) {
+ if (p[0] == 'P' && p[1] == 'K') {
+ if (p[2] == '\003' && p[3] == '\004') {
+ /* Regular file entry. */
+ __archive_read_consume(a, skipped);
+ return zip_read_local_file_header(a,
+ entry, zip);
+ }
+
+ /*
+ * TODO: We cannot restore permissions
+ * based only on the local file headers.
+ * Consider scanning the central
+ * directory and returning additional
+ * entries for at least directories.
+ * This would allow us to properly set
+ * directory permissions.
+ *
+ * This won't help us fix symlinks
+ * and may not help with regular file
+ * permissions, either. <sigh>
+ */
+ if (p[2] == '\001' && p[3] == '\002') {
+ return (ARCHIVE_EOF);
+ }
+
+ /* End of central directory? Must be an
+ * empty archive. */
+ if ((p[2] == '\005' && p[3] == '\006')
+ || (p[2] == '\006' && p[3] == '\006'))
+ return (ARCHIVE_EOF);
+ }
+ ++p;
+ ++skipped;
+ }
+ __archive_read_consume(a, skipped);
+ }
+}
+
+static int
+archive_read_format_zip_read_data_skip_streamable(struct archive_read *a)
+{
+ struct zip *zip;
+ int64_t bytes_skipped;
+
+ zip = (struct zip *)(a->format->data);
+ bytes_skipped = __archive_read_consume(a, zip->unconsumed);
+ zip->unconsumed = 0;
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+
+ /* If we've already read to end of data, we're done. */
+ if (zip->end_of_entry)
+ return (ARCHIVE_OK);
+
+ /* So we know we're streaming... */
+ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+ || zip->entry->compressed_size > 0) {
+ /* We know the compressed length, so we can just skip. */
+ bytes_skipped = __archive_read_consume(a,
+ zip->entry_bytes_remaining);
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+ return (ARCHIVE_OK);
+ }
+
+ if (zip->init_decryption) {
+ int r;
+
+ zip->has_encrypted_entries = 1;
+ if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED)
+ r = read_decryption_header(a);
+ else if (zip->entry->compression == WINZIP_AES_ENCRYPTION)
+ r = init_WinZip_AES_decryption(a);
+ else
+ r = init_traditional_PKWARE_decryption(a);
+ if (r != ARCHIVE_OK)
+ return (r);
+ zip->init_decryption = 0;
+ }
+
+ /* We're streaming and we don't know the length. */
+ /* If the body is compressed and we know the format, we can
+ * find an exact end-of-entry by decompressing it. */
+ switch (zip->entry->compression) {
+#ifdef HAVE_ZLIB_H
+ case 8: /* Deflate compression. */
+ while (!zip->end_of_entry) {
+ int64_t offset = 0;
+ const void *buff = NULL;
+ size_t size = 0;
+ int r;
+ r = zip_read_data_deflate(a, &buff, &size, &offset);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ return ARCHIVE_OK;
+#endif
+ default: /* Uncompressed or unknown. */
+ /* Scan for a PK\007\010 signature. */
+ for (;;) {
+ const char *p, *buff;
+ ssize_t bytes_avail;
+ buff = __archive_read_ahead(a, 16, &bytes_avail);
+ if (bytes_avail < 16) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file data");
+ return (ARCHIVE_FATAL);
+ }
+ p = buff;
+ while (p <= buff + bytes_avail - 16) {
+ if (p[3] == 'P') { p += 3; }
+ else if (p[3] == 'K') { p += 2; }
+ else if (p[3] == '\007') { p += 1; }
+ else if (p[3] == '\010' && p[2] == '\007'
+ && p[1] == 'K' && p[0] == 'P') {
+ if (zip->entry->flags & LA_USED_ZIP64)
+ __archive_read_consume(a,
+ p - buff + 24);
+ else
+ __archive_read_consume(a,
+ p - buff + 16);
+ return ARCHIVE_OK;
+ } else { p += 4; }
+ }
+ __archive_read_consume(a, p - buff);
+ }
+ }
+}
+
+int
+archive_read_support_format_zip_streamable(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct zip *zip;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_zip");
+
+ zip = (struct zip *)calloc(1, sizeof(*zip));
+ if (zip == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate zip data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Streamable reader doesn't support mac extensions. */
+ zip->process_mac_extensions = 0;
+
+ /*
+ * Until enough data has been read, we cannot tell about
+ * any encrypted entries yet.
+ */
+ zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+ zip->crc32func = real_crc32;
+
+ r = __archive_read_register_format(a,
+ zip,
+ "zip",
+ archive_read_format_zip_streamable_bid,
+ archive_read_format_zip_options,
+ archive_read_format_zip_streamable_read_header,
+ archive_read_format_zip_read_data,
+ archive_read_format_zip_read_data_skip_streamable,
+ NULL,
+ archive_read_format_zip_cleanup,
+ archive_read_support_format_zip_capabilities_streamable,
+ archive_read_format_zip_has_encrypted_entries);
+
+ if (r != ARCHIVE_OK)
+ free(zip);
+ return (ARCHIVE_OK);
+}
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Seeking-mode support
+ */
+
+static int
+archive_read_support_format_zip_capabilities_seekable(struct archive_read * a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA |
+ ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
+}
+
+/*
+ * TODO: This is a performance sink because it forces the read core to
+ * drop buffered data from the start of file, which will then have to
+ * be re-read again if this bidder loses.
+ *
+ * We workaround this a little by passing in the best bid so far so
+ * that later bidders can do nothing if they know they'll never
+ * outbid. But we can certainly do better...
+ */
+static int
+read_eocd(struct zip *zip, const char *p, int64_t current_offset)
+{
+ uint16_t disk_num;
+ uint32_t cd_size, cd_offset;
+
+ disk_num = archive_le16dec(p + 4);
+ cd_size = archive_le32dec(p + 12);
+ cd_offset = archive_le32dec(p + 16);
+
+ /* Sanity-check the EOCD we've found. */
+
+ /* This must be the first volume. */
+ if (disk_num != 0)
+ return 0;
+ /* Central directory must be on this volume. */
+ if (disk_num != archive_le16dec(p + 6))
+ return 0;
+ /* All central directory entries must be on this volume. */
+ if (archive_le16dec(p + 10) != archive_le16dec(p + 8))
+ return 0;
+ /* Central directory can't extend beyond start of EOCD record. */
+ if (cd_offset + cd_size > current_offset)
+ return 0;
+
+ /* Save the central directory location for later use. */
+ zip->central_directory_offset = cd_offset;
+ zip->central_directory_offset_adjusted = current_offset - cd_size;
+
+ /* This is just a tiny bit higher than the maximum
+ returned by the streaming Zip bidder. This ensures
+ that the more accurate seeking Zip parser wins
+ whenever seek is available. */
+ return 32;
+}
+
+/*
+ * Examine Zip64 EOCD locator: If it's valid, store the information
+ * from it.
+ */
+static int
+read_zip64_eocd(struct archive_read *a, struct zip *zip, const char *p)
+{
+ int64_t eocd64_offset;
+ int64_t eocd64_size;
+
+ /* Sanity-check the locator record. */
+
+ /* Central dir must be on first volume. */
+ if (archive_le32dec(p + 4) != 0)
+ return 0;
+ /* Must be only a single volume. */
+ if (archive_le32dec(p + 16) != 1)
+ return 0;
+
+ /* Find the Zip64 EOCD record. */
+ eocd64_offset = archive_le64dec(p + 8);
+ if (__archive_read_seek(a, eocd64_offset, SEEK_SET) < 0)
+ return 0;
+ if ((p = __archive_read_ahead(a, 56, NULL)) == NULL)
+ return 0;
+ /* Make sure we can read all of it. */
+ eocd64_size = archive_le64dec(p + 4) + 12;
+ if (eocd64_size < 56 || eocd64_size > 16384)
+ return 0;
+ if ((p = __archive_read_ahead(a, (size_t)eocd64_size, NULL)) == NULL)
+ return 0;
+
+ /* Sanity-check the EOCD64 */
+ if (archive_le32dec(p + 16) != 0) /* Must be disk #0 */
+ return 0;
+ if (archive_le32dec(p + 20) != 0) /* CD must be on disk #0 */
+ return 0;
+ /* CD can't be split. */
+ if (archive_le64dec(p + 24) != archive_le64dec(p + 32))
+ return 0;
+
+ /* Save the central directory offset for later use. */
+ zip->central_directory_offset = archive_le64dec(p + 48);
+ /* TODO: Needs scanning backwards to find the eocd64 instead of assuming */
+ zip->central_directory_offset_adjusted = zip->central_directory_offset;
+
+ return 32;
+}
+
+static int
+archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid)
+{
+ struct zip *zip = (struct zip *)a->format->data;
+ int64_t file_size, current_offset;
+ const char *p;
+ int i, tail;
+
+ /* If someone has already bid more than 32, then avoid
+ trashing the look-ahead buffers with a seek. */
+ if (best_bid > 32)
+ return (-1);
+
+ file_size = __archive_read_seek(a, 0, SEEK_END);
+ if (file_size <= 0)
+ return 0;
+
+ /* Search last 16k of file for end-of-central-directory
+ * record (which starts with PK\005\006) */
+ tail = (int)zipmin(1024 * 16, file_size);
+ current_offset = __archive_read_seek(a, -tail, SEEK_END);
+ if (current_offset < 0)
+ return 0;
+ if ((p = __archive_read_ahead(a, (size_t)tail, NULL)) == NULL)
+ return 0;
+ /* Boyer-Moore search backwards from the end, since we want
+ * to match the last EOCD in the file (there can be more than
+ * one if there is an uncompressed Zip archive as a member
+ * within this Zip archive). */
+ for (i = tail - 22; i > 0;) {
+ switch (p[i]) {
+ case 'P':
+ if (memcmp(p + i, "PK\005\006", 4) == 0) {
+ int ret = read_eocd(zip, p + i,
+ current_offset + i);
+ /* Zip64 EOCD locator precedes
+ * regular EOCD if present. */
+ if (i >= 20 && memcmp(p + i - 20, "PK\006\007", 4) == 0) {
+ int ret_zip64 = read_zip64_eocd(a, zip, p + i - 20);
+ if (ret_zip64 > ret)
+ ret = ret_zip64;
+ }
+ return (ret);
+ }
+ i -= 4;
+ break;
+ case 'K': i -= 1; break;
+ case 005: i -= 2; break;
+ case 006: i -= 3; break;
+ default: i -= 4; break;
+ }
+ }
+ return 0;
+}
+
+/* The red-black trees are only used in seeking mode to manage
+ * the in-memory copy of the central directory. */
+
+static int
+cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2)
+{
+ const struct zip_entry *e1 = (const struct zip_entry *)n1;
+ const struct zip_entry *e2 = (const struct zip_entry *)n2;
+
+ if (e1->local_header_offset > e2->local_header_offset)
+ return -1;
+ if (e1->local_header_offset < e2->local_header_offset)
+ return 1;
+ return 0;
+}
+
+static int
+cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ /* This function won't be called */
+ (void)n; /* UNUSED */
+ (void)key; /* UNUSED */
+ return 1;
+}
+
+static const struct archive_rb_tree_ops rb_ops = {
+ &cmp_node, &cmp_key
+};
+
+static int
+rsrc_cmp_node(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct zip_entry *e1 = (const struct zip_entry *)n1;
+ const struct zip_entry *e2 = (const struct zip_entry *)n2;
+
+ return (strcmp(e2->rsrcname.s, e1->rsrcname.s));
+}
+
+static int
+rsrc_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ const struct zip_entry *e = (const struct zip_entry *)n;
+ return (strcmp((const char *)key, e->rsrcname.s));
+}
+
+static const struct archive_rb_tree_ops rb_rsrc_ops = {
+ &rsrc_cmp_node, &rsrc_cmp_key
+};
+
+static const char *
+rsrc_basename(const char *name, size_t name_length)
+{
+ const char *s, *r;
+
+ r = s = name;
+ for (;;) {
+ s = memchr(s, '/', name_length - (s - name));
+ if (s == NULL)
+ break;
+ r = ++s;
+ }
+ return (r);
+}
+
+static void
+expose_parent_dirs(struct zip *zip, const char *name, size_t name_length)
+{
+ struct archive_string str;
+ struct zip_entry *dir;
+ char *s;
+
+ archive_string_init(&str);
+ archive_strncpy(&str, name, name_length);
+ for (;;) {
+ s = strrchr(str.s, '/');
+ if (s == NULL)
+ break;
+ *s = '\0';
+ /* Transfer the parent directory from zip->tree_rsrc RB
+ * tree to zip->tree RB tree to expose. */
+ dir = (struct zip_entry *)
+ __archive_rb_tree_find_node(&zip->tree_rsrc, str.s);
+ if (dir == NULL)
+ break;
+ __archive_rb_tree_remove_node(&zip->tree_rsrc, &dir->node);
+ archive_string_free(&dir->rsrcname);
+ __archive_rb_tree_insert_node(&zip->tree, &dir->node);
+ }
+ archive_string_free(&str);
+}
+
+static int
+slurp_central_directory(struct archive_read *a, struct archive_entry* entry,
+ struct zip *zip)
+{
+ ssize_t i;
+ unsigned found;
+ int64_t correction;
+ ssize_t bytes_avail;
+ const char *p;
+
+ /*
+ * Find the start of the central directory. The end-of-CD
+ * record has our starting point, but there are lots of
+ * Zip archives which have had other data prepended to the
+ * file, which makes the recorded offsets all too small.
+ * So we search forward from the specified offset until we
+ * find the real start of the central directory. Then we
+ * know the correction we need to apply to account for leading
+ * padding.
+ */
+ if (__archive_read_seek(a, zip->central_directory_offset_adjusted, SEEK_SET)
+ < 0)
+ return ARCHIVE_FATAL;
+
+ found = 0;
+ while (!found) {
+ if ((p = __archive_read_ahead(a, 20, &bytes_avail)) == NULL)
+ return ARCHIVE_FATAL;
+ for (found = 0, i = 0; !found && i < bytes_avail - 4;) {
+ switch (p[i + 3]) {
+ case 'P': i += 3; break;
+ case 'K': i += 2; break;
+ case 001: i += 1; break;
+ case 002:
+ if (memcmp(p + i, "PK\001\002", 4) == 0) {
+ p += i;
+ found = 1;
+ } else
+ i += 4;
+ break;
+ case 005: i += 1; break;
+ case 006:
+ if (memcmp(p + i, "PK\005\006", 4) == 0) {
+ p += i;
+ found = 1;
+ } else if (memcmp(p + i, "PK\006\006", 4) == 0) {
+ p += i;
+ found = 1;
+ } else
+ i += 1;
+ break;
+ default: i += 4; break;
+ }
+ }
+ __archive_read_consume(a, i);
+ }
+ correction = archive_filter_bytes(&a->archive, 0)
+ - zip->central_directory_offset;
+
+ __archive_rb_tree_init(&zip->tree, &rb_ops);
+ __archive_rb_tree_init(&zip->tree_rsrc, &rb_rsrc_ops);
+
+ zip->central_directory_entries_total = 0;
+ while (1) {
+ struct zip_entry *zip_entry;
+ size_t filename_length, extra_length, comment_length;
+ uint32_t external_attributes;
+ const char *name, *r;
+
+ if ((p = __archive_read_ahead(a, 4, NULL)) == NULL)
+ return ARCHIVE_FATAL;
+ if (memcmp(p, "PK\006\006", 4) == 0
+ || memcmp(p, "PK\005\006", 4) == 0) {
+ break;
+ } else if (memcmp(p, "PK\001\002", 4) != 0) {
+ archive_set_error(&a->archive,
+ -1, "Invalid central directory signature");
+ return ARCHIVE_FATAL;
+ }
+ if ((p = __archive_read_ahead(a, 46, NULL)) == NULL)
+ return ARCHIVE_FATAL;
+
+ zip_entry = calloc(1, sizeof(struct zip_entry));
+ if (zip_entry == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate zip entry");
+ return ARCHIVE_FATAL;
+ }
+ zip_entry->next = zip->zip_entries;
+ zip_entry->flags |= LA_FROM_CENTRAL_DIRECTORY;
+ zip->zip_entries = zip_entry;
+ zip->central_directory_entries_total++;
+
+ /* version = p[4]; */
+ zip_entry->system = p[5];
+ /* version_required = archive_le16dec(p + 6); */
+ zip_entry->zip_flags = archive_le16dec(p + 8);
+ if (zip_entry->zip_flags
+ & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)){
+ zip->has_encrypted_entries = 1;
+ }
+ zip_entry->compression = (char)archive_le16dec(p + 10);
+ zip_entry->mtime = zip_time(p + 12);
+ zip_entry->crc32 = archive_le32dec(p + 16);
+ if (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
+ zip_entry->decdat = p[13];
+ else
+ zip_entry->decdat = p[19];
+ zip_entry->compressed_size = archive_le32dec(p + 20);
+ zip_entry->uncompressed_size = archive_le32dec(p + 24);
+ filename_length = archive_le16dec(p + 28);
+ extra_length = archive_le16dec(p + 30);
+ comment_length = archive_le16dec(p + 32);
+ /* disk_start = archive_le16dec(p + 34);
+ * Better be zero.
+ * internal_attributes = archive_le16dec(p + 36);
+ * text bit */
+ external_attributes = archive_le32dec(p + 38);
+ zip_entry->local_header_offset =
+ archive_le32dec(p + 42) + correction;
+
+ /* If we can't guess the mode, leave it zero here;
+ when we read the local file header we might get
+ more information. */
+ if (zip_entry->system == 3) {
+ zip_entry->mode = external_attributes >> 16;
+ } else if (zip_entry->system == 0) {
+ // Interpret MSDOS directory bit
+ if (0x10 == (external_attributes & 0x10)) {
+ zip_entry->mode = AE_IFDIR | 0775;
+ } else {
+ zip_entry->mode = AE_IFREG | 0664;
+ }
+ if (0x01 == (external_attributes & 0x01)) {
+ // Read-only bit; strip write permissions
+ zip_entry->mode &= 0555;
+ }
+ } else {
+ zip_entry->mode = 0;
+ }
+
+ /* We're done with the regular data; get the filename and
+ * extra data. */
+ __archive_read_consume(a, 46);
+ p = __archive_read_ahead(a, filename_length + extra_length,
+ NULL);
+ if (p == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file header");
+ return ARCHIVE_FATAL;
+ }
+ if (ARCHIVE_OK != process_extra(a, entry, p + filename_length,
+ extra_length, zip_entry)) {
+ return ARCHIVE_FATAL;
+ }
+
+ /*
+ * Mac resource fork files are stored under the
+ * "__MACOSX/" directory, so we should check if
+ * it is.
+ */
+ if (!zip->process_mac_extensions) {
+ /* Treat every entry as a regular entry. */
+ __archive_rb_tree_insert_node(&zip->tree,
+ &zip_entry->node);
+ } else {
+ name = p;
+ r = rsrc_basename(name, filename_length);
+ if (filename_length >= 9 &&
+ strncmp("__MACOSX/", name, 9) == 0) {
+ /* If this file is not a resource fork nor
+ * a directory. We should treat it as a non
+ * resource fork file to expose it. */
+ if (name[filename_length-1] != '/' &&
+ (r - name < 3 || r[0] != '.' ||
+ r[1] != '_')) {
+ __archive_rb_tree_insert_node(
+ &zip->tree, &zip_entry->node);
+ /* Expose its parent directories. */
+ expose_parent_dirs(zip, name,
+ filename_length);
+ } else {
+ /* This file is a resource fork file or
+ * a directory. */
+ archive_strncpy(&(zip_entry->rsrcname),
+ name, filename_length);
+ __archive_rb_tree_insert_node(
+ &zip->tree_rsrc, &zip_entry->node);
+ }
+ } else {
+ /* Generate resource fork name to find its
+ * resource file at zip->tree_rsrc. */
+ archive_strcpy(&(zip_entry->rsrcname),
+ "__MACOSX/");
+ archive_strncat(&(zip_entry->rsrcname),
+ name, r - name);
+ archive_strcat(&(zip_entry->rsrcname), "._");
+ archive_strncat(&(zip_entry->rsrcname),
+ name + (r - name),
+ filename_length - (r - name));
+ /* Register an entry to RB tree to sort it by
+ * file offset. */
+ __archive_rb_tree_insert_node(&zip->tree,
+ &zip_entry->node);
+ }
+ }
+
+ /* Skip the comment too ... */
+ __archive_read_consume(a,
+ filename_length + extra_length + comment_length);
+ }
+
+ return ARCHIVE_OK;
+}
+
+static ssize_t
+zip_get_local_file_header_size(struct archive_read *a, size_t extra)
+{
+ const char *p;
+ ssize_t filename_length, extra_length;
+
+ if ((p = __archive_read_ahead(a, extra + 30, NULL)) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file header");
+ return (ARCHIVE_WARN);
+ }
+ p += extra;
+
+ if (memcmp(p, "PK\003\004", 4) != 0) {
+ archive_set_error(&a->archive, -1, "Damaged Zip archive");
+ return ARCHIVE_WARN;
+ }
+ filename_length = archive_le16dec(p + 26);
+ extra_length = archive_le16dec(p + 28);
+
+ return (30 + filename_length + extra_length);
+}
+
+static int
+zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry,
+ struct zip_entry *rsrc)
+{
+ struct zip *zip = (struct zip *)a->format->data;
+ unsigned char *metadata, *mp;
+ int64_t offset = archive_filter_bytes(&a->archive, 0);
+ size_t remaining_bytes, metadata_bytes;
+ ssize_t hsize;
+ int ret = ARCHIVE_OK, eof;
+
+ switch(rsrc->compression) {
+ case 0: /* No compression. */
+ if (rsrc->uncompressed_size != rsrc->compressed_size) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Malformed OS X metadata entry: "
+ "inconsistent size");
+ return (ARCHIVE_FATAL);
+ }
+#ifdef HAVE_ZLIB_H
+ case 8: /* Deflate compression. */
+#endif
+ break;
+ default: /* Unsupported compression. */
+ /* Return a warning. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported ZIP compression method (%s)",
+ compression_name(rsrc->compression));
+ /* We can't decompress this entry, but we will
+ * be able to skip() it and try the next entry. */
+ return (ARCHIVE_WARN);
+ }
+
+ if (rsrc->uncompressed_size > (4 * 1024 * 1024)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Mac metadata is too large: %jd > 4M bytes",
+ (intmax_t)rsrc->uncompressed_size);
+ return (ARCHIVE_WARN);
+ }
+ if (rsrc->compressed_size > (4 * 1024 * 1024)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Mac metadata is too large: %jd > 4M bytes",
+ (intmax_t)rsrc->compressed_size);
+ return (ARCHIVE_WARN);
+ }
+
+ metadata = malloc((size_t)rsrc->uncompressed_size);
+ if (metadata == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Mac metadata");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (offset < rsrc->local_header_offset)
+ __archive_read_consume(a, rsrc->local_header_offset - offset);
+ else if (offset != rsrc->local_header_offset) {
+ __archive_read_seek(a, rsrc->local_header_offset, SEEK_SET);
+ }
+
+ hsize = zip_get_local_file_header_size(a, 0);
+ __archive_read_consume(a, hsize);
+
+ remaining_bytes = (size_t)rsrc->compressed_size;
+ metadata_bytes = (size_t)rsrc->uncompressed_size;
+ mp = metadata;
+ eof = 0;
+ while (!eof && remaining_bytes) {
+ const unsigned char *p;
+ ssize_t bytes_avail;
+ size_t bytes_used;
+
+ p = __archive_read_ahead(a, 1, &bytes_avail);
+ if (p == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file header");
+ ret = ARCHIVE_WARN;
+ goto exit_mac_metadata;
+ }
+ if ((size_t)bytes_avail > remaining_bytes)
+ bytes_avail = remaining_bytes;
+ switch(rsrc->compression) {
+ case 0: /* No compression. */
+ if ((size_t)bytes_avail > metadata_bytes)
+ bytes_avail = metadata_bytes;
+ memcpy(mp, p, bytes_avail);
+ bytes_used = (size_t)bytes_avail;
+ metadata_bytes -= bytes_used;
+ mp += bytes_used;
+ if (metadata_bytes == 0)
+ eof = 1;
+ break;
+#ifdef HAVE_ZLIB_H
+ case 8: /* Deflate compression. */
+ {
+ int r;
+
+ ret = zip_deflate_init(a, zip);
+ if (ret != ARCHIVE_OK)
+ goto exit_mac_metadata;
+ zip->stream.next_in =
+ (Bytef *)(uintptr_t)(const void *)p;
+ zip->stream.avail_in = (uInt)bytes_avail;
+ zip->stream.total_in = 0;
+ zip->stream.next_out = mp;
+ zip->stream.avail_out = (uInt)metadata_bytes;
+ zip->stream.total_out = 0;
+
+ r = inflate(&zip->stream, 0);
+ switch (r) {
+ case Z_OK:
+ break;
+ case Z_STREAM_END:
+ eof = 1;
+ break;
+ case Z_MEM_ERROR:
+ archive_set_error(&a->archive, ENOMEM,
+ "Out of memory for ZIP decompression");
+ ret = ARCHIVE_FATAL;
+ goto exit_mac_metadata;
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "ZIP decompression failed (%d)", r);
+ ret = ARCHIVE_FATAL;
+ goto exit_mac_metadata;
+ }
+ bytes_used = zip->stream.total_in;
+ metadata_bytes -= zip->stream.total_out;
+ mp += zip->stream.total_out;
+ break;
+ }
+#endif
+ default:
+ bytes_used = 0;
+ break;
+ }
+ __archive_read_consume(a, bytes_used);
+ remaining_bytes -= bytes_used;
+ }
+ archive_entry_copy_mac_metadata(entry, metadata,
+ (size_t)rsrc->uncompressed_size - metadata_bytes);
+
+exit_mac_metadata:
+ __archive_read_seek(a, offset, SEEK_SET);
+ zip->decompress_init = 0;
+ free(metadata);
+ return (ret);
+}
+
+static int
+archive_read_format_zip_seekable_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct zip *zip = (struct zip *)a->format->data;
+ struct zip_entry *rsrc;
+ int64_t offset;
+ int r, ret = ARCHIVE_OK;
+
+ /*
+ * It should be sufficient to call archive_read_next_header() for
+ * a reader to determine if an entry is encrypted or not. If the
+ * encryption of an entry is only detectable when calling
+ * archive_read_data(), so be it. We'll do the same check there
+ * as well.
+ */
+ if (zip->has_encrypted_entries ==
+ ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW)
+ zip->has_encrypted_entries = 0;
+
+ a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
+ if (a->archive.archive_format_name == NULL)
+ a->archive.archive_format_name = "ZIP";
+
+ if (zip->zip_entries == NULL) {
+ r = slurp_central_directory(a, entry, zip);
+ if (r != ARCHIVE_OK)
+ return r;
+ /* Get first entry whose local header offset is lower than
+ * other entries in the archive file. */
+ zip->entry =
+ (struct zip_entry *)ARCHIVE_RB_TREE_MIN(&zip->tree);
+ } else if (zip->entry != NULL) {
+ /* Get next entry in local header offset order. */
+ zip->entry = (struct zip_entry *)__archive_rb_tree_iterate(
+ &zip->tree, &zip->entry->node, ARCHIVE_RB_DIR_RIGHT);
+ }
+
+ if (zip->entry == NULL)
+ return ARCHIVE_EOF;
+
+ if (zip->entry->rsrcname.s)
+ rsrc = (struct zip_entry *)__archive_rb_tree_find_node(
+ &zip->tree_rsrc, zip->entry->rsrcname.s);
+ else
+ rsrc = NULL;
+
+ if (zip->cctx_valid)
+ archive_decrypto_aes_ctr_release(&zip->cctx);
+ if (zip->hctx_valid)
+ archive_hmac_sha1_cleanup(&zip->hctx);
+ zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0;
+ __archive_read_reset_passphrase(a);
+
+ /* File entries are sorted by the header offset, we should mostly
+ * use __archive_read_consume to advance a read point to avoid
+ * redundant data reading. */
+ offset = archive_filter_bytes(&a->archive, 0);
+ if (offset < zip->entry->local_header_offset)
+ __archive_read_consume(a,
+ zip->entry->local_header_offset - offset);
+ else if (offset != zip->entry->local_header_offset) {
+ __archive_read_seek(a, zip->entry->local_header_offset,
+ SEEK_SET);
+ }
+ zip->unconsumed = 0;
+ r = zip_read_local_file_header(a, entry, zip);
+ if (r != ARCHIVE_OK)
+ return r;
+ if (rsrc) {
+ int ret2 = zip_read_mac_metadata(a, entry, rsrc);
+ if (ret2 < ret)
+ ret = ret2;
+ }
+ return (ret);
+}
+
+/*
+ * We're going to seek for the next header anyway, so we don't
+ * need to bother doing anything here.
+ */
+static int
+archive_read_format_zip_read_data_skip_seekable(struct archive_read *a)
+{
+ struct zip *zip;
+ zip = (struct zip *)(a->format->data);
+
+ zip->unconsumed = 0;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_support_format_zip_seekable(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct zip *zip;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_zip_seekable");
+
+ zip = (struct zip *)calloc(1, sizeof(*zip));
+ if (zip == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate zip data");
+ return (ARCHIVE_FATAL);
+ }
+
+#ifdef HAVE_COPYFILE_H
+ /* Set this by default on Mac OS. */
+ zip->process_mac_extensions = 1;
+#endif
+
+ /*
+ * Until enough data has been read, we cannot tell about
+ * any encrypted entries yet.
+ */
+ zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+ zip->crc32func = real_crc32;
+
+ r = __archive_read_register_format(a,
+ zip,
+ "zip",
+ archive_read_format_zip_seekable_bid,
+ archive_read_format_zip_options,
+ archive_read_format_zip_seekable_read_header,
+ archive_read_format_zip_read_data,
+ archive_read_format_zip_read_data_skip_seekable,
+ NULL,
+ archive_read_format_zip_cleanup,
+ archive_read_support_format_zip_capabilities_seekable,
+ archive_read_format_zip_has_encrypted_entries);
+
+ if (r != ARCHIVE_OK)
+ free(zip);
+ return (ARCHIVE_OK);
+}
+
+/*# vim:set noet:*/
diff --git a/contrib/libs/libarchive/libarchive/archive_string.c b/contrib/libs/libarchive/libarchive/archive_string.c
new file mode 100644
index 00000000000..c89b315399c
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_string.c
@@ -0,0 +1,4244 @@
+/*-
+ * Copyright (c) 2003-2011 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_string.c 201095 2009-12-28 02:33:22Z kientzle $");
+
+/*
+ * Basic resizable string support, to simplify manipulating arbitrary-sized
+ * strings while minimizing heap activity.
+ *
+ * In particular, the buffer used by a string object is only grown, it
+ * never shrinks, so you can clear and reuse the same string object
+ * without incurring additional memory allocations.
+ */
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_ICONV_H
+#include <iconv.h>
+#endif
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+#ifdef HAVE_LOCALCHARSET_H
+#error #include <localcharset.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include <windows.h>
+#include <locale.h>
+#endif
+
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_string_composition.h"
+
+#if !defined(HAVE_WMEMCPY) && !defined(wmemcpy)
+#define wmemcpy(a,b,i) (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t))
+#endif
+
+#if !defined(HAVE_WMEMMOVE) && !defined(wmemmove)
+#define wmemmove(a,b,i) (wchar_t *)memmove((a), (b), (i) * sizeof(wchar_t))
+#endif
+
+#undef max
+#define max(a, b) ((a)>(b)?(a):(b))
+
+struct archive_string_conv {
+ struct archive_string_conv *next;
+ char *from_charset;
+ char *to_charset;
+ unsigned from_cp;
+ unsigned to_cp;
+ /* Set 1 if from_charset and to_charset are the same. */
+ int same;
+ int flag;
+#define SCONV_TO_CHARSET 1 /* MBS is being converted to specified
+ * charset. */
+#define SCONV_FROM_CHARSET (1<<1) /* MBS is being converted from
+ * specified charset. */
+#define SCONV_BEST_EFFORT (1<<2) /* Copy at least ASCII code. */
+#define SCONV_WIN_CP (1<<3) /* Use Windows API for converting
+ * MBS. */
+#define SCONV_UTF8_LIBARCHIVE_2 (1<<4) /* Incorrect UTF-8 made by libarchive
+ * 2.x in the wrong assumption. */
+#define SCONV_NORMALIZATION_C (1<<6) /* Need normalization to be Form C.
+ * Before UTF-8 characters are actually
+ * processed. */
+#define SCONV_NORMALIZATION_D (1<<7) /* Need normalization to be Form D.
+ * Before UTF-8 characters are actually
+ * processed.
+ * Currently this only for MAC OS X. */
+#define SCONV_TO_UTF8 (1<<8) /* "to charset" side is UTF-8. */
+#define SCONV_FROM_UTF8 (1<<9) /* "from charset" side is UTF-8. */
+#define SCONV_TO_UTF16BE (1<<10) /* "to charset" side is UTF-16BE. */
+#define SCONV_FROM_UTF16BE (1<<11) /* "from charset" side is UTF-16BE. */
+#define SCONV_TO_UTF16LE (1<<12) /* "to charset" side is UTF-16LE. */
+#define SCONV_FROM_UTF16LE (1<<13) /* "from charset" side is UTF-16LE. */
+#define SCONV_TO_UTF16 (SCONV_TO_UTF16BE | SCONV_TO_UTF16LE)
+#define SCONV_FROM_UTF16 (SCONV_FROM_UTF16BE | SCONV_FROM_UTF16LE)
+
+#if HAVE_ICONV
+ iconv_t cd;
+ iconv_t cd_w;/* Use at archive_mstring on
+ * Windows. */
+#endif
+ /* A temporary buffer for normalization. */
+ struct archive_string utftmp;
+ int (*converter[2])(struct archive_string *, const void *, size_t,
+ struct archive_string_conv *);
+ int nconverter;
+};
+
+#define CP_C_LOCALE 0 /* "C" locale only for this file. */
+#define CP_UTF16LE 1200
+#define CP_UTF16BE 1201
+
+#define IS_HIGH_SURROGATE_LA(uc) ((uc) >= 0xD800 && (uc) <= 0xDBFF)
+#define IS_LOW_SURROGATE_LA(uc) ((uc) >= 0xDC00 && (uc) <= 0xDFFF)
+#define IS_SURROGATE_PAIR_LA(uc) ((uc) >= 0xD800 && (uc) <= 0xDFFF)
+#define UNICODE_MAX 0x10FFFF
+#define UNICODE_R_CHAR 0xFFFD /* Replacement character. */
+/* Set U+FFFD(Replacement character) in UTF-8. */
+static const char utf8_replacement_char[] = {0xef, 0xbf, 0xbd};
+
+static struct archive_string_conv *find_sconv_object(struct archive *,
+ const char *, const char *);
+static void add_sconv_object(struct archive *, struct archive_string_conv *);
+static struct archive_string_conv *create_sconv_object(const char *,
+ const char *, unsigned, int);
+static void free_sconv_object(struct archive_string_conv *);
+static struct archive_string_conv *get_sconv_object(struct archive *,
+ const char *, const char *, int);
+static unsigned make_codepage_from_charset(const char *);
+static unsigned get_current_codepage(void);
+static unsigned get_current_oemcp(void);
+static size_t mbsnbytes(const void *, size_t);
+static size_t utf16nbytes(const void *, size_t);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+static int archive_wstring_append_from_mbs_in_codepage(
+ struct archive_wstring *, const char *, size_t,
+ struct archive_string_conv *);
+static int archive_string_append_from_wcs_in_codepage(struct archive_string *,
+ const wchar_t *, size_t, struct archive_string_conv *);
+static int is_big_endian(void);
+static int strncat_in_codepage(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+static int win_strncat_from_utf16be(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+static int win_strncat_from_utf16le(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+static int win_strncat_to_utf16be(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+static int win_strncat_to_utf16le(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+#endif
+static int best_effort_strncat_from_utf16be(struct archive_string *,
+ const void *, size_t, struct archive_string_conv *);
+static int best_effort_strncat_from_utf16le(struct archive_string *,
+ const void *, size_t, struct archive_string_conv *);
+static int best_effort_strncat_to_utf16be(struct archive_string *,
+ const void *, size_t, struct archive_string_conv *);
+static int best_effort_strncat_to_utf16le(struct archive_string *,
+ const void *, size_t, struct archive_string_conv *);
+#if defined(HAVE_ICONV)
+static int iconv_strncat_in_locale(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+#endif
+static int best_effort_strncat_in_locale(struct archive_string *,
+ const void *, size_t, struct archive_string_conv *);
+static int _utf8_to_unicode(uint32_t *, const char *, size_t);
+static int utf8_to_unicode(uint32_t *, const char *, size_t);
+static inline uint32_t combine_surrogate_pair(uint32_t, uint32_t);
+static int cesu8_to_unicode(uint32_t *, const char *, size_t);
+static size_t unicode_to_utf8(char *, size_t, uint32_t);
+static int utf16_to_unicode(uint32_t *, const char *, size_t, int);
+static size_t unicode_to_utf16be(char *, size_t, uint32_t);
+static size_t unicode_to_utf16le(char *, size_t, uint32_t);
+static int strncat_from_utf8_libarchive2(struct archive_string *,
+ const void *, size_t, struct archive_string_conv *);
+static int strncat_from_utf8_to_utf8(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+static int archive_string_normalize_C(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+static int archive_string_normalize_D(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+static int archive_string_append_unicode(struct archive_string *,
+ const void *, size_t, struct archive_string_conv *);
+
+static struct archive_string *
+archive_string_append(struct archive_string *as, const char *p, size_t s)
+{
+ if (archive_string_ensure(as, as->length + s + 1) == NULL)
+ return (NULL);
+ if (s)
+ memmove(as->s + as->length, p, s);
+ as->length += s;
+ as->s[as->length] = 0;
+ return (as);
+}
+
+static struct archive_wstring *
+archive_wstring_append(struct archive_wstring *as, const wchar_t *p, size_t s)
+{
+ if (archive_wstring_ensure(as, as->length + s + 1) == NULL)
+ return (NULL);
+ if (s)
+ wmemmove(as->s + as->length, p, s);
+ as->length += s;
+ as->s[as->length] = 0;
+ return (as);
+}
+
+struct archive_string *
+archive_array_append(struct archive_string *as, const char *p, size_t s)
+{
+ return archive_string_append(as, p, s);
+}
+
+void
+archive_string_concat(struct archive_string *dest, struct archive_string *src)
+{
+ if (archive_string_append(dest, src->s, src->length) == NULL)
+ __archive_errx(1, "Out of memory");
+}
+
+void
+archive_wstring_concat(struct archive_wstring *dest,
+ struct archive_wstring *src)
+{
+ if (archive_wstring_append(dest, src->s, src->length) == NULL)
+ __archive_errx(1, "Out of memory");
+}
+
+void
+archive_string_free(struct archive_string *as)
+{
+ as->length = 0;
+ as->buffer_length = 0;
+ free(as->s);
+ as->s = NULL;
+}
+
+void
+archive_wstring_free(struct archive_wstring *as)
+{
+ as->length = 0;
+ as->buffer_length = 0;
+ free(as->s);
+ as->s = NULL;
+}
+
+struct archive_wstring *
+archive_wstring_ensure(struct archive_wstring *as, size_t s)
+{
+ return (struct archive_wstring *)
+ archive_string_ensure((struct archive_string *)as,
+ s * sizeof(wchar_t));
+}
+
+/* Returns NULL on any allocation failure. */
+struct archive_string *
+archive_string_ensure(struct archive_string *as, size_t s)
+{
+ char *p;
+ size_t new_length;
+
+ /* If buffer is already big enough, don't reallocate. */
+ if (as->s && (s <= as->buffer_length))
+ return (as);
+
+ /*
+ * Growing the buffer at least exponentially ensures that
+ * append operations are always linear in the number of
+ * characters appended. Using a smaller growth rate for
+ * larger buffers reduces memory waste somewhat at the cost of
+ * a larger constant factor.
+ */
+ if (as->buffer_length < 32)
+ /* Start with a minimum 32-character buffer. */
+ new_length = 32;
+ else if (as->buffer_length < 8192)
+ /* Buffers under 8k are doubled for speed. */
+ new_length = as->buffer_length + as->buffer_length;
+ else {
+ /* Buffers 8k and over grow by at least 25% each time. */
+ new_length = as->buffer_length + as->buffer_length / 4;
+ /* Be safe: If size wraps, fail. */
+ if (new_length < as->buffer_length) {
+ /* On failure, wipe the string and return NULL. */
+ archive_string_free(as);
+ errno = ENOMEM;/* Make sure errno has ENOMEM. */
+ return (NULL);
+ }
+ }
+ /*
+ * The computation above is a lower limit to how much we'll
+ * grow the buffer. In any case, we have to grow it enough to
+ * hold the request.
+ */
+ if (new_length < s)
+ new_length = s;
+ /* Now we can reallocate the buffer. */
+ p = (char *)realloc(as->s, new_length);
+ if (p == NULL) {
+ /* On failure, wipe the string and return NULL. */
+ archive_string_free(as);
+ errno = ENOMEM;/* Make sure errno has ENOMEM. */
+ return (NULL);
+ }
+
+ as->s = p;
+ as->buffer_length = new_length;
+ return (as);
+}
+
+/*
+ * TODO: See if there's a way to avoid scanning
+ * the source string twice. Then test to see
+ * if it actually helps (remember that we're almost
+ * always called with pretty short arguments, so
+ * such an optimization might not help).
+ */
+struct archive_string *
+archive_strncat(struct archive_string *as, const void *_p, size_t n)
+{
+ size_t s;
+ const char *p, *pp;
+
+ p = (const char *)_p;
+
+ /* Like strlen(p), except won't examine positions beyond p[n]. */
+ s = 0;
+ pp = p;
+ while (s < n && *pp) {
+ pp++;
+ s++;
+ }
+ if ((as = archive_string_append(as, p, s)) == NULL)
+ __archive_errx(1, "Out of memory");
+ return (as);
+}
+
+struct archive_wstring *
+archive_wstrncat(struct archive_wstring *as, const wchar_t *p, size_t n)
+{
+ size_t s;
+ const wchar_t *pp;
+
+ /* Like strlen(p), except won't examine positions beyond p[n]. */
+ s = 0;
+ pp = p;
+ while (s < n && *pp) {
+ pp++;
+ s++;
+ }
+ if ((as = archive_wstring_append(as, p, s)) == NULL)
+ __archive_errx(1, "Out of memory");
+ return (as);
+}
+
+struct archive_string *
+archive_strcat(struct archive_string *as, const void *p)
+{
+ /* strcat is just strncat without an effective limit.
+ * Assert that we'll never get called with a source
+ * string over 16MB.
+ * TODO: Review all uses of strcat in the source
+ * and try to replace them with strncat().
+ */
+ return archive_strncat(as, p, 0x1000000);
+}
+
+struct archive_wstring *
+archive_wstrcat(struct archive_wstring *as, const wchar_t *p)
+{
+ /* Ditto. */
+ return archive_wstrncat(as, p, 0x1000000);
+}
+
+struct archive_string *
+archive_strappend_char(struct archive_string *as, char c)
+{
+ if ((as = archive_string_append(as, &c, 1)) == NULL)
+ __archive_errx(1, "Out of memory");
+ return (as);
+}
+
+struct archive_wstring *
+archive_wstrappend_wchar(struct archive_wstring *as, wchar_t c)
+{
+ if ((as = archive_wstring_append(as, &c, 1)) == NULL)
+ __archive_errx(1, "Out of memory");
+ return (as);
+}
+
+/*
+ * Get the "current character set" name to use with iconv.
+ * On FreeBSD, the empty character set name "" chooses
+ * the correct character encoding for the current locale,
+ * so this isn't necessary.
+ * But iconv on Mac OS 10.6 doesn't seem to handle this correctly;
+ * on that system, we have to explicitly call nl_langinfo()
+ * to get the right name. Not sure about other platforms.
+ *
+ * NOTE: GNU libiconv does not recognize the character-set name
+ * which some platform nl_langinfo(CODESET) returns, so we should
+ * use locale_charset() instead of nl_langinfo(CODESET) for GNU libiconv.
+ */
+static const char *
+default_iconv_charset(const char *charset) {
+ if (charset != NULL && charset[0] != '\0')
+ return charset;
+#if HAVE_LOCALE_CHARSET && !defined(__APPLE__)
+ /* locale_charset() is broken on Mac OS */
+ return locale_charset();
+#elif HAVE_NL_LANGINFO
+ return nl_langinfo(CODESET);
+#else
+ return "";
+#endif
+}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * Convert MBS to WCS.
+ * Note: returns -1 if conversion fails.
+ */
+int
+archive_wstring_append_from_mbs(struct archive_wstring *dest,
+ const char *p, size_t len)
+{
+ return archive_wstring_append_from_mbs_in_codepage(dest, p, len, NULL);
+}
+
+static int
+archive_wstring_append_from_mbs_in_codepage(struct archive_wstring *dest,
+ const char *s, size_t length, struct archive_string_conv *sc)
+{
+ int count, ret = 0;
+ UINT from_cp;
+
+ if (sc != NULL)
+ from_cp = sc->from_cp;
+ else
+ from_cp = get_current_codepage();
+
+ if (from_cp == CP_C_LOCALE) {
+ /*
+ * "C" locale special processing.
+ */
+ wchar_t *ws;
+ const unsigned char *mp;
+
+ if (NULL == archive_wstring_ensure(dest,
+ dest->length + length + 1))
+ return (-1);
+
+ ws = dest->s + dest->length;
+ mp = (const unsigned char *)s;
+ count = 0;
+ while (count < (int)length && *mp) {
+ *ws++ = (wchar_t)*mp++;
+ count++;
+ }
+ } else if (sc != NULL &&
+ (sc->flag & (SCONV_NORMALIZATION_C | SCONV_NORMALIZATION_D))) {
+ /*
+ * Normalize UTF-8 and UTF-16BE and convert it directly
+ * to UTF-16 as wchar_t.
+ */
+ struct archive_string u16;
+ int saved_flag = sc->flag;/* save current flag. */
+
+ if (is_big_endian())
+ sc->flag |= SCONV_TO_UTF16BE;
+ else
+ sc->flag |= SCONV_TO_UTF16LE;
+
+ if (sc->flag & SCONV_FROM_UTF16) {
+ /*
+ * UTF-16BE/LE NFD ===> UTF-16 NFC
+ * UTF-16BE/LE NFC ===> UTF-16 NFD
+ */
+ count = (int)utf16nbytes(s, length);
+ } else {
+ /*
+ * UTF-8 NFD ===> UTF-16 NFC
+ * UTF-8 NFC ===> UTF-16 NFD
+ */
+ count = (int)mbsnbytes(s, length);
+ }
+ u16.s = (char *)dest->s;
+ u16.length = dest->length << 1;;
+ u16.buffer_length = dest->buffer_length;
+ if (sc->flag & SCONV_NORMALIZATION_C)
+ ret = archive_string_normalize_C(&u16, s, count, sc);
+ else
+ ret = archive_string_normalize_D(&u16, s, count, sc);
+ dest->s = (wchar_t *)u16.s;
+ dest->length = u16.length >> 1;
+ dest->buffer_length = u16.buffer_length;
+ sc->flag = saved_flag;/* restore the saved flag. */
+ return (ret);
+ } else if (sc != NULL && (sc->flag & SCONV_FROM_UTF16)) {
+ count = (int)utf16nbytes(s, length);
+ count >>= 1; /* to be WCS length */
+ /* Allocate memory for WCS. */
+ if (NULL == archive_wstring_ensure(dest,
+ dest->length + count + 1))
+ return (-1);
+ wmemcpy(dest->s + dest->length, (const wchar_t *)s, count);
+ if ((sc->flag & SCONV_FROM_UTF16BE) && !is_big_endian()) {
+ uint16_t *u16 = (uint16_t *)(dest->s + dest->length);
+ int b;
+ for (b = 0; b < count; b++) {
+ uint16_t val = archive_le16dec(u16+b);
+ archive_be16enc(u16+b, val);
+ }
+ } else if ((sc->flag & SCONV_FROM_UTF16LE) && is_big_endian()) {
+ uint16_t *u16 = (uint16_t *)(dest->s + dest->length);
+ int b;
+ for (b = 0; b < count; b++) {
+ uint16_t val = archive_be16dec(u16+b);
+ archive_le16enc(u16+b, val);
+ }
+ }
+ } else {
+ DWORD mbflag;
+ size_t buffsize;
+
+ if (sc == NULL)
+ mbflag = 0;
+ else if (sc->flag & SCONV_FROM_CHARSET) {
+ /* Do not trust the length which comes from
+ * an archive file. */
+ length = mbsnbytes(s, length);
+ mbflag = 0;
+ } else
+ mbflag = MB_PRECOMPOSED;
+
+ buffsize = dest->length + length + 1;
+ do {
+ /* Allocate memory for WCS. */
+ if (NULL == archive_wstring_ensure(dest, buffsize))
+ return (-1);
+ /* Convert MBS to WCS. */
+ count = MultiByteToWideChar(from_cp,
+ mbflag, s, (int)length, dest->s + dest->length,
+ (int)(dest->buffer_length >> 1) -1);
+ if (count == 0 &&
+ GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ /* Expand the WCS buffer. */
+ buffsize = dest->buffer_length << 1;
+ continue;
+ }
+ if (count == 0 && length != 0)
+ ret = -1;
+ break;
+ } while (1);
+ }
+ dest->length += count;
+ dest->s[dest->length] = L'\0';
+ return (ret);
+}
+
+#else
+
+/*
+ * Convert MBS to WCS.
+ * Note: returns -1 if conversion fails.
+ */
+int
+archive_wstring_append_from_mbs(struct archive_wstring *dest,
+ const char *p, size_t len)
+{
+ size_t r;
+ int ret_val = 0;
+ /*
+ * No single byte will be more than one wide character,
+ * so this length estimate will always be big enough.
+ */
+ // size_t wcs_length = len;
+ size_t mbs_length = len;
+ const char *mbs = p;
+ wchar_t *wcs;
+#if HAVE_MBRTOWC
+ mbstate_t shift_state;
+
+ memset(&shift_state, 0, sizeof(shift_state));
+#endif
+ /*
+ * As we decided to have wcs_length == mbs_length == len
+ * we can use len here instead of wcs_length
+ */
+ if (NULL == archive_wstring_ensure(dest, dest->length + len + 1))
+ return (-1);
+ wcs = dest->s + dest->length;
+ /*
+ * We cannot use mbsrtowcs/mbstowcs here because those may convert
+ * extra MBS when strlen(p) > len and one wide character consists of
+ * multi bytes.
+ */
+ while (*mbs && mbs_length > 0) {
+ /*
+ * The buffer we allocated is always big enough.
+ * Keep this code path in a comment if we decide to choose
+ * smaller wcs_length in the future
+ */
+/*
+ if (wcs_length == 0) {
+ dest->length = wcs - dest->s;
+ dest->s[dest->length] = L'\0';
+ wcs_length = mbs_length;
+ if (NULL == archive_wstring_ensure(dest,
+ dest->length + wcs_length + 1))
+ return (-1);
+ wcs = dest->s + dest->length;
+ }
+*/
+#if HAVE_MBRTOWC
+ r = mbrtowc(wcs, mbs, mbs_length, &shift_state);
+#else
+ r = mbtowc(wcs, mbs, mbs_length);
+#endif
+ if (r == (size_t)-1 || r == (size_t)-2) {
+ ret_val = -1;
+ break;
+ }
+ if (r == 0 || r > mbs_length)
+ break;
+ wcs++;
+ // wcs_length--;
+ mbs += r;
+ mbs_length -= r;
+ }
+ dest->length = wcs - dest->s;
+ dest->s[dest->length] = L'\0';
+ return (ret_val);
+}
+
+#endif
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * WCS ==> MBS.
+ * Note: returns -1 if conversion fails.
+ *
+ * Win32 builds use WideCharToMultiByte from the Windows API.
+ * (Maybe Cygwin should too? WideCharToMultiByte will know a
+ * lot more about local character encodings than the wcrtomb()
+ * wrapper is going to know.)
+ */
+int
+archive_string_append_from_wcs(struct archive_string *as,
+ const wchar_t *w, size_t len)
+{
+ return archive_string_append_from_wcs_in_codepage(as, w, len, NULL);
+}
+
+static int
+archive_string_append_from_wcs_in_codepage(struct archive_string *as,
+ const wchar_t *ws, size_t len, struct archive_string_conv *sc)
+{
+ BOOL defchar_used, *dp;
+ int count, ret = 0;
+ UINT to_cp;
+ int wslen = (int)len;
+
+ if (sc != NULL)
+ to_cp = sc->to_cp;
+ else
+ to_cp = get_current_codepage();
+
+ if (to_cp == CP_C_LOCALE) {
+ /*
+ * "C" locale special processing.
+ */
+ const wchar_t *wp = ws;
+ char *p;
+
+ if (NULL == archive_string_ensure(as,
+ as->length + wslen +1))
+ return (-1);
+ p = as->s + as->length;
+ count = 0;
+ defchar_used = 0;
+ while (count < wslen && *wp) {
+ if (*wp > 255) {
+ *p++ = '?';
+ wp++;
+ defchar_used = 1;
+ } else
+ *p++ = (char)*wp++;
+ count++;
+ }
+ } else if (sc != NULL && (sc->flag & SCONV_TO_UTF16)) {
+ uint16_t *u16;
+
+ if (NULL ==
+ archive_string_ensure(as, as->length + len * 2 + 2))
+ return (-1);
+ u16 = (uint16_t *)(as->s + as->length);
+ count = 0;
+ defchar_used = 0;
+ if (sc->flag & SCONV_TO_UTF16BE) {
+ while (count < (int)len && *ws) {
+ archive_be16enc(u16+count, *ws);
+ ws++;
+ count++;
+ }
+ } else {
+ while (count < (int)len && *ws) {
+ archive_le16enc(u16+count, *ws);
+ ws++;
+ count++;
+ }
+ }
+ count <<= 1; /* to be byte size */
+ } else {
+ /* Make sure the MBS buffer has plenty to set. */
+ if (NULL ==
+ archive_string_ensure(as, as->length + len * 2 + 1))
+ return (-1);
+ do {
+ defchar_used = 0;
+ if (to_cp == CP_UTF8 || sc == NULL)
+ dp = NULL;
+ else
+ dp = &defchar_used;
+ count = WideCharToMultiByte(to_cp, 0, ws, wslen,
+ as->s + as->length,
+ (int)as->buffer_length - (int)as->length - 1, NULL, dp);
+ if (count == 0 &&
+ GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ /* Expand the MBS buffer and retry. */
+ if (NULL == archive_string_ensure(as,
+ as->buffer_length + len))
+ return (-1);
+ continue;
+ }
+ if (count == 0)
+ ret = -1;
+ break;
+ } while (1);
+ }
+ as->length += count;
+ as->s[as->length] = '\0';
+ return (defchar_used?-1:ret);
+}
+
+#elif defined(HAVE_WCTOMB) || defined(HAVE_WCRTOMB)
+
+/*
+ * Translates a wide character string into current locale character set
+ * and appends to the archive_string. Note: returns -1 if conversion
+ * fails.
+ */
+int
+archive_string_append_from_wcs(struct archive_string *as,
+ const wchar_t *w, size_t len)
+{
+ /* We cannot use the standard wcstombs() here because it
+ * cannot tell us how big the output buffer should be. So
+ * I've built a loop around wcrtomb() or wctomb() that
+ * converts a character at a time and resizes the string as
+ * needed. We prefer wcrtomb() when it's available because
+ * it's thread-safe. */
+ int n, ret_val = 0;
+ char *p;
+ char *end;
+#if HAVE_WCRTOMB
+ mbstate_t shift_state;
+
+ memset(&shift_state, 0, sizeof(shift_state));
+#else
+ /* Clear the shift state before starting. */
+ wctomb(NULL, L'\0');
+#endif
+ /*
+ * Allocate buffer for MBS.
+ * We need this allocation here since it is possible that
+ * as->s is still NULL.
+ */
+ if (archive_string_ensure(as, as->length + len + 1) == NULL)
+ return (-1);
+
+ p = as->s + as->length;
+ end = as->s + as->buffer_length - MB_CUR_MAX -1;
+ while (*w != L'\0' && len > 0) {
+ if (p >= end) {
+ as->length = p - as->s;
+ as->s[as->length] = '\0';
+ /* Re-allocate buffer for MBS. */
+ if (archive_string_ensure(as,
+ as->length + max(len * 2,
+ (size_t)MB_CUR_MAX) + 1) == NULL)
+ return (-1);
+ p = as->s + as->length;
+ end = as->s + as->buffer_length - MB_CUR_MAX -1;
+ }
+#if HAVE_WCRTOMB
+ n = wcrtomb(p, *w++, &shift_state);
+#else
+ n = wctomb(p, *w++);
+#endif
+ if (n == -1) {
+ if (errno == EILSEQ) {
+ /* Skip an illegal wide char. */
+ *p++ = '?';
+ ret_val = -1;
+ } else {
+ ret_val = -1;
+ break;
+ }
+ } else
+ p += n;
+ len--;
+ }
+ as->length = p - as->s;
+ as->s[as->length] = '\0';
+ return (ret_val);
+}
+
+#else /* HAVE_WCTOMB || HAVE_WCRTOMB */
+
+/*
+ * TODO: Test if __STDC_ISO_10646__ is defined.
+ * Non-Windows uses ISO C wcrtomb() or wctomb() to perform the conversion
+ * one character at a time. If a non-Windows platform doesn't have
+ * either of these, fall back to the built-in UTF8 conversion.
+ */
+int
+archive_string_append_from_wcs(struct archive_string *as,
+ const wchar_t *w, size_t len)
+{
+ (void)as;/* UNUSED */
+ (void)w;/* UNUSED */
+ (void)len;/* UNUSED */
+ errno = ENOSYS;
+ return (-1);
+}
+
+#endif /* HAVE_WCTOMB || HAVE_WCRTOMB */
+
+/*
+ * Find a string conversion object by a pair of 'from' charset name
+ * and 'to' charset name from an archive object.
+ * Return NULL if not found.
+ */
+static struct archive_string_conv *
+find_sconv_object(struct archive *a, const char *fc, const char *tc)
+{
+ struct archive_string_conv *sc;
+
+ if (a == NULL)
+ return (NULL);
+
+ for (sc = a->sconv; sc != NULL; sc = sc->next) {
+ if (strcmp(sc->from_charset, fc) == 0 &&
+ strcmp(sc->to_charset, tc) == 0)
+ break;
+ }
+ return (sc);
+}
+
+/*
+ * Register a string object to an archive object.
+ */
+static void
+add_sconv_object(struct archive *a, struct archive_string_conv *sc)
+{
+ struct archive_string_conv **psc;
+
+ /* Add a new sconv to sconv list. */
+ psc = &(a->sconv);
+ while (*psc != NULL)
+ psc = &((*psc)->next);
+ *psc = sc;
+}
+
+static void
+add_converter(struct archive_string_conv *sc, int (*converter)
+ (struct archive_string *, const void *, size_t,
+ struct archive_string_conv *))
+{
+ if (sc == NULL || sc->nconverter >= 2)
+ __archive_errx(1, "Programming error");
+ sc->converter[sc->nconverter++] = converter;
+}
+
+static void
+setup_converter(struct archive_string_conv *sc)
+{
+
+ /* Reset. */
+ sc->nconverter = 0;
+
+ /*
+ * Perform special sequence for the incorrect UTF-8 filenames
+ * made by libarchive2.x.
+ */
+ if (sc->flag & SCONV_UTF8_LIBARCHIVE_2) {
+ add_converter(sc, strncat_from_utf8_libarchive2);
+ return;
+ }
+
+ /*
+ * Convert a string to UTF-16BE/LE.
+ */
+ if (sc->flag & SCONV_TO_UTF16) {
+ /*
+ * If the current locale is UTF-8, we can translate
+ * a UTF-8 string into a UTF-16BE string.
+ */
+ if (sc->flag & SCONV_FROM_UTF8) {
+ add_converter(sc, archive_string_append_unicode);
+ return;
+ }
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (sc->flag & SCONV_WIN_CP) {
+ if (sc->flag & SCONV_TO_UTF16BE)
+ add_converter(sc, win_strncat_to_utf16be);
+ else
+ add_converter(sc, win_strncat_to_utf16le);
+ return;
+ }
+#endif
+
+#if defined(HAVE_ICONV)
+ if (sc->cd != (iconv_t)-1) {
+ add_converter(sc, iconv_strncat_in_locale);
+ return;
+ }
+#endif
+
+ if (sc->flag & SCONV_BEST_EFFORT) {
+ if (sc->flag & SCONV_TO_UTF16BE)
+ add_converter(sc,
+ best_effort_strncat_to_utf16be);
+ else
+ add_converter(sc,
+ best_effort_strncat_to_utf16le);
+ } else
+ /* Make sure we have no converter. */
+ sc->nconverter = 0;
+ return;
+ }
+
+ /*
+ * Convert a string from UTF-16BE/LE.
+ */
+ if (sc->flag & SCONV_FROM_UTF16) {
+ /*
+ * At least we should normalize a UTF-16BE string.
+ */
+ if (sc->flag & SCONV_NORMALIZATION_D)
+ add_converter(sc,archive_string_normalize_D);
+ else if (sc->flag & SCONV_NORMALIZATION_C)
+ add_converter(sc, archive_string_normalize_C);
+
+ if (sc->flag & SCONV_TO_UTF8) {
+ /*
+ * If the current locale is UTF-8, we can translate
+ * a UTF-16BE/LE string into a UTF-8 string directly.
+ */
+ if (!(sc->flag &
+ (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C)))
+ add_converter(sc,
+ archive_string_append_unicode);
+ return;
+ }
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (sc->flag & SCONV_WIN_CP) {
+ if (sc->flag & SCONV_FROM_UTF16BE)
+ add_converter(sc, win_strncat_from_utf16be);
+ else
+ add_converter(sc, win_strncat_from_utf16le);
+ return;
+ }
+#endif
+
+#if defined(HAVE_ICONV)
+ if (sc->cd != (iconv_t)-1) {
+ add_converter(sc, iconv_strncat_in_locale);
+ return;
+ }
+#endif
+
+ if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE))
+ == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE))
+ add_converter(sc, best_effort_strncat_from_utf16be);
+ else if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE))
+ == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE))
+ add_converter(sc, best_effort_strncat_from_utf16le);
+ else
+ /* Make sure we have no converter. */
+ sc->nconverter = 0;
+ return;
+ }
+
+ if (sc->flag & SCONV_FROM_UTF8) {
+ /*
+ * At least we should normalize a UTF-8 string.
+ */
+ if (sc->flag & SCONV_NORMALIZATION_D)
+ add_converter(sc,archive_string_normalize_D);
+ else if (sc->flag & SCONV_NORMALIZATION_C)
+ add_converter(sc, archive_string_normalize_C);
+
+ /*
+ * Copy UTF-8 string with a check of CESU-8.
+ * Apparently, iconv does not check surrogate pairs in UTF-8
+ * when both from-charset and to-charset are UTF-8, and then
+ * we use our UTF-8 copy code.
+ */
+ if (sc->flag & SCONV_TO_UTF8) {
+ /*
+ * If the current locale is UTF-8, we can translate
+ * a UTF-16BE string into a UTF-8 string directly.
+ */
+ if (!(sc->flag &
+ (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C)))
+ add_converter(sc, strncat_from_utf8_to_utf8);
+ return;
+ }
+ }
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /*
+ * On Windows we can use Windows API for a string conversion.
+ */
+ if (sc->flag & SCONV_WIN_CP) {
+ add_converter(sc, strncat_in_codepage);
+ return;
+ }
+#endif
+
+#if HAVE_ICONV
+ if (sc->cd != (iconv_t)-1) {
+ add_converter(sc, iconv_strncat_in_locale);
+ /*
+ * iconv generally does not support UTF-8-MAC and so
+ * we have to the output of iconv from NFC to NFD if
+ * need.
+ */
+ if ((sc->flag & SCONV_FROM_CHARSET) &&
+ (sc->flag & SCONV_TO_UTF8)) {
+ if (sc->flag & SCONV_NORMALIZATION_D)
+ add_converter(sc, archive_string_normalize_D);
+ }
+ return;
+ }
+#endif
+
+ /*
+ * Try conversion in the best effort or no conversion.
+ */
+ if ((sc->flag & SCONV_BEST_EFFORT) || sc->same)
+ add_converter(sc, best_effort_strncat_in_locale);
+ else
+ /* Make sure we have no converter. */
+ sc->nconverter = 0;
+}
+
+/*
+ * Return canonicalized charset-name but this supports just UTF-8, UTF-16BE
+ * and CP932 which are referenced in create_sconv_object().
+ */
+static const char *
+canonical_charset_name(const char *charset)
+{
+ char cs[16];
+ char *p;
+ const char *s;
+
+ if (charset == NULL || charset[0] == '\0'
+ || strlen(charset) > 15)
+ return (charset);
+
+ /* Copy name to uppercase. */
+ p = cs;
+ s = charset;
+ while (*s) {
+ char c = *s++;
+ if (c >= 'a' && c <= 'z')
+ c -= 'a' - 'A';
+ *p++ = c;
+ }
+ *p++ = '\0';
+
+ if (strcmp(cs, "UTF-8") == 0 ||
+ strcmp(cs, "UTF8") == 0)
+ return ("UTF-8");
+ if (strcmp(cs, "UTF-16BE") == 0 ||
+ strcmp(cs, "UTF16BE") == 0)
+ return ("UTF-16BE");
+ if (strcmp(cs, "UTF-16LE") == 0 ||
+ strcmp(cs, "UTF16LE") == 0)
+ return ("UTF-16LE");
+ if (strcmp(cs, "CP932") == 0)
+ return ("CP932");
+ return (charset);
+}
+
+/*
+ * Create a string conversion object.
+ */
+static struct archive_string_conv *
+create_sconv_object(const char *fc, const char *tc,
+ unsigned current_codepage, int flag)
+{
+ struct archive_string_conv *sc;
+
+ sc = calloc(1, sizeof(*sc));
+ if (sc == NULL)
+ return (NULL);
+ sc->next = NULL;
+ sc->from_charset = strdup(fc);
+ if (sc->from_charset == NULL) {
+ free(sc);
+ return (NULL);
+ }
+ sc->to_charset = strdup(tc);
+ if (sc->to_charset == NULL) {
+ free(sc->from_charset);
+ free(sc);
+ return (NULL);
+ }
+ archive_string_init(&sc->utftmp);
+
+ if (flag & SCONV_TO_CHARSET) {
+ /*
+ * Convert characters from the current locale charset to
+ * a specified charset.
+ */
+ sc->from_cp = current_codepage;
+ sc->to_cp = make_codepage_from_charset(tc);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (IsValidCodePage(sc->to_cp))
+ flag |= SCONV_WIN_CP;
+#endif
+ } else if (flag & SCONV_FROM_CHARSET) {
+ /*
+ * Convert characters from a specified charset to
+ * the current locale charset.
+ */
+ sc->to_cp = current_codepage;
+ sc->from_cp = make_codepage_from_charset(fc);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (IsValidCodePage(sc->from_cp))
+ flag |= SCONV_WIN_CP;
+#endif
+ }
+
+ /*
+ * Check if "from charset" and "to charset" are the same.
+ */
+ if (strcmp(fc, tc) == 0 ||
+ (sc->from_cp != (unsigned)-1 && sc->from_cp == sc->to_cp))
+ sc->same = 1;
+ else
+ sc->same = 0;
+
+ /*
+ * Mark if "from charset" or "to charset" are UTF-8 or UTF-16BE/LE.
+ */
+ if (strcmp(tc, "UTF-8") == 0)
+ flag |= SCONV_TO_UTF8;
+ else if (strcmp(tc, "UTF-16BE") == 0)
+ flag |= SCONV_TO_UTF16BE;
+ else if (strcmp(tc, "UTF-16LE") == 0)
+ flag |= SCONV_TO_UTF16LE;
+ if (strcmp(fc, "UTF-8") == 0)
+ flag |= SCONV_FROM_UTF8;
+ else if (strcmp(fc, "UTF-16BE") == 0)
+ flag |= SCONV_FROM_UTF16BE;
+ else if (strcmp(fc, "UTF-16LE") == 0)
+ flag |= SCONV_FROM_UTF16LE;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (sc->to_cp == CP_UTF8)
+ flag |= SCONV_TO_UTF8;
+ else if (sc->to_cp == CP_UTF16BE)
+ flag |= SCONV_TO_UTF16BE | SCONV_WIN_CP;
+ else if (sc->to_cp == CP_UTF16LE)
+ flag |= SCONV_TO_UTF16LE | SCONV_WIN_CP;
+ if (sc->from_cp == CP_UTF8)
+ flag |= SCONV_FROM_UTF8;
+ else if (sc->from_cp == CP_UTF16BE)
+ flag |= SCONV_FROM_UTF16BE | SCONV_WIN_CP;
+ else if (sc->from_cp == CP_UTF16LE)
+ flag |= SCONV_FROM_UTF16LE | SCONV_WIN_CP;
+#endif
+
+ /*
+ * Set a flag for Unicode NFD. Usually iconv cannot correctly
+ * handle it. So we have to translate NFD characters to NFC ones
+ * ourselves before iconv handles. Another reason is to prevent
+ * that the same sight of two filenames, one is NFC and other
+ * is NFD, would be in its directory.
+ * On Mac OS X, although its filesystem layer automatically
+ * convert filenames to NFD, it would be useful for filename
+ * comparing to find out the same filenames that we normalize
+ * that to be NFD ourselves.
+ */
+ if ((flag & SCONV_FROM_CHARSET) &&
+ (flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8))) {
+#if defined(__APPLE__)
+ if (flag & SCONV_TO_UTF8)
+ flag |= SCONV_NORMALIZATION_D;
+ else
+#endif
+ flag |= SCONV_NORMALIZATION_C;
+ }
+#if defined(__APPLE__)
+ /*
+ * In case writing an archive file, make sure that a filename
+ * going to be passed to iconv is a Unicode NFC string since
+ * a filename in HFS Plus filesystem is a Unicode NFD one and
+ * iconv cannot handle it with "UTF-8" charset. It is simpler
+ * than a use of "UTF-8-MAC" charset.
+ */
+ if ((flag & SCONV_TO_CHARSET) &&
+ (flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) &&
+ !(flag & (SCONV_TO_UTF16 | SCONV_TO_UTF8)))
+ flag |= SCONV_NORMALIZATION_C;
+ /*
+ * In case reading an archive file. make sure that a filename
+ * will be passed to users is a Unicode NFD string in order to
+ * correctly compare the filename with other one which comes
+ * from HFS Plus filesystem.
+ */
+ if ((flag & SCONV_FROM_CHARSET) &&
+ !(flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) &&
+ (flag & SCONV_TO_UTF8))
+ flag |= SCONV_NORMALIZATION_D;
+#endif
+
+#if defined(HAVE_ICONV)
+ sc->cd_w = (iconv_t)-1;
+ /*
+ * Create an iconv object.
+ */
+ if (((flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) &&
+ (flag & (SCONV_FROM_UTF8 | SCONV_FROM_UTF16))) ||
+ (flag & SCONV_WIN_CP)) {
+ /* This case we won't use iconv. */
+ sc->cd = (iconv_t)-1;
+ } else {
+ sc->cd = iconv_open(tc, fc);
+ if (sc->cd == (iconv_t)-1 && (sc->flag & SCONV_BEST_EFFORT)) {
+ /*
+ * Unfortunately, all of iconv implements do support
+ * "CP932" character-set, so we should use "SJIS"
+ * instead if iconv_open failed.
+ */
+ if (strcmp(tc, "CP932") == 0)
+ sc->cd = iconv_open("SJIS", fc);
+ else if (strcmp(fc, "CP932") == 0)
+ sc->cd = iconv_open(tc, "SJIS");
+ }
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /*
+ * archive_mstring on Windows directly convert multi-bytes
+ * into archive_wstring in order not to depend on locale
+ * so that you can do a I18N programming. This will be
+ * used only in archive_mstring_copy_mbs_len_l so far.
+ */
+ if (flag & SCONV_FROM_CHARSET) {
+ sc->cd_w = iconv_open("UTF-8", fc);
+ if (sc->cd_w == (iconv_t)-1 &&
+ (sc->flag & SCONV_BEST_EFFORT)) {
+ if (strcmp(fc, "CP932") == 0)
+ sc->cd_w = iconv_open("UTF-8", "SJIS");
+ }
+ }
+#endif /* _WIN32 && !__CYGWIN__ */
+ }
+#endif /* HAVE_ICONV */
+
+ sc->flag = flag;
+
+ /*
+ * Set up converters.
+ */
+ setup_converter(sc);
+
+ return (sc);
+}
+
+/*
+ * Free a string conversion object.
+ */
+static void
+free_sconv_object(struct archive_string_conv *sc)
+{
+ free(sc->from_charset);
+ free(sc->to_charset);
+ archive_string_free(&sc->utftmp);
+#if HAVE_ICONV
+ if (sc->cd != (iconv_t)-1)
+ iconv_close(sc->cd);
+ if (sc->cd_w != (iconv_t)-1)
+ iconv_close(sc->cd_w);
+#endif
+ free(sc);
+}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+# if defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+# define GetOEMCP() CP_OEMCP
+# endif
+
+static unsigned
+my_atoi(const char *p)
+{
+ unsigned cp;
+
+ cp = 0;
+ while (*p) {
+ if (*p >= '0' && *p <= '9')
+ cp = cp * 10 + (*p - '0');
+ else
+ return (-1);
+ p++;
+ }
+ return (cp);
+}
+
+/*
+ * Translate Charset name (as used by iconv) into CodePage (as used by Windows)
+ * Return -1 if failed.
+ *
+ * Note: This translation code may be insufficient.
+ */
+static struct charset {
+ const char *name;
+ unsigned cp;
+} charsets[] = {
+ /* MUST BE SORTED! */
+ {"ASCII", 1252},
+ {"ASMO-708", 708},
+ {"BIG5", 950},
+ {"CHINESE", 936},
+ {"CP367", 1252},
+ {"CP819", 1252},
+ {"CP1025", 21025},
+ {"DOS-720", 720},
+ {"DOS-862", 862},
+ {"EUC-CN", 51936},
+ {"EUC-JP", 51932},
+ {"EUC-KR", 949},
+ {"EUCCN", 51936},
+ {"EUCJP", 51932},
+ {"EUCKR", 949},
+ {"GB18030", 54936},
+ {"GB2312", 936},
+ {"HEBREW", 1255},
+ {"HZ-GB-2312", 52936},
+ {"IBM273", 20273},
+ {"IBM277", 20277},
+ {"IBM278", 20278},
+ {"IBM280", 20280},
+ {"IBM284", 20284},
+ {"IBM285", 20285},
+ {"IBM290", 20290},
+ {"IBM297", 20297},
+ {"IBM367", 1252},
+ {"IBM420", 20420},
+ {"IBM423", 20423},
+ {"IBM424", 20424},
+ {"IBM819", 1252},
+ {"IBM871", 20871},
+ {"IBM880", 20880},
+ {"IBM905", 20905},
+ {"IBM924", 20924},
+ {"ISO-8859-1", 28591},
+ {"ISO-8859-13", 28603},
+ {"ISO-8859-15", 28605},
+ {"ISO-8859-2", 28592},
+ {"ISO-8859-3", 28593},
+ {"ISO-8859-4", 28594},
+ {"ISO-8859-5", 28595},
+ {"ISO-8859-6", 28596},
+ {"ISO-8859-7", 28597},
+ {"ISO-8859-8", 28598},
+ {"ISO-8859-9", 28599},
+ {"ISO8859-1", 28591},
+ {"ISO8859-13", 28603},
+ {"ISO8859-15", 28605},
+ {"ISO8859-2", 28592},
+ {"ISO8859-3", 28593},
+ {"ISO8859-4", 28594},
+ {"ISO8859-5", 28595},
+ {"ISO8859-6", 28596},
+ {"ISO8859-7", 28597},
+ {"ISO8859-8", 28598},
+ {"ISO8859-9", 28599},
+ {"JOHAB", 1361},
+ {"KOI8-R", 20866},
+ {"KOI8-U", 21866},
+ {"KS_C_5601-1987", 949},
+ {"LATIN1", 1252},
+ {"LATIN2", 28592},
+ {"MACINTOSH", 10000},
+ {"SHIFT-JIS", 932},
+ {"SHIFT_JIS", 932},
+ {"SJIS", 932},
+ {"US", 1252},
+ {"US-ASCII", 1252},
+ {"UTF-16", 1200},
+ {"UTF-16BE", 1201},
+ {"UTF-16LE", 1200},
+ {"UTF-8", CP_UTF8},
+ {"X-EUROPA", 29001},
+ {"X-MAC-ARABIC", 10004},
+ {"X-MAC-CE", 10029},
+ {"X-MAC-CHINESEIMP", 10008},
+ {"X-MAC-CHINESETRAD", 10002},
+ {"X-MAC-CROATIAN", 10082},
+ {"X-MAC-CYRILLIC", 10007},
+ {"X-MAC-GREEK", 10006},
+ {"X-MAC-HEBREW", 10005},
+ {"X-MAC-ICELANDIC", 10079},
+ {"X-MAC-JAPANESE", 10001},
+ {"X-MAC-KOREAN", 10003},
+ {"X-MAC-ROMANIAN", 10010},
+ {"X-MAC-THAI", 10021},
+ {"X-MAC-TURKISH", 10081},
+ {"X-MAC-UKRAINIAN", 10017},
+};
+static unsigned
+make_codepage_from_charset(const char *charset)
+{
+ char cs[16];
+ char *p;
+ unsigned cp;
+ int a, b;
+
+ if (charset == NULL || strlen(charset) > 15)
+ return -1;
+
+ /* Copy name to uppercase. */
+ p = cs;
+ while (*charset) {
+ char c = *charset++;
+ if (c >= 'a' && c <= 'z')
+ c -= 'a' - 'A';
+ *p++ = c;
+ }
+ *p++ = '\0';
+ cp = -1;
+
+ /* Look it up in the table first, so that we can easily
+ * override CP367, which we map to 1252 instead of 367. */
+ a = 0;
+ b = sizeof(charsets)/sizeof(charsets[0]);
+ while (b > a) {
+ int c = (b + a) / 2;
+ int r = strcmp(charsets[c].name, cs);
+ if (r < 0)
+ a = c + 1;
+ else if (r > 0)
+ b = c;
+ else
+ return charsets[c].cp;
+ }
+
+ /* If it's not in the table, try to parse it. */
+ switch (*cs) {
+ case 'C':
+ if (cs[1] == 'P' && cs[2] >= '0' && cs[2] <= '9') {
+ cp = my_atoi(cs + 2);
+ } else if (strcmp(cs, "CP_ACP") == 0)
+ cp = get_current_codepage();
+ else if (strcmp(cs, "CP_OEMCP") == 0)
+ cp = get_current_oemcp();
+ break;
+ case 'I':
+ if (cs[1] == 'B' && cs[2] == 'M' &&
+ cs[3] >= '0' && cs[3] <= '9') {
+ cp = my_atoi(cs + 3);
+ }
+ break;
+ case 'W':
+ if (strncmp(cs, "WINDOWS-", 8) == 0) {
+ cp = my_atoi(cs + 8);
+ if (cp != 874 && (cp < 1250 || cp > 1258))
+ cp = -1;/* This may invalid code. */
+ }
+ break;
+ }
+ return (cp);
+}
+
+/*
+ * Return ANSI Code Page of current locale set by setlocale().
+ */
+static unsigned
+get_current_codepage(void)
+{
+ char *locale, *p;
+ unsigned cp;
+
+ locale = setlocale(LC_CTYPE, NULL);
+ if (locale == NULL)
+ return (GetACP());
+ if (locale[0] == 'C' && locale[1] == '\0')
+ return (CP_C_LOCALE);
+ p = strrchr(locale, '.');
+ if (p == NULL)
+ return (GetACP());
+ if (strcmp(p+1, "utf8") == 0)
+ return CP_UTF8;
+ cp = my_atoi(p+1);
+ if ((int)cp <= 0)
+ return (GetACP());
+ return (cp);
+}
+
+/*
+ * Translation table between Locale Name and ACP/OEMCP.
+ */
+static struct {
+ unsigned acp;
+ unsigned ocp;
+ const char *locale;
+} acp_ocp_map[] = {
+ { 950, 950, "Chinese_Taiwan" },
+ { 936, 936, "Chinese_People's Republic of China" },
+ { 950, 950, "Chinese_Taiwan" },
+ { 1250, 852, "Czech_Czech Republic" },
+ { 1252, 850, "Danish_Denmark" },
+ { 1252, 850, "Dutch_Netherlands" },
+ { 1252, 850, "Dutch_Belgium" },
+ { 1252, 437, "English_United States" },
+ { 1252, 850, "English_Australia" },
+ { 1252, 850, "English_Canada" },
+ { 1252, 850, "English_New Zealand" },
+ { 1252, 850, "English_United Kingdom" },
+ { 1252, 437, "English_United States" },
+ { 1252, 850, "Finnish_Finland" },
+ { 1252, 850, "French_France" },
+ { 1252, 850, "French_Belgium" },
+ { 1252, 850, "French_Canada" },
+ { 1252, 850, "French_Switzerland" },
+ { 1252, 850, "German_Germany" },
+ { 1252, 850, "German_Austria" },
+ { 1252, 850, "German_Switzerland" },
+ { 1253, 737, "Greek_Greece" },
+ { 1250, 852, "Hungarian_Hungary" },
+ { 1252, 850, "Icelandic_Iceland" },
+ { 1252, 850, "Italian_Italy" },
+ { 1252, 850, "Italian_Switzerland" },
+ { 932, 932, "Japanese_Japan" },
+ { 949, 949, "Korean_Korea" },
+ { 1252, 850, "Norwegian (BokmOl)_Norway" },
+ { 1252, 850, "Norwegian (BokmOl)_Norway" },
+ { 1252, 850, "Norwegian-Nynorsk_Norway" },
+ { 1250, 852, "Polish_Poland" },
+ { 1252, 850, "Portuguese_Portugal" },
+ { 1252, 850, "Portuguese_Brazil" },
+ { 1251, 866, "Russian_Russia" },
+ { 1250, 852, "Slovak_Slovakia" },
+ { 1252, 850, "Spanish_Spain" },
+ { 1252, 850, "Spanish_Mexico" },
+ { 1252, 850, "Spanish_Spain" },
+ { 1252, 850, "Swedish_Sweden" },
+ { 1254, 857, "Turkish_Turkey" },
+ { 0, 0, NULL}
+};
+
+/*
+ * Return OEM Code Page of current locale set by setlocale().
+ */
+static unsigned
+get_current_oemcp(void)
+{
+ int i;
+ char *locale, *p;
+ size_t len;
+
+ locale = setlocale(LC_CTYPE, NULL);
+ if (locale == NULL)
+ return (GetOEMCP());
+ if (locale[0] == 'C' && locale[1] == '\0')
+ return (CP_C_LOCALE);
+
+ p = strrchr(locale, '.');
+ if (p == NULL)
+ return (GetOEMCP());
+ len = p - locale;
+ for (i = 0; acp_ocp_map[i].acp; i++) {
+ if (strncmp(acp_ocp_map[i].locale, locale, len) == 0)
+ return (acp_ocp_map[i].ocp);
+ }
+ return (GetOEMCP());
+}
+#else
+
+/*
+ * POSIX platform does not use CodePage.
+ */
+
+static unsigned
+get_current_codepage(void)
+{
+ return (-1);/* Unknown */
+}
+static unsigned
+make_codepage_from_charset(const char *charset)
+{
+ (void)charset; /* UNUSED */
+ return (-1);/* Unknown */
+}
+static unsigned
+get_current_oemcp(void)
+{
+ return (-1);/* Unknown */
+}
+
+#endif /* defined(_WIN32) && !defined(__CYGWIN__) */
+
+/*
+ * Return a string conversion object.
+ */
+static struct archive_string_conv *
+get_sconv_object(struct archive *a, const char *fc, const char *tc, int flag)
+{
+ struct archive_string_conv *sc;
+ unsigned current_codepage;
+
+ /* Check if we have made the sconv object. */
+ sc = find_sconv_object(a, fc, tc);
+ if (sc != NULL)
+ return (sc);
+
+ if (a == NULL)
+ current_codepage = get_current_codepage();
+ else
+ current_codepage = a->current_codepage;
+
+ sc = create_sconv_object(canonical_charset_name(fc),
+ canonical_charset_name(tc), current_codepage, flag);
+ if (sc == NULL) {
+ if (a != NULL)
+ archive_set_error(a, ENOMEM,
+ "Could not allocate memory for "
+ "a string conversion object");
+ return (NULL);
+ }
+
+ /*
+ * If there is no converter for current string conversion object,
+ * we cannot handle this conversion.
+ */
+ if (sc->nconverter == 0) {
+ if (a != NULL) {
+#if HAVE_ICONV
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "iconv_open failed : Cannot handle ``%s''",
+ (flag & SCONV_TO_CHARSET)?tc:fc);
+#else
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "A character-set conversion not fully supported "
+ "on this platform");
+#endif
+ }
+ /* Failed; free a sconv object. */
+ free_sconv_object(sc);
+ return (NULL);
+ }
+
+ /*
+ * Success!
+ */
+ if (a != NULL)
+ add_sconv_object(a, sc);
+ return (sc);
+}
+
+static const char *
+get_current_charset(struct archive *a)
+{
+ const char *cur_charset;
+
+ if (a == NULL)
+ cur_charset = default_iconv_charset("");
+ else {
+ cur_charset = default_iconv_charset(a->current_code);
+ if (a->current_code == NULL) {
+ a->current_code = strdup(cur_charset);
+ a->current_codepage = get_current_codepage();
+ a->current_oemcp = get_current_oemcp();
+ }
+ }
+ return (cur_charset);
+}
+
+/*
+ * Make and Return a string conversion object.
+ * Return NULL if the platform does not support the specified conversion
+ * and best_effort is 0.
+ * If best_effort is set, A string conversion object must be returned
+ * unless memory allocation for the object fails, but the conversion
+ * might fail when non-ASCII code is found.
+ */
+struct archive_string_conv *
+archive_string_conversion_to_charset(struct archive *a, const char *charset,
+ int best_effort)
+{
+ int flag = SCONV_TO_CHARSET;
+
+ if (best_effort)
+ flag |= SCONV_BEST_EFFORT;
+ return (get_sconv_object(a, get_current_charset(a), charset, flag));
+}
+
+struct archive_string_conv *
+archive_string_conversion_from_charset(struct archive *a, const char *charset,
+ int best_effort)
+{
+ int flag = SCONV_FROM_CHARSET;
+
+ if (best_effort)
+ flag |= SCONV_BEST_EFFORT;
+ return (get_sconv_object(a, charset, get_current_charset(a), flag));
+}
+
+/*
+ * archive_string_default_conversion_*_archive() are provided for Windows
+ * platform because other archiver application use CP_OEMCP for
+ * MultiByteToWideChar() and WideCharToMultiByte() for the filenames
+ * in tar or zip files. But mbstowcs/wcstombs(CRT) usually use CP_ACP
+ * unless you use setlocale(LC_ALL, ".OCP")(specify CP_OEMCP).
+ * So we should make a string conversion between CP_ACP and CP_OEMCP
+ * for compatibility.
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+struct archive_string_conv *
+archive_string_default_conversion_for_read(struct archive *a)
+{
+ const char *cur_charset = get_current_charset(a);
+ char oemcp[16];
+
+ /* NOTE: a check of cur_charset is unneeded but we need
+ * that get_current_charset() has been surely called at
+ * this time whatever C compiler optimized. */
+ if (cur_charset != NULL &&
+ (a->current_codepage == CP_C_LOCALE ||
+ a->current_codepage == a->current_oemcp))
+ return (NULL);/* no conversion. */
+
+ _snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp);
+ /* Make sure a null termination must be set. */
+ oemcp[sizeof(oemcp)-1] = '\0';
+ return (get_sconv_object(a, oemcp, cur_charset,
+ SCONV_FROM_CHARSET));
+}
+
+struct archive_string_conv *
+archive_string_default_conversion_for_write(struct archive *a)
+{
+ const char *cur_charset = get_current_charset(a);
+ char oemcp[16];
+
+ /* NOTE: a check of cur_charset is unneeded but we need
+ * that get_current_charset() has been surely called at
+ * this time whatever C compiler optimized. */
+ if (cur_charset != NULL &&
+ (a->current_codepage == CP_C_LOCALE ||
+ a->current_codepage == a->current_oemcp))
+ return (NULL);/* no conversion. */
+
+ _snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp);
+ /* Make sure a null termination must be set. */
+ oemcp[sizeof(oemcp)-1] = '\0';
+ return (get_sconv_object(a, cur_charset, oemcp,
+ SCONV_TO_CHARSET));
+}
+#else
+struct archive_string_conv *
+archive_string_default_conversion_for_read(struct archive *a)
+{
+ (void)a; /* UNUSED */
+ return (NULL);
+}
+
+struct archive_string_conv *
+archive_string_default_conversion_for_write(struct archive *a)
+{
+ (void)a; /* UNUSED */
+ return (NULL);
+}
+#endif
+
+/*
+ * Dispose of all character conversion objects in the archive object.
+ */
+void
+archive_string_conversion_free(struct archive *a)
+{
+ struct archive_string_conv *sc;
+ struct archive_string_conv *sc_next;
+
+ for (sc = a->sconv; sc != NULL; sc = sc_next) {
+ sc_next = sc->next;
+ free_sconv_object(sc);
+ }
+ a->sconv = NULL;
+ free(a->current_code);
+ a->current_code = NULL;
+}
+
+/*
+ * Return a conversion charset name.
+ */
+const char *
+archive_string_conversion_charset_name(struct archive_string_conv *sc)
+{
+ if (sc->flag & SCONV_TO_CHARSET)
+ return (sc->to_charset);
+ else
+ return (sc->from_charset);
+}
+
+/*
+ * Change the behavior of a string conversion.
+ */
+void
+archive_string_conversion_set_opt(struct archive_string_conv *sc, int opt)
+{
+ switch (opt) {
+ /*
+ * A filename in UTF-8 was made with libarchive 2.x in a wrong
+ * assumption that wchar_t was Unicode.
+ * This option enables simulating the assumption in order to read
+ * that filename correctly.
+ */
+ case SCONV_SET_OPT_UTF8_LIBARCHIVE2X:
+#if (defined(_WIN32) && !defined(__CYGWIN__)) \
+ || defined(__STDC_ISO_10646__) || defined(__APPLE__)
+ /*
+ * Nothing to do for it since wchar_t on these platforms
+ * is really Unicode.
+ */
+ (void)sc; /* UNUSED */
+#else
+ if ((sc->flag & SCONV_UTF8_LIBARCHIVE_2) == 0) {
+ sc->flag |= SCONV_UTF8_LIBARCHIVE_2;
+ /* Set up string converters. */
+ setup_converter(sc);
+ }
+#endif
+ break;
+ case SCONV_SET_OPT_NORMALIZATION_C:
+ if ((sc->flag & SCONV_NORMALIZATION_C) == 0) {
+ sc->flag |= SCONV_NORMALIZATION_C;
+ sc->flag &= ~SCONV_NORMALIZATION_D;
+ /* Set up string converters. */
+ setup_converter(sc);
+ }
+ break;
+ case SCONV_SET_OPT_NORMALIZATION_D:
+#if defined(HAVE_ICONV)
+ /*
+ * If iconv will take the string, do not change the
+ * setting of the normalization.
+ */
+ if (!(sc->flag & SCONV_WIN_CP) &&
+ (sc->flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) &&
+ !(sc->flag & (SCONV_TO_UTF16 | SCONV_TO_UTF8)))
+ break;
+#endif
+ if ((sc->flag & SCONV_NORMALIZATION_D) == 0) {
+ sc->flag |= SCONV_NORMALIZATION_D;
+ sc->flag &= ~SCONV_NORMALIZATION_C;
+ /* Set up string converters. */
+ setup_converter(sc);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ *
+ * Copy one archive_string to another in locale conversion.
+ *
+ * archive_strncat_l();
+ * archive_strncpy_l();
+ *
+ */
+
+static size_t
+mbsnbytes(const void *_p, size_t n)
+{
+ size_t s;
+ const char *p, *pp;
+
+ if (_p == NULL)
+ return (0);
+ p = (const char *)_p;
+
+ /* Like strlen(p), except won't examine positions beyond p[n]. */
+ s = 0;
+ pp = p;
+ while (s < n && *pp) {
+ pp++;
+ s++;
+ }
+ return (s);
+}
+
+static size_t
+utf16nbytes(const void *_p, size_t n)
+{
+ size_t s;
+ const char *p, *pp;
+
+ if (_p == NULL)
+ return (0);
+ p = (const char *)_p;
+
+ /* Like strlen(p), except won't examine positions beyond p[n]. */
+ s = 0;
+ pp = p;
+ n >>= 1;
+ while (s < n && (pp[0] || pp[1])) {
+ pp += 2;
+ s++;
+ }
+ return (s<<1);
+}
+
+int
+archive_strncpy_l(struct archive_string *as, const void *_p, size_t n,
+ struct archive_string_conv *sc)
+{
+ as->length = 0;
+ return (archive_strncat_l(as, _p, n, sc));
+}
+
+int
+archive_strncat_l(struct archive_string *as, const void *_p, size_t n,
+ struct archive_string_conv *sc)
+{
+ const void *s;
+ size_t length = 0;
+ int i, r = 0, r2;
+
+ if (_p != NULL && n > 0) {
+ if (sc != NULL && (sc->flag & SCONV_FROM_UTF16))
+ length = utf16nbytes(_p, n);
+ else
+ length = mbsnbytes(_p, n);
+ }
+
+ /* We must allocate memory even if there is no data for conversion
+ * or copy. This simulates archive_string_append behavior. */
+ if (length == 0) {
+ int tn = 1;
+ if (sc != NULL && (sc->flag & SCONV_TO_UTF16))
+ tn = 2;
+ if (archive_string_ensure(as, as->length + tn) == NULL)
+ return (-1);
+ as->s[as->length] = 0;
+ if (tn == 2)
+ as->s[as->length+1] = 0;
+ return (0);
+ }
+
+ /*
+ * If sc is NULL, we just make a copy.
+ */
+ if (sc == NULL) {
+ if (archive_string_append(as, _p, length) == NULL)
+ return (-1);/* No memory */
+ return (0);
+ }
+
+ s = _p;
+ i = 0;
+ if (sc->nconverter > 1) {
+ sc->utftmp.length = 0;
+ r2 = sc->converter[0](&(sc->utftmp), s, length, sc);
+ if (r2 != 0 && errno == ENOMEM)
+ return (r2);
+ if (r > r2)
+ r = r2;
+ s = sc->utftmp.s;
+ length = sc->utftmp.length;
+ ++i;
+ }
+ r2 = sc->converter[i](as, s, length, sc);
+ if (r > r2)
+ r = r2;
+ return (r);
+}
+
+#if HAVE_ICONV
+
+/*
+ * Return -1 if conversion fails.
+ */
+static int
+iconv_strncat_in_locale(struct archive_string *as, const void *_p,
+ size_t length, struct archive_string_conv *sc)
+{
+ ICONV_CONST char *itp;
+ size_t remaining;
+ iconv_t cd;
+ char *outp;
+ size_t avail, bs;
+ int return_value = 0; /* success */
+ int to_size, from_size;
+
+ if (sc->flag & SCONV_TO_UTF16)
+ to_size = 2;
+ else
+ to_size = 1;
+ if (sc->flag & SCONV_FROM_UTF16)
+ from_size = 2;
+ else
+ from_size = 1;
+
+ if (archive_string_ensure(as, as->length + length*2+to_size) == NULL)
+ return (-1);
+
+ cd = sc->cd;
+ itp = (char *)(uintptr_t)_p;
+ remaining = length;
+ outp = as->s + as->length;
+ avail = as->buffer_length - as->length - to_size;
+ while (remaining >= (size_t)from_size) {
+ size_t result = iconv(cd, &itp, &remaining, &outp, &avail);
+
+ if (result != (size_t)-1)
+ break; /* Conversion completed. */
+
+ if (errno == EILSEQ || errno == EINVAL) {
+ /*
+ * If an output charset is UTF-8 or UTF-16BE/LE,
+ * unknown character should be U+FFFD
+ * (replacement character).
+ */
+ if (sc->flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) {
+ size_t rbytes;
+ if (sc->flag & SCONV_TO_UTF8)
+ rbytes = sizeof(utf8_replacement_char);
+ else
+ rbytes = 2;
+
+ if (avail < rbytes) {
+ as->length = outp - as->s;
+ bs = as->buffer_length +
+ (remaining * to_size) + rbytes;
+ if (NULL ==
+ archive_string_ensure(as, bs))
+ return (-1);
+ outp = as->s + as->length;
+ avail = as->buffer_length
+ - as->length - to_size;
+ }
+ if (sc->flag & SCONV_TO_UTF8)
+ memcpy(outp, utf8_replacement_char, sizeof(utf8_replacement_char));
+ else if (sc->flag & SCONV_TO_UTF16BE)
+ archive_be16enc(outp, UNICODE_R_CHAR);
+ else
+ archive_le16enc(outp, UNICODE_R_CHAR);
+ outp += rbytes;
+ avail -= rbytes;
+ } else {
+ /* Skip the illegal input bytes. */
+ *outp++ = '?';
+ avail--;
+ }
+ itp += from_size;
+ remaining -= from_size;
+ return_value = -1; /* failure */
+ } else {
+ /* E2BIG no output buffer,
+ * Increase an output buffer. */
+ as->length = outp - as->s;
+ bs = as->buffer_length + remaining * 2;
+ if (NULL == archive_string_ensure(as, bs))
+ return (-1);
+ outp = as->s + as->length;
+ avail = as->buffer_length - as->length - to_size;
+ }
+ }
+ as->length = outp - as->s;
+ as->s[as->length] = 0;
+ if (to_size == 2)
+ as->s[as->length+1] = 0;
+ return (return_value);
+}
+
+#endif /* HAVE_ICONV */
+
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * Translate a string from a some CodePage to an another CodePage by
+ * Windows APIs, and copy the result. Return -1 if conversion fails.
+ */
+static int
+strncat_in_codepage(struct archive_string *as,
+ const void *_p, size_t length, struct archive_string_conv *sc)
+{
+ const char *s = (const char *)_p;
+ struct archive_wstring aws;
+ size_t l;
+ int r, saved_flag;
+
+ archive_string_init(&aws);
+ saved_flag = sc->flag;
+ sc->flag &= ~(SCONV_NORMALIZATION_D | SCONV_NORMALIZATION_C);
+ r = archive_wstring_append_from_mbs_in_codepage(&aws, s, length, sc);
+ sc->flag = saved_flag;
+ if (r != 0) {
+ archive_wstring_free(&aws);
+ if (errno != ENOMEM)
+ archive_string_append(as, s, length);
+ return (-1);
+ }
+
+ l = as->length;
+ r = archive_string_append_from_wcs_in_codepage(
+ as, aws.s, aws.length, sc);
+ if (r != 0 && errno != ENOMEM && l == as->length)
+ archive_string_append(as, s, length);
+ archive_wstring_free(&aws);
+ return (r);
+}
+
+/*
+ * Test whether MBS ==> WCS is okay.
+ */
+static int
+invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc)
+{
+ const char *p = (const char *)_p;
+ unsigned codepage;
+ DWORD mbflag = MB_ERR_INVALID_CHARS;
+
+ if (sc->flag & SCONV_FROM_CHARSET)
+ codepage = sc->to_cp;
+ else
+ codepage = sc->from_cp;
+
+ if (codepage == CP_C_LOCALE)
+ return (0);
+ if (codepage != CP_UTF8)
+ mbflag |= MB_PRECOMPOSED;
+
+ if (MultiByteToWideChar(codepage, mbflag, p, (int)n, NULL, 0) == 0)
+ return (-1); /* Invalid */
+ return (0); /* Okay */
+}
+
+#else
+
+/*
+ * Test whether MBS ==> WCS is okay.
+ */
+static int
+invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc)
+{
+ const char *p = (const char *)_p;
+ size_t r;
+
+#if HAVE_MBRTOWC
+ mbstate_t shift_state;
+
+ memset(&shift_state, 0, sizeof(shift_state));
+#else
+ /* Clear the shift state before starting. */
+ mbtowc(NULL, NULL, 0);
+#endif
+ while (n) {
+ wchar_t wc;
+
+#if HAVE_MBRTOWC
+ r = mbrtowc(&wc, p, n, &shift_state);
+#else
+ r = mbtowc(&wc, p, n);
+#endif
+ if (r == (size_t)-1 || r == (size_t)-2)
+ return (-1);/* Invalid. */
+ if (r == 0)
+ break;
+ p += r;
+ n -= r;
+ }
+ (void)sc; /* UNUSED */
+ return (0); /* All Okey. */
+}
+
+#endif /* defined(_WIN32) && !defined(__CYGWIN__) */
+
+/*
+ * Basically returns -1 because we cannot make a conversion of charset
+ * without iconv but in some cases this would return 0.
+ * Returns 0 if all copied characters are ASCII.
+ * Returns 0 if both from-locale and to-locale are the same and those
+ * can be WCS with no error.
+ */
+static int
+best_effort_strncat_in_locale(struct archive_string *as, const void *_p,
+ size_t length, struct archive_string_conv *sc)
+{
+ size_t remaining;
+ const uint8_t *itp;
+ int return_value = 0; /* success */
+
+ /*
+ * If both from-locale and to-locale is the same, this makes a copy.
+ * And then this checks all copied MBS can be WCS if so returns 0.
+ */
+ if (sc->same) {
+ if (archive_string_append(as, _p, length) == NULL)
+ return (-1);/* No memory */
+ return (invalid_mbs(_p, length, sc));
+ }
+
+ /*
+ * If a character is ASCII, this just copies it. If not, this
+ * assigns '?' character instead but in UTF-8 locale this assigns
+ * byte sequence 0xEF 0xBD 0xBD, which are code point U+FFFD,
+ * a Replacement Character in Unicode.
+ */
+
+ remaining = length;
+ itp = (const uint8_t *)_p;
+ while (*itp && remaining > 0) {
+ if (*itp > 127) {
+ // Non-ASCII: Substitute with suitable replacement
+ if (sc->flag & SCONV_TO_UTF8) {
+ if (archive_string_append(as, utf8_replacement_char, sizeof(utf8_replacement_char)) == NULL) {
+ __archive_errx(1, "Out of memory");
+ }
+ } else {
+ archive_strappend_char(as, '?');
+ }
+ return_value = -1;
+ } else {
+ archive_strappend_char(as, *itp);
+ }
+ ++itp;
+ }
+ return (return_value);
+}
+
+
+/*
+ * Unicode conversion functions.
+ * - UTF-8 <===> UTF-8 in removing surrogate pairs.
+ * - UTF-8 NFD ===> UTF-8 NFC in removing surrogate pairs.
+ * - UTF-8 made by libarchive 2.x ===> UTF-8.
+ * - UTF-16BE <===> UTF-8.
+ *
+ */
+
+/*
+ * Utility to convert a single UTF-8 sequence.
+ *
+ * Usually return used bytes, return used byte in negative value when
+ * a unicode character is replaced with U+FFFD.
+ * See also http://unicode.org/review/pr-121.html Public Review Issue #121
+ * Recommended Practice for Replacement Characters.
+ */
+static int
+_utf8_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+ static const char utf8_count[256] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */
+ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */
+ 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */
+ };
+ int ch, i;
+ int cnt;
+ uint32_t wc;
+
+ /* Sanity check. */
+ if (n == 0)
+ return (0);
+ /*
+ * Decode 1-4 bytes depending on the value of the first byte.
+ */
+ ch = (unsigned char)*s;
+ if (ch == 0)
+ return (0); /* Standard: return 0 for end-of-string. */
+ cnt = utf8_count[ch];
+
+ /* Invalid sequence or there are not plenty bytes. */
+ if ((int)n < cnt) {
+ cnt = (int)n;
+ for (i = 1; i < cnt; i++) {
+ if ((s[i] & 0xc0) != 0x80) {
+ cnt = i;
+ break;
+ }
+ }
+ goto invalid_sequence;
+ }
+
+ /* Make a Unicode code point from a single UTF-8 sequence. */
+ switch (cnt) {
+ case 1: /* 1 byte sequence. */
+ *pwc = ch & 0x7f;
+ return (cnt);
+ case 2: /* 2 bytes sequence. */
+ if ((s[1] & 0xc0) != 0x80) {
+ cnt = 1;
+ goto invalid_sequence;
+ }
+ *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f);
+ return (cnt);
+ case 3: /* 3 bytes sequence. */
+ if ((s[1] & 0xc0) != 0x80) {
+ cnt = 1;
+ goto invalid_sequence;
+ }
+ if ((s[2] & 0xc0) != 0x80) {
+ cnt = 2;
+ goto invalid_sequence;
+ }
+ wc = ((ch & 0x0f) << 12)
+ | ((s[1] & 0x3f) << 6)
+ | (s[2] & 0x3f);
+ if (wc < 0x800)
+ goto invalid_sequence;/* Overlong sequence. */
+ break;
+ case 4: /* 4 bytes sequence. */
+ if ((s[1] & 0xc0) != 0x80) {
+ cnt = 1;
+ goto invalid_sequence;
+ }
+ if ((s[2] & 0xc0) != 0x80) {
+ cnt = 2;
+ goto invalid_sequence;
+ }
+ if ((s[3] & 0xc0) != 0x80) {
+ cnt = 3;
+ goto invalid_sequence;
+ }
+ wc = ((ch & 0x07) << 18)
+ | ((s[1] & 0x3f) << 12)
+ | ((s[2] & 0x3f) << 6)
+ | (s[3] & 0x3f);
+ if (wc < 0x10000)
+ goto invalid_sequence;/* Overlong sequence. */
+ break;
+ default: /* Others are all invalid sequence. */
+ if (ch == 0xc0 || ch == 0xc1)
+ cnt = 2;
+ else if (ch >= 0xf5 && ch <= 0xf7)
+ cnt = 4;
+ else if (ch >= 0xf8 && ch <= 0xfb)
+ cnt = 5;
+ else if (ch == 0xfc || ch == 0xfd)
+ cnt = 6;
+ else
+ cnt = 1;
+ if ((int)n < cnt)
+ cnt = (int)n;
+ for (i = 1; i < cnt; i++) {
+ if ((s[i] & 0xc0) != 0x80) {
+ cnt = i;
+ break;
+ }
+ }
+ goto invalid_sequence;
+ }
+
+ /* The code point larger than 0x10FFFF is not legal
+ * Unicode values. */
+ if (wc > UNICODE_MAX)
+ goto invalid_sequence;
+ /* Correctly gets a Unicode, returns used bytes. */
+ *pwc = wc;
+ return (cnt);
+invalid_sequence:
+ *pwc = UNICODE_R_CHAR;/* set the Replacement Character instead. */
+ return (cnt * -1);
+}
+
+static int
+utf8_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+ int cnt;
+
+ cnt = _utf8_to_unicode(pwc, s, n);
+ /* Any of Surrogate pair is not legal Unicode values. */
+ if (cnt == 3 && IS_SURROGATE_PAIR_LA(*pwc))
+ return (-3);
+ return (cnt);
+}
+
+static inline uint32_t
+combine_surrogate_pair(uint32_t uc, uint32_t uc2)
+{
+ uc -= 0xD800;
+ uc *= 0x400;
+ uc += uc2 - 0xDC00;
+ uc += 0x10000;
+ return (uc);
+}
+
+/*
+ * Convert a single UTF-8/CESU-8 sequence to a Unicode code point in
+ * removing surrogate pairs.
+ *
+ * CESU-8: The Compatibility Encoding Scheme for UTF-16.
+ *
+ * Usually return used bytes, return used byte in negative value when
+ * a unicode character is replaced with U+FFFD.
+ */
+static int
+cesu8_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+ uint32_t wc = 0;
+ int cnt;
+
+ cnt = _utf8_to_unicode(&wc, s, n);
+ if (cnt == 3 && IS_HIGH_SURROGATE_LA(wc)) {
+ uint32_t wc2 = 0;
+ if (n - 3 < 3) {
+ /* Invalid byte sequence. */
+ goto invalid_sequence;
+ }
+ cnt = _utf8_to_unicode(&wc2, s+3, n-3);
+ if (cnt != 3 || !IS_LOW_SURROGATE_LA(wc2)) {
+ /* Invalid byte sequence. */
+ goto invalid_sequence;
+ }
+ wc = combine_surrogate_pair(wc, wc2);
+ cnt = 6;
+ } else if (cnt == 3 && IS_LOW_SURROGATE_LA(wc)) {
+ /* Invalid byte sequence. */
+ goto invalid_sequence;
+ }
+ *pwc = wc;
+ return (cnt);
+invalid_sequence:
+ *pwc = UNICODE_R_CHAR;/* set the Replacement Character instead. */
+ if (cnt > 0)
+ cnt *= -1;
+ return (cnt);
+}
+
+/*
+ * Convert a Unicode code point to a single UTF-8 sequence.
+ *
+ * NOTE:This function does not check if the Unicode is legal or not.
+ * Please you definitely check it before calling this.
+ */
+static size_t
+unicode_to_utf8(char *p, size_t remaining, uint32_t uc)
+{
+ char *_p = p;
+
+ /* Invalid Unicode char maps to Replacement character */
+ if (uc > UNICODE_MAX)
+ uc = UNICODE_R_CHAR;
+ /* Translate code point to UTF8 */
+ if (uc <= 0x7f) {
+ if (remaining == 0)
+ return (0);
+ *p++ = (char)uc;
+ } else if (uc <= 0x7ff) {
+ if (remaining < 2)
+ return (0);
+ *p++ = 0xc0 | ((uc >> 6) & 0x1f);
+ *p++ = 0x80 | (uc & 0x3f);
+ } else if (uc <= 0xffff) {
+ if (remaining < 3)
+ return (0);
+ *p++ = 0xe0 | ((uc >> 12) & 0x0f);
+ *p++ = 0x80 | ((uc >> 6) & 0x3f);
+ *p++ = 0x80 | (uc & 0x3f);
+ } else {
+ if (remaining < 4)
+ return (0);
+ *p++ = 0xf0 | ((uc >> 18) & 0x07);
+ *p++ = 0x80 | ((uc >> 12) & 0x3f);
+ *p++ = 0x80 | ((uc >> 6) & 0x3f);
+ *p++ = 0x80 | (uc & 0x3f);
+ }
+ return (p - _p);
+}
+
+static int
+utf16be_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+ return (utf16_to_unicode(pwc, s, n, 1));
+}
+
+static int
+utf16le_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+ return (utf16_to_unicode(pwc, s, n, 0));
+}
+
+static int
+utf16_to_unicode(uint32_t *pwc, const char *s, size_t n, int be)
+{
+ const char *utf16 = s;
+ unsigned uc;
+
+ if (n == 0)
+ return (0);
+ if (n == 1) {
+ /* set the Replacement Character instead. */
+ *pwc = UNICODE_R_CHAR;
+ return (-1);
+ }
+
+ if (be)
+ uc = archive_be16dec(utf16);
+ else
+ uc = archive_le16dec(utf16);
+ utf16 += 2;
+
+ /* If this is a surrogate pair, assemble the full code point.*/
+ if (IS_HIGH_SURROGATE_LA(uc)) {
+ unsigned uc2;
+
+ if (n >= 4) {
+ if (be)
+ uc2 = archive_be16dec(utf16);
+ else
+ uc2 = archive_le16dec(utf16);
+ } else
+ uc2 = 0;
+ if (IS_LOW_SURROGATE_LA(uc2)) {
+ uc = combine_surrogate_pair(uc, uc2);
+ utf16 += 2;
+ } else {
+ /* Undescribed code point should be U+FFFD
+ * (replacement character). */
+ *pwc = UNICODE_R_CHAR;
+ return (-2);
+ }
+ }
+
+ /*
+ * Surrogate pair values(0xd800 through 0xdfff) are only
+ * used by UTF-16, so, after above calculation, the code
+ * must not be surrogate values, and Unicode has no codes
+ * larger than 0x10ffff. Thus, those are not legal Unicode
+ * values.
+ */
+ if (IS_SURROGATE_PAIR_LA(uc) || uc > UNICODE_MAX) {
+ /* Undescribed code point should be U+FFFD
+ * (replacement character). */
+ *pwc = UNICODE_R_CHAR;
+ return (((int)(utf16 - s)) * -1);
+ }
+ *pwc = uc;
+ return ((int)(utf16 - s));
+}
+
+static size_t
+unicode_to_utf16be(char *p, size_t remaining, uint32_t uc)
+{
+ char *utf16 = p;
+
+ if (uc > 0xffff) {
+ /* We have a code point that won't fit into a
+ * wchar_t; convert it to a surrogate pair. */
+ if (remaining < 4)
+ return (0);
+ uc -= 0x10000;
+ archive_be16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800);
+ archive_be16enc(utf16+2, (uc & 0x3ff) + 0xDC00);
+ return (4);
+ } else {
+ if (remaining < 2)
+ return (0);
+ archive_be16enc(utf16, uc);
+ return (2);
+ }
+}
+
+static size_t
+unicode_to_utf16le(char *p, size_t remaining, uint32_t uc)
+{
+ char *utf16 = p;
+
+ if (uc > 0xffff) {
+ /* We have a code point that won't fit into a
+ * wchar_t; convert it to a surrogate pair. */
+ if (remaining < 4)
+ return (0);
+ uc -= 0x10000;
+ archive_le16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800);
+ archive_le16enc(utf16+2, (uc & 0x3ff) + 0xDC00);
+ return (4);
+ } else {
+ if (remaining < 2)
+ return (0);
+ archive_le16enc(utf16, uc);
+ return (2);
+ }
+}
+
+/*
+ * Copy UTF-8 string in checking surrogate pair.
+ * If any surrogate pair are found, it would be canonicalized.
+ */
+static int
+strncat_from_utf8_to_utf8(struct archive_string *as, const void *_p,
+ size_t len, struct archive_string_conv *sc)
+{
+ const char *s;
+ char *p, *endp;
+ int n, ret = 0;
+
+ (void)sc; /* UNUSED */
+
+ if (archive_string_ensure(as, as->length + len + 1) == NULL)
+ return (-1);
+
+ s = (const char *)_p;
+ p = as->s + as->length;
+ endp = as->s + as->buffer_length -1;
+ do {
+ uint32_t uc;
+ const char *ss = s;
+ size_t w;
+
+ /*
+ * Forward byte sequence until a conversion of that is needed.
+ */
+ while ((n = utf8_to_unicode(&uc, s, len)) > 0) {
+ s += n;
+ len -= n;
+ }
+ if (ss < s) {
+ if (p + (s - ss) > endp) {
+ as->length = p - as->s;
+ if (archive_string_ensure(as,
+ as->buffer_length + len + 1) == NULL)
+ return (-1);
+ p = as->s + as->length;
+ endp = as->s + as->buffer_length -1;
+ }
+
+ memcpy(p, ss, s - ss);
+ p += s - ss;
+ }
+
+ /*
+ * If n is negative, current byte sequence needs a replacement.
+ */
+ if (n < 0) {
+ if (n == -3 && IS_SURROGATE_PAIR_LA(uc)) {
+ /* Current byte sequence may be CESU-8. */
+ n = cesu8_to_unicode(&uc, s, len);
+ }
+ if (n < 0) {
+ ret = -1;
+ n *= -1;/* Use a replaced unicode character. */
+ }
+
+ /* Rebuild UTF-8 byte sequence. */
+ while ((w = unicode_to_utf8(p, endp - p, uc)) == 0) {
+ as->length = p - as->s;
+ if (archive_string_ensure(as,
+ as->buffer_length + len + 1) == NULL)
+ return (-1);
+ p = as->s + as->length;
+ endp = as->s + as->buffer_length -1;
+ }
+ p += w;
+ s += n;
+ len -= n;
+ }
+ } while (n > 0);
+ as->length = p - as->s;
+ as->s[as->length] = '\0';
+ return (ret);
+}
+
+static int
+archive_string_append_unicode(struct archive_string *as, const void *_p,
+ size_t len, struct archive_string_conv *sc)
+{
+ const char *s;
+ char *p, *endp;
+ uint32_t uc;
+ size_t w;
+ int n, ret = 0, ts, tm;
+ int (*parse)(uint32_t *, const char *, size_t);
+ size_t (*unparse)(char *, size_t, uint32_t);
+
+ if (sc->flag & SCONV_TO_UTF16BE) {
+ unparse = unicode_to_utf16be;
+ ts = 2;
+ } else if (sc->flag & SCONV_TO_UTF16LE) {
+ unparse = unicode_to_utf16le;
+ ts = 2;
+ } else if (sc->flag & SCONV_TO_UTF8) {
+ unparse = unicode_to_utf8;
+ ts = 1;
+ } else {
+ /*
+ * This case is going to be converted to another
+ * character-set through iconv.
+ */
+ if (sc->flag & SCONV_FROM_UTF16BE) {
+ unparse = unicode_to_utf16be;
+ ts = 2;
+ } else if (sc->flag & SCONV_FROM_UTF16LE) {
+ unparse = unicode_to_utf16le;
+ ts = 2;
+ } else {
+ unparse = unicode_to_utf8;
+ ts = 1;
+ }
+ }
+
+ if (sc->flag & SCONV_FROM_UTF16BE) {
+ parse = utf16be_to_unicode;
+ tm = 1;
+ } else if (sc->flag & SCONV_FROM_UTF16LE) {
+ parse = utf16le_to_unicode;
+ tm = 1;
+ } else {
+ parse = cesu8_to_unicode;
+ tm = ts;
+ }
+
+ if (archive_string_ensure(as, as->length + len * tm + ts) == NULL)
+ return (-1);
+
+ s = (const char *)_p;
+ p = as->s + as->length;
+ endp = as->s + as->buffer_length - ts;
+ while ((n = parse(&uc, s, len)) != 0) {
+ if (n < 0) {
+ /* Use a replaced unicode character. */
+ n *= -1;
+ ret = -1;
+ }
+ s += n;
+ len -= n;
+ while ((w = unparse(p, endp - p, uc)) == 0) {
+ /* There is not enough output buffer so
+ * we have to expand it. */
+ as->length = p - as->s;
+ if (archive_string_ensure(as,
+ as->buffer_length + len * tm + ts) == NULL)
+ return (-1);
+ p = as->s + as->length;
+ endp = as->s + as->buffer_length - ts;
+ }
+ p += w;
+ }
+ as->length = p - as->s;
+ as->s[as->length] = '\0';
+ if (ts == 2)
+ as->s[as->length+1] = '\0';
+ return (ret);
+}
+
+/*
+ * Following Constants for Hangul compositions this information comes from
+ * Unicode Standard Annex #15 http://unicode.org/reports/tr15/
+ */
+#define HC_SBASE 0xAC00
+#define HC_LBASE 0x1100
+#define HC_VBASE 0x1161
+#define HC_TBASE 0x11A7
+#define HC_LCOUNT 19
+#define HC_VCOUNT 21
+#define HC_TCOUNT 28
+#define HC_NCOUNT (HC_VCOUNT * HC_TCOUNT)
+#define HC_SCOUNT (HC_LCOUNT * HC_NCOUNT)
+
+static uint32_t
+get_nfc(uint32_t uc, uint32_t uc2)
+{
+ int t, b;
+
+ t = 0;
+ b = sizeof(u_composition_table)/sizeof(u_composition_table[0]) -1;
+ while (b >= t) {
+ int m = (t + b) / 2;
+ if (u_composition_table[m].cp1 < uc)
+ t = m + 1;
+ else if (u_composition_table[m].cp1 > uc)
+ b = m - 1;
+ else if (u_composition_table[m].cp2 < uc2)
+ t = m + 1;
+ else if (u_composition_table[m].cp2 > uc2)
+ b = m - 1;
+ else
+ return (u_composition_table[m].nfc);
+ }
+ return (0);
+}
+
+#define FDC_MAX 10 /* The maximum number of Following Decomposable
+ * Characters. */
+
+/*
+ * Update first code point.
+ */
+#define UPDATE_UC(new_uc) do { \
+ uc = new_uc; \
+ ucptr = NULL; \
+} while (0)
+
+/*
+ * Replace first code point with second code point.
+ */
+#define REPLACE_UC_WITH_UC2() do { \
+ uc = uc2; \
+ ucptr = uc2ptr; \
+ n = n2; \
+} while (0)
+
+#define EXPAND_BUFFER() do { \
+ as->length = p - as->s; \
+ if (archive_string_ensure(as, \
+ as->buffer_length + len * tm + ts) == NULL)\
+ return (-1); \
+ p = as->s + as->length; \
+ endp = as->s + as->buffer_length - ts; \
+} while (0)
+
+#define UNPARSE(p, endp, uc) do { \
+ while ((w = unparse(p, (endp) - (p), uc)) == 0) {\
+ EXPAND_BUFFER(); \
+ } \
+ p += w; \
+} while (0)
+
+/*
+ * Write first code point.
+ * If the code point has not be changed from its original code,
+ * this just copies it from its original buffer pointer.
+ * If not, this converts it to UTF-8 byte sequence and copies it.
+ */
+#define WRITE_UC() do { \
+ if (ucptr) { \
+ if (p + n > endp) \
+ EXPAND_BUFFER(); \
+ switch (n) { \
+ case 4: \
+ *p++ = *ucptr++; \
+ /* FALL THROUGH */ \
+ case 3: \
+ *p++ = *ucptr++; \
+ /* FALL THROUGH */ \
+ case 2: \
+ *p++ = *ucptr++; \
+ /* FALL THROUGH */ \
+ case 1: \
+ *p++ = *ucptr; \
+ break; \
+ } \
+ ucptr = NULL; \
+ } else { \
+ UNPARSE(p, endp, uc); \
+ } \
+} while (0)
+
+/*
+ * Collect following decomposable code points.
+ */
+#define COLLECT_CPS(start) do { \
+ int _i; \
+ for (_i = start; _i < FDC_MAX ; _i++) { \
+ nx = parse(&ucx[_i], s, len); \
+ if (nx <= 0) \
+ break; \
+ cx = CCC(ucx[_i]); \
+ if (cl >= cx && cl != 228 && cx != 228)\
+ break; \
+ s += nx; \
+ len -= nx; \
+ cl = cx; \
+ ccx[_i] = cx; \
+ } \
+ if (_i >= FDC_MAX) { \
+ ret = -1; \
+ ucx_size = FDC_MAX; \
+ } else \
+ ucx_size = _i; \
+} while (0)
+
+/*
+ * Normalize UTF-8/UTF-16BE characters to Form C and copy the result.
+ *
+ * TODO: Convert composition exclusions, which are never converted
+ * from NFC,NFD,NFKC and NFKD, to Form C.
+ */
+static int
+archive_string_normalize_C(struct archive_string *as, const void *_p,
+ size_t len, struct archive_string_conv *sc)
+{
+ const char *s = (const char *)_p;
+ char *p, *endp;
+ uint32_t uc, uc2;
+ size_t w;
+ int always_replace, n, n2, ret = 0, spair, ts, tm;
+ int (*parse)(uint32_t *, const char *, size_t);
+ size_t (*unparse)(char *, size_t, uint32_t);
+
+ always_replace = 1;
+ ts = 1;/* text size. */
+ if (sc->flag & SCONV_TO_UTF16BE) {
+ unparse = unicode_to_utf16be;
+ ts = 2;
+ if (sc->flag & SCONV_FROM_UTF16BE)
+ always_replace = 0;
+ } else if (sc->flag & SCONV_TO_UTF16LE) {
+ unparse = unicode_to_utf16le;
+ ts = 2;
+ if (sc->flag & SCONV_FROM_UTF16LE)
+ always_replace = 0;
+ } else if (sc->flag & SCONV_TO_UTF8) {
+ unparse = unicode_to_utf8;
+ if (sc->flag & SCONV_FROM_UTF8)
+ always_replace = 0;
+ } else {
+ /*
+ * This case is going to be converted to another
+ * character-set through iconv.
+ */
+ always_replace = 0;
+ if (sc->flag & SCONV_FROM_UTF16BE) {
+ unparse = unicode_to_utf16be;
+ ts = 2;
+ } else if (sc->flag & SCONV_FROM_UTF16LE) {
+ unparse = unicode_to_utf16le;
+ ts = 2;
+ } else {
+ unparse = unicode_to_utf8;
+ }
+ }
+
+ if (sc->flag & SCONV_FROM_UTF16BE) {
+ parse = utf16be_to_unicode;
+ tm = 1;
+ spair = 4;/* surrogate pair size in UTF-16. */
+ } else if (sc->flag & SCONV_FROM_UTF16LE) {
+ parse = utf16le_to_unicode;
+ tm = 1;
+ spair = 4;/* surrogate pair size in UTF-16. */
+ } else {
+ parse = cesu8_to_unicode;
+ tm = ts;
+ spair = 6;/* surrogate pair size in UTF-8. */
+ }
+
+ if (archive_string_ensure(as, as->length + len * tm + ts) == NULL)
+ return (-1);
+
+ p = as->s + as->length;
+ endp = as->s + as->buffer_length - ts;
+ while ((n = parse(&uc, s, len)) != 0) {
+ const char *ucptr, *uc2ptr;
+
+ if (n < 0) {
+ /* Use a replaced unicode character. */
+ UNPARSE(p, endp, uc);
+ s += n*-1;
+ len -= n*-1;
+ ret = -1;
+ continue;
+ } else if (n == spair || always_replace)
+ /* uc is converted from a surrogate pair.
+ * this should be treated as a changed code. */
+ ucptr = NULL;
+ else
+ ucptr = s;
+ s += n;
+ len -= n;
+
+ /* Read second code point. */
+ while ((n2 = parse(&uc2, s, len)) > 0) {
+ uint32_t ucx[FDC_MAX];
+ int ccx[FDC_MAX];
+ int cl, cx, i, nx, ucx_size;
+ int LIndex,SIndex;
+ uint32_t nfc;
+
+ if (n2 == spair || always_replace)
+ /* uc2 is converted from a surrogate pair.
+ * this should be treated as a changed code. */
+ uc2ptr = NULL;
+ else
+ uc2ptr = s;
+ s += n2;
+ len -= n2;
+
+ /*
+ * If current second code point is out of decomposable
+ * code points, finding compositions is unneeded.
+ */
+ if (!IS_DECOMPOSABLE_BLOCK(uc2)) {
+ WRITE_UC();
+ REPLACE_UC_WITH_UC2();
+ continue;
+ }
+
+ /*
+ * Try to combine current code points.
+ */
+ /*
+ * We have to combine Hangul characters according to
+ * http://uniicode.org/reports/tr15/#Hangul
+ */
+ if (0 <= (LIndex = uc - HC_LBASE) &&
+ LIndex < HC_LCOUNT) {
+ /*
+ * Hangul Composition.
+ * 1. Two current code points are L and V.
+ */
+ int VIndex = uc2 - HC_VBASE;
+ if (0 <= VIndex && VIndex < HC_VCOUNT) {
+ /* Make syllable of form LV. */
+ UPDATE_UC(HC_SBASE +
+ (LIndex * HC_VCOUNT + VIndex) *
+ HC_TCOUNT);
+ } else {
+ WRITE_UC();
+ REPLACE_UC_WITH_UC2();
+ }
+ continue;
+ } else if (0 <= (SIndex = uc - HC_SBASE) &&
+ SIndex < HC_SCOUNT && (SIndex % HC_TCOUNT) == 0) {
+ /*
+ * Hangul Composition.
+ * 2. Two current code points are LV and T.
+ */
+ int TIndex = uc2 - HC_TBASE;
+ if (0 < TIndex && TIndex < HC_TCOUNT) {
+ /* Make syllable of form LVT. */
+ UPDATE_UC(uc + TIndex);
+ } else {
+ WRITE_UC();
+ REPLACE_UC_WITH_UC2();
+ }
+ continue;
+ } else if ((nfc = get_nfc(uc, uc2)) != 0) {
+ /* A composition to current code points
+ * is found. */
+ UPDATE_UC(nfc);
+ continue;
+ } else if ((cl = CCC(uc2)) == 0) {
+ /* Clearly 'uc2' the second code point is not
+ * a decomposable code. */
+ WRITE_UC();
+ REPLACE_UC_WITH_UC2();
+ continue;
+ }
+
+ /*
+ * Collect following decomposable code points.
+ */
+ cx = 0;
+ ucx[0] = uc2;
+ ccx[0] = cl;
+ COLLECT_CPS(1);
+
+ /*
+ * Find a composed code in the collected code points.
+ */
+ i = 1;
+ while (i < ucx_size) {
+ int j;
+
+ if ((nfc = get_nfc(uc, ucx[i])) == 0) {
+ i++;
+ continue;
+ }
+
+ /*
+ * nfc is composed of uc and ucx[i].
+ */
+ UPDATE_UC(nfc);
+
+ /*
+ * Remove ucx[i] by shifting
+ * following code points.
+ */
+ for (j = i; j+1 < ucx_size; j++) {
+ ucx[j] = ucx[j+1];
+ ccx[j] = ccx[j+1];
+ }
+ ucx_size --;
+
+ /*
+ * Collect following code points blocked
+ * by ucx[i] the removed code point.
+ */
+ if (ucx_size > 0 && i == ucx_size &&
+ nx > 0 && cx == cl) {
+ cl = ccx[ucx_size-1];
+ COLLECT_CPS(ucx_size);
+ }
+ /*
+ * Restart finding a composed code with
+ * the updated uc from the top of the
+ * collected code points.
+ */
+ i = 0;
+ }
+
+ /*
+ * Apparently the current code points are not
+ * decomposed characters or already composed.
+ */
+ WRITE_UC();
+ for (i = 0; i < ucx_size; i++)
+ UNPARSE(p, endp, ucx[i]);
+
+ /*
+ * Flush out remaining canonical combining characters.
+ */
+ if (nx > 0 && cx == cl && len > 0) {
+ while ((nx = parse(&ucx[0], s, len))
+ > 0) {
+ cx = CCC(ucx[0]);
+ if (cl > cx)
+ break;
+ s += nx;
+ len -= nx;
+ cl = cx;
+ UNPARSE(p, endp, ucx[0]);
+ }
+ }
+ break;
+ }
+ if (n2 < 0) {
+ WRITE_UC();
+ /* Use a replaced unicode character. */
+ UNPARSE(p, endp, uc2);
+ s += n2*-1;
+ len -= n2*-1;
+ ret = -1;
+ continue;
+ } else if (n2 == 0) {
+ WRITE_UC();
+ break;
+ }
+ }
+ as->length = p - as->s;
+ as->s[as->length] = '\0';
+ if (ts == 2)
+ as->s[as->length+1] = '\0';
+ return (ret);
+}
+
+static int
+get_nfd(uint32_t *cp1, uint32_t *cp2, uint32_t uc)
+{
+ int t, b;
+
+ /*
+ * These are not converted to NFD on Mac OS.
+ */
+ if ((uc >= 0x2000 && uc <= 0x2FFF) ||
+ (uc >= 0xF900 && uc <= 0xFAFF) ||
+ (uc >= 0x2F800 && uc <= 0x2FAFF))
+ return (0);
+ /*
+ * Those code points are not converted to NFD on Mac OS.
+ * I do not know the reason because it is undocumented.
+ * NFC NFD
+ * 1109A ==> 11099 110BA
+ * 1109C ==> 1109B 110BA
+ * 110AB ==> 110A5 110BA
+ */
+ if (uc == 0x1109A || uc == 0x1109C || uc == 0x110AB)
+ return (0);
+
+ t = 0;
+ b = sizeof(u_decomposition_table)/sizeof(u_decomposition_table[0]) -1;
+ while (b >= t) {
+ int m = (t + b) / 2;
+ if (u_decomposition_table[m].nfc < uc)
+ t = m + 1;
+ else if (u_decomposition_table[m].nfc > uc)
+ b = m - 1;
+ else {
+ *cp1 = u_decomposition_table[m].cp1;
+ *cp2 = u_decomposition_table[m].cp2;
+ return (1);
+ }
+ }
+ return (0);
+}
+
+#define REPLACE_UC_WITH(cp) do { \
+ uc = cp; \
+ ucptr = NULL; \
+} while (0)
+
+/*
+ * Normalize UTF-8 characters to Form D and copy the result.
+ */
+static int
+archive_string_normalize_D(struct archive_string *as, const void *_p,
+ size_t len, struct archive_string_conv *sc)
+{
+ const char *s = (const char *)_p;
+ char *p, *endp;
+ uint32_t uc, uc2;
+ size_t w;
+ int always_replace, n, n2, ret = 0, spair, ts, tm;
+ int (*parse)(uint32_t *, const char *, size_t);
+ size_t (*unparse)(char *, size_t, uint32_t);
+
+ always_replace = 1;
+ ts = 1;/* text size. */
+ if (sc->flag & SCONV_TO_UTF16BE) {
+ unparse = unicode_to_utf16be;
+ ts = 2;
+ if (sc->flag & SCONV_FROM_UTF16BE)
+ always_replace = 0;
+ } else if (sc->flag & SCONV_TO_UTF16LE) {
+ unparse = unicode_to_utf16le;
+ ts = 2;
+ if (sc->flag & SCONV_FROM_UTF16LE)
+ always_replace = 0;
+ } else if (sc->flag & SCONV_TO_UTF8) {
+ unparse = unicode_to_utf8;
+ if (sc->flag & SCONV_FROM_UTF8)
+ always_replace = 0;
+ } else {
+ /*
+ * This case is going to be converted to another
+ * character-set through iconv.
+ */
+ always_replace = 0;
+ if (sc->flag & SCONV_FROM_UTF16BE) {
+ unparse = unicode_to_utf16be;
+ ts = 2;
+ } else if (sc->flag & SCONV_FROM_UTF16LE) {
+ unparse = unicode_to_utf16le;
+ ts = 2;
+ } else {
+ unparse = unicode_to_utf8;
+ }
+ }
+
+ if (sc->flag & SCONV_FROM_UTF16BE) {
+ parse = utf16be_to_unicode;
+ tm = 1;
+ spair = 4;/* surrogate pair size in UTF-16. */
+ } else if (sc->flag & SCONV_FROM_UTF16LE) {
+ parse = utf16le_to_unicode;
+ tm = 1;
+ spair = 4;/* surrogate pair size in UTF-16. */
+ } else {
+ parse = cesu8_to_unicode;
+ tm = ts;
+ spair = 6;/* surrogate pair size in UTF-8. */
+ }
+
+ if (archive_string_ensure(as, as->length + len * tm + ts) == NULL)
+ return (-1);
+
+ p = as->s + as->length;
+ endp = as->s + as->buffer_length - ts;
+ while ((n = parse(&uc, s, len)) != 0) {
+ const char *ucptr;
+ uint32_t cp1, cp2;
+ int SIndex;
+ struct {
+ uint32_t uc;
+ int ccc;
+ } fdc[FDC_MAX];
+ int fdi, fdj;
+ int ccc;
+
+check_first_code:
+ if (n < 0) {
+ /* Use a replaced unicode character. */
+ UNPARSE(p, endp, uc);
+ s += n*-1;
+ len -= n*-1;
+ ret = -1;
+ continue;
+ } else if (n == spair || always_replace)
+ /* uc is converted from a surrogate pair.
+ * this should be treated as a changed code. */
+ ucptr = NULL;
+ else
+ ucptr = s;
+ s += n;
+ len -= n;
+
+ /* Hangul Decomposition. */
+ if ((SIndex = uc - HC_SBASE) >= 0 && SIndex < HC_SCOUNT) {
+ int L = HC_LBASE + SIndex / HC_NCOUNT;
+ int V = HC_VBASE + (SIndex % HC_NCOUNT) / HC_TCOUNT;
+ int T = HC_TBASE + SIndex % HC_TCOUNT;
+
+ REPLACE_UC_WITH(L);
+ WRITE_UC();
+ REPLACE_UC_WITH(V);
+ WRITE_UC();
+ if (T != HC_TBASE) {
+ REPLACE_UC_WITH(T);
+ WRITE_UC();
+ }
+ continue;
+ }
+ if (IS_DECOMPOSABLE_BLOCK(uc) && CCC(uc) != 0) {
+ WRITE_UC();
+ continue;
+ }
+
+ fdi = 0;
+ while (get_nfd(&cp1, &cp2, uc) && fdi < FDC_MAX) {
+ int k;
+
+ for (k = fdi; k > 0; k--)
+ fdc[k] = fdc[k-1];
+ fdc[0].ccc = CCC(cp2);
+ fdc[0].uc = cp2;
+ fdi++;
+ REPLACE_UC_WITH(cp1);
+ }
+
+ /* Read following code points. */
+ while ((n2 = parse(&uc2, s, len)) > 0 &&
+ (ccc = CCC(uc2)) != 0 && fdi < FDC_MAX) {
+ int j, k;
+
+ s += n2;
+ len -= n2;
+ for (j = 0; j < fdi; j++) {
+ if (fdc[j].ccc > ccc)
+ break;
+ }
+ if (j < fdi) {
+ for (k = fdi; k > j; k--)
+ fdc[k] = fdc[k-1];
+ fdc[j].ccc = ccc;
+ fdc[j].uc = uc2;
+ } else {
+ fdc[fdi].ccc = ccc;
+ fdc[fdi].uc = uc2;
+ }
+ fdi++;
+ }
+
+ WRITE_UC();
+ for (fdj = 0; fdj < fdi; fdj++) {
+ REPLACE_UC_WITH(fdc[fdj].uc);
+ WRITE_UC();
+ }
+
+ if (n2 == 0)
+ break;
+ REPLACE_UC_WITH(uc2);
+ n = n2;
+ goto check_first_code;
+ }
+ as->length = p - as->s;
+ as->s[as->length] = '\0';
+ if (ts == 2)
+ as->s[as->length+1] = '\0';
+ return (ret);
+}
+
+/*
+ * libarchive 2.x made incorrect UTF-8 strings in the wrong assumption
+ * that WCS is Unicode. It is true for several platforms but some are false.
+ * And then people who did not use UTF-8 locale on the non Unicode WCS
+ * platform and made a tar file with libarchive(mostly bsdtar) 2.x. Those
+ * now cannot get right filename from libarchive 3.x and later since we
+ * fixed the wrong assumption and it is incompatible to older its versions.
+ * So we provide special option, "compat-2x.x", for resolving it.
+ * That option enable the string conversion of libarchive 2.x.
+ *
+ * Translates the wrong UTF-8 string made by libarchive 2.x into current
+ * locale character set and appends to the archive_string.
+ * Note: returns -1 if conversion fails.
+ */
+static int
+strncat_from_utf8_libarchive2(struct archive_string *as,
+ const void *_p, size_t len, struct archive_string_conv *sc)
+{
+ const char *s;
+ int n;
+ char *p;
+ char *end;
+ uint32_t unicode;
+#if HAVE_WCRTOMB
+ mbstate_t shift_state;
+
+ memset(&shift_state, 0, sizeof(shift_state));
+#else
+ /* Clear the shift state before starting. */
+ wctomb(NULL, L'\0');
+#endif
+ (void)sc; /* UNUSED */
+ /*
+ * Allocate buffer for MBS.
+ * We need this allocation here since it is possible that
+ * as->s is still NULL.
+ */
+ if (archive_string_ensure(as, as->length + len + 1) == NULL)
+ return (-1);
+
+ s = (const char *)_p;
+ p = as->s + as->length;
+ end = as->s + as->buffer_length - MB_CUR_MAX -1;
+ while ((n = _utf8_to_unicode(&unicode, s, len)) != 0) {
+ wchar_t wc;
+
+ if (p >= end) {
+ as->length = p - as->s;
+ /* Re-allocate buffer for MBS. */
+ if (archive_string_ensure(as,
+ as->length + max(len * 2,
+ (size_t)MB_CUR_MAX) + 1) == NULL)
+ return (-1);
+ p = as->s + as->length;
+ end = as->s + as->buffer_length - MB_CUR_MAX -1;
+ }
+
+ /*
+ * As libarchive 2.x, translates the UTF-8 characters into
+ * wide-characters in the assumption that WCS is Unicode.
+ */
+ if (n < 0) {
+ n *= -1;
+ wc = L'?';
+ } else
+ wc = (wchar_t)unicode;
+
+ s += n;
+ len -= n;
+ /*
+ * Translates the wide-character into the current locale MBS.
+ */
+#if HAVE_WCRTOMB
+ n = (int)wcrtomb(p, wc, &shift_state);
+#else
+ n = (int)wctomb(p, wc);
+#endif
+ if (n == -1)
+ return (-1);
+ p += n;
+ }
+ as->length = p - as->s;
+ as->s[as->length] = '\0';
+ return (0);
+}
+
+
+/*
+ * Conversion functions between current locale dependent MBS and UTF-16BE.
+ * strncat_from_utf16be() : UTF-16BE --> MBS
+ * strncat_to_utf16be() : MBS --> UTF16BE
+ */
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * Convert a UTF-16BE/LE string to current locale and copy the result.
+ * Return -1 if conversion fails.
+ */
+static int
+win_strncat_from_utf16(struct archive_string *as, const void *_p, size_t bytes,
+ struct archive_string_conv *sc, int be)
+{
+ struct archive_string tmp;
+ const char *u16;
+ int ll;
+ BOOL defchar;
+ char *mbs;
+ size_t mbs_size, b;
+ int ret = 0;
+
+ bytes &= ~1;
+ if (archive_string_ensure(as, as->length + bytes +1) == NULL)
+ return (-1);
+
+ mbs = as->s + as->length;
+ mbs_size = as->buffer_length - as->length -1;
+
+ if (sc->to_cp == CP_C_LOCALE) {
+ /*
+ * "C" locale special process.
+ */
+ u16 = _p;
+ ll = 0;
+ for (b = 0; b < bytes; b += 2) {
+ uint16_t val;
+ if (be)
+ val = archive_be16dec(u16+b);
+ else
+ val = archive_le16dec(u16+b);
+ if (val > 255) {
+ *mbs++ = '?';
+ ret = -1;
+ } else
+ *mbs++ = (char)(val&0xff);
+ ll++;
+ }
+ as->length += ll;
+ as->s[as->length] = '\0';
+ return (ret);
+ }
+
+ archive_string_init(&tmp);
+ if (be) {
+ if (is_big_endian()) {
+ u16 = _p;
+ } else {
+ if (archive_string_ensure(&tmp, bytes+2) == NULL)
+ return (-1);
+ memcpy(tmp.s, _p, bytes);
+ for (b = 0; b < bytes; b += 2) {
+ uint16_t val = archive_be16dec(tmp.s+b);
+ archive_le16enc(tmp.s+b, val);
+ }
+ u16 = tmp.s;
+ }
+ } else {
+ if (!is_big_endian()) {
+ u16 = _p;
+ } else {
+ if (archive_string_ensure(&tmp, bytes+2) == NULL)
+ return (-1);
+ memcpy(tmp.s, _p, bytes);
+ for (b = 0; b < bytes; b += 2) {
+ uint16_t val = archive_le16dec(tmp.s+b);
+ archive_be16enc(tmp.s+b, val);
+ }
+ u16 = tmp.s;
+ }
+ }
+
+ do {
+ defchar = 0;
+ ll = WideCharToMultiByte(sc->to_cp, 0,
+ (LPCWSTR)u16, (int)bytes>>1, mbs, (int)mbs_size,
+ NULL, &defchar);
+ /* Exit loop if we succeeded */
+ if (ll != 0 ||
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ break;
+ }
+ /* Else expand buffer and loop to try again. */
+ ll = WideCharToMultiByte(sc->to_cp, 0,
+ (LPCWSTR)u16, (int)bytes, NULL, 0, NULL, NULL);
+ if (archive_string_ensure(as, ll +1) == NULL)
+ return (-1);
+ mbs = as->s + as->length;
+ mbs_size = as->buffer_length - as->length -1;
+ } while (1);
+ archive_string_free(&tmp);
+ as->length += ll;
+ as->s[as->length] = '\0';
+ if (ll == 0 || defchar)
+ ret = -1;
+ return (ret);
+}
+
+static int
+win_strncat_from_utf16be(struct archive_string *as, const void *_p,
+ size_t bytes, struct archive_string_conv *sc)
+{
+ return (win_strncat_from_utf16(as, _p, bytes, sc, 1));
+}
+
+static int
+win_strncat_from_utf16le(struct archive_string *as, const void *_p,
+ size_t bytes, struct archive_string_conv *sc)
+{
+ return (win_strncat_from_utf16(as, _p, bytes, sc, 0));
+}
+
+static int
+is_big_endian(void)
+{
+ uint16_t d = 1;
+
+ return (archive_be16dec(&d) == 1);
+}
+
+/*
+ * Convert a current locale string to UTF-16BE/LE and copy the result.
+ * Return -1 if conversion fails.
+ */
+static int
+win_strncat_to_utf16(struct archive_string *as16, const void *_p,
+ size_t length, struct archive_string_conv *sc, int bigendian)
+{
+ const char *s = (const char *)_p;
+ char *u16;
+ size_t count, avail;
+
+ if (archive_string_ensure(as16,
+ as16->length + (length + 1) * 2) == NULL)
+ return (-1);
+
+ u16 = as16->s + as16->length;
+ avail = as16->buffer_length - 2;
+ if (sc->from_cp == CP_C_LOCALE) {
+ /*
+ * "C" locale special process.
+ */
+ count = 0;
+ while (count < length && *s) {
+ if (bigendian)
+ archive_be16enc(u16, *s);
+ else
+ archive_le16enc(u16, *s);
+ u16 += 2;
+ s++;
+ count++;
+ }
+ as16->length += count << 1;
+ as16->s[as16->length] = 0;
+ as16->s[as16->length+1] = 0;
+ return (0);
+ }
+ do {
+ count = MultiByteToWideChar(sc->from_cp,
+ MB_PRECOMPOSED, s, (int)length, (LPWSTR)u16, (int)avail>>1);
+ /* Exit loop if we succeeded */
+ if (count != 0 ||
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ break;
+ }
+ /* Expand buffer and try again */
+ count = MultiByteToWideChar(sc->from_cp,
+ MB_PRECOMPOSED, s, (int)length, NULL, 0);
+ if (archive_string_ensure(as16, (count +1) * 2)
+ == NULL)
+ return (-1);
+ u16 = as16->s + as16->length;
+ avail = as16->buffer_length - 2;
+ } while (1);
+ as16->length += count * 2;
+ as16->s[as16->length] = 0;
+ as16->s[as16->length+1] = 0;
+ if (count == 0)
+ return (-1);
+
+ if (is_big_endian()) {
+ if (!bigendian) {
+ while (count > 0) {
+ uint16_t v = archive_be16dec(u16);
+ archive_le16enc(u16, v);
+ u16 += 2;
+ count--;
+ }
+ }
+ } else {
+ if (bigendian) {
+ while (count > 0) {
+ uint16_t v = archive_le16dec(u16);
+ archive_be16enc(u16, v);
+ u16 += 2;
+ count--;
+ }
+ }
+ }
+ return (0);
+}
+
+static int
+win_strncat_to_utf16be(struct archive_string *as16, const void *_p,
+ size_t length, struct archive_string_conv *sc)
+{
+ return (win_strncat_to_utf16(as16, _p, length, sc, 1));
+}
+
+static int
+win_strncat_to_utf16le(struct archive_string *as16, const void *_p,
+ size_t length, struct archive_string_conv *sc)
+{
+ return (win_strncat_to_utf16(as16, _p, length, sc, 0));
+}
+
+#endif /* _WIN32 && !__CYGWIN__ */
+
+/*
+ * Do the best effort for conversions.
+ * We cannot handle UTF-16BE character-set without such iconv,
+ * but there is a chance if a string consists just ASCII code or
+ * a current locale is UTF-8.
+ */
+
+/*
+ * Convert a UTF-16BE string to current locale and copy the result.
+ * Return -1 if conversion fails.
+ */
+static int
+best_effort_strncat_from_utf16(struct archive_string *as, const void *_p,
+ size_t bytes, struct archive_string_conv *sc, int be)
+{
+ const char *utf16 = (const char *)_p;
+ char *mbs;
+ uint32_t uc;
+ int n, ret;
+
+ (void)sc; /* UNUSED */
+ /*
+ * Other case, we should do the best effort.
+ * If all character are ASCII(<0x7f), we can convert it.
+ * if not , we set a alternative character and return -1.
+ */
+ ret = 0;
+ if (archive_string_ensure(as, as->length + bytes +1) == NULL)
+ return (-1);
+ mbs = as->s + as->length;
+
+ while ((n = utf16_to_unicode(&uc, utf16, bytes, be)) != 0) {
+ if (n < 0) {
+ n *= -1;
+ ret = -1;
+ }
+ bytes -= n;
+ utf16 += n;
+
+ if (uc > 127) {
+ /* We cannot handle it. */
+ *mbs++ = '?';
+ ret = -1;
+ } else
+ *mbs++ = (char)uc;
+ }
+ as->length = mbs - as->s;
+ as->s[as->length] = '\0';
+ return (ret);
+}
+
+static int
+best_effort_strncat_from_utf16be(struct archive_string *as, const void *_p,
+ size_t bytes, struct archive_string_conv *sc)
+{
+ return (best_effort_strncat_from_utf16(as, _p, bytes, sc, 1));
+}
+
+static int
+best_effort_strncat_from_utf16le(struct archive_string *as, const void *_p,
+ size_t bytes, struct archive_string_conv *sc)
+{
+ return (best_effort_strncat_from_utf16(as, _p, bytes, sc, 0));
+}
+
+/*
+ * Convert a current locale string to UTF-16BE/LE and copy the result.
+ * Return -1 if conversion fails.
+ */
+static int
+best_effort_strncat_to_utf16(struct archive_string *as16, const void *_p,
+ size_t length, struct archive_string_conv *sc, int bigendian)
+{
+ const char *s = (const char *)_p;
+ char *utf16;
+ size_t remaining;
+ int ret;
+
+ (void)sc; /* UNUSED */
+ /*
+ * Other case, we should do the best effort.
+ * If all character are ASCII(<0x7f), we can convert it.
+ * if not , we set a alternative character and return -1.
+ */
+ ret = 0;
+ remaining = length;
+
+ if (archive_string_ensure(as16,
+ as16->length + (length + 1) * 2) == NULL)
+ return (-1);
+
+ utf16 = as16->s + as16->length;
+ while (remaining--) {
+ unsigned c = *s++;
+ if (c > 127) {
+ /* We cannot handle it. */
+ c = UNICODE_R_CHAR;
+ ret = -1;
+ }
+ if (bigendian)
+ archive_be16enc(utf16, c);
+ else
+ archive_le16enc(utf16, c);
+ utf16 += 2;
+ }
+ as16->length = utf16 - as16->s;
+ as16->s[as16->length] = 0;
+ as16->s[as16->length+1] = 0;
+ return (ret);
+}
+
+static int
+best_effort_strncat_to_utf16be(struct archive_string *as16, const void *_p,
+ size_t length, struct archive_string_conv *sc)
+{
+ return (best_effort_strncat_to_utf16(as16, _p, length, sc, 1));
+}
+
+static int
+best_effort_strncat_to_utf16le(struct archive_string *as16, const void *_p,
+ size_t length, struct archive_string_conv *sc)
+{
+ return (best_effort_strncat_to_utf16(as16, _p, length, sc, 0));
+}
+
+
+/*
+ * Multistring operations.
+ */
+
+void
+archive_mstring_clean(struct archive_mstring *aes)
+{
+ archive_wstring_free(&(aes->aes_wcs));
+ archive_string_free(&(aes->aes_mbs));
+ archive_string_free(&(aes->aes_utf8));
+ archive_string_free(&(aes->aes_mbs_in_locale));
+ aes->aes_set = 0;
+}
+
+void
+archive_mstring_copy(struct archive_mstring *dest, struct archive_mstring *src)
+{
+ dest->aes_set = src->aes_set;
+ archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs));
+ archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8));
+ archive_wstring_copy(&(dest->aes_wcs), &(src->aes_wcs));
+}
+
+int
+archive_mstring_get_utf8(struct archive *a, struct archive_mstring *aes,
+ const char **p)
+{
+ struct archive_string_conv *sc;
+ int r;
+
+ /* If we already have a UTF8 form, return that immediately. */
+ if (aes->aes_set & AES_SET_UTF8) {
+ *p = aes->aes_utf8.s;
+ return (0);
+ }
+
+ *p = NULL;
+ /* Try converting WCS to MBS first if MBS does not exist yet. */
+ if ((aes->aes_set & AES_SET_MBS) == 0) {
+ const char *pm; /* unused */
+ archive_mstring_get_mbs(a, aes, &pm); /* ignore errors, we'll handle it later */
+ }
+ if (aes->aes_set & AES_SET_MBS) {
+ sc = archive_string_conversion_to_charset(a, "UTF-8", 1);
+ if (sc == NULL)
+ return (-1);/* Couldn't allocate memory for sc. */
+ r = archive_strncpy_l(&(aes->aes_utf8), aes->aes_mbs.s,
+ aes->aes_mbs.length, sc);
+ if (a == NULL)
+ free_sconv_object(sc);
+ if (r == 0) {
+ aes->aes_set |= AES_SET_UTF8;
+ *p = aes->aes_utf8.s;
+ return (0);/* success. */
+ } else
+ return (-1);/* failure. */
+ }
+ return (0);/* success. */
+}
+
+int
+archive_mstring_get_mbs(struct archive *a, struct archive_mstring *aes,
+ const char **p)
+{
+ struct archive_string_conv *sc;
+ int r, ret = 0;
+
+ /* If we already have an MBS form, return that immediately. */
+ if (aes->aes_set & AES_SET_MBS) {
+ *p = aes->aes_mbs.s;
+ return (ret);
+ }
+
+ *p = NULL;
+ /* If there's a WCS form, try converting with the native locale. */
+ if (aes->aes_set & AES_SET_WCS) {
+ archive_string_empty(&(aes->aes_mbs));
+ r = archive_string_append_from_wcs(&(aes->aes_mbs),
+ aes->aes_wcs.s, aes->aes_wcs.length);
+ *p = aes->aes_mbs.s;
+ if (r == 0) {
+ aes->aes_set |= AES_SET_MBS;
+ return (ret);
+ } else
+ ret = -1;
+ }
+
+ /* If there's a UTF-8 form, try converting with the native locale. */
+ if (aes->aes_set & AES_SET_UTF8) {
+ archive_string_empty(&(aes->aes_mbs));
+ sc = archive_string_conversion_from_charset(a, "UTF-8", 1);
+ if (sc == NULL)
+ return (-1);/* Couldn't allocate memory for sc. */
+ r = archive_strncpy_l(&(aes->aes_mbs),
+ aes->aes_utf8.s, aes->aes_utf8.length, sc);
+ if (a == NULL)
+ free_sconv_object(sc);
+ *p = aes->aes_mbs.s;
+ if (r == 0) {
+ aes->aes_set |= AES_SET_MBS;
+ ret = 0;/* success; overwrite previous error. */
+ } else
+ ret = -1;/* failure. */
+ }
+ return (ret);
+}
+
+int
+archive_mstring_get_wcs(struct archive *a, struct archive_mstring *aes,
+ const wchar_t **wp)
+{
+ int r, ret = 0;
+
+ (void)a;/* UNUSED */
+ /* Return WCS form if we already have it. */
+ if (aes->aes_set & AES_SET_WCS) {
+ *wp = aes->aes_wcs.s;
+ return (ret);
+ }
+
+ *wp = NULL;
+ /* Try converting UTF8 to MBS first if MBS does not exist yet. */
+ if ((aes->aes_set & AES_SET_MBS) == 0) {
+ const char *p; /* unused */
+ archive_mstring_get_mbs(a, aes, &p); /* ignore errors, we'll handle it later */
+ }
+ /* Try converting MBS to WCS using native locale. */
+ if (aes->aes_set & AES_SET_MBS) {
+ archive_wstring_empty(&(aes->aes_wcs));
+ r = archive_wstring_append_from_mbs(&(aes->aes_wcs),
+ aes->aes_mbs.s, aes->aes_mbs.length);
+ if (r == 0) {
+ aes->aes_set |= AES_SET_WCS;
+ *wp = aes->aes_wcs.s;
+ } else
+ ret = -1;/* failure. */
+ }
+ return (ret);
+}
+
+int
+archive_mstring_get_mbs_l(struct archive *a, struct archive_mstring *aes,
+ const char **p, size_t *length, struct archive_string_conv *sc)
+{
+ int ret = 0;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ int r;
+
+ /*
+ * Internationalization programming on Windows must use Wide
+ * characters because Windows platform cannot make locale UTF-8.
+ */
+ if (sc != NULL && (aes->aes_set & AES_SET_WCS) != 0) {
+ archive_string_empty(&(aes->aes_mbs_in_locale));
+ r = archive_string_append_from_wcs_in_codepage(
+ &(aes->aes_mbs_in_locale), aes->aes_wcs.s,
+ aes->aes_wcs.length, sc);
+ if (r == 0) {
+ *p = aes->aes_mbs_in_locale.s;
+ if (length != NULL)
+ *length = aes->aes_mbs_in_locale.length;
+ return (0);
+ } else if (errno == ENOMEM)
+ return (-1);
+ else
+ ret = -1;
+ }
+#endif
+
+ /* If there is not an MBS form but there is a WCS or UTF8 form, try converting
+ * with the native locale to be used for translating it to specified
+ * character-set. */
+ if ((aes->aes_set & AES_SET_MBS) == 0) {
+ const char *pm; /* unused */
+ archive_mstring_get_mbs(a, aes, &pm); /* ignore errors, we'll handle it later */
+ }
+ /* If we already have an MBS form, use it to be translated to
+ * specified character-set. */
+ if (aes->aes_set & AES_SET_MBS) {
+ if (sc == NULL) {
+ /* Conversion is unneeded. */
+ *p = aes->aes_mbs.s;
+ if (length != NULL)
+ *length = aes->aes_mbs.length;
+ return (0);
+ }
+ ret = archive_strncpy_l(&(aes->aes_mbs_in_locale),
+ aes->aes_mbs.s, aes->aes_mbs.length, sc);
+ *p = aes->aes_mbs_in_locale.s;
+ if (length != NULL)
+ *length = aes->aes_mbs_in_locale.length;
+ } else {
+ *p = NULL;
+ if (length != NULL)
+ *length = 0;
+ }
+ return (ret);
+}
+
+int
+archive_mstring_copy_mbs(struct archive_mstring *aes, const char *mbs)
+{
+ if (mbs == NULL) {
+ aes->aes_set = 0;
+ return (0);
+ }
+ return (archive_mstring_copy_mbs_len(aes, mbs, strlen(mbs)));
+}
+
+int
+archive_mstring_copy_mbs_len(struct archive_mstring *aes, const char *mbs,
+ size_t len)
+{
+ if (mbs == NULL) {
+ aes->aes_set = 0;
+ return (0);
+ }
+ aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */
+ archive_strncpy(&(aes->aes_mbs), mbs, len);
+ archive_string_empty(&(aes->aes_utf8));
+ archive_wstring_empty(&(aes->aes_wcs));
+ return (0);
+}
+
+int
+archive_mstring_copy_wcs(struct archive_mstring *aes, const wchar_t *wcs)
+{
+ return archive_mstring_copy_wcs_len(aes, wcs,
+ wcs == NULL ? 0 : wcslen(wcs));
+}
+
+int
+archive_mstring_copy_utf8(struct archive_mstring *aes, const char *utf8)
+{
+ if (utf8 == NULL) {
+ aes->aes_set = 0;
+ return (0);
+ }
+ aes->aes_set = AES_SET_UTF8;
+ archive_string_empty(&(aes->aes_mbs));
+ archive_string_empty(&(aes->aes_wcs));
+ archive_strncpy(&(aes->aes_utf8), utf8, strlen(utf8));
+ return (int)strlen(utf8);
+}
+
+int
+archive_mstring_copy_wcs_len(struct archive_mstring *aes, const wchar_t *wcs,
+ size_t len)
+{
+ if (wcs == NULL) {
+ aes->aes_set = 0;
+ return (0);
+ }
+ aes->aes_set = AES_SET_WCS; /* Only WCS form set. */
+ archive_string_empty(&(aes->aes_mbs));
+ archive_string_empty(&(aes->aes_utf8));
+ archive_wstrncpy(&(aes->aes_wcs), wcs, len);
+ return (0);
+}
+
+int
+archive_mstring_copy_mbs_len_l(struct archive_mstring *aes,
+ const char *mbs, size_t len, struct archive_string_conv *sc)
+{
+ int r;
+
+ if (mbs == NULL) {
+ aes->aes_set = 0;
+ return (0);
+ }
+ archive_string_empty(&(aes->aes_mbs));
+ archive_wstring_empty(&(aes->aes_wcs));
+ archive_string_empty(&(aes->aes_utf8));
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /*
+ * Internationalization programming on Windows must use Wide
+ * characters because Windows platform cannot make locale UTF-8.
+ */
+ if (sc == NULL) {
+ if (archive_string_append(&(aes->aes_mbs),
+ mbs, mbsnbytes(mbs, len)) == NULL) {
+ aes->aes_set = 0;
+ r = -1;
+ } else {
+ aes->aes_set = AES_SET_MBS;
+ r = 0;
+ }
+#if defined(HAVE_ICONV)
+ } else if (sc != NULL && sc->cd_w != (iconv_t)-1) {
+ /*
+ * This case happens only when MultiByteToWideChar() cannot
+ * handle sc->from_cp, and we have to iconv in order to
+ * translate character-set to wchar_t,UTF-16.
+ */
+ iconv_t cd = sc->cd;
+ unsigned from_cp;
+ int flag;
+
+ /*
+ * Translate multi-bytes from some character-set to UTF-8.
+ */
+ sc->cd = sc->cd_w;
+ r = archive_strncpy_l(&(aes->aes_utf8), mbs, len, sc);
+ sc->cd = cd;
+ if (r != 0) {
+ aes->aes_set = 0;
+ return (r);
+ }
+ aes->aes_set = AES_SET_UTF8;
+
+ /*
+ * Append the UTF-8 string into wstring.
+ */
+ flag = sc->flag;
+ sc->flag &= ~(SCONV_NORMALIZATION_C
+ | SCONV_TO_UTF16| SCONV_FROM_UTF16);
+ from_cp = sc->from_cp;
+ sc->from_cp = CP_UTF8;
+ r = archive_wstring_append_from_mbs_in_codepage(&(aes->aes_wcs),
+ aes->aes_utf8.s, aes->aes_utf8.length, sc);
+ sc->flag = flag;
+ sc->from_cp = from_cp;
+ if (r == 0)
+ aes->aes_set |= AES_SET_WCS;
+#endif
+ } else {
+ r = archive_wstring_append_from_mbs_in_codepage(
+ &(aes->aes_wcs), mbs, len, sc);
+ if (r == 0)
+ aes->aes_set = AES_SET_WCS;
+ else
+ aes->aes_set = 0;
+ }
+#else
+ r = archive_strncpy_l(&(aes->aes_mbs), mbs, len, sc);
+ if (r == 0)
+ aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */
+ else
+ aes->aes_set = 0;
+#endif
+ return (r);
+}
+
+/*
+ * The 'update' form tries to proactively update all forms of
+ * this string (WCS and MBS) and returns an error if any of
+ * them fail. This is used by the 'pax' handler, for instance,
+ * to detect and report character-conversion failures early while
+ * still allowing clients to get potentially useful values from
+ * the more tolerant lazy conversions. (get_mbs and get_wcs will
+ * strive to give the user something useful, so you can get hopefully
+ * usable values even if some of the character conversions are failing.)
+ */
+int
+archive_mstring_update_utf8(struct archive *a, struct archive_mstring *aes,
+ const char *utf8)
+{
+ struct archive_string_conv *sc;
+ int r;
+
+ if (utf8 == NULL) {
+ aes->aes_set = 0;
+ return (0); /* Succeeded in clearing everything. */
+ }
+
+ /* Save the UTF8 string. */
+ archive_strcpy(&(aes->aes_utf8), utf8);
+
+ /* Empty the mbs and wcs strings. */
+ archive_string_empty(&(aes->aes_mbs));
+ archive_wstring_empty(&(aes->aes_wcs));
+
+ aes->aes_set = AES_SET_UTF8; /* Only UTF8 is set now. */
+
+ /* Try converting UTF-8 to MBS, return false on failure. */
+ sc = archive_string_conversion_from_charset(a, "UTF-8", 1);
+ if (sc == NULL)
+ return (-1);/* Couldn't allocate memory for sc. */
+ r = archive_strcpy_l(&(aes->aes_mbs), utf8, sc);
+ if (a == NULL)
+ free_sconv_object(sc);
+ if (r != 0)
+ return (-1);
+ aes->aes_set = AES_SET_UTF8 | AES_SET_MBS; /* Both UTF8 and MBS set. */
+
+ /* Try converting MBS to WCS, return false on failure. */
+ if (archive_wstring_append_from_mbs(&(aes->aes_wcs), aes->aes_mbs.s,
+ aes->aes_mbs.length))
+ return (-1);
+ aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS;
+
+ /* All conversions succeeded. */
+ return (0);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_string.h b/contrib/libs/libarchive/libarchive/archive_string.h
new file mode 100644
index 00000000000..49d7d3064a3
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_string.h
@@ -0,0 +1,243 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_string.h 201092 2009-12-28 02:26:06Z kientzle $
+ *
+ */
+
+#ifndef ARCHIVE_STRING_H_INCLUDED
+#define ARCHIVE_STRING_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+#include <stdarg.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h> /* required for wchar_t on some systems */
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+
+#include "archive.h"
+
+/*
+ * Basic resizable/reusable string support similar to Java's "StringBuffer."
+ *
+ * Unlike sbuf(9), the buffers here are fully reusable and track the
+ * length throughout.
+ */
+
+struct archive_string {
+ char *s; /* Pointer to the storage */
+ size_t length; /* Length of 's' in characters */
+ size_t buffer_length; /* Length of malloc-ed storage in bytes. */
+};
+
+struct archive_wstring {
+ wchar_t *s; /* Pointer to the storage */
+ size_t length; /* Length of 's' in characters */
+ size_t buffer_length; /* Length of malloc-ed storage in bytes. */
+};
+
+struct archive_string_conv;
+
+/* Initialize an archive_string object on the stack or elsewhere. */
+#define archive_string_init(a) \
+ do { (a)->s = NULL; (a)->length = 0; (a)->buffer_length = 0; } while(0)
+
+/* Append a C char to an archive_string, resizing as necessary. */
+struct archive_string *
+archive_strappend_char(struct archive_string *, char);
+
+/* Ditto for a wchar_t and an archive_wstring. */
+struct archive_wstring *
+archive_wstrappend_wchar(struct archive_wstring *, wchar_t);
+
+/* Append a raw array to an archive_string, resizing as necessary */
+struct archive_string *
+archive_array_append(struct archive_string *, const char *, size_t);
+
+/* Convert a Unicode string to current locale and append the result. */
+/* Returns -1 if conversion fails. */
+int
+archive_string_append_from_wcs(struct archive_string *, const wchar_t *, size_t);
+
+
+/* Create a string conversion object.
+ * Return NULL and set a error message if the conversion is not supported
+ * on the platform. */
+struct archive_string_conv *
+archive_string_conversion_to_charset(struct archive *, const char *, int);
+struct archive_string_conv *
+archive_string_conversion_from_charset(struct archive *, const char *, int);
+/* Create the default string conversion object for reading/writing an archive.
+ * Return NULL if the conversion is unneeded.
+ * Note: On non Windows platform this always returns NULL.
+ */
+struct archive_string_conv *
+archive_string_default_conversion_for_read(struct archive *);
+struct archive_string_conv *
+archive_string_default_conversion_for_write(struct archive *);
+/* Dispose of a string conversion object. */
+void
+archive_string_conversion_free(struct archive *);
+const char *
+archive_string_conversion_charset_name(struct archive_string_conv *);
+void
+archive_string_conversion_set_opt(struct archive_string_conv *, int);
+#define SCONV_SET_OPT_UTF8_LIBARCHIVE2X 1
+#define SCONV_SET_OPT_NORMALIZATION_C 2
+#define SCONV_SET_OPT_NORMALIZATION_D 4
+
+
+/* Copy one archive_string to another in locale conversion.
+ * Return -1 if conversion fails. */
+int
+archive_strncpy_l(struct archive_string *, const void *, size_t,
+ struct archive_string_conv *);
+
+/* Copy one archive_string to another in locale conversion.
+ * Return -1 if conversion fails. */
+int
+archive_strncat_l(struct archive_string *, const void *, size_t,
+ struct archive_string_conv *);
+
+
+/* Copy one archive_string to another */
+#define archive_string_copy(dest, src) \
+ ((dest)->length = 0, archive_string_concat((dest), (src)))
+#define archive_wstring_copy(dest, src) \
+ ((dest)->length = 0, archive_wstring_concat((dest), (src)))
+
+/* Concatenate one archive_string to another */
+void archive_string_concat(struct archive_string *dest, struct archive_string *src);
+void archive_wstring_concat(struct archive_wstring *dest, struct archive_wstring *src);
+
+/* Ensure that the underlying buffer is at least as large as the request. */
+struct archive_string *
+archive_string_ensure(struct archive_string *, size_t);
+struct archive_wstring *
+archive_wstring_ensure(struct archive_wstring *, size_t);
+
+/* Append C string, which may lack trailing \0. */
+/* The source is declared void * here because this gets used with
+ * "signed char *", "unsigned char *" and "char *" arguments.
+ * Declaring it "char *" as with some of the other functions just
+ * leads to a lot of extra casts. */
+struct archive_string *
+archive_strncat(struct archive_string *, const void *, size_t);
+struct archive_wstring *
+archive_wstrncat(struct archive_wstring *, const wchar_t *, size_t);
+
+/* Append a C string to an archive_string, resizing as necessary. */
+struct archive_string *
+archive_strcat(struct archive_string *, const void *);
+struct archive_wstring *
+archive_wstrcat(struct archive_wstring *, const wchar_t *);
+
+/* Copy a C string to an archive_string, resizing as necessary. */
+#define archive_strcpy(as,p) \
+ archive_strncpy((as), (p), ((p) == NULL ? 0 : strlen(p)))
+#define archive_wstrcpy(as,p) \
+ archive_wstrncpy((as), (p), ((p) == NULL ? 0 : wcslen(p)))
+#define archive_strcpy_l(as,p,lo) \
+ archive_strncpy_l((as), (p), ((p) == NULL ? 0 : strlen(p)), (lo))
+
+/* Copy a C string to an archive_string with limit, resizing as necessary. */
+#define archive_strncpy(as,p,l) \
+ ((as)->length=0, archive_strncat((as), (p), (l)))
+#define archive_wstrncpy(as,p,l) \
+ ((as)->length = 0, archive_wstrncat((as), (p), (l)))
+
+/* Return length of string. */
+#define archive_strlen(a) ((a)->length)
+
+/* Set string length to zero. */
+#define archive_string_empty(a) ((a)->length = 0)
+#define archive_wstring_empty(a) ((a)->length = 0)
+
+/* Release any allocated storage resources. */
+void archive_string_free(struct archive_string *);
+void archive_wstring_free(struct archive_wstring *);
+
+/* Like 'vsprintf', but resizes the underlying string as necessary. */
+/* Note: This only implements a small subset of standard printf functionality. */
+void archive_string_vsprintf(struct archive_string *, const char *,
+ va_list) __LA_PRINTF(2, 0);
+void archive_string_sprintf(struct archive_string *, const char *, ...)
+ __LA_PRINTF(2, 3);
+
+/* Translates from MBS to Unicode. */
+/* Returns non-zero if conversion failed in any way. */
+int archive_wstring_append_from_mbs(struct archive_wstring *dest,
+ const char *, size_t);
+
+
+/* A "multistring" can hold Unicode, UTF8, or MBS versions of
+ * the string. If you set and read the same version, no translation
+ * is done. If you set and read different versions, the library
+ * will attempt to transparently convert.
+ */
+struct archive_mstring {
+ struct archive_string aes_mbs;
+ struct archive_string aes_utf8;
+ struct archive_wstring aes_wcs;
+ struct archive_string aes_mbs_in_locale;
+ /* Bitmap of which of the above are valid. Because we're lazy
+ * about malloc-ing and reusing the underlying storage, we
+ * can't rely on NULL pointers to indicate whether a string
+ * has been set. */
+ int aes_set;
+#define AES_SET_MBS 1
+#define AES_SET_UTF8 2
+#define AES_SET_WCS 4
+};
+
+void archive_mstring_clean(struct archive_mstring *);
+void archive_mstring_copy(struct archive_mstring *dest, struct archive_mstring *src);
+int archive_mstring_get_mbs(struct archive *, struct archive_mstring *, const char **);
+int archive_mstring_get_utf8(struct archive *, struct archive_mstring *, const char **);
+int archive_mstring_get_wcs(struct archive *, struct archive_mstring *, const wchar_t **);
+int archive_mstring_get_mbs_l(struct archive *, struct archive_mstring *, const char **,
+ size_t *, struct archive_string_conv *);
+int archive_mstring_copy_mbs(struct archive_mstring *, const char *mbs);
+int archive_mstring_copy_mbs_len(struct archive_mstring *, const char *mbs,
+ size_t);
+int archive_mstring_copy_utf8(struct archive_mstring *, const char *utf8);
+int archive_mstring_copy_wcs(struct archive_mstring *, const wchar_t *wcs);
+int archive_mstring_copy_wcs_len(struct archive_mstring *,
+ const wchar_t *wcs, size_t);
+int archive_mstring_copy_mbs_len_l(struct archive_mstring *,
+ const char *mbs, size_t, struct archive_string_conv *);
+int archive_mstring_update_utf8(struct archive *, struct archive_mstring *aes, const char *utf8);
+
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_string_composition.h b/contrib/libs/libarchive/libarchive/archive_string_composition.h
new file mode 100644
index 00000000000..d0ac340961a
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_string_composition.h
@@ -0,0 +1,2292 @@
+/*-
+ * Copyright (c) 2011-2012 libarchive Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * ATTENTION!
+ * This file is generated by build/utils/gen_archive_string_composition_h.sh
+ * from http://unicode.org/Public/6.0.0/ucd/UnicodeData.txt
+ *
+ * See also http://unicode.org/report/tr15/
+ */
+
+#ifndef ARCHIVE_STRING_COMPOSITION_H_INCLUDED
+#define ARCHIVE_STRING_COMPOSITION_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+struct unicode_composition_table {
+ uint32_t cp1;
+ uint32_t cp2;
+ uint32_t nfc;
+};
+
+static const struct unicode_composition_table u_composition_table[] = {
+ { 0x0003C , 0x00338 , 0x0226E },
+ { 0x0003D , 0x00338 , 0x02260 },
+ { 0x0003E , 0x00338 , 0x0226F },
+ { 0x00041 , 0x00300 , 0x000C0 },
+ { 0x00041 , 0x00301 , 0x000C1 },
+ { 0x00041 , 0x00302 , 0x000C2 },
+ { 0x00041 , 0x00303 , 0x000C3 },
+ { 0x00041 , 0x00304 , 0x00100 },
+ { 0x00041 , 0x00306 , 0x00102 },
+ { 0x00041 , 0x00307 , 0x00226 },
+ { 0x00041 , 0x00308 , 0x000C4 },
+ { 0x00041 , 0x00309 , 0x01EA2 },
+ { 0x00041 , 0x0030A , 0x000C5 },
+ { 0x00041 , 0x0030C , 0x001CD },
+ { 0x00041 , 0x0030F , 0x00200 },
+ { 0x00041 , 0x00311 , 0x00202 },
+ { 0x00041 , 0x00323 , 0x01EA0 },
+ { 0x00041 , 0x00325 , 0x01E00 },
+ { 0x00041 , 0x00328 , 0x00104 },
+ { 0x00042 , 0x00307 , 0x01E02 },
+ { 0x00042 , 0x00323 , 0x01E04 },
+ { 0x00042 , 0x00331 , 0x01E06 },
+ { 0x00043 , 0x00301 , 0x00106 },
+ { 0x00043 , 0x00302 , 0x00108 },
+ { 0x00043 , 0x00307 , 0x0010A },
+ { 0x00043 , 0x0030C , 0x0010C },
+ { 0x00043 , 0x00327 , 0x000C7 },
+ { 0x00044 , 0x00307 , 0x01E0A },
+ { 0x00044 , 0x0030C , 0x0010E },
+ { 0x00044 , 0x00323 , 0x01E0C },
+ { 0x00044 , 0x00327 , 0x01E10 },
+ { 0x00044 , 0x0032D , 0x01E12 },
+ { 0x00044 , 0x00331 , 0x01E0E },
+ { 0x00045 , 0x00300 , 0x000C8 },
+ { 0x00045 , 0x00301 , 0x000C9 },
+ { 0x00045 , 0x00302 , 0x000CA },
+ { 0x00045 , 0x00303 , 0x01EBC },
+ { 0x00045 , 0x00304 , 0x00112 },
+ { 0x00045 , 0x00306 , 0x00114 },
+ { 0x00045 , 0x00307 , 0x00116 },
+ { 0x00045 , 0x00308 , 0x000CB },
+ { 0x00045 , 0x00309 , 0x01EBA },
+ { 0x00045 , 0x0030C , 0x0011A },
+ { 0x00045 , 0x0030F , 0x00204 },
+ { 0x00045 , 0x00311 , 0x00206 },
+ { 0x00045 , 0x00323 , 0x01EB8 },
+ { 0x00045 , 0x00327 , 0x00228 },
+ { 0x00045 , 0x00328 , 0x00118 },
+ { 0x00045 , 0x0032D , 0x01E18 },
+ { 0x00045 , 0x00330 , 0x01E1A },
+ { 0x00046 , 0x00307 , 0x01E1E },
+ { 0x00047 , 0x00301 , 0x001F4 },
+ { 0x00047 , 0x00302 , 0x0011C },
+ { 0x00047 , 0x00304 , 0x01E20 },
+ { 0x00047 , 0x00306 , 0x0011E },
+ { 0x00047 , 0x00307 , 0x00120 },
+ { 0x00047 , 0x0030C , 0x001E6 },
+ { 0x00047 , 0x00327 , 0x00122 },
+ { 0x00048 , 0x00302 , 0x00124 },
+ { 0x00048 , 0x00307 , 0x01E22 },
+ { 0x00048 , 0x00308 , 0x01E26 },
+ { 0x00048 , 0x0030C , 0x0021E },
+ { 0x00048 , 0x00323 , 0x01E24 },
+ { 0x00048 , 0x00327 , 0x01E28 },
+ { 0x00048 , 0x0032E , 0x01E2A },
+ { 0x00049 , 0x00300 , 0x000CC },
+ { 0x00049 , 0x00301 , 0x000CD },
+ { 0x00049 , 0x00302 , 0x000CE },
+ { 0x00049 , 0x00303 , 0x00128 },
+ { 0x00049 , 0x00304 , 0x0012A },
+ { 0x00049 , 0x00306 , 0x0012C },
+ { 0x00049 , 0x00307 , 0x00130 },
+ { 0x00049 , 0x00308 , 0x000CF },
+ { 0x00049 , 0x00309 , 0x01EC8 },
+ { 0x00049 , 0x0030C , 0x001CF },
+ { 0x00049 , 0x0030F , 0x00208 },
+ { 0x00049 , 0x00311 , 0x0020A },
+ { 0x00049 , 0x00323 , 0x01ECA },
+ { 0x00049 , 0x00328 , 0x0012E },
+ { 0x00049 , 0x00330 , 0x01E2C },
+ { 0x0004A , 0x00302 , 0x00134 },
+ { 0x0004B , 0x00301 , 0x01E30 },
+ { 0x0004B , 0x0030C , 0x001E8 },
+ { 0x0004B , 0x00323 , 0x01E32 },
+ { 0x0004B , 0x00327 , 0x00136 },
+ { 0x0004B , 0x00331 , 0x01E34 },
+ { 0x0004C , 0x00301 , 0x00139 },
+ { 0x0004C , 0x0030C , 0x0013D },
+ { 0x0004C , 0x00323 , 0x01E36 },
+ { 0x0004C , 0x00327 , 0x0013B },
+ { 0x0004C , 0x0032D , 0x01E3C },
+ { 0x0004C , 0x00331 , 0x01E3A },
+ { 0x0004D , 0x00301 , 0x01E3E },
+ { 0x0004D , 0x00307 , 0x01E40 },
+ { 0x0004D , 0x00323 , 0x01E42 },
+ { 0x0004E , 0x00300 , 0x001F8 },
+ { 0x0004E , 0x00301 , 0x00143 },
+ { 0x0004E , 0x00303 , 0x000D1 },
+ { 0x0004E , 0x00307 , 0x01E44 },
+ { 0x0004E , 0x0030C , 0x00147 },
+ { 0x0004E , 0x00323 , 0x01E46 },
+ { 0x0004E , 0x00327 , 0x00145 },
+ { 0x0004E , 0x0032D , 0x01E4A },
+ { 0x0004E , 0x00331 , 0x01E48 },
+ { 0x0004F , 0x00300 , 0x000D2 },
+ { 0x0004F , 0x00301 , 0x000D3 },
+ { 0x0004F , 0x00302 , 0x000D4 },
+ { 0x0004F , 0x00303 , 0x000D5 },
+ { 0x0004F , 0x00304 , 0x0014C },
+ { 0x0004F , 0x00306 , 0x0014E },
+ { 0x0004F , 0x00307 , 0x0022E },
+ { 0x0004F , 0x00308 , 0x000D6 },
+ { 0x0004F , 0x00309 , 0x01ECE },
+ { 0x0004F , 0x0030B , 0x00150 },
+ { 0x0004F , 0x0030C , 0x001D1 },
+ { 0x0004F , 0x0030F , 0x0020C },
+ { 0x0004F , 0x00311 , 0x0020E },
+ { 0x0004F , 0x0031B , 0x001A0 },
+ { 0x0004F , 0x00323 , 0x01ECC },
+ { 0x0004F , 0x00328 , 0x001EA },
+ { 0x00050 , 0x00301 , 0x01E54 },
+ { 0x00050 , 0x00307 , 0x01E56 },
+ { 0x00052 , 0x00301 , 0x00154 },
+ { 0x00052 , 0x00307 , 0x01E58 },
+ { 0x00052 , 0x0030C , 0x00158 },
+ { 0x00052 , 0x0030F , 0x00210 },
+ { 0x00052 , 0x00311 , 0x00212 },
+ { 0x00052 , 0x00323 , 0x01E5A },
+ { 0x00052 , 0x00327 , 0x00156 },
+ { 0x00052 , 0x00331 , 0x01E5E },
+ { 0x00053 , 0x00301 , 0x0015A },
+ { 0x00053 , 0x00302 , 0x0015C },
+ { 0x00053 , 0x00307 , 0x01E60 },
+ { 0x00053 , 0x0030C , 0x00160 },
+ { 0x00053 , 0x00323 , 0x01E62 },
+ { 0x00053 , 0x00326 , 0x00218 },
+ { 0x00053 , 0x00327 , 0x0015E },
+ { 0x00054 , 0x00307 , 0x01E6A },
+ { 0x00054 , 0x0030C , 0x00164 },
+ { 0x00054 , 0x00323 , 0x01E6C },
+ { 0x00054 , 0x00326 , 0x0021A },
+ { 0x00054 , 0x00327 , 0x00162 },
+ { 0x00054 , 0x0032D , 0x01E70 },
+ { 0x00054 , 0x00331 , 0x01E6E },
+ { 0x00055 , 0x00300 , 0x000D9 },
+ { 0x00055 , 0x00301 , 0x000DA },
+ { 0x00055 , 0x00302 , 0x000DB },
+ { 0x00055 , 0x00303 , 0x00168 },
+ { 0x00055 , 0x00304 , 0x0016A },
+ { 0x00055 , 0x00306 , 0x0016C },
+ { 0x00055 , 0x00308 , 0x000DC },
+ { 0x00055 , 0x00309 , 0x01EE6 },
+ { 0x00055 , 0x0030A , 0x0016E },
+ { 0x00055 , 0x0030B , 0x00170 },
+ { 0x00055 , 0x0030C , 0x001D3 },
+ { 0x00055 , 0x0030F , 0x00214 },
+ { 0x00055 , 0x00311 , 0x00216 },
+ { 0x00055 , 0x0031B , 0x001AF },
+ { 0x00055 , 0x00323 , 0x01EE4 },
+ { 0x00055 , 0x00324 , 0x01E72 },
+ { 0x00055 , 0x00328 , 0x00172 },
+ { 0x00055 , 0x0032D , 0x01E76 },
+ { 0x00055 , 0x00330 , 0x01E74 },
+ { 0x00056 , 0x00303 , 0x01E7C },
+ { 0x00056 , 0x00323 , 0x01E7E },
+ { 0x00057 , 0x00300 , 0x01E80 },
+ { 0x00057 , 0x00301 , 0x01E82 },
+ { 0x00057 , 0x00302 , 0x00174 },
+ { 0x00057 , 0x00307 , 0x01E86 },
+ { 0x00057 , 0x00308 , 0x01E84 },
+ { 0x00057 , 0x00323 , 0x01E88 },
+ { 0x00058 , 0x00307 , 0x01E8A },
+ { 0x00058 , 0x00308 , 0x01E8C },
+ { 0x00059 , 0x00300 , 0x01EF2 },
+ { 0x00059 , 0x00301 , 0x000DD },
+ { 0x00059 , 0x00302 , 0x00176 },
+ { 0x00059 , 0x00303 , 0x01EF8 },
+ { 0x00059 , 0x00304 , 0x00232 },
+ { 0x00059 , 0x00307 , 0x01E8E },
+ { 0x00059 , 0x00308 , 0x00178 },
+ { 0x00059 , 0x00309 , 0x01EF6 },
+ { 0x00059 , 0x00323 , 0x01EF4 },
+ { 0x0005A , 0x00301 , 0x00179 },
+ { 0x0005A , 0x00302 , 0x01E90 },
+ { 0x0005A , 0x00307 , 0x0017B },
+ { 0x0005A , 0x0030C , 0x0017D },
+ { 0x0005A , 0x00323 , 0x01E92 },
+ { 0x0005A , 0x00331 , 0x01E94 },
+ { 0x00061 , 0x00300 , 0x000E0 },
+ { 0x00061 , 0x00301 , 0x000E1 },
+ { 0x00061 , 0x00302 , 0x000E2 },
+ { 0x00061 , 0x00303 , 0x000E3 },
+ { 0x00061 , 0x00304 , 0x00101 },
+ { 0x00061 , 0x00306 , 0x00103 },
+ { 0x00061 , 0x00307 , 0x00227 },
+ { 0x00061 , 0x00308 , 0x000E4 },
+ { 0x00061 , 0x00309 , 0x01EA3 },
+ { 0x00061 , 0x0030A , 0x000E5 },
+ { 0x00061 , 0x0030C , 0x001CE },
+ { 0x00061 , 0x0030F , 0x00201 },
+ { 0x00061 , 0x00311 , 0x00203 },
+ { 0x00061 , 0x00323 , 0x01EA1 },
+ { 0x00061 , 0x00325 , 0x01E01 },
+ { 0x00061 , 0x00328 , 0x00105 },
+ { 0x00062 , 0x00307 , 0x01E03 },
+ { 0x00062 , 0x00323 , 0x01E05 },
+ { 0x00062 , 0x00331 , 0x01E07 },
+ { 0x00063 , 0x00301 , 0x00107 },
+ { 0x00063 , 0x00302 , 0x00109 },
+ { 0x00063 , 0x00307 , 0x0010B },
+ { 0x00063 , 0x0030C , 0x0010D },
+ { 0x00063 , 0x00327 , 0x000E7 },
+ { 0x00064 , 0x00307 , 0x01E0B },
+ { 0x00064 , 0x0030C , 0x0010F },
+ { 0x00064 , 0x00323 , 0x01E0D },
+ { 0x00064 , 0x00327 , 0x01E11 },
+ { 0x00064 , 0x0032D , 0x01E13 },
+ { 0x00064 , 0x00331 , 0x01E0F },
+ { 0x00065 , 0x00300 , 0x000E8 },
+ { 0x00065 , 0x00301 , 0x000E9 },
+ { 0x00065 , 0x00302 , 0x000EA },
+ { 0x00065 , 0x00303 , 0x01EBD },
+ { 0x00065 , 0x00304 , 0x00113 },
+ { 0x00065 , 0x00306 , 0x00115 },
+ { 0x00065 , 0x00307 , 0x00117 },
+ { 0x00065 , 0x00308 , 0x000EB },
+ { 0x00065 , 0x00309 , 0x01EBB },
+ { 0x00065 , 0x0030C , 0x0011B },
+ { 0x00065 , 0x0030F , 0x00205 },
+ { 0x00065 , 0x00311 , 0x00207 },
+ { 0x00065 , 0x00323 , 0x01EB9 },
+ { 0x00065 , 0x00327 , 0x00229 },
+ { 0x00065 , 0x00328 , 0x00119 },
+ { 0x00065 , 0x0032D , 0x01E19 },
+ { 0x00065 , 0x00330 , 0x01E1B },
+ { 0x00066 , 0x00307 , 0x01E1F },
+ { 0x00067 , 0x00301 , 0x001F5 },
+ { 0x00067 , 0x00302 , 0x0011D },
+ { 0x00067 , 0x00304 , 0x01E21 },
+ { 0x00067 , 0x00306 , 0x0011F },
+ { 0x00067 , 0x00307 , 0x00121 },
+ { 0x00067 , 0x0030C , 0x001E7 },
+ { 0x00067 , 0x00327 , 0x00123 },
+ { 0x00068 , 0x00302 , 0x00125 },
+ { 0x00068 , 0x00307 , 0x01E23 },
+ { 0x00068 , 0x00308 , 0x01E27 },
+ { 0x00068 , 0x0030C , 0x0021F },
+ { 0x00068 , 0x00323 , 0x01E25 },
+ { 0x00068 , 0x00327 , 0x01E29 },
+ { 0x00068 , 0x0032E , 0x01E2B },
+ { 0x00068 , 0x00331 , 0x01E96 },
+ { 0x00069 , 0x00300 , 0x000EC },
+ { 0x00069 , 0x00301 , 0x000ED },
+ { 0x00069 , 0x00302 , 0x000EE },
+ { 0x00069 , 0x00303 , 0x00129 },
+ { 0x00069 , 0x00304 , 0x0012B },
+ { 0x00069 , 0x00306 , 0x0012D },
+ { 0x00069 , 0x00308 , 0x000EF },
+ { 0x00069 , 0x00309 , 0x01EC9 },
+ { 0x00069 , 0x0030C , 0x001D0 },
+ { 0x00069 , 0x0030F , 0x00209 },
+ { 0x00069 , 0x00311 , 0x0020B },
+ { 0x00069 , 0x00323 , 0x01ECB },
+ { 0x00069 , 0x00328 , 0x0012F },
+ { 0x00069 , 0x00330 , 0x01E2D },
+ { 0x0006A , 0x00302 , 0x00135 },
+ { 0x0006A , 0x0030C , 0x001F0 },
+ { 0x0006B , 0x00301 , 0x01E31 },
+ { 0x0006B , 0x0030C , 0x001E9 },
+ { 0x0006B , 0x00323 , 0x01E33 },
+ { 0x0006B , 0x00327 , 0x00137 },
+ { 0x0006B , 0x00331 , 0x01E35 },
+ { 0x0006C , 0x00301 , 0x0013A },
+ { 0x0006C , 0x0030C , 0x0013E },
+ { 0x0006C , 0x00323 , 0x01E37 },
+ { 0x0006C , 0x00327 , 0x0013C },
+ { 0x0006C , 0x0032D , 0x01E3D },
+ { 0x0006C , 0x00331 , 0x01E3B },
+ { 0x0006D , 0x00301 , 0x01E3F },
+ { 0x0006D , 0x00307 , 0x01E41 },
+ { 0x0006D , 0x00323 , 0x01E43 },
+ { 0x0006E , 0x00300 , 0x001F9 },
+ { 0x0006E , 0x00301 , 0x00144 },
+ { 0x0006E , 0x00303 , 0x000F1 },
+ { 0x0006E , 0x00307 , 0x01E45 },
+ { 0x0006E , 0x0030C , 0x00148 },
+ { 0x0006E , 0x00323 , 0x01E47 },
+ { 0x0006E , 0x00327 , 0x00146 },
+ { 0x0006E , 0x0032D , 0x01E4B },
+ { 0x0006E , 0x00331 , 0x01E49 },
+ { 0x0006F , 0x00300 , 0x000F2 },
+ { 0x0006F , 0x00301 , 0x000F3 },
+ { 0x0006F , 0x00302 , 0x000F4 },
+ { 0x0006F , 0x00303 , 0x000F5 },
+ { 0x0006F , 0x00304 , 0x0014D },
+ { 0x0006F , 0x00306 , 0x0014F },
+ { 0x0006F , 0x00307 , 0x0022F },
+ { 0x0006F , 0x00308 , 0x000F6 },
+ { 0x0006F , 0x00309 , 0x01ECF },
+ { 0x0006F , 0x0030B , 0x00151 },
+ { 0x0006F , 0x0030C , 0x001D2 },
+ { 0x0006F , 0x0030F , 0x0020D },
+ { 0x0006F , 0x00311 , 0x0020F },
+ { 0x0006F , 0x0031B , 0x001A1 },
+ { 0x0006F , 0x00323 , 0x01ECD },
+ { 0x0006F , 0x00328 , 0x001EB },
+ { 0x00070 , 0x00301 , 0x01E55 },
+ { 0x00070 , 0x00307 , 0x01E57 },
+ { 0x00072 , 0x00301 , 0x00155 },
+ { 0x00072 , 0x00307 , 0x01E59 },
+ { 0x00072 , 0x0030C , 0x00159 },
+ { 0x00072 , 0x0030F , 0x00211 },
+ { 0x00072 , 0x00311 , 0x00213 },
+ { 0x00072 , 0x00323 , 0x01E5B },
+ { 0x00072 , 0x00327 , 0x00157 },
+ { 0x00072 , 0x00331 , 0x01E5F },
+ { 0x00073 , 0x00301 , 0x0015B },
+ { 0x00073 , 0x00302 , 0x0015D },
+ { 0x00073 , 0x00307 , 0x01E61 },
+ { 0x00073 , 0x0030C , 0x00161 },
+ { 0x00073 , 0x00323 , 0x01E63 },
+ { 0x00073 , 0x00326 , 0x00219 },
+ { 0x00073 , 0x00327 , 0x0015F },
+ { 0x00074 , 0x00307 , 0x01E6B },
+ { 0x00074 , 0x00308 , 0x01E97 },
+ { 0x00074 , 0x0030C , 0x00165 },
+ { 0x00074 , 0x00323 , 0x01E6D },
+ { 0x00074 , 0x00326 , 0x0021B },
+ { 0x00074 , 0x00327 , 0x00163 },
+ { 0x00074 , 0x0032D , 0x01E71 },
+ { 0x00074 , 0x00331 , 0x01E6F },
+ { 0x00075 , 0x00300 , 0x000F9 },
+ { 0x00075 , 0x00301 , 0x000FA },
+ { 0x00075 , 0x00302 , 0x000FB },
+ { 0x00075 , 0x00303 , 0x00169 },
+ { 0x00075 , 0x00304 , 0x0016B },
+ { 0x00075 , 0x00306 , 0x0016D },
+ { 0x00075 , 0x00308 , 0x000FC },
+ { 0x00075 , 0x00309 , 0x01EE7 },
+ { 0x00075 , 0x0030A , 0x0016F },
+ { 0x00075 , 0x0030B , 0x00171 },
+ { 0x00075 , 0x0030C , 0x001D4 },
+ { 0x00075 , 0x0030F , 0x00215 },
+ { 0x00075 , 0x00311 , 0x00217 },
+ { 0x00075 , 0x0031B , 0x001B0 },
+ { 0x00075 , 0x00323 , 0x01EE5 },
+ { 0x00075 , 0x00324 , 0x01E73 },
+ { 0x00075 , 0x00328 , 0x00173 },
+ { 0x00075 , 0x0032D , 0x01E77 },
+ { 0x00075 , 0x00330 , 0x01E75 },
+ { 0x00076 , 0x00303 , 0x01E7D },
+ { 0x00076 , 0x00323 , 0x01E7F },
+ { 0x00077 , 0x00300 , 0x01E81 },
+ { 0x00077 , 0x00301 , 0x01E83 },
+ { 0x00077 , 0x00302 , 0x00175 },
+ { 0x00077 , 0x00307 , 0x01E87 },
+ { 0x00077 , 0x00308 , 0x01E85 },
+ { 0x00077 , 0x0030A , 0x01E98 },
+ { 0x00077 , 0x00323 , 0x01E89 },
+ { 0x00078 , 0x00307 , 0x01E8B },
+ { 0x00078 , 0x00308 , 0x01E8D },
+ { 0x00079 , 0x00300 , 0x01EF3 },
+ { 0x00079 , 0x00301 , 0x000FD },
+ { 0x00079 , 0x00302 , 0x00177 },
+ { 0x00079 , 0x00303 , 0x01EF9 },
+ { 0x00079 , 0x00304 , 0x00233 },
+ { 0x00079 , 0x00307 , 0x01E8F },
+ { 0x00079 , 0x00308 , 0x000FF },
+ { 0x00079 , 0x00309 , 0x01EF7 },
+ { 0x00079 , 0x0030A , 0x01E99 },
+ { 0x00079 , 0x00323 , 0x01EF5 },
+ { 0x0007A , 0x00301 , 0x0017A },
+ { 0x0007A , 0x00302 , 0x01E91 },
+ { 0x0007A , 0x00307 , 0x0017C },
+ { 0x0007A , 0x0030C , 0x0017E },
+ { 0x0007A , 0x00323 , 0x01E93 },
+ { 0x0007A , 0x00331 , 0x01E95 },
+ { 0x000A8 , 0x00300 , 0x01FED },
+ { 0x000A8 , 0x00301 , 0x00385 },
+ { 0x000A8 , 0x00342 , 0x01FC1 },
+ { 0x000C2 , 0x00300 , 0x01EA6 },
+ { 0x000C2 , 0x00301 , 0x01EA4 },
+ { 0x000C2 , 0x00303 , 0x01EAA },
+ { 0x000C2 , 0x00309 , 0x01EA8 },
+ { 0x000C4 , 0x00304 , 0x001DE },
+ { 0x000C5 , 0x00301 , 0x001FA },
+ { 0x000C6 , 0x00301 , 0x001FC },
+ { 0x000C6 , 0x00304 , 0x001E2 },
+ { 0x000C7 , 0x00301 , 0x01E08 },
+ { 0x000CA , 0x00300 , 0x01EC0 },
+ { 0x000CA , 0x00301 , 0x01EBE },
+ { 0x000CA , 0x00303 , 0x01EC4 },
+ { 0x000CA , 0x00309 , 0x01EC2 },
+ { 0x000CF , 0x00301 , 0x01E2E },
+ { 0x000D4 , 0x00300 , 0x01ED2 },
+ { 0x000D4 , 0x00301 , 0x01ED0 },
+ { 0x000D4 , 0x00303 , 0x01ED6 },
+ { 0x000D4 , 0x00309 , 0x01ED4 },
+ { 0x000D5 , 0x00301 , 0x01E4C },
+ { 0x000D5 , 0x00304 , 0x0022C },
+ { 0x000D5 , 0x00308 , 0x01E4E },
+ { 0x000D6 , 0x00304 , 0x0022A },
+ { 0x000D8 , 0x00301 , 0x001FE },
+ { 0x000DC , 0x00300 , 0x001DB },
+ { 0x000DC , 0x00301 , 0x001D7 },
+ { 0x000DC , 0x00304 , 0x001D5 },
+ { 0x000DC , 0x0030C , 0x001D9 },
+ { 0x000E2 , 0x00300 , 0x01EA7 },
+ { 0x000E2 , 0x00301 , 0x01EA5 },
+ { 0x000E2 , 0x00303 , 0x01EAB },
+ { 0x000E2 , 0x00309 , 0x01EA9 },
+ { 0x000E4 , 0x00304 , 0x001DF },
+ { 0x000E5 , 0x00301 , 0x001FB },
+ { 0x000E6 , 0x00301 , 0x001FD },
+ { 0x000E6 , 0x00304 , 0x001E3 },
+ { 0x000E7 , 0x00301 , 0x01E09 },
+ { 0x000EA , 0x00300 , 0x01EC1 },
+ { 0x000EA , 0x00301 , 0x01EBF },
+ { 0x000EA , 0x00303 , 0x01EC5 },
+ { 0x000EA , 0x00309 , 0x01EC3 },
+ { 0x000EF , 0x00301 , 0x01E2F },
+ { 0x000F4 , 0x00300 , 0x01ED3 },
+ { 0x000F4 , 0x00301 , 0x01ED1 },
+ { 0x000F4 , 0x00303 , 0x01ED7 },
+ { 0x000F4 , 0x00309 , 0x01ED5 },
+ { 0x000F5 , 0x00301 , 0x01E4D },
+ { 0x000F5 , 0x00304 , 0x0022D },
+ { 0x000F5 , 0x00308 , 0x01E4F },
+ { 0x000F6 , 0x00304 , 0x0022B },
+ { 0x000F8 , 0x00301 , 0x001FF },
+ { 0x000FC , 0x00300 , 0x001DC },
+ { 0x000FC , 0x00301 , 0x001D8 },
+ { 0x000FC , 0x00304 , 0x001D6 },
+ { 0x000FC , 0x0030C , 0x001DA },
+ { 0x00102 , 0x00300 , 0x01EB0 },
+ { 0x00102 , 0x00301 , 0x01EAE },
+ { 0x00102 , 0x00303 , 0x01EB4 },
+ { 0x00102 , 0x00309 , 0x01EB2 },
+ { 0x00103 , 0x00300 , 0x01EB1 },
+ { 0x00103 , 0x00301 , 0x01EAF },
+ { 0x00103 , 0x00303 , 0x01EB5 },
+ { 0x00103 , 0x00309 , 0x01EB3 },
+ { 0x00112 , 0x00300 , 0x01E14 },
+ { 0x00112 , 0x00301 , 0x01E16 },
+ { 0x00113 , 0x00300 , 0x01E15 },
+ { 0x00113 , 0x00301 , 0x01E17 },
+ { 0x0014C , 0x00300 , 0x01E50 },
+ { 0x0014C , 0x00301 , 0x01E52 },
+ { 0x0014D , 0x00300 , 0x01E51 },
+ { 0x0014D , 0x00301 , 0x01E53 },
+ { 0x0015A , 0x00307 , 0x01E64 },
+ { 0x0015B , 0x00307 , 0x01E65 },
+ { 0x00160 , 0x00307 , 0x01E66 },
+ { 0x00161 , 0x00307 , 0x01E67 },
+ { 0x00168 , 0x00301 , 0x01E78 },
+ { 0x00169 , 0x00301 , 0x01E79 },
+ { 0x0016A , 0x00308 , 0x01E7A },
+ { 0x0016B , 0x00308 , 0x01E7B },
+ { 0x0017F , 0x00307 , 0x01E9B },
+ { 0x001A0 , 0x00300 , 0x01EDC },
+ { 0x001A0 , 0x00301 , 0x01EDA },
+ { 0x001A0 , 0x00303 , 0x01EE0 },
+ { 0x001A0 , 0x00309 , 0x01EDE },
+ { 0x001A0 , 0x00323 , 0x01EE2 },
+ { 0x001A1 , 0x00300 , 0x01EDD },
+ { 0x001A1 , 0x00301 , 0x01EDB },
+ { 0x001A1 , 0x00303 , 0x01EE1 },
+ { 0x001A1 , 0x00309 , 0x01EDF },
+ { 0x001A1 , 0x00323 , 0x01EE3 },
+ { 0x001AF , 0x00300 , 0x01EEA },
+ { 0x001AF , 0x00301 , 0x01EE8 },
+ { 0x001AF , 0x00303 , 0x01EEE },
+ { 0x001AF , 0x00309 , 0x01EEC },
+ { 0x001AF , 0x00323 , 0x01EF0 },
+ { 0x001B0 , 0x00300 , 0x01EEB },
+ { 0x001B0 , 0x00301 , 0x01EE9 },
+ { 0x001B0 , 0x00303 , 0x01EEF },
+ { 0x001B0 , 0x00309 , 0x01EED },
+ { 0x001B0 , 0x00323 , 0x01EF1 },
+ { 0x001B7 , 0x0030C , 0x001EE },
+ { 0x001EA , 0x00304 , 0x001EC },
+ { 0x001EB , 0x00304 , 0x001ED },
+ { 0x00226 , 0x00304 , 0x001E0 },
+ { 0x00227 , 0x00304 , 0x001E1 },
+ { 0x00228 , 0x00306 , 0x01E1C },
+ { 0x00229 , 0x00306 , 0x01E1D },
+ { 0x0022E , 0x00304 , 0x00230 },
+ { 0x0022F , 0x00304 , 0x00231 },
+ { 0x00292 , 0x0030C , 0x001EF },
+ { 0x00391 , 0x00300 , 0x01FBA },
+ { 0x00391 , 0x00301 , 0x00386 },
+ { 0x00391 , 0x00304 , 0x01FB9 },
+ { 0x00391 , 0x00306 , 0x01FB8 },
+ { 0x00391 , 0x00313 , 0x01F08 },
+ { 0x00391 , 0x00314 , 0x01F09 },
+ { 0x00391 , 0x00345 , 0x01FBC },
+ { 0x00395 , 0x00300 , 0x01FC8 },
+ { 0x00395 , 0x00301 , 0x00388 },
+ { 0x00395 , 0x00313 , 0x01F18 },
+ { 0x00395 , 0x00314 , 0x01F19 },
+ { 0x00397 , 0x00300 , 0x01FCA },
+ { 0x00397 , 0x00301 , 0x00389 },
+ { 0x00397 , 0x00313 , 0x01F28 },
+ { 0x00397 , 0x00314 , 0x01F29 },
+ { 0x00397 , 0x00345 , 0x01FCC },
+ { 0x00399 , 0x00300 , 0x01FDA },
+ { 0x00399 , 0x00301 , 0x0038A },
+ { 0x00399 , 0x00304 , 0x01FD9 },
+ { 0x00399 , 0x00306 , 0x01FD8 },
+ { 0x00399 , 0x00308 , 0x003AA },
+ { 0x00399 , 0x00313 , 0x01F38 },
+ { 0x00399 , 0x00314 , 0x01F39 },
+ { 0x0039F , 0x00300 , 0x01FF8 },
+ { 0x0039F , 0x00301 , 0x0038C },
+ { 0x0039F , 0x00313 , 0x01F48 },
+ { 0x0039F , 0x00314 , 0x01F49 },
+ { 0x003A1 , 0x00314 , 0x01FEC },
+ { 0x003A5 , 0x00300 , 0x01FEA },
+ { 0x003A5 , 0x00301 , 0x0038E },
+ { 0x003A5 , 0x00304 , 0x01FE9 },
+ { 0x003A5 , 0x00306 , 0x01FE8 },
+ { 0x003A5 , 0x00308 , 0x003AB },
+ { 0x003A5 , 0x00314 , 0x01F59 },
+ { 0x003A9 , 0x00300 , 0x01FFA },
+ { 0x003A9 , 0x00301 , 0x0038F },
+ { 0x003A9 , 0x00313 , 0x01F68 },
+ { 0x003A9 , 0x00314 , 0x01F69 },
+ { 0x003A9 , 0x00345 , 0x01FFC },
+ { 0x003AC , 0x00345 , 0x01FB4 },
+ { 0x003AE , 0x00345 , 0x01FC4 },
+ { 0x003B1 , 0x00300 , 0x01F70 },
+ { 0x003B1 , 0x00301 , 0x003AC },
+ { 0x003B1 , 0x00304 , 0x01FB1 },
+ { 0x003B1 , 0x00306 , 0x01FB0 },
+ { 0x003B1 , 0x00313 , 0x01F00 },
+ { 0x003B1 , 0x00314 , 0x01F01 },
+ { 0x003B1 , 0x00342 , 0x01FB6 },
+ { 0x003B1 , 0x00345 , 0x01FB3 },
+ { 0x003B5 , 0x00300 , 0x01F72 },
+ { 0x003B5 , 0x00301 , 0x003AD },
+ { 0x003B5 , 0x00313 , 0x01F10 },
+ { 0x003B5 , 0x00314 , 0x01F11 },
+ { 0x003B7 , 0x00300 , 0x01F74 },
+ { 0x003B7 , 0x00301 , 0x003AE },
+ { 0x003B7 , 0x00313 , 0x01F20 },
+ { 0x003B7 , 0x00314 , 0x01F21 },
+ { 0x003B7 , 0x00342 , 0x01FC6 },
+ { 0x003B7 , 0x00345 , 0x01FC3 },
+ { 0x003B9 , 0x00300 , 0x01F76 },
+ { 0x003B9 , 0x00301 , 0x003AF },
+ { 0x003B9 , 0x00304 , 0x01FD1 },
+ { 0x003B9 , 0x00306 , 0x01FD0 },
+ { 0x003B9 , 0x00308 , 0x003CA },
+ { 0x003B9 , 0x00313 , 0x01F30 },
+ { 0x003B9 , 0x00314 , 0x01F31 },
+ { 0x003B9 , 0x00342 , 0x01FD6 },
+ { 0x003BF , 0x00300 , 0x01F78 },
+ { 0x003BF , 0x00301 , 0x003CC },
+ { 0x003BF , 0x00313 , 0x01F40 },
+ { 0x003BF , 0x00314 , 0x01F41 },
+ { 0x003C1 , 0x00313 , 0x01FE4 },
+ { 0x003C1 , 0x00314 , 0x01FE5 },
+ { 0x003C5 , 0x00300 , 0x01F7A },
+ { 0x003C5 , 0x00301 , 0x003CD },
+ { 0x003C5 , 0x00304 , 0x01FE1 },
+ { 0x003C5 , 0x00306 , 0x01FE0 },
+ { 0x003C5 , 0x00308 , 0x003CB },
+ { 0x003C5 , 0x00313 , 0x01F50 },
+ { 0x003C5 , 0x00314 , 0x01F51 },
+ { 0x003C5 , 0x00342 , 0x01FE6 },
+ { 0x003C9 , 0x00300 , 0x01F7C },
+ { 0x003C9 , 0x00301 , 0x003CE },
+ { 0x003C9 , 0x00313 , 0x01F60 },
+ { 0x003C9 , 0x00314 , 0x01F61 },
+ { 0x003C9 , 0x00342 , 0x01FF6 },
+ { 0x003C9 , 0x00345 , 0x01FF3 },
+ { 0x003CA , 0x00300 , 0x01FD2 },
+ { 0x003CA , 0x00301 , 0x00390 },
+ { 0x003CA , 0x00342 , 0x01FD7 },
+ { 0x003CB , 0x00300 , 0x01FE2 },
+ { 0x003CB , 0x00301 , 0x003B0 },
+ { 0x003CB , 0x00342 , 0x01FE7 },
+ { 0x003CE , 0x00345 , 0x01FF4 },
+ { 0x003D2 , 0x00301 , 0x003D3 },
+ { 0x003D2 , 0x00308 , 0x003D4 },
+ { 0x00406 , 0x00308 , 0x00407 },
+ { 0x00410 , 0x00306 , 0x004D0 },
+ { 0x00410 , 0x00308 , 0x004D2 },
+ { 0x00413 , 0x00301 , 0x00403 },
+ { 0x00415 , 0x00300 , 0x00400 },
+ { 0x00415 , 0x00306 , 0x004D6 },
+ { 0x00415 , 0x00308 , 0x00401 },
+ { 0x00416 , 0x00306 , 0x004C1 },
+ { 0x00416 , 0x00308 , 0x004DC },
+ { 0x00417 , 0x00308 , 0x004DE },
+ { 0x00418 , 0x00300 , 0x0040D },
+ { 0x00418 , 0x00304 , 0x004E2 },
+ { 0x00418 , 0x00306 , 0x00419 },
+ { 0x00418 , 0x00308 , 0x004E4 },
+ { 0x0041A , 0x00301 , 0x0040C },
+ { 0x0041E , 0x00308 , 0x004E6 },
+ { 0x00423 , 0x00304 , 0x004EE },
+ { 0x00423 , 0x00306 , 0x0040E },
+ { 0x00423 , 0x00308 , 0x004F0 },
+ { 0x00423 , 0x0030B , 0x004F2 },
+ { 0x00427 , 0x00308 , 0x004F4 },
+ { 0x0042B , 0x00308 , 0x004F8 },
+ { 0x0042D , 0x00308 , 0x004EC },
+ { 0x00430 , 0x00306 , 0x004D1 },
+ { 0x00430 , 0x00308 , 0x004D3 },
+ { 0x00433 , 0x00301 , 0x00453 },
+ { 0x00435 , 0x00300 , 0x00450 },
+ { 0x00435 , 0x00306 , 0x004D7 },
+ { 0x00435 , 0x00308 , 0x00451 },
+ { 0x00436 , 0x00306 , 0x004C2 },
+ { 0x00436 , 0x00308 , 0x004DD },
+ { 0x00437 , 0x00308 , 0x004DF },
+ { 0x00438 , 0x00300 , 0x0045D },
+ { 0x00438 , 0x00304 , 0x004E3 },
+ { 0x00438 , 0x00306 , 0x00439 },
+ { 0x00438 , 0x00308 , 0x004E5 },
+ { 0x0043A , 0x00301 , 0x0045C },
+ { 0x0043E , 0x00308 , 0x004E7 },
+ { 0x00443 , 0x00304 , 0x004EF },
+ { 0x00443 , 0x00306 , 0x0045E },
+ { 0x00443 , 0x00308 , 0x004F1 },
+ { 0x00443 , 0x0030B , 0x004F3 },
+ { 0x00447 , 0x00308 , 0x004F5 },
+ { 0x0044B , 0x00308 , 0x004F9 },
+ { 0x0044D , 0x00308 , 0x004ED },
+ { 0x00456 , 0x00308 , 0x00457 },
+ { 0x00474 , 0x0030F , 0x00476 },
+ { 0x00475 , 0x0030F , 0x00477 },
+ { 0x004D8 , 0x00308 , 0x004DA },
+ { 0x004D9 , 0x00308 , 0x004DB },
+ { 0x004E8 , 0x00308 , 0x004EA },
+ { 0x004E9 , 0x00308 , 0x004EB },
+ { 0x00627 , 0x00653 , 0x00622 },
+ { 0x00627 , 0x00654 , 0x00623 },
+ { 0x00627 , 0x00655 , 0x00625 },
+ { 0x00648 , 0x00654 , 0x00624 },
+ { 0x0064A , 0x00654 , 0x00626 },
+ { 0x006C1 , 0x00654 , 0x006C2 },
+ { 0x006D2 , 0x00654 , 0x006D3 },
+ { 0x006D5 , 0x00654 , 0x006C0 },
+ { 0x00928 , 0x0093C , 0x00929 },
+ { 0x00930 , 0x0093C , 0x00931 },
+ { 0x00933 , 0x0093C , 0x00934 },
+ { 0x009C7 , 0x009BE , 0x009CB },
+ { 0x009C7 , 0x009D7 , 0x009CC },
+ { 0x00B47 , 0x00B3E , 0x00B4B },
+ { 0x00B47 , 0x00B56 , 0x00B48 },
+ { 0x00B47 , 0x00B57 , 0x00B4C },
+ { 0x00B92 , 0x00BD7 , 0x00B94 },
+ { 0x00BC6 , 0x00BBE , 0x00BCA },
+ { 0x00BC6 , 0x00BD7 , 0x00BCC },
+ { 0x00BC7 , 0x00BBE , 0x00BCB },
+ { 0x00C46 , 0x00C56 , 0x00C48 },
+ { 0x00CBF , 0x00CD5 , 0x00CC0 },
+ { 0x00CC6 , 0x00CC2 , 0x00CCA },
+ { 0x00CC6 , 0x00CD5 , 0x00CC7 },
+ { 0x00CC6 , 0x00CD6 , 0x00CC8 },
+ { 0x00CCA , 0x00CD5 , 0x00CCB },
+ { 0x00D46 , 0x00D3E , 0x00D4A },
+ { 0x00D46 , 0x00D57 , 0x00D4C },
+ { 0x00D47 , 0x00D3E , 0x00D4B },
+ { 0x00DD9 , 0x00DCA , 0x00DDA },
+ { 0x00DD9 , 0x00DCF , 0x00DDC },
+ { 0x00DD9 , 0x00DDF , 0x00DDE },
+ { 0x00DDC , 0x00DCA , 0x00DDD },
+ { 0x01025 , 0x0102E , 0x01026 },
+ { 0x01B05 , 0x01B35 , 0x01B06 },
+ { 0x01B07 , 0x01B35 , 0x01B08 },
+ { 0x01B09 , 0x01B35 , 0x01B0A },
+ { 0x01B0B , 0x01B35 , 0x01B0C },
+ { 0x01B0D , 0x01B35 , 0x01B0E },
+ { 0x01B11 , 0x01B35 , 0x01B12 },
+ { 0x01B3A , 0x01B35 , 0x01B3B },
+ { 0x01B3C , 0x01B35 , 0x01B3D },
+ { 0x01B3E , 0x01B35 , 0x01B40 },
+ { 0x01B3F , 0x01B35 , 0x01B41 },
+ { 0x01B42 , 0x01B35 , 0x01B43 },
+ { 0x01E36 , 0x00304 , 0x01E38 },
+ { 0x01E37 , 0x00304 , 0x01E39 },
+ { 0x01E5A , 0x00304 , 0x01E5C },
+ { 0x01E5B , 0x00304 , 0x01E5D },
+ { 0x01E62 , 0x00307 , 0x01E68 },
+ { 0x01E63 , 0x00307 , 0x01E69 },
+ { 0x01EA0 , 0x00302 , 0x01EAC },
+ { 0x01EA0 , 0x00306 , 0x01EB6 },
+ { 0x01EA1 , 0x00302 , 0x01EAD },
+ { 0x01EA1 , 0x00306 , 0x01EB7 },
+ { 0x01EB8 , 0x00302 , 0x01EC6 },
+ { 0x01EB9 , 0x00302 , 0x01EC7 },
+ { 0x01ECC , 0x00302 , 0x01ED8 },
+ { 0x01ECD , 0x00302 , 0x01ED9 },
+ { 0x01F00 , 0x00300 , 0x01F02 },
+ { 0x01F00 , 0x00301 , 0x01F04 },
+ { 0x01F00 , 0x00342 , 0x01F06 },
+ { 0x01F00 , 0x00345 , 0x01F80 },
+ { 0x01F01 , 0x00300 , 0x01F03 },
+ { 0x01F01 , 0x00301 , 0x01F05 },
+ { 0x01F01 , 0x00342 , 0x01F07 },
+ { 0x01F01 , 0x00345 , 0x01F81 },
+ { 0x01F02 , 0x00345 , 0x01F82 },
+ { 0x01F03 , 0x00345 , 0x01F83 },
+ { 0x01F04 , 0x00345 , 0x01F84 },
+ { 0x01F05 , 0x00345 , 0x01F85 },
+ { 0x01F06 , 0x00345 , 0x01F86 },
+ { 0x01F07 , 0x00345 , 0x01F87 },
+ { 0x01F08 , 0x00300 , 0x01F0A },
+ { 0x01F08 , 0x00301 , 0x01F0C },
+ { 0x01F08 , 0x00342 , 0x01F0E },
+ { 0x01F08 , 0x00345 , 0x01F88 },
+ { 0x01F09 , 0x00300 , 0x01F0B },
+ { 0x01F09 , 0x00301 , 0x01F0D },
+ { 0x01F09 , 0x00342 , 0x01F0F },
+ { 0x01F09 , 0x00345 , 0x01F89 },
+ { 0x01F0A , 0x00345 , 0x01F8A },
+ { 0x01F0B , 0x00345 , 0x01F8B },
+ { 0x01F0C , 0x00345 , 0x01F8C },
+ { 0x01F0D , 0x00345 , 0x01F8D },
+ { 0x01F0E , 0x00345 , 0x01F8E },
+ { 0x01F0F , 0x00345 , 0x01F8F },
+ { 0x01F10 , 0x00300 , 0x01F12 },
+ { 0x01F10 , 0x00301 , 0x01F14 },
+ { 0x01F11 , 0x00300 , 0x01F13 },
+ { 0x01F11 , 0x00301 , 0x01F15 },
+ { 0x01F18 , 0x00300 , 0x01F1A },
+ { 0x01F18 , 0x00301 , 0x01F1C },
+ { 0x01F19 , 0x00300 , 0x01F1B },
+ { 0x01F19 , 0x00301 , 0x01F1D },
+ { 0x01F20 , 0x00300 , 0x01F22 },
+ { 0x01F20 , 0x00301 , 0x01F24 },
+ { 0x01F20 , 0x00342 , 0x01F26 },
+ { 0x01F20 , 0x00345 , 0x01F90 },
+ { 0x01F21 , 0x00300 , 0x01F23 },
+ { 0x01F21 , 0x00301 , 0x01F25 },
+ { 0x01F21 , 0x00342 , 0x01F27 },
+ { 0x01F21 , 0x00345 , 0x01F91 },
+ { 0x01F22 , 0x00345 , 0x01F92 },
+ { 0x01F23 , 0x00345 , 0x01F93 },
+ { 0x01F24 , 0x00345 , 0x01F94 },
+ { 0x01F25 , 0x00345 , 0x01F95 },
+ { 0x01F26 , 0x00345 , 0x01F96 },
+ { 0x01F27 , 0x00345 , 0x01F97 },
+ { 0x01F28 , 0x00300 , 0x01F2A },
+ { 0x01F28 , 0x00301 , 0x01F2C },
+ { 0x01F28 , 0x00342 , 0x01F2E },
+ { 0x01F28 , 0x00345 , 0x01F98 },
+ { 0x01F29 , 0x00300 , 0x01F2B },
+ { 0x01F29 , 0x00301 , 0x01F2D },
+ { 0x01F29 , 0x00342 , 0x01F2F },
+ { 0x01F29 , 0x00345 , 0x01F99 },
+ { 0x01F2A , 0x00345 , 0x01F9A },
+ { 0x01F2B , 0x00345 , 0x01F9B },
+ { 0x01F2C , 0x00345 , 0x01F9C },
+ { 0x01F2D , 0x00345 , 0x01F9D },
+ { 0x01F2E , 0x00345 , 0x01F9E },
+ { 0x01F2F , 0x00345 , 0x01F9F },
+ { 0x01F30 , 0x00300 , 0x01F32 },
+ { 0x01F30 , 0x00301 , 0x01F34 },
+ { 0x01F30 , 0x00342 , 0x01F36 },
+ { 0x01F31 , 0x00300 , 0x01F33 },
+ { 0x01F31 , 0x00301 , 0x01F35 },
+ { 0x01F31 , 0x00342 , 0x01F37 },
+ { 0x01F38 , 0x00300 , 0x01F3A },
+ { 0x01F38 , 0x00301 , 0x01F3C },
+ { 0x01F38 , 0x00342 , 0x01F3E },
+ { 0x01F39 , 0x00300 , 0x01F3B },
+ { 0x01F39 , 0x00301 , 0x01F3D },
+ { 0x01F39 , 0x00342 , 0x01F3F },
+ { 0x01F40 , 0x00300 , 0x01F42 },
+ { 0x01F40 , 0x00301 , 0x01F44 },
+ { 0x01F41 , 0x00300 , 0x01F43 },
+ { 0x01F41 , 0x00301 , 0x01F45 },
+ { 0x01F48 , 0x00300 , 0x01F4A },
+ { 0x01F48 , 0x00301 , 0x01F4C },
+ { 0x01F49 , 0x00300 , 0x01F4B },
+ { 0x01F49 , 0x00301 , 0x01F4D },
+ { 0x01F50 , 0x00300 , 0x01F52 },
+ { 0x01F50 , 0x00301 , 0x01F54 },
+ { 0x01F50 , 0x00342 , 0x01F56 },
+ { 0x01F51 , 0x00300 , 0x01F53 },
+ { 0x01F51 , 0x00301 , 0x01F55 },
+ { 0x01F51 , 0x00342 , 0x01F57 },
+ { 0x01F59 , 0x00300 , 0x01F5B },
+ { 0x01F59 , 0x00301 , 0x01F5D },
+ { 0x01F59 , 0x00342 , 0x01F5F },
+ { 0x01F60 , 0x00300 , 0x01F62 },
+ { 0x01F60 , 0x00301 , 0x01F64 },
+ { 0x01F60 , 0x00342 , 0x01F66 },
+ { 0x01F60 , 0x00345 , 0x01FA0 },
+ { 0x01F61 , 0x00300 , 0x01F63 },
+ { 0x01F61 , 0x00301 , 0x01F65 },
+ { 0x01F61 , 0x00342 , 0x01F67 },
+ { 0x01F61 , 0x00345 , 0x01FA1 },
+ { 0x01F62 , 0x00345 , 0x01FA2 },
+ { 0x01F63 , 0x00345 , 0x01FA3 },
+ { 0x01F64 , 0x00345 , 0x01FA4 },
+ { 0x01F65 , 0x00345 , 0x01FA5 },
+ { 0x01F66 , 0x00345 , 0x01FA6 },
+ { 0x01F67 , 0x00345 , 0x01FA7 },
+ { 0x01F68 , 0x00300 , 0x01F6A },
+ { 0x01F68 , 0x00301 , 0x01F6C },
+ { 0x01F68 , 0x00342 , 0x01F6E },
+ { 0x01F68 , 0x00345 , 0x01FA8 },
+ { 0x01F69 , 0x00300 , 0x01F6B },
+ { 0x01F69 , 0x00301 , 0x01F6D },
+ { 0x01F69 , 0x00342 , 0x01F6F },
+ { 0x01F69 , 0x00345 , 0x01FA9 },
+ { 0x01F6A , 0x00345 , 0x01FAA },
+ { 0x01F6B , 0x00345 , 0x01FAB },
+ { 0x01F6C , 0x00345 , 0x01FAC },
+ { 0x01F6D , 0x00345 , 0x01FAD },
+ { 0x01F6E , 0x00345 , 0x01FAE },
+ { 0x01F6F , 0x00345 , 0x01FAF },
+ { 0x01F70 , 0x00345 , 0x01FB2 },
+ { 0x01F74 , 0x00345 , 0x01FC2 },
+ { 0x01F7C , 0x00345 , 0x01FF2 },
+ { 0x01FB6 , 0x00345 , 0x01FB7 },
+ { 0x01FBF , 0x00300 , 0x01FCD },
+ { 0x01FBF , 0x00301 , 0x01FCE },
+ { 0x01FBF , 0x00342 , 0x01FCF },
+ { 0x01FC6 , 0x00345 , 0x01FC7 },
+ { 0x01FF6 , 0x00345 , 0x01FF7 },
+ { 0x01FFE , 0x00300 , 0x01FDD },
+ { 0x01FFE , 0x00301 , 0x01FDE },
+ { 0x01FFE , 0x00342 , 0x01FDF },
+ { 0x02190 , 0x00338 , 0x0219A },
+ { 0x02192 , 0x00338 , 0x0219B },
+ { 0x02194 , 0x00338 , 0x021AE },
+ { 0x021D0 , 0x00338 , 0x021CD },
+ { 0x021D2 , 0x00338 , 0x021CF },
+ { 0x021D4 , 0x00338 , 0x021CE },
+ { 0x02203 , 0x00338 , 0x02204 },
+ { 0x02208 , 0x00338 , 0x02209 },
+ { 0x0220B , 0x00338 , 0x0220C },
+ { 0x02223 , 0x00338 , 0x02224 },
+ { 0x02225 , 0x00338 , 0x02226 },
+ { 0x0223C , 0x00338 , 0x02241 },
+ { 0x02243 , 0x00338 , 0x02244 },
+ { 0x02245 , 0x00338 , 0x02247 },
+ { 0x02248 , 0x00338 , 0x02249 },
+ { 0x0224D , 0x00338 , 0x0226D },
+ { 0x02261 , 0x00338 , 0x02262 },
+ { 0x02264 , 0x00338 , 0x02270 },
+ { 0x02265 , 0x00338 , 0x02271 },
+ { 0x02272 , 0x00338 , 0x02274 },
+ { 0x02273 , 0x00338 , 0x02275 },
+ { 0x02276 , 0x00338 , 0x02278 },
+ { 0x02277 , 0x00338 , 0x02279 },
+ { 0x0227A , 0x00338 , 0x02280 },
+ { 0x0227B , 0x00338 , 0x02281 },
+ { 0x0227C , 0x00338 , 0x022E0 },
+ { 0x0227D , 0x00338 , 0x022E1 },
+ { 0x02282 , 0x00338 , 0x02284 },
+ { 0x02283 , 0x00338 , 0x02285 },
+ { 0x02286 , 0x00338 , 0x02288 },
+ { 0x02287 , 0x00338 , 0x02289 },
+ { 0x02291 , 0x00338 , 0x022E2 },
+ { 0x02292 , 0x00338 , 0x022E3 },
+ { 0x022A2 , 0x00338 , 0x022AC },
+ { 0x022A8 , 0x00338 , 0x022AD },
+ { 0x022A9 , 0x00338 , 0x022AE },
+ { 0x022AB , 0x00338 , 0x022AF },
+ { 0x022B2 , 0x00338 , 0x022EA },
+ { 0x022B3 , 0x00338 , 0x022EB },
+ { 0x022B4 , 0x00338 , 0x022EC },
+ { 0x022B5 , 0x00338 , 0x022ED },
+ { 0x03046 , 0x03099 , 0x03094 },
+ { 0x0304B , 0x03099 , 0x0304C },
+ { 0x0304D , 0x03099 , 0x0304E },
+ { 0x0304F , 0x03099 , 0x03050 },
+ { 0x03051 , 0x03099 , 0x03052 },
+ { 0x03053 , 0x03099 , 0x03054 },
+ { 0x03055 , 0x03099 , 0x03056 },
+ { 0x03057 , 0x03099 , 0x03058 },
+ { 0x03059 , 0x03099 , 0x0305A },
+ { 0x0305B , 0x03099 , 0x0305C },
+ { 0x0305D , 0x03099 , 0x0305E },
+ { 0x0305F , 0x03099 , 0x03060 },
+ { 0x03061 , 0x03099 , 0x03062 },
+ { 0x03064 , 0x03099 , 0x03065 },
+ { 0x03066 , 0x03099 , 0x03067 },
+ { 0x03068 , 0x03099 , 0x03069 },
+ { 0x0306F , 0x03099 , 0x03070 },
+ { 0x0306F , 0x0309A , 0x03071 },
+ { 0x03072 , 0x03099 , 0x03073 },
+ { 0x03072 , 0x0309A , 0x03074 },
+ { 0x03075 , 0x03099 , 0x03076 },
+ { 0x03075 , 0x0309A , 0x03077 },
+ { 0x03078 , 0x03099 , 0x03079 },
+ { 0x03078 , 0x0309A , 0x0307A },
+ { 0x0307B , 0x03099 , 0x0307C },
+ { 0x0307B , 0x0309A , 0x0307D },
+ { 0x0309D , 0x03099 , 0x0309E },
+ { 0x030A6 , 0x03099 , 0x030F4 },
+ { 0x030AB , 0x03099 , 0x030AC },
+ { 0x030AD , 0x03099 , 0x030AE },
+ { 0x030AF , 0x03099 , 0x030B0 },
+ { 0x030B1 , 0x03099 , 0x030B2 },
+ { 0x030B3 , 0x03099 , 0x030B4 },
+ { 0x030B5 , 0x03099 , 0x030B6 },
+ { 0x030B7 , 0x03099 , 0x030B8 },
+ { 0x030B9 , 0x03099 , 0x030BA },
+ { 0x030BB , 0x03099 , 0x030BC },
+ { 0x030BD , 0x03099 , 0x030BE },
+ { 0x030BF , 0x03099 , 0x030C0 },
+ { 0x030C1 , 0x03099 , 0x030C2 },
+ { 0x030C4 , 0x03099 , 0x030C5 },
+ { 0x030C6 , 0x03099 , 0x030C7 },
+ { 0x030C8 , 0x03099 , 0x030C9 },
+ { 0x030CF , 0x03099 , 0x030D0 },
+ { 0x030CF , 0x0309A , 0x030D1 },
+ { 0x030D2 , 0x03099 , 0x030D3 },
+ { 0x030D2 , 0x0309A , 0x030D4 },
+ { 0x030D5 , 0x03099 , 0x030D6 },
+ { 0x030D5 , 0x0309A , 0x030D7 },
+ { 0x030D8 , 0x03099 , 0x030D9 },
+ { 0x030D8 , 0x0309A , 0x030DA },
+ { 0x030DB , 0x03099 , 0x030DC },
+ { 0x030DB , 0x0309A , 0x030DD },
+ { 0x030EF , 0x03099 , 0x030F7 },
+ { 0x030F0 , 0x03099 , 0x030F8 },
+ { 0x030F1 , 0x03099 , 0x030F9 },
+ { 0x030F2 , 0x03099 , 0x030FA },
+ { 0x030FD , 0x03099 , 0x030FE },
+ { 0x11099 , 0x110BA , 0x1109A },
+ { 0x1109B , 0x110BA , 0x1109C },
+ { 0x110A5 , 0x110BA , 0x110AB },
+};
+
+#define CANONICAL_CLASS_MIN 0x0300
+#define CANONICAL_CLASS_MAX 0x1D244
+
+#define IS_DECOMPOSABLE_BLOCK(uc) \
+ (((uc)>>8) <= 0x1D2 && u_decomposable_blocks[(uc)>>8])
+static const char u_decomposable_blocks[0x1D2+1] = {
+ 0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,1,1,1,1,1,1,1,0,0,
+ 1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,
+ 0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,
+};
+
+/* Get Canonical Combining Class(CCC). */
+#define CCC(uc) \
+ (((uc) > 0x1D244)?0:\
+ ccc_val[ccc_val_index[ccc_index[(uc)>>8]][((uc)>>4)&0x0F]][(uc)&0x0F])
+
+/* The table of the value of Canonical Combining Class */
+static const unsigned char ccc_val[][16] = {
+ /* idx=0: XXXX0 - XXXXF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=1: 00300 - 0030F */
+ {230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=2: 00310 - 0031F */
+ {230, 230, 230, 230, 230, 232, 220, 220, 220, 220, 232, 216, 220, 220, 220, 220 },
+ /* idx=3: 00320 - 0032F */
+ {220, 202, 202, 220, 220, 220, 220, 202, 202, 220, 220, 220, 220, 220, 220, 220 },
+ /* idx=4: 00330 - 0033F */
+ {220, 220, 220, 220, 1, 1, 1, 1, 1, 220, 220, 220, 220, 230, 230, 230 },
+ /* idx=5: 00340 - 0034F */
+ {230, 230, 230, 230, 230, 240, 230, 220, 220, 220, 230, 230, 230, 220, 220, 0 },
+ /* idx=6: 00350 - 0035F */
+ {230, 230, 230, 220, 220, 220, 220, 230, 232, 220, 220, 230, 233, 234, 234, 233 },
+ /* idx=7: 00360 - 0036F */
+ {234, 234, 233, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=8: 00480 - 0048F */
+ {0, 0, 0, 230, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=9: 00590 - 0059F */
+ {0, 220, 230, 230, 230, 230, 220, 230, 230, 230, 222, 220, 230, 230, 230, 230 },
+ /* idx=10: 005A0 - 005AF */
+ {230, 230, 220, 220, 220, 220, 220, 220, 230, 230, 220, 230, 230, 222, 228, 230 },
+ /* idx=11: 005B0 - 005BF */
+ {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23 },
+ /* idx=12: 005C0 - 005CF */
+ {0, 24, 25, 0, 230, 220, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=13: 00610 - 0061F */
+ {230, 230, 230, 230, 230, 230, 230, 230, 30, 31, 32, 0, 0, 0, 0, 0 },
+ /* idx=14: 00640 - 0064F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 28, 29, 30, 31 },
+ /* idx=15: 00650 - 0065F */
+ {32, 33, 34, 230, 230, 220, 220, 230, 230, 230, 230, 230, 220, 230, 230, 220 },
+ /* idx=16: 00670 - 0067F */
+ {35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=17: 006D0 - 006DF */
+ {0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 230, 230, 230, 0, 0, 230 },
+ /* idx=18: 006E0 - 006EF */
+ {230, 230, 230, 220, 230, 0, 0, 230, 230, 0, 220, 230, 230, 220, 0, 0 },
+ /* idx=19: 00710 - 0071F */
+ {0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=20: 00730 - 0073F */
+ {230, 220, 230, 230, 220, 230, 230, 220, 220, 220, 230, 220, 220, 230, 220, 230 },
+ /* idx=21: 00740 - 0074F */
+ {230, 230, 220, 230, 220, 230, 220, 230, 220, 230, 230, 0, 0, 0, 0, 0 },
+ /* idx=22: 007E0 - 007EF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 230 },
+ /* idx=23: 007F0 - 007FF */
+ {230, 230, 220, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=24: 00810 - 0081F */
+ {0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 0, 230, 230, 230, 230, 230 },
+ /* idx=25: 00820 - 0082F */
+ {230, 230, 230, 230, 0, 230, 230, 230, 0, 230, 230, 230, 230, 230, 0, 0 },
+ /* idx=26: 00850 - 0085F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 220, 0, 0, 0, 0 },
+ /* idx=27: 00930 - 0093F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=28: 00940 - 0094F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=29: 00950 - 0095F */
+ {0, 230, 220, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=30: 009B0 - 009BF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=31: 009C0 - 009CF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=32: 00A30 - 00A3F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=33: 00A40 - 00A4F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=34: 00AB0 - 00ABF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=35: 00AC0 - 00ACF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=36: 00B30 - 00B3F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=37: 00B40 - 00B4F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=38: 00BC0 - 00BCF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=39: 00C40 - 00C4F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=40: 00C50 - 00C5F */
+ {0, 0, 0, 0, 0, 84, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=41: 00CB0 - 00CBF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=42: 00CC0 - 00CCF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=43: 00D40 - 00D4F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=44: 00DC0 - 00DCF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0 },
+ /* idx=45: 00E30 - 00E3F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 103, 103, 9, 0, 0, 0, 0, 0 },
+ /* idx=46: 00E40 - 00E4F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 107, 107, 107, 107, 0, 0, 0, 0 },
+ /* idx=47: 00EB0 - 00EBF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 118, 118, 0, 0, 0, 0, 0, 0 },
+ /* idx=48: 00EC0 - 00ECF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 122, 122, 122, 122, 0, 0, 0, 0 },
+ /* idx=49: 00F10 - 00F1F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 0, 0, 0, 0, 0, 0 },
+ /* idx=50: 00F30 - 00F3F */
+ {0, 0, 0, 0, 0, 220, 0, 220, 0, 216, 0, 0, 0, 0, 0, 0 },
+ /* idx=51: 00F70 - 00F7F */
+ {0, 129, 130, 0, 132, 0, 0, 0, 0, 0, 130, 130, 130, 130, 0, 0 },
+ /* idx=52: 00F80 - 00F8F */
+ {130, 0, 230, 230, 9, 0, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=53: 00FC0 - 00FCF */
+ {0, 0, 0, 0, 0, 0, 220, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=54: 01030 - 0103F */
+ {0, 0, 0, 0, 0, 0, 0, 7, 0, 9, 9, 0, 0, 0, 0, 0 },
+ /* idx=55: 01080 - 0108F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 0 },
+ /* idx=56: 01350 - 0135F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230 },
+ /* idx=57: 01710 - 0171F */
+ {0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=58: 01730 - 0173F */
+ {0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=59: 017D0 - 017DF */
+ {0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 0, 0 },
+ /* idx=60: 018A0 - 018AF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0 },
+ /* idx=61: 01930 - 0193F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 230, 220, 0, 0, 0, 0 },
+ /* idx=62: 01A10 - 01A1F */
+ {0, 0, 0, 0, 0, 0, 0, 230, 220, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=63: 01A60 - 01A6F */
+ {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=64: 01A70 - 01A7F */
+ {0, 0, 0, 0, 0, 230, 230, 230, 230, 230, 230, 230, 230, 0, 0, 220 },
+ /* idx=65: 01B30 - 01B3F */
+ {0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=66: 01B40 - 01B4F */
+ {0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=67: 01B60 - 01B6F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 220, 230, 230, 230 },
+ /* idx=68: 01B70 - 01B7F */
+ {230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=69: 01BA0 - 01BAF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0 },
+ /* idx=70: 01BE0 - 01BEF */
+ {0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=71: 01BF0 - 01BFF */
+ {0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=72: 01C30 - 01C3F */
+ {0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=73: 01CD0 - 01CDF */
+ {230, 230, 230, 0, 1, 220, 220, 220, 220, 220, 230, 230, 220, 220, 220, 220 },
+ /* idx=74: 01CE0 - 01CEF */
+ {230, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 220, 0, 0 },
+ /* idx=75: 01DC0 - 01DCF */
+ {230, 230, 220, 230, 230, 230, 230, 230, 230, 230, 220, 230, 230, 234, 214, 220 },
+ /* idx=76: 01DD0 - 01DDF */
+ {202, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=77: 01DE0 - 01DEF */
+ {230, 230, 230, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=78: 01DF0 - 01DFF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 220, 230, 220 },
+ /* idx=79: 020D0 - 020DF */
+ {230, 230, 1, 1, 230, 230, 230, 230, 1, 1, 1, 230, 230, 0, 0, 0 },
+ /* idx=80: 020E0 - 020EF */
+ {0, 230, 0, 0, 0, 1, 1, 230, 220, 230, 1, 1, 220, 220, 220, 220 },
+ /* idx=81: 020F0 - 020FF */
+ {230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=82: 02CE0 - 02CEF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230 },
+ /* idx=83: 02CF0 - 02CFF */
+ {230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=84: 02D70 - 02D7F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9 },
+ /* idx=85: 02DE0 - 02DEF */
+ {230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=86: 02DF0 - 02DFF */
+ {230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=87: 03020 - 0302F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 228, 232, 222, 224, 224 },
+ /* idx=88: 03090 - 0309F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0 },
+ /* idx=89: 0A660 - 0A66F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230 },
+ /* idx=90: 0A670 - 0A67F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 0, 0 },
+ /* idx=91: 0A6F0 - 0A6FF */
+ {230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=92: 0A800 - 0A80F */
+ {0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=93: 0A8C0 - 0A8CF */
+ {0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=94: 0A8E0 - 0A8EF */
+ {230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=95: 0A8F0 - 0A8FF */
+ {230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=96: 0A920 - 0A92F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 220, 0, 0 },
+ /* idx=97: 0A950 - 0A95F */
+ {0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=98: 0A9B0 - 0A9BF */
+ {0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=99: 0A9C0 - 0A9CF */
+ {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=100: 0AAB0 - 0AABF */
+ {230, 0, 230, 230, 220, 0, 0, 230, 230, 0, 0, 0, 0, 0, 230, 230 },
+ /* idx=101: 0AAC0 - 0AACF */
+ {0, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=102: 0ABE0 - 0ABEF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=103: 0FB10 - 0FB1F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 0 },
+ /* idx=104: 0FE20 - 0FE2F */
+ {230, 230, 230, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=105: 101F0 - 101FF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 0 },
+ /* idx=106: 10A00 - 10A0F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 230 },
+ /* idx=107: 10A30 - 10A3F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 230, 1, 220, 0, 0, 0, 0, 9 },
+ /* idx=108: 11040 - 1104F */
+ {0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=109: 110B0 - 110BF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 7, 0, 0, 0, 0, 0 },
+ /* idx=110: 1D160 - 1D16F */
+ {0, 0, 0, 0, 0, 216, 216, 1, 1, 1, 0, 0, 0, 226, 216, 216 },
+ /* idx=111: 1D170 - 1D17F */
+ {216, 216, 216, 0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 220, 220, 220 },
+ /* idx=112: 1D180 - 1D18F */
+ {220, 220, 220, 0, 0, 230, 230, 230, 230, 230, 220, 220, 0, 0, 0, 0 },
+ /* idx=113: 1D1A0 - 1D1AF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 0, 0 },
+ /* idx=114: 1D240 - 1D24F */
+ {0, 0, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+/* The index table to ccc_val[*][16] */
+static const unsigned char ccc_val_index[][16] = {
+ /* idx=0: XXX00 - XXXFF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=1: 00300 - 003FF */
+ { 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=2: 00400 - 004FF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=3: 00500 - 005FF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,10,11,12, 0, 0, 0 },
+ /* idx=4: 00600 - 006FF */
+ { 0,13, 0, 0,14,15, 0,16, 0, 0, 0, 0, 0,17,18, 0 },
+ /* idx=5: 00700 - 007FF */
+ { 0,19, 0,20,21, 0, 0, 0, 0, 0, 0, 0, 0, 0,22,23 },
+ /* idx=6: 00800 - 008FF */
+ { 0,24,25, 0, 0,26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=7: 00900 - 009FF */
+ { 0, 0, 0,27,28,29, 0, 0, 0, 0, 0,30,31, 0, 0, 0 },
+ /* idx=8: 00A00 - 00AFF */
+ { 0, 0, 0,32,33, 0, 0, 0, 0, 0, 0,34,35, 0, 0, 0 },
+ /* idx=9: 00B00 - 00BFF */
+ { 0, 0, 0,36,37, 0, 0, 0, 0, 0, 0, 0,38, 0, 0, 0 },
+ /* idx=10: 00C00 - 00CFF */
+ { 0, 0, 0, 0,39,40, 0, 0, 0, 0, 0,41,42, 0, 0, 0 },
+ /* idx=11: 00D00 - 00DFF */
+ { 0, 0, 0, 0,43, 0, 0, 0, 0, 0, 0, 0,44, 0, 0, 0 },
+ /* idx=12: 00E00 - 00EFF */
+ { 0, 0, 0,45,46, 0, 0, 0, 0, 0, 0,47,48, 0, 0, 0 },
+ /* idx=13: 00F00 - 00FFF */
+ { 0,49, 0,50, 0, 0, 0,51,52, 0, 0, 0,53, 0, 0, 0 },
+ /* idx=14: 01000 - 010FF */
+ { 0, 0, 0,54, 0, 0, 0, 0,55, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=15: 01300 - 013FF */
+ { 0, 0, 0, 0, 0,56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=16: 01700 - 017FF */
+ { 0,57, 0,58, 0, 0, 0, 0, 0, 0, 0, 0, 0,59, 0, 0 },
+ /* idx=17: 01800 - 018FF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,60, 0, 0, 0, 0, 0 },
+ /* idx=18: 01900 - 019FF */
+ { 0, 0, 0,61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=19: 01A00 - 01AFF */
+ { 0,62, 0, 0, 0, 0,63,64, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=20: 01B00 - 01BFF */
+ { 0, 0, 0,65,66, 0,67,68, 0, 0,69, 0, 0, 0,70,71 },
+ /* idx=21: 01C00 - 01CFF */
+ { 0, 0, 0,72, 0, 0, 0, 0, 0, 0, 0, 0, 0,73,74, 0 },
+ /* idx=22: 01D00 - 01DFF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,75,76,77,78 },
+ /* idx=23: 02000 - 020FF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,79,80,81 },
+ /* idx=24: 02C00 - 02CFF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,82,83 },
+ /* idx=25: 02D00 - 02DFF */
+ { 0, 0, 0, 0, 0, 0, 0,84, 0, 0, 0, 0, 0, 0,85,86 },
+ /* idx=26: 03000 - 030FF */
+ { 0, 0,87, 0, 0, 0, 0, 0, 0,88, 0, 0, 0, 0, 0, 0 },
+ /* idx=27: 0A600 - 0A6FF */
+ { 0, 0, 0, 0, 0, 0,89,90, 0, 0, 0, 0, 0, 0, 0,91 },
+ /* idx=28: 0A800 - 0A8FF */
+ {92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,93, 0,94,95 },
+ /* idx=29: 0A900 - 0A9FF */
+ { 0, 0,96, 0, 0,97, 0, 0, 0, 0, 0,98,99, 0, 0, 0 },
+ /* idx=30: 0AA00 - 0AAFF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,100,101, 0, 0, 0 },
+ /* idx=31: 0AB00 - 0ABFF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,102, 0 },
+ /* idx=32: 0FB00 - 0FBFF */
+ { 0,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=33: 0FE00 - 0FEFF */
+ { 0, 0,104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=34: 10100 - 101FF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105 },
+ /* idx=35: 10A00 - 10AFF */
+ {106, 0, 0,107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=36: 11000 - 110FF */
+ { 0, 0, 0, 0,108, 0, 0, 0, 0, 0, 0,109, 0, 0, 0, 0 },
+ /* idx=37: 1D100 - 1D1FF */
+ { 0, 0, 0, 0, 0, 0,110,111,112, 0,113, 0, 0, 0, 0, 0 },
+ /* idx=38: 1D200 - 1D2FF */
+ { 0, 0, 0, 0,114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+/* The index table to ccc_val_index[*][16] */
+static const unsigned char ccc_index[] = {
+ 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, 0, 0,15, 0, 0, 0,16,
+ 17,18,19,20,21,22, 0, 0,23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,24,25, 0, 0,
+ 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,27, 0,
+ 28,29,30,31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,32, 0, 0,33, 0, 0,34, 0, 0, 0, 0, 0, 0,
+ 0, 0,35, 0, 0, 0, 0, 0,36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,37,38,};
+
+struct unicode_decomposition_table {
+ uint32_t nfc;
+ uint32_t cp1;
+ uint32_t cp2;
+};
+
+static const struct unicode_decomposition_table u_decomposition_table[] = {
+ { 0x000C0 , 0x00041 , 0x00300 },
+ { 0x000C1 , 0x00041 , 0x00301 },
+ { 0x000C2 , 0x00041 , 0x00302 },
+ { 0x000C3 , 0x00041 , 0x00303 },
+ { 0x000C4 , 0x00041 , 0x00308 },
+ { 0x000C5 , 0x00041 , 0x0030A },
+ { 0x000C7 , 0x00043 , 0x00327 },
+ { 0x000C8 , 0x00045 , 0x00300 },
+ { 0x000C9 , 0x00045 , 0x00301 },
+ { 0x000CA , 0x00045 , 0x00302 },
+ { 0x000CB , 0x00045 , 0x00308 },
+ { 0x000CC , 0x00049 , 0x00300 },
+ { 0x000CD , 0x00049 , 0x00301 },
+ { 0x000CE , 0x00049 , 0x00302 },
+ { 0x000CF , 0x00049 , 0x00308 },
+ { 0x000D1 , 0x0004E , 0x00303 },
+ { 0x000D2 , 0x0004F , 0x00300 },
+ { 0x000D3 , 0x0004F , 0x00301 },
+ { 0x000D4 , 0x0004F , 0x00302 },
+ { 0x000D5 , 0x0004F , 0x00303 },
+ { 0x000D6 , 0x0004F , 0x00308 },
+ { 0x000D9 , 0x00055 , 0x00300 },
+ { 0x000DA , 0x00055 , 0x00301 },
+ { 0x000DB , 0x00055 , 0x00302 },
+ { 0x000DC , 0x00055 , 0x00308 },
+ { 0x000DD , 0x00059 , 0x00301 },
+ { 0x000E0 , 0x00061 , 0x00300 },
+ { 0x000E1 , 0x00061 , 0x00301 },
+ { 0x000E2 , 0x00061 , 0x00302 },
+ { 0x000E3 , 0x00061 , 0x00303 },
+ { 0x000E4 , 0x00061 , 0x00308 },
+ { 0x000E5 , 0x00061 , 0x0030A },
+ { 0x000E7 , 0x00063 , 0x00327 },
+ { 0x000E8 , 0x00065 , 0x00300 },
+ { 0x000E9 , 0x00065 , 0x00301 },
+ { 0x000EA , 0x00065 , 0x00302 },
+ { 0x000EB , 0x00065 , 0x00308 },
+ { 0x000EC , 0x00069 , 0x00300 },
+ { 0x000ED , 0x00069 , 0x00301 },
+ { 0x000EE , 0x00069 , 0x00302 },
+ { 0x000EF , 0x00069 , 0x00308 },
+ { 0x000F1 , 0x0006E , 0x00303 },
+ { 0x000F2 , 0x0006F , 0x00300 },
+ { 0x000F3 , 0x0006F , 0x00301 },
+ { 0x000F4 , 0x0006F , 0x00302 },
+ { 0x000F5 , 0x0006F , 0x00303 },
+ { 0x000F6 , 0x0006F , 0x00308 },
+ { 0x000F9 , 0x00075 , 0x00300 },
+ { 0x000FA , 0x00075 , 0x00301 },
+ { 0x000FB , 0x00075 , 0x00302 },
+ { 0x000FC , 0x00075 , 0x00308 },
+ { 0x000FD , 0x00079 , 0x00301 },
+ { 0x000FF , 0x00079 , 0x00308 },
+ { 0x00100 , 0x00041 , 0x00304 },
+ { 0x00101 , 0x00061 , 0x00304 },
+ { 0x00102 , 0x00041 , 0x00306 },
+ { 0x00103 , 0x00061 , 0x00306 },
+ { 0x00104 , 0x00041 , 0x00328 },
+ { 0x00105 , 0x00061 , 0x00328 },
+ { 0x00106 , 0x00043 , 0x00301 },
+ { 0x00107 , 0x00063 , 0x00301 },
+ { 0x00108 , 0x00043 , 0x00302 },
+ { 0x00109 , 0x00063 , 0x00302 },
+ { 0x0010A , 0x00043 , 0x00307 },
+ { 0x0010B , 0x00063 , 0x00307 },
+ { 0x0010C , 0x00043 , 0x0030C },
+ { 0x0010D , 0x00063 , 0x0030C },
+ { 0x0010E , 0x00044 , 0x0030C },
+ { 0x0010F , 0x00064 , 0x0030C },
+ { 0x00112 , 0x00045 , 0x00304 },
+ { 0x00113 , 0x00065 , 0x00304 },
+ { 0x00114 , 0x00045 , 0x00306 },
+ { 0x00115 , 0x00065 , 0x00306 },
+ { 0x00116 , 0x00045 , 0x00307 },
+ { 0x00117 , 0x00065 , 0x00307 },
+ { 0x00118 , 0x00045 , 0x00328 },
+ { 0x00119 , 0x00065 , 0x00328 },
+ { 0x0011A , 0x00045 , 0x0030C },
+ { 0x0011B , 0x00065 , 0x0030C },
+ { 0x0011C , 0x00047 , 0x00302 },
+ { 0x0011D , 0x00067 , 0x00302 },
+ { 0x0011E , 0x00047 , 0x00306 },
+ { 0x0011F , 0x00067 , 0x00306 },
+ { 0x00120 , 0x00047 , 0x00307 },
+ { 0x00121 , 0x00067 , 0x00307 },
+ { 0x00122 , 0x00047 , 0x00327 },
+ { 0x00123 , 0x00067 , 0x00327 },
+ { 0x00124 , 0x00048 , 0x00302 },
+ { 0x00125 , 0x00068 , 0x00302 },
+ { 0x00128 , 0x00049 , 0x00303 },
+ { 0x00129 , 0x00069 , 0x00303 },
+ { 0x0012A , 0x00049 , 0x00304 },
+ { 0x0012B , 0x00069 , 0x00304 },
+ { 0x0012C , 0x00049 , 0x00306 },
+ { 0x0012D , 0x00069 , 0x00306 },
+ { 0x0012E , 0x00049 , 0x00328 },
+ { 0x0012F , 0x00069 , 0x00328 },
+ { 0x00130 , 0x00049 , 0x00307 },
+ { 0x00134 , 0x0004A , 0x00302 },
+ { 0x00135 , 0x0006A , 0x00302 },
+ { 0x00136 , 0x0004B , 0x00327 },
+ { 0x00137 , 0x0006B , 0x00327 },
+ { 0x00139 , 0x0004C , 0x00301 },
+ { 0x0013A , 0x0006C , 0x00301 },
+ { 0x0013B , 0x0004C , 0x00327 },
+ { 0x0013C , 0x0006C , 0x00327 },
+ { 0x0013D , 0x0004C , 0x0030C },
+ { 0x0013E , 0x0006C , 0x0030C },
+ { 0x00143 , 0x0004E , 0x00301 },
+ { 0x00144 , 0x0006E , 0x00301 },
+ { 0x00145 , 0x0004E , 0x00327 },
+ { 0x00146 , 0x0006E , 0x00327 },
+ { 0x00147 , 0x0004E , 0x0030C },
+ { 0x00148 , 0x0006E , 0x0030C },
+ { 0x0014C , 0x0004F , 0x00304 },
+ { 0x0014D , 0x0006F , 0x00304 },
+ { 0x0014E , 0x0004F , 0x00306 },
+ { 0x0014F , 0x0006F , 0x00306 },
+ { 0x00150 , 0x0004F , 0x0030B },
+ { 0x00151 , 0x0006F , 0x0030B },
+ { 0x00154 , 0x00052 , 0x00301 },
+ { 0x00155 , 0x00072 , 0x00301 },
+ { 0x00156 , 0x00052 , 0x00327 },
+ { 0x00157 , 0x00072 , 0x00327 },
+ { 0x00158 , 0x00052 , 0x0030C },
+ { 0x00159 , 0x00072 , 0x0030C },
+ { 0x0015A , 0x00053 , 0x00301 },
+ { 0x0015B , 0x00073 , 0x00301 },
+ { 0x0015C , 0x00053 , 0x00302 },
+ { 0x0015D , 0x00073 , 0x00302 },
+ { 0x0015E , 0x00053 , 0x00327 },
+ { 0x0015F , 0x00073 , 0x00327 },
+ { 0x00160 , 0x00053 , 0x0030C },
+ { 0x00161 , 0x00073 , 0x0030C },
+ { 0x00162 , 0x00054 , 0x00327 },
+ { 0x00163 , 0x00074 , 0x00327 },
+ { 0x00164 , 0x00054 , 0x0030C },
+ { 0x00165 , 0x00074 , 0x0030C },
+ { 0x00168 , 0x00055 , 0x00303 },
+ { 0x00169 , 0x00075 , 0x00303 },
+ { 0x0016A , 0x00055 , 0x00304 },
+ { 0x0016B , 0x00075 , 0x00304 },
+ { 0x0016C , 0x00055 , 0x00306 },
+ { 0x0016D , 0x00075 , 0x00306 },
+ { 0x0016E , 0x00055 , 0x0030A },
+ { 0x0016F , 0x00075 , 0x0030A },
+ { 0x00170 , 0x00055 , 0x0030B },
+ { 0x00171 , 0x00075 , 0x0030B },
+ { 0x00172 , 0x00055 , 0x00328 },
+ { 0x00173 , 0x00075 , 0x00328 },
+ { 0x00174 , 0x00057 , 0x00302 },
+ { 0x00175 , 0x00077 , 0x00302 },
+ { 0x00176 , 0x00059 , 0x00302 },
+ { 0x00177 , 0x00079 , 0x00302 },
+ { 0x00178 , 0x00059 , 0x00308 },
+ { 0x00179 , 0x0005A , 0x00301 },
+ { 0x0017A , 0x0007A , 0x00301 },
+ { 0x0017B , 0x0005A , 0x00307 },
+ { 0x0017C , 0x0007A , 0x00307 },
+ { 0x0017D , 0x0005A , 0x0030C },
+ { 0x0017E , 0x0007A , 0x0030C },
+ { 0x001A0 , 0x0004F , 0x0031B },
+ { 0x001A1 , 0x0006F , 0x0031B },
+ { 0x001AF , 0x00055 , 0x0031B },
+ { 0x001B0 , 0x00075 , 0x0031B },
+ { 0x001CD , 0x00041 , 0x0030C },
+ { 0x001CE , 0x00061 , 0x0030C },
+ { 0x001CF , 0x00049 , 0x0030C },
+ { 0x001D0 , 0x00069 , 0x0030C },
+ { 0x001D1 , 0x0004F , 0x0030C },
+ { 0x001D2 , 0x0006F , 0x0030C },
+ { 0x001D3 , 0x00055 , 0x0030C },
+ { 0x001D4 , 0x00075 , 0x0030C },
+ { 0x001D5 , 0x000DC , 0x00304 },
+ { 0x001D6 , 0x000FC , 0x00304 },
+ { 0x001D7 , 0x000DC , 0x00301 },
+ { 0x001D8 , 0x000FC , 0x00301 },
+ { 0x001D9 , 0x000DC , 0x0030C },
+ { 0x001DA , 0x000FC , 0x0030C },
+ { 0x001DB , 0x000DC , 0x00300 },
+ { 0x001DC , 0x000FC , 0x00300 },
+ { 0x001DE , 0x000C4 , 0x00304 },
+ { 0x001DF , 0x000E4 , 0x00304 },
+ { 0x001E0 , 0x00226 , 0x00304 },
+ { 0x001E1 , 0x00227 , 0x00304 },
+ { 0x001E2 , 0x000C6 , 0x00304 },
+ { 0x001E3 , 0x000E6 , 0x00304 },
+ { 0x001E6 , 0x00047 , 0x0030C },
+ { 0x001E7 , 0x00067 , 0x0030C },
+ { 0x001E8 , 0x0004B , 0x0030C },
+ { 0x001E9 , 0x0006B , 0x0030C },
+ { 0x001EA , 0x0004F , 0x00328 },
+ { 0x001EB , 0x0006F , 0x00328 },
+ { 0x001EC , 0x001EA , 0x00304 },
+ { 0x001ED , 0x001EB , 0x00304 },
+ { 0x001EE , 0x001B7 , 0x0030C },
+ { 0x001EF , 0x00292 , 0x0030C },
+ { 0x001F0 , 0x0006A , 0x0030C },
+ { 0x001F4 , 0x00047 , 0x00301 },
+ { 0x001F5 , 0x00067 , 0x00301 },
+ { 0x001F8 , 0x0004E , 0x00300 },
+ { 0x001F9 , 0x0006E , 0x00300 },
+ { 0x001FA , 0x000C5 , 0x00301 },
+ { 0x001FB , 0x000E5 , 0x00301 },
+ { 0x001FC , 0x000C6 , 0x00301 },
+ { 0x001FD , 0x000E6 , 0x00301 },
+ { 0x001FE , 0x000D8 , 0x00301 },
+ { 0x001FF , 0x000F8 , 0x00301 },
+ { 0x00200 , 0x00041 , 0x0030F },
+ { 0x00201 , 0x00061 , 0x0030F },
+ { 0x00202 , 0x00041 , 0x00311 },
+ { 0x00203 , 0x00061 , 0x00311 },
+ { 0x00204 , 0x00045 , 0x0030F },
+ { 0x00205 , 0x00065 , 0x0030F },
+ { 0x00206 , 0x00045 , 0x00311 },
+ { 0x00207 , 0x00065 , 0x00311 },
+ { 0x00208 , 0x00049 , 0x0030F },
+ { 0x00209 , 0x00069 , 0x0030F },
+ { 0x0020A , 0x00049 , 0x00311 },
+ { 0x0020B , 0x00069 , 0x00311 },
+ { 0x0020C , 0x0004F , 0x0030F },
+ { 0x0020D , 0x0006F , 0x0030F },
+ { 0x0020E , 0x0004F , 0x00311 },
+ { 0x0020F , 0x0006F , 0x00311 },
+ { 0x00210 , 0x00052 , 0x0030F },
+ { 0x00211 , 0x00072 , 0x0030F },
+ { 0x00212 , 0x00052 , 0x00311 },
+ { 0x00213 , 0x00072 , 0x00311 },
+ { 0x00214 , 0x00055 , 0x0030F },
+ { 0x00215 , 0x00075 , 0x0030F },
+ { 0x00216 , 0x00055 , 0x00311 },
+ { 0x00217 , 0x00075 , 0x00311 },
+ { 0x00218 , 0x00053 , 0x00326 },
+ { 0x00219 , 0x00073 , 0x00326 },
+ { 0x0021A , 0x00054 , 0x00326 },
+ { 0x0021B , 0x00074 , 0x00326 },
+ { 0x0021E , 0x00048 , 0x0030C },
+ { 0x0021F , 0x00068 , 0x0030C },
+ { 0x00226 , 0x00041 , 0x00307 },
+ { 0x00227 , 0x00061 , 0x00307 },
+ { 0x00228 , 0x00045 , 0x00327 },
+ { 0x00229 , 0x00065 , 0x00327 },
+ { 0x0022A , 0x000D6 , 0x00304 },
+ { 0x0022B , 0x000F6 , 0x00304 },
+ { 0x0022C , 0x000D5 , 0x00304 },
+ { 0x0022D , 0x000F5 , 0x00304 },
+ { 0x0022E , 0x0004F , 0x00307 },
+ { 0x0022F , 0x0006F , 0x00307 },
+ { 0x00230 , 0x0022E , 0x00304 },
+ { 0x00231 , 0x0022F , 0x00304 },
+ { 0x00232 , 0x00059 , 0x00304 },
+ { 0x00233 , 0x00079 , 0x00304 },
+ { 0x00385 , 0x000A8 , 0x00301 },
+ { 0x00386 , 0x00391 , 0x00301 },
+ { 0x00388 , 0x00395 , 0x00301 },
+ { 0x00389 , 0x00397 , 0x00301 },
+ { 0x0038A , 0x00399 , 0x00301 },
+ { 0x0038C , 0x0039F , 0x00301 },
+ { 0x0038E , 0x003A5 , 0x00301 },
+ { 0x0038F , 0x003A9 , 0x00301 },
+ { 0x00390 , 0x003CA , 0x00301 },
+ { 0x003AA , 0x00399 , 0x00308 },
+ { 0x003AB , 0x003A5 , 0x00308 },
+ { 0x003AC , 0x003B1 , 0x00301 },
+ { 0x003AD , 0x003B5 , 0x00301 },
+ { 0x003AE , 0x003B7 , 0x00301 },
+ { 0x003AF , 0x003B9 , 0x00301 },
+ { 0x003B0 , 0x003CB , 0x00301 },
+ { 0x003CA , 0x003B9 , 0x00308 },
+ { 0x003CB , 0x003C5 , 0x00308 },
+ { 0x003CC , 0x003BF , 0x00301 },
+ { 0x003CD , 0x003C5 , 0x00301 },
+ { 0x003CE , 0x003C9 , 0x00301 },
+ { 0x003D3 , 0x003D2 , 0x00301 },
+ { 0x003D4 , 0x003D2 , 0x00308 },
+ { 0x00400 , 0x00415 , 0x00300 },
+ { 0x00401 , 0x00415 , 0x00308 },
+ { 0x00403 , 0x00413 , 0x00301 },
+ { 0x00407 , 0x00406 , 0x00308 },
+ { 0x0040C , 0x0041A , 0x00301 },
+ { 0x0040D , 0x00418 , 0x00300 },
+ { 0x0040E , 0x00423 , 0x00306 },
+ { 0x00419 , 0x00418 , 0x00306 },
+ { 0x00439 , 0x00438 , 0x00306 },
+ { 0x00450 , 0x00435 , 0x00300 },
+ { 0x00451 , 0x00435 , 0x00308 },
+ { 0x00453 , 0x00433 , 0x00301 },
+ { 0x00457 , 0x00456 , 0x00308 },
+ { 0x0045C , 0x0043A , 0x00301 },
+ { 0x0045D , 0x00438 , 0x00300 },
+ { 0x0045E , 0x00443 , 0x00306 },
+ { 0x00476 , 0x00474 , 0x0030F },
+ { 0x00477 , 0x00475 , 0x0030F },
+ { 0x004C1 , 0x00416 , 0x00306 },
+ { 0x004C2 , 0x00436 , 0x00306 },
+ { 0x004D0 , 0x00410 , 0x00306 },
+ { 0x004D1 , 0x00430 , 0x00306 },
+ { 0x004D2 , 0x00410 , 0x00308 },
+ { 0x004D3 , 0x00430 , 0x00308 },
+ { 0x004D6 , 0x00415 , 0x00306 },
+ { 0x004D7 , 0x00435 , 0x00306 },
+ { 0x004DA , 0x004D8 , 0x00308 },
+ { 0x004DB , 0x004D9 , 0x00308 },
+ { 0x004DC , 0x00416 , 0x00308 },
+ { 0x004DD , 0x00436 , 0x00308 },
+ { 0x004DE , 0x00417 , 0x00308 },
+ { 0x004DF , 0x00437 , 0x00308 },
+ { 0x004E2 , 0x00418 , 0x00304 },
+ { 0x004E3 , 0x00438 , 0x00304 },
+ { 0x004E4 , 0x00418 , 0x00308 },
+ { 0x004E5 , 0x00438 , 0x00308 },
+ { 0x004E6 , 0x0041E , 0x00308 },
+ { 0x004E7 , 0x0043E , 0x00308 },
+ { 0x004EA , 0x004E8 , 0x00308 },
+ { 0x004EB , 0x004E9 , 0x00308 },
+ { 0x004EC , 0x0042D , 0x00308 },
+ { 0x004ED , 0x0044D , 0x00308 },
+ { 0x004EE , 0x00423 , 0x00304 },
+ { 0x004EF , 0x00443 , 0x00304 },
+ { 0x004F0 , 0x00423 , 0x00308 },
+ { 0x004F1 , 0x00443 , 0x00308 },
+ { 0x004F2 , 0x00423 , 0x0030B },
+ { 0x004F3 , 0x00443 , 0x0030B },
+ { 0x004F4 , 0x00427 , 0x00308 },
+ { 0x004F5 , 0x00447 , 0x00308 },
+ { 0x004F8 , 0x0042B , 0x00308 },
+ { 0x004F9 , 0x0044B , 0x00308 },
+ { 0x00622 , 0x00627 , 0x00653 },
+ { 0x00623 , 0x00627 , 0x00654 },
+ { 0x00624 , 0x00648 , 0x00654 },
+ { 0x00625 , 0x00627 , 0x00655 },
+ { 0x00626 , 0x0064A , 0x00654 },
+ { 0x006C0 , 0x006D5 , 0x00654 },
+ { 0x006C2 , 0x006C1 , 0x00654 },
+ { 0x006D3 , 0x006D2 , 0x00654 },
+ { 0x00929 , 0x00928 , 0x0093C },
+ { 0x00931 , 0x00930 , 0x0093C },
+ { 0x00934 , 0x00933 , 0x0093C },
+ { 0x009CB , 0x009C7 , 0x009BE },
+ { 0x009CC , 0x009C7 , 0x009D7 },
+ { 0x00B48 , 0x00B47 , 0x00B56 },
+ { 0x00B4B , 0x00B47 , 0x00B3E },
+ { 0x00B4C , 0x00B47 , 0x00B57 },
+ { 0x00B94 , 0x00B92 , 0x00BD7 },
+ { 0x00BCA , 0x00BC6 , 0x00BBE },
+ { 0x00BCB , 0x00BC7 , 0x00BBE },
+ { 0x00BCC , 0x00BC6 , 0x00BD7 },
+ { 0x00C48 , 0x00C46 , 0x00C56 },
+ { 0x00CC0 , 0x00CBF , 0x00CD5 },
+ { 0x00CC7 , 0x00CC6 , 0x00CD5 },
+ { 0x00CC8 , 0x00CC6 , 0x00CD6 },
+ { 0x00CCA , 0x00CC6 , 0x00CC2 },
+ { 0x00CCB , 0x00CCA , 0x00CD5 },
+ { 0x00D4A , 0x00D46 , 0x00D3E },
+ { 0x00D4B , 0x00D47 , 0x00D3E },
+ { 0x00D4C , 0x00D46 , 0x00D57 },
+ { 0x00DDA , 0x00DD9 , 0x00DCA },
+ { 0x00DDC , 0x00DD9 , 0x00DCF },
+ { 0x00DDD , 0x00DDC , 0x00DCA },
+ { 0x00DDE , 0x00DD9 , 0x00DDF },
+ { 0x01026 , 0x01025 , 0x0102E },
+ { 0x01B06 , 0x01B05 , 0x01B35 },
+ { 0x01B08 , 0x01B07 , 0x01B35 },
+ { 0x01B0A , 0x01B09 , 0x01B35 },
+ { 0x01B0C , 0x01B0B , 0x01B35 },
+ { 0x01B0E , 0x01B0D , 0x01B35 },
+ { 0x01B12 , 0x01B11 , 0x01B35 },
+ { 0x01B3B , 0x01B3A , 0x01B35 },
+ { 0x01B3D , 0x01B3C , 0x01B35 },
+ { 0x01B40 , 0x01B3E , 0x01B35 },
+ { 0x01B41 , 0x01B3F , 0x01B35 },
+ { 0x01B43 , 0x01B42 , 0x01B35 },
+ { 0x01E00 , 0x00041 , 0x00325 },
+ { 0x01E01 , 0x00061 , 0x00325 },
+ { 0x01E02 , 0x00042 , 0x00307 },
+ { 0x01E03 , 0x00062 , 0x00307 },
+ { 0x01E04 , 0x00042 , 0x00323 },
+ { 0x01E05 , 0x00062 , 0x00323 },
+ { 0x01E06 , 0x00042 , 0x00331 },
+ { 0x01E07 , 0x00062 , 0x00331 },
+ { 0x01E08 , 0x000C7 , 0x00301 },
+ { 0x01E09 , 0x000E7 , 0x00301 },
+ { 0x01E0A , 0x00044 , 0x00307 },
+ { 0x01E0B , 0x00064 , 0x00307 },
+ { 0x01E0C , 0x00044 , 0x00323 },
+ { 0x01E0D , 0x00064 , 0x00323 },
+ { 0x01E0E , 0x00044 , 0x00331 },
+ { 0x01E0F , 0x00064 , 0x00331 },
+ { 0x01E10 , 0x00044 , 0x00327 },
+ { 0x01E11 , 0x00064 , 0x00327 },
+ { 0x01E12 , 0x00044 , 0x0032D },
+ { 0x01E13 , 0x00064 , 0x0032D },
+ { 0x01E14 , 0x00112 , 0x00300 },
+ { 0x01E15 , 0x00113 , 0x00300 },
+ { 0x01E16 , 0x00112 , 0x00301 },
+ { 0x01E17 , 0x00113 , 0x00301 },
+ { 0x01E18 , 0x00045 , 0x0032D },
+ { 0x01E19 , 0x00065 , 0x0032D },
+ { 0x01E1A , 0x00045 , 0x00330 },
+ { 0x01E1B , 0x00065 , 0x00330 },
+ { 0x01E1C , 0x00228 , 0x00306 },
+ { 0x01E1D , 0x00229 , 0x00306 },
+ { 0x01E1E , 0x00046 , 0x00307 },
+ { 0x01E1F , 0x00066 , 0x00307 },
+ { 0x01E20 , 0x00047 , 0x00304 },
+ { 0x01E21 , 0x00067 , 0x00304 },
+ { 0x01E22 , 0x00048 , 0x00307 },
+ { 0x01E23 , 0x00068 , 0x00307 },
+ { 0x01E24 , 0x00048 , 0x00323 },
+ { 0x01E25 , 0x00068 , 0x00323 },
+ { 0x01E26 , 0x00048 , 0x00308 },
+ { 0x01E27 , 0x00068 , 0x00308 },
+ { 0x01E28 , 0x00048 , 0x00327 },
+ { 0x01E29 , 0x00068 , 0x00327 },
+ { 0x01E2A , 0x00048 , 0x0032E },
+ { 0x01E2B , 0x00068 , 0x0032E },
+ { 0x01E2C , 0x00049 , 0x00330 },
+ { 0x01E2D , 0x00069 , 0x00330 },
+ { 0x01E2E , 0x000CF , 0x00301 },
+ { 0x01E2F , 0x000EF , 0x00301 },
+ { 0x01E30 , 0x0004B , 0x00301 },
+ { 0x01E31 , 0x0006B , 0x00301 },
+ { 0x01E32 , 0x0004B , 0x00323 },
+ { 0x01E33 , 0x0006B , 0x00323 },
+ { 0x01E34 , 0x0004B , 0x00331 },
+ { 0x01E35 , 0x0006B , 0x00331 },
+ { 0x01E36 , 0x0004C , 0x00323 },
+ { 0x01E37 , 0x0006C , 0x00323 },
+ { 0x01E38 , 0x01E36 , 0x00304 },
+ { 0x01E39 , 0x01E37 , 0x00304 },
+ { 0x01E3A , 0x0004C , 0x00331 },
+ { 0x01E3B , 0x0006C , 0x00331 },
+ { 0x01E3C , 0x0004C , 0x0032D },
+ { 0x01E3D , 0x0006C , 0x0032D },
+ { 0x01E3E , 0x0004D , 0x00301 },
+ { 0x01E3F , 0x0006D , 0x00301 },
+ { 0x01E40 , 0x0004D , 0x00307 },
+ { 0x01E41 , 0x0006D , 0x00307 },
+ { 0x01E42 , 0x0004D , 0x00323 },
+ { 0x01E43 , 0x0006D , 0x00323 },
+ { 0x01E44 , 0x0004E , 0x00307 },
+ { 0x01E45 , 0x0006E , 0x00307 },
+ { 0x01E46 , 0x0004E , 0x00323 },
+ { 0x01E47 , 0x0006E , 0x00323 },
+ { 0x01E48 , 0x0004E , 0x00331 },
+ { 0x01E49 , 0x0006E , 0x00331 },
+ { 0x01E4A , 0x0004E , 0x0032D },
+ { 0x01E4B , 0x0006E , 0x0032D },
+ { 0x01E4C , 0x000D5 , 0x00301 },
+ { 0x01E4D , 0x000F5 , 0x00301 },
+ { 0x01E4E , 0x000D5 , 0x00308 },
+ { 0x01E4F , 0x000F5 , 0x00308 },
+ { 0x01E50 , 0x0014C , 0x00300 },
+ { 0x01E51 , 0x0014D , 0x00300 },
+ { 0x01E52 , 0x0014C , 0x00301 },
+ { 0x01E53 , 0x0014D , 0x00301 },
+ { 0x01E54 , 0x00050 , 0x00301 },
+ { 0x01E55 , 0x00070 , 0x00301 },
+ { 0x01E56 , 0x00050 , 0x00307 },
+ { 0x01E57 , 0x00070 , 0x00307 },
+ { 0x01E58 , 0x00052 , 0x00307 },
+ { 0x01E59 , 0x00072 , 0x00307 },
+ { 0x01E5A , 0x00052 , 0x00323 },
+ { 0x01E5B , 0x00072 , 0x00323 },
+ { 0x01E5C , 0x01E5A , 0x00304 },
+ { 0x01E5D , 0x01E5B , 0x00304 },
+ { 0x01E5E , 0x00052 , 0x00331 },
+ { 0x01E5F , 0x00072 , 0x00331 },
+ { 0x01E60 , 0x00053 , 0x00307 },
+ { 0x01E61 , 0x00073 , 0x00307 },
+ { 0x01E62 , 0x00053 , 0x00323 },
+ { 0x01E63 , 0x00073 , 0x00323 },
+ { 0x01E64 , 0x0015A , 0x00307 },
+ { 0x01E65 , 0x0015B , 0x00307 },
+ { 0x01E66 , 0x00160 , 0x00307 },
+ { 0x01E67 , 0x00161 , 0x00307 },
+ { 0x01E68 , 0x01E62 , 0x00307 },
+ { 0x01E69 , 0x01E63 , 0x00307 },
+ { 0x01E6A , 0x00054 , 0x00307 },
+ { 0x01E6B , 0x00074 , 0x00307 },
+ { 0x01E6C , 0x00054 , 0x00323 },
+ { 0x01E6D , 0x00074 , 0x00323 },
+ { 0x01E6E , 0x00054 , 0x00331 },
+ { 0x01E6F , 0x00074 , 0x00331 },
+ { 0x01E70 , 0x00054 , 0x0032D },
+ { 0x01E71 , 0x00074 , 0x0032D },
+ { 0x01E72 , 0x00055 , 0x00324 },
+ { 0x01E73 , 0x00075 , 0x00324 },
+ { 0x01E74 , 0x00055 , 0x00330 },
+ { 0x01E75 , 0x00075 , 0x00330 },
+ { 0x01E76 , 0x00055 , 0x0032D },
+ { 0x01E77 , 0x00075 , 0x0032D },
+ { 0x01E78 , 0x00168 , 0x00301 },
+ { 0x01E79 , 0x00169 , 0x00301 },
+ { 0x01E7A , 0x0016A , 0x00308 },
+ { 0x01E7B , 0x0016B , 0x00308 },
+ { 0x01E7C , 0x00056 , 0x00303 },
+ { 0x01E7D , 0x00076 , 0x00303 },
+ { 0x01E7E , 0x00056 , 0x00323 },
+ { 0x01E7F , 0x00076 , 0x00323 },
+ { 0x01E80 , 0x00057 , 0x00300 },
+ { 0x01E81 , 0x00077 , 0x00300 },
+ { 0x01E82 , 0x00057 , 0x00301 },
+ { 0x01E83 , 0x00077 , 0x00301 },
+ { 0x01E84 , 0x00057 , 0x00308 },
+ { 0x01E85 , 0x00077 , 0x00308 },
+ { 0x01E86 , 0x00057 , 0x00307 },
+ { 0x01E87 , 0x00077 , 0x00307 },
+ { 0x01E88 , 0x00057 , 0x00323 },
+ { 0x01E89 , 0x00077 , 0x00323 },
+ { 0x01E8A , 0x00058 , 0x00307 },
+ { 0x01E8B , 0x00078 , 0x00307 },
+ { 0x01E8C , 0x00058 , 0x00308 },
+ { 0x01E8D , 0x00078 , 0x00308 },
+ { 0x01E8E , 0x00059 , 0x00307 },
+ { 0x01E8F , 0x00079 , 0x00307 },
+ { 0x01E90 , 0x0005A , 0x00302 },
+ { 0x01E91 , 0x0007A , 0x00302 },
+ { 0x01E92 , 0x0005A , 0x00323 },
+ { 0x01E93 , 0x0007A , 0x00323 },
+ { 0x01E94 , 0x0005A , 0x00331 },
+ { 0x01E95 , 0x0007A , 0x00331 },
+ { 0x01E96 , 0x00068 , 0x00331 },
+ { 0x01E97 , 0x00074 , 0x00308 },
+ { 0x01E98 , 0x00077 , 0x0030A },
+ { 0x01E99 , 0x00079 , 0x0030A },
+ { 0x01E9B , 0x0017F , 0x00307 },
+ { 0x01EA0 , 0x00041 , 0x00323 },
+ { 0x01EA1 , 0x00061 , 0x00323 },
+ { 0x01EA2 , 0x00041 , 0x00309 },
+ { 0x01EA3 , 0x00061 , 0x00309 },
+ { 0x01EA4 , 0x000C2 , 0x00301 },
+ { 0x01EA5 , 0x000E2 , 0x00301 },
+ { 0x01EA6 , 0x000C2 , 0x00300 },
+ { 0x01EA7 , 0x000E2 , 0x00300 },
+ { 0x01EA8 , 0x000C2 , 0x00309 },
+ { 0x01EA9 , 0x000E2 , 0x00309 },
+ { 0x01EAA , 0x000C2 , 0x00303 },
+ { 0x01EAB , 0x000E2 , 0x00303 },
+ { 0x01EAC , 0x01EA0 , 0x00302 },
+ { 0x01EAD , 0x01EA1 , 0x00302 },
+ { 0x01EAE , 0x00102 , 0x00301 },
+ { 0x01EAF , 0x00103 , 0x00301 },
+ { 0x01EB0 , 0x00102 , 0x00300 },
+ { 0x01EB1 , 0x00103 , 0x00300 },
+ { 0x01EB2 , 0x00102 , 0x00309 },
+ { 0x01EB3 , 0x00103 , 0x00309 },
+ { 0x01EB4 , 0x00102 , 0x00303 },
+ { 0x01EB5 , 0x00103 , 0x00303 },
+ { 0x01EB6 , 0x01EA0 , 0x00306 },
+ { 0x01EB7 , 0x01EA1 , 0x00306 },
+ { 0x01EB8 , 0x00045 , 0x00323 },
+ { 0x01EB9 , 0x00065 , 0x00323 },
+ { 0x01EBA , 0x00045 , 0x00309 },
+ { 0x01EBB , 0x00065 , 0x00309 },
+ { 0x01EBC , 0x00045 , 0x00303 },
+ { 0x01EBD , 0x00065 , 0x00303 },
+ { 0x01EBE , 0x000CA , 0x00301 },
+ { 0x01EBF , 0x000EA , 0x00301 },
+ { 0x01EC0 , 0x000CA , 0x00300 },
+ { 0x01EC1 , 0x000EA , 0x00300 },
+ { 0x01EC2 , 0x000CA , 0x00309 },
+ { 0x01EC3 , 0x000EA , 0x00309 },
+ { 0x01EC4 , 0x000CA , 0x00303 },
+ { 0x01EC5 , 0x000EA , 0x00303 },
+ { 0x01EC6 , 0x01EB8 , 0x00302 },
+ { 0x01EC7 , 0x01EB9 , 0x00302 },
+ { 0x01EC8 , 0x00049 , 0x00309 },
+ { 0x01EC9 , 0x00069 , 0x00309 },
+ { 0x01ECA , 0x00049 , 0x00323 },
+ { 0x01ECB , 0x00069 , 0x00323 },
+ { 0x01ECC , 0x0004F , 0x00323 },
+ { 0x01ECD , 0x0006F , 0x00323 },
+ { 0x01ECE , 0x0004F , 0x00309 },
+ { 0x01ECF , 0x0006F , 0x00309 },
+ { 0x01ED0 , 0x000D4 , 0x00301 },
+ { 0x01ED1 , 0x000F4 , 0x00301 },
+ { 0x01ED2 , 0x000D4 , 0x00300 },
+ { 0x01ED3 , 0x000F4 , 0x00300 },
+ { 0x01ED4 , 0x000D4 , 0x00309 },
+ { 0x01ED5 , 0x000F4 , 0x00309 },
+ { 0x01ED6 , 0x000D4 , 0x00303 },
+ { 0x01ED7 , 0x000F4 , 0x00303 },
+ { 0x01ED8 , 0x01ECC , 0x00302 },
+ { 0x01ED9 , 0x01ECD , 0x00302 },
+ { 0x01EDA , 0x001A0 , 0x00301 },
+ { 0x01EDB , 0x001A1 , 0x00301 },
+ { 0x01EDC , 0x001A0 , 0x00300 },
+ { 0x01EDD , 0x001A1 , 0x00300 },
+ { 0x01EDE , 0x001A0 , 0x00309 },
+ { 0x01EDF , 0x001A1 , 0x00309 },
+ { 0x01EE0 , 0x001A0 , 0x00303 },
+ { 0x01EE1 , 0x001A1 , 0x00303 },
+ { 0x01EE2 , 0x001A0 , 0x00323 },
+ { 0x01EE3 , 0x001A1 , 0x00323 },
+ { 0x01EE4 , 0x00055 , 0x00323 },
+ { 0x01EE5 , 0x00075 , 0x00323 },
+ { 0x01EE6 , 0x00055 , 0x00309 },
+ { 0x01EE7 , 0x00075 , 0x00309 },
+ { 0x01EE8 , 0x001AF , 0x00301 },
+ { 0x01EE9 , 0x001B0 , 0x00301 },
+ { 0x01EEA , 0x001AF , 0x00300 },
+ { 0x01EEB , 0x001B0 , 0x00300 },
+ { 0x01EEC , 0x001AF , 0x00309 },
+ { 0x01EED , 0x001B0 , 0x00309 },
+ { 0x01EEE , 0x001AF , 0x00303 },
+ { 0x01EEF , 0x001B0 , 0x00303 },
+ { 0x01EF0 , 0x001AF , 0x00323 },
+ { 0x01EF1 , 0x001B0 , 0x00323 },
+ { 0x01EF2 , 0x00059 , 0x00300 },
+ { 0x01EF3 , 0x00079 , 0x00300 },
+ { 0x01EF4 , 0x00059 , 0x00323 },
+ { 0x01EF5 , 0x00079 , 0x00323 },
+ { 0x01EF6 , 0x00059 , 0x00309 },
+ { 0x01EF7 , 0x00079 , 0x00309 },
+ { 0x01EF8 , 0x00059 , 0x00303 },
+ { 0x01EF9 , 0x00079 , 0x00303 },
+ { 0x01F00 , 0x003B1 , 0x00313 },
+ { 0x01F01 , 0x003B1 , 0x00314 },
+ { 0x01F02 , 0x01F00 , 0x00300 },
+ { 0x01F03 , 0x01F01 , 0x00300 },
+ { 0x01F04 , 0x01F00 , 0x00301 },
+ { 0x01F05 , 0x01F01 , 0x00301 },
+ { 0x01F06 , 0x01F00 , 0x00342 },
+ { 0x01F07 , 0x01F01 , 0x00342 },
+ { 0x01F08 , 0x00391 , 0x00313 },
+ { 0x01F09 , 0x00391 , 0x00314 },
+ { 0x01F0A , 0x01F08 , 0x00300 },
+ { 0x01F0B , 0x01F09 , 0x00300 },
+ { 0x01F0C , 0x01F08 , 0x00301 },
+ { 0x01F0D , 0x01F09 , 0x00301 },
+ { 0x01F0E , 0x01F08 , 0x00342 },
+ { 0x01F0F , 0x01F09 , 0x00342 },
+ { 0x01F10 , 0x003B5 , 0x00313 },
+ { 0x01F11 , 0x003B5 , 0x00314 },
+ { 0x01F12 , 0x01F10 , 0x00300 },
+ { 0x01F13 , 0x01F11 , 0x00300 },
+ { 0x01F14 , 0x01F10 , 0x00301 },
+ { 0x01F15 , 0x01F11 , 0x00301 },
+ { 0x01F18 , 0x00395 , 0x00313 },
+ { 0x01F19 , 0x00395 , 0x00314 },
+ { 0x01F1A , 0x01F18 , 0x00300 },
+ { 0x01F1B , 0x01F19 , 0x00300 },
+ { 0x01F1C , 0x01F18 , 0x00301 },
+ { 0x01F1D , 0x01F19 , 0x00301 },
+ { 0x01F20 , 0x003B7 , 0x00313 },
+ { 0x01F21 , 0x003B7 , 0x00314 },
+ { 0x01F22 , 0x01F20 , 0x00300 },
+ { 0x01F23 , 0x01F21 , 0x00300 },
+ { 0x01F24 , 0x01F20 , 0x00301 },
+ { 0x01F25 , 0x01F21 , 0x00301 },
+ { 0x01F26 , 0x01F20 , 0x00342 },
+ { 0x01F27 , 0x01F21 , 0x00342 },
+ { 0x01F28 , 0x00397 , 0x00313 },
+ { 0x01F29 , 0x00397 , 0x00314 },
+ { 0x01F2A , 0x01F28 , 0x00300 },
+ { 0x01F2B , 0x01F29 , 0x00300 },
+ { 0x01F2C , 0x01F28 , 0x00301 },
+ { 0x01F2D , 0x01F29 , 0x00301 },
+ { 0x01F2E , 0x01F28 , 0x00342 },
+ { 0x01F2F , 0x01F29 , 0x00342 },
+ { 0x01F30 , 0x003B9 , 0x00313 },
+ { 0x01F31 , 0x003B9 , 0x00314 },
+ { 0x01F32 , 0x01F30 , 0x00300 },
+ { 0x01F33 , 0x01F31 , 0x00300 },
+ { 0x01F34 , 0x01F30 , 0x00301 },
+ { 0x01F35 , 0x01F31 , 0x00301 },
+ { 0x01F36 , 0x01F30 , 0x00342 },
+ { 0x01F37 , 0x01F31 , 0x00342 },
+ { 0x01F38 , 0x00399 , 0x00313 },
+ { 0x01F39 , 0x00399 , 0x00314 },
+ { 0x01F3A , 0x01F38 , 0x00300 },
+ { 0x01F3B , 0x01F39 , 0x00300 },
+ { 0x01F3C , 0x01F38 , 0x00301 },
+ { 0x01F3D , 0x01F39 , 0x00301 },
+ { 0x01F3E , 0x01F38 , 0x00342 },
+ { 0x01F3F , 0x01F39 , 0x00342 },
+ { 0x01F40 , 0x003BF , 0x00313 },
+ { 0x01F41 , 0x003BF , 0x00314 },
+ { 0x01F42 , 0x01F40 , 0x00300 },
+ { 0x01F43 , 0x01F41 , 0x00300 },
+ { 0x01F44 , 0x01F40 , 0x00301 },
+ { 0x01F45 , 0x01F41 , 0x00301 },
+ { 0x01F48 , 0x0039F , 0x00313 },
+ { 0x01F49 , 0x0039F , 0x00314 },
+ { 0x01F4A , 0x01F48 , 0x00300 },
+ { 0x01F4B , 0x01F49 , 0x00300 },
+ { 0x01F4C , 0x01F48 , 0x00301 },
+ { 0x01F4D , 0x01F49 , 0x00301 },
+ { 0x01F50 , 0x003C5 , 0x00313 },
+ { 0x01F51 , 0x003C5 , 0x00314 },
+ { 0x01F52 , 0x01F50 , 0x00300 },
+ { 0x01F53 , 0x01F51 , 0x00300 },
+ { 0x01F54 , 0x01F50 , 0x00301 },
+ { 0x01F55 , 0x01F51 , 0x00301 },
+ { 0x01F56 , 0x01F50 , 0x00342 },
+ { 0x01F57 , 0x01F51 , 0x00342 },
+ { 0x01F59 , 0x003A5 , 0x00314 },
+ { 0x01F5B , 0x01F59 , 0x00300 },
+ { 0x01F5D , 0x01F59 , 0x00301 },
+ { 0x01F5F , 0x01F59 , 0x00342 },
+ { 0x01F60 , 0x003C9 , 0x00313 },
+ { 0x01F61 , 0x003C9 , 0x00314 },
+ { 0x01F62 , 0x01F60 , 0x00300 },
+ { 0x01F63 , 0x01F61 , 0x00300 },
+ { 0x01F64 , 0x01F60 , 0x00301 },
+ { 0x01F65 , 0x01F61 , 0x00301 },
+ { 0x01F66 , 0x01F60 , 0x00342 },
+ { 0x01F67 , 0x01F61 , 0x00342 },
+ { 0x01F68 , 0x003A9 , 0x00313 },
+ { 0x01F69 , 0x003A9 , 0x00314 },
+ { 0x01F6A , 0x01F68 , 0x00300 },
+ { 0x01F6B , 0x01F69 , 0x00300 },
+ { 0x01F6C , 0x01F68 , 0x00301 },
+ { 0x01F6D , 0x01F69 , 0x00301 },
+ { 0x01F6E , 0x01F68 , 0x00342 },
+ { 0x01F6F , 0x01F69 , 0x00342 },
+ { 0x01F70 , 0x003B1 , 0x00300 },
+ { 0x01F72 , 0x003B5 , 0x00300 },
+ { 0x01F74 , 0x003B7 , 0x00300 },
+ { 0x01F76 , 0x003B9 , 0x00300 },
+ { 0x01F78 , 0x003BF , 0x00300 },
+ { 0x01F7A , 0x003C5 , 0x00300 },
+ { 0x01F7C , 0x003C9 , 0x00300 },
+ { 0x01F80 , 0x01F00 , 0x00345 },
+ { 0x01F81 , 0x01F01 , 0x00345 },
+ { 0x01F82 , 0x01F02 , 0x00345 },
+ { 0x01F83 , 0x01F03 , 0x00345 },
+ { 0x01F84 , 0x01F04 , 0x00345 },
+ { 0x01F85 , 0x01F05 , 0x00345 },
+ { 0x01F86 , 0x01F06 , 0x00345 },
+ { 0x01F87 , 0x01F07 , 0x00345 },
+ { 0x01F88 , 0x01F08 , 0x00345 },
+ { 0x01F89 , 0x01F09 , 0x00345 },
+ { 0x01F8A , 0x01F0A , 0x00345 },
+ { 0x01F8B , 0x01F0B , 0x00345 },
+ { 0x01F8C , 0x01F0C , 0x00345 },
+ { 0x01F8D , 0x01F0D , 0x00345 },
+ { 0x01F8E , 0x01F0E , 0x00345 },
+ { 0x01F8F , 0x01F0F , 0x00345 },
+ { 0x01F90 , 0x01F20 , 0x00345 },
+ { 0x01F91 , 0x01F21 , 0x00345 },
+ { 0x01F92 , 0x01F22 , 0x00345 },
+ { 0x01F93 , 0x01F23 , 0x00345 },
+ { 0x01F94 , 0x01F24 , 0x00345 },
+ { 0x01F95 , 0x01F25 , 0x00345 },
+ { 0x01F96 , 0x01F26 , 0x00345 },
+ { 0x01F97 , 0x01F27 , 0x00345 },
+ { 0x01F98 , 0x01F28 , 0x00345 },
+ { 0x01F99 , 0x01F29 , 0x00345 },
+ { 0x01F9A , 0x01F2A , 0x00345 },
+ { 0x01F9B , 0x01F2B , 0x00345 },
+ { 0x01F9C , 0x01F2C , 0x00345 },
+ { 0x01F9D , 0x01F2D , 0x00345 },
+ { 0x01F9E , 0x01F2E , 0x00345 },
+ { 0x01F9F , 0x01F2F , 0x00345 },
+ { 0x01FA0 , 0x01F60 , 0x00345 },
+ { 0x01FA1 , 0x01F61 , 0x00345 },
+ { 0x01FA2 , 0x01F62 , 0x00345 },
+ { 0x01FA3 , 0x01F63 , 0x00345 },
+ { 0x01FA4 , 0x01F64 , 0x00345 },
+ { 0x01FA5 , 0x01F65 , 0x00345 },
+ { 0x01FA6 , 0x01F66 , 0x00345 },
+ { 0x01FA7 , 0x01F67 , 0x00345 },
+ { 0x01FA8 , 0x01F68 , 0x00345 },
+ { 0x01FA9 , 0x01F69 , 0x00345 },
+ { 0x01FAA , 0x01F6A , 0x00345 },
+ { 0x01FAB , 0x01F6B , 0x00345 },
+ { 0x01FAC , 0x01F6C , 0x00345 },
+ { 0x01FAD , 0x01F6D , 0x00345 },
+ { 0x01FAE , 0x01F6E , 0x00345 },
+ { 0x01FAF , 0x01F6F , 0x00345 },
+ { 0x01FB0 , 0x003B1 , 0x00306 },
+ { 0x01FB1 , 0x003B1 , 0x00304 },
+ { 0x01FB2 , 0x01F70 , 0x00345 },
+ { 0x01FB3 , 0x003B1 , 0x00345 },
+ { 0x01FB4 , 0x003AC , 0x00345 },
+ { 0x01FB6 , 0x003B1 , 0x00342 },
+ { 0x01FB7 , 0x01FB6 , 0x00345 },
+ { 0x01FB8 , 0x00391 , 0x00306 },
+ { 0x01FB9 , 0x00391 , 0x00304 },
+ { 0x01FBA , 0x00391 , 0x00300 },
+ { 0x01FBC , 0x00391 , 0x00345 },
+ { 0x01FC1 , 0x000A8 , 0x00342 },
+ { 0x01FC2 , 0x01F74 , 0x00345 },
+ { 0x01FC3 , 0x003B7 , 0x00345 },
+ { 0x01FC4 , 0x003AE , 0x00345 },
+ { 0x01FC6 , 0x003B7 , 0x00342 },
+ { 0x01FC7 , 0x01FC6 , 0x00345 },
+ { 0x01FC8 , 0x00395 , 0x00300 },
+ { 0x01FCA , 0x00397 , 0x00300 },
+ { 0x01FCC , 0x00397 , 0x00345 },
+ { 0x01FCD , 0x01FBF , 0x00300 },
+ { 0x01FCE , 0x01FBF , 0x00301 },
+ { 0x01FCF , 0x01FBF , 0x00342 },
+ { 0x01FD0 , 0x003B9 , 0x00306 },
+ { 0x01FD1 , 0x003B9 , 0x00304 },
+ { 0x01FD2 , 0x003CA , 0x00300 },
+ { 0x01FD6 , 0x003B9 , 0x00342 },
+ { 0x01FD7 , 0x003CA , 0x00342 },
+ { 0x01FD8 , 0x00399 , 0x00306 },
+ { 0x01FD9 , 0x00399 , 0x00304 },
+ { 0x01FDA , 0x00399 , 0x00300 },
+ { 0x01FDD , 0x01FFE , 0x00300 },
+ { 0x01FDE , 0x01FFE , 0x00301 },
+ { 0x01FDF , 0x01FFE , 0x00342 },
+ { 0x01FE0 , 0x003C5 , 0x00306 },
+ { 0x01FE1 , 0x003C5 , 0x00304 },
+ { 0x01FE2 , 0x003CB , 0x00300 },
+ { 0x01FE4 , 0x003C1 , 0x00313 },
+ { 0x01FE5 , 0x003C1 , 0x00314 },
+ { 0x01FE6 , 0x003C5 , 0x00342 },
+ { 0x01FE7 , 0x003CB , 0x00342 },
+ { 0x01FE8 , 0x003A5 , 0x00306 },
+ { 0x01FE9 , 0x003A5 , 0x00304 },
+ { 0x01FEA , 0x003A5 , 0x00300 },
+ { 0x01FEC , 0x003A1 , 0x00314 },
+ { 0x01FED , 0x000A8 , 0x00300 },
+ { 0x01FF2 , 0x01F7C , 0x00345 },
+ { 0x01FF3 , 0x003C9 , 0x00345 },
+ { 0x01FF4 , 0x003CE , 0x00345 },
+ { 0x01FF6 , 0x003C9 , 0x00342 },
+ { 0x01FF7 , 0x01FF6 , 0x00345 },
+ { 0x01FF8 , 0x0039F , 0x00300 },
+ { 0x01FFA , 0x003A9 , 0x00300 },
+ { 0x01FFC , 0x003A9 , 0x00345 },
+ { 0x0219A , 0x02190 , 0x00338 },
+ { 0x0219B , 0x02192 , 0x00338 },
+ { 0x021AE , 0x02194 , 0x00338 },
+ { 0x021CD , 0x021D0 , 0x00338 },
+ { 0x021CE , 0x021D4 , 0x00338 },
+ { 0x021CF , 0x021D2 , 0x00338 },
+ { 0x02204 , 0x02203 , 0x00338 },
+ { 0x02209 , 0x02208 , 0x00338 },
+ { 0x0220C , 0x0220B , 0x00338 },
+ { 0x02224 , 0x02223 , 0x00338 },
+ { 0x02226 , 0x02225 , 0x00338 },
+ { 0x02241 , 0x0223C , 0x00338 },
+ { 0x02244 , 0x02243 , 0x00338 },
+ { 0x02247 , 0x02245 , 0x00338 },
+ { 0x02249 , 0x02248 , 0x00338 },
+ { 0x02260 , 0x0003D , 0x00338 },
+ { 0x02262 , 0x02261 , 0x00338 },
+ { 0x0226D , 0x0224D , 0x00338 },
+ { 0x0226E , 0x0003C , 0x00338 },
+ { 0x0226F , 0x0003E , 0x00338 },
+ { 0x02270 , 0x02264 , 0x00338 },
+ { 0x02271 , 0x02265 , 0x00338 },
+ { 0x02274 , 0x02272 , 0x00338 },
+ { 0x02275 , 0x02273 , 0x00338 },
+ { 0x02278 , 0x02276 , 0x00338 },
+ { 0x02279 , 0x02277 , 0x00338 },
+ { 0x02280 , 0x0227A , 0x00338 },
+ { 0x02281 , 0x0227B , 0x00338 },
+ { 0x02284 , 0x02282 , 0x00338 },
+ { 0x02285 , 0x02283 , 0x00338 },
+ { 0x02288 , 0x02286 , 0x00338 },
+ { 0x02289 , 0x02287 , 0x00338 },
+ { 0x022AC , 0x022A2 , 0x00338 },
+ { 0x022AD , 0x022A8 , 0x00338 },
+ { 0x022AE , 0x022A9 , 0x00338 },
+ { 0x022AF , 0x022AB , 0x00338 },
+ { 0x022E0 , 0x0227C , 0x00338 },
+ { 0x022E1 , 0x0227D , 0x00338 },
+ { 0x022E2 , 0x02291 , 0x00338 },
+ { 0x022E3 , 0x02292 , 0x00338 },
+ { 0x022EA , 0x022B2 , 0x00338 },
+ { 0x022EB , 0x022B3 , 0x00338 },
+ { 0x022EC , 0x022B4 , 0x00338 },
+ { 0x022ED , 0x022B5 , 0x00338 },
+ { 0x0304C , 0x0304B , 0x03099 },
+ { 0x0304E , 0x0304D , 0x03099 },
+ { 0x03050 , 0x0304F , 0x03099 },
+ { 0x03052 , 0x03051 , 0x03099 },
+ { 0x03054 , 0x03053 , 0x03099 },
+ { 0x03056 , 0x03055 , 0x03099 },
+ { 0x03058 , 0x03057 , 0x03099 },
+ { 0x0305A , 0x03059 , 0x03099 },
+ { 0x0305C , 0x0305B , 0x03099 },
+ { 0x0305E , 0x0305D , 0x03099 },
+ { 0x03060 , 0x0305F , 0x03099 },
+ { 0x03062 , 0x03061 , 0x03099 },
+ { 0x03065 , 0x03064 , 0x03099 },
+ { 0x03067 , 0x03066 , 0x03099 },
+ { 0x03069 , 0x03068 , 0x03099 },
+ { 0x03070 , 0x0306F , 0x03099 },
+ { 0x03071 , 0x0306F , 0x0309A },
+ { 0x03073 , 0x03072 , 0x03099 },
+ { 0x03074 , 0x03072 , 0x0309A },
+ { 0x03076 , 0x03075 , 0x03099 },
+ { 0x03077 , 0x03075 , 0x0309A },
+ { 0x03079 , 0x03078 , 0x03099 },
+ { 0x0307A , 0x03078 , 0x0309A },
+ { 0x0307C , 0x0307B , 0x03099 },
+ { 0x0307D , 0x0307B , 0x0309A },
+ { 0x03094 , 0x03046 , 0x03099 },
+ { 0x0309E , 0x0309D , 0x03099 },
+ { 0x030AC , 0x030AB , 0x03099 },
+ { 0x030AE , 0x030AD , 0x03099 },
+ { 0x030B0 , 0x030AF , 0x03099 },
+ { 0x030B2 , 0x030B1 , 0x03099 },
+ { 0x030B4 , 0x030B3 , 0x03099 },
+ { 0x030B6 , 0x030B5 , 0x03099 },
+ { 0x030B8 , 0x030B7 , 0x03099 },
+ { 0x030BA , 0x030B9 , 0x03099 },
+ { 0x030BC , 0x030BB , 0x03099 },
+ { 0x030BE , 0x030BD , 0x03099 },
+ { 0x030C0 , 0x030BF , 0x03099 },
+ { 0x030C2 , 0x030C1 , 0x03099 },
+ { 0x030C5 , 0x030C4 , 0x03099 },
+ { 0x030C7 , 0x030C6 , 0x03099 },
+ { 0x030C9 , 0x030C8 , 0x03099 },
+ { 0x030D0 , 0x030CF , 0x03099 },
+ { 0x030D1 , 0x030CF , 0x0309A },
+ { 0x030D3 , 0x030D2 , 0x03099 },
+ { 0x030D4 , 0x030D2 , 0x0309A },
+ { 0x030D6 , 0x030D5 , 0x03099 },
+ { 0x030D7 , 0x030D5 , 0x0309A },
+ { 0x030D9 , 0x030D8 , 0x03099 },
+ { 0x030DA , 0x030D8 , 0x0309A },
+ { 0x030DC , 0x030DB , 0x03099 },
+ { 0x030DD , 0x030DB , 0x0309A },
+ { 0x030F4 , 0x030A6 , 0x03099 },
+ { 0x030F7 , 0x030EF , 0x03099 },
+ { 0x030F8 , 0x030F0 , 0x03099 },
+ { 0x030F9 , 0x030F1 , 0x03099 },
+ { 0x030FA , 0x030F2 , 0x03099 },
+ { 0x030FE , 0x030FD , 0x03099 },
+ { 0x1109A , 0x11099 , 0x110BA },
+ { 0x1109C , 0x1109B , 0x110BA },
+ { 0x110AB , 0x110A5 , 0x110BA },
+};
+
+#endif /* ARCHIVE_STRING_COMPOSITION_H_INCLUDED */
+
diff --git a/contrib/libs/libarchive/libarchive/archive_string_sprintf.c b/contrib/libs/libarchive/libarchive/archive_string_sprintf.c
new file mode 100644
index 00000000000..969a5603a49
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_string_sprintf.c
@@ -0,0 +1,192 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_string_sprintf.c 189435 2009-03-06 05:14:55Z kientzle $");
+
+/*
+ * The use of printf()-family functions can be troublesome
+ * for space-constrained applications. In addition, correctly
+ * implementing this function in terms of vsnprintf() requires
+ * two calls (one to determine the size, another to format the
+ * result), which in turn requires duplicating the argument list
+ * using va_copy, which isn't yet universally available. <sigh>
+ *
+ * So, I've implemented a bare minimum of printf()-like capability
+ * here. This is only used to format error messages, so doesn't
+ * require any floating-point support or field-width handling.
+ */
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+
+#include "archive_string.h"
+#include "archive_private.h"
+
+/*
+ * Utility functions to format signed/unsigned integers and append
+ * them to an archive_string.
+ */
+static void
+append_uint(struct archive_string *as, uintmax_t d, unsigned base)
+{
+ static const char digits[] = "0123456789abcdef";
+ if (d >= base)
+ append_uint(as, d/base, base);
+ archive_strappend_char(as, digits[d % base]);
+}
+
+static void
+append_int(struct archive_string *as, intmax_t d, unsigned base)
+{
+ uintmax_t ud;
+
+ if (d < 0) {
+ archive_strappend_char(as, '-');
+ ud = (d == INTMAX_MIN) ? (uintmax_t)(INTMAX_MAX) + 1 : (uintmax_t)(-d);
+ } else
+ ud = d;
+ append_uint(as, ud, base);
+}
+
+
+void
+archive_string_sprintf(struct archive_string *as, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ archive_string_vsprintf(as, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Like 'vsprintf', but ensures the target is big enough, resizing if
+ * necessary.
+ */
+void
+archive_string_vsprintf(struct archive_string *as, const char *fmt,
+ va_list ap)
+{
+ char long_flag;
+ intmax_t s; /* Signed integer temp. */
+ uintmax_t u; /* Unsigned integer temp. */
+ const char *p, *p2;
+ const wchar_t *pw;
+
+ if (archive_string_ensure(as, 64) == NULL)
+ __archive_errx(1, "Out of memory");
+
+ if (fmt == NULL) {
+ as->s[0] = 0;
+ return;
+ }
+
+ for (p = fmt; *p != '\0'; p++) {
+ const char *saved_p = p;
+
+ if (*p != '%') {
+ archive_strappend_char(as, *p);
+ continue;
+ }
+
+ p++;
+
+ long_flag = '\0';
+ switch(*p) {
+ case 'j':
+ case 'l':
+ case 'z':
+ long_flag = *p;
+ p++;
+ break;
+ }
+
+ switch (*p) {
+ case '%':
+ archive_strappend_char(as, '%');
+ break;
+ case 'c':
+ s = va_arg(ap, int);
+ archive_strappend_char(as, (char)s);
+ break;
+ case 'd':
+ switch(long_flag) {
+ case 'j': s = va_arg(ap, intmax_t); break;
+ case 'l': s = va_arg(ap, long); break;
+ case 'z': s = va_arg(ap, ssize_t); break;
+ default: s = va_arg(ap, int); break;
+ }
+ append_int(as, s, 10);
+ break;
+ case 's':
+ switch(long_flag) {
+ case 'l':
+ pw = va_arg(ap, wchar_t *);
+ if (pw == NULL)
+ pw = L"(null)";
+ if (archive_string_append_from_wcs(as, pw,
+ wcslen(pw)) != 0 && errno == ENOMEM)
+ __archive_errx(1, "Out of memory");
+ break;
+ default:
+ p2 = va_arg(ap, char *);
+ if (p2 == NULL)
+ p2 = "(null)";
+ archive_strcat(as, p2);
+ break;
+ }
+ break;
+ case 'S':
+ pw = va_arg(ap, wchar_t *);
+ if (pw == NULL)
+ pw = L"(null)";
+ if (archive_string_append_from_wcs(as, pw,
+ wcslen(pw)) != 0 && errno == ENOMEM)
+ __archive_errx(1, "Out of memory");
+ break;
+ case 'o': case 'u': case 'x': case 'X':
+ /* Common handling for unsigned integer formats. */
+ switch(long_flag) {
+ case 'j': u = va_arg(ap, uintmax_t); break;
+ case 'l': u = va_arg(ap, unsigned long); break;
+ case 'z': u = va_arg(ap, size_t); break;
+ default: u = va_arg(ap, unsigned int); break;
+ }
+ /* Format it in the correct base. */
+ switch (*p) {
+ case 'o': append_uint(as, u, 8); break;
+ case 'u': append_uint(as, u, 10); break;
+ default: append_uint(as, u, 16); break;
+ }
+ break;
+ default:
+ /* Rewind and print the initial '%' literally. */
+ p = saved_p;
+ archive_strappend_char(as, *p);
+ }
+ }
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_util.c b/contrib/libs/libarchive/libarchive/archive_util.c
new file mode 100644
index 00000000000..0fd344d05b5
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_util.c
@@ -0,0 +1,704 @@
+/*-
+ * Copyright (c) 2009-2012,2014 Michihiro NAKAJIMA
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:14Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+/* don't use bcrypt when XP needs to be supported */
+#include <bcrypt.h>
+
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#endif
+
+#elif defined(HAVE_WINCRYPT_H)
+#include <wincrypt.h>
+#endif
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+#ifdef HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#ifdef HAVE_LZ4_H
+#include <lz4.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_random_private.h"
+#include "archive_string.h"
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+static int archive_utility_string_sort_helper(char **, unsigned int);
+
+/* Generic initialization of 'struct archive' objects. */
+int
+__archive_clean(struct archive *a)
+{
+ archive_string_conversion_free(a);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_version_number(void)
+{
+ return (ARCHIVE_VERSION_NUMBER);
+}
+
+const char *
+archive_version_string(void)
+{
+ return (ARCHIVE_VERSION_STRING);
+}
+
+int
+archive_errno(struct archive *a)
+{
+ return (a->archive_error_number);
+}
+
+const char *
+archive_error_string(struct archive *a)
+{
+
+ if (a->error != NULL && *a->error != '\0')
+ return (a->error);
+ else
+ return (NULL);
+}
+
+int
+archive_file_count(struct archive *a)
+{
+ return (a->file_count);
+}
+
+int
+archive_format(struct archive *a)
+{
+ return (a->archive_format);
+}
+
+const char *
+archive_format_name(struct archive *a)
+{
+ return (a->archive_format_name);
+}
+
+
+int
+archive_compression(struct archive *a)
+{
+ return archive_filter_code(a, 0);
+}
+
+const char *
+archive_compression_name(struct archive *a)
+{
+ return archive_filter_name(a, 0);
+}
+
+
+/*
+ * Return a count of the number of compressed bytes processed.
+ */
+la_int64_t
+archive_position_compressed(struct archive *a)
+{
+ return archive_filter_bytes(a, -1);
+}
+
+/*
+ * Return a count of the number of uncompressed bytes processed.
+ */
+la_int64_t
+archive_position_uncompressed(struct archive *a)
+{
+ return archive_filter_bytes(a, 0);
+}
+
+void
+archive_clear_error(struct archive *a)
+{
+ archive_string_empty(&a->error_string);
+ a->error = NULL;
+ a->archive_error_number = 0;
+}
+
+void
+archive_set_error(struct archive *a, int error_number, const char *fmt, ...)
+{
+ va_list ap;
+
+ a->archive_error_number = error_number;
+ if (fmt == NULL) {
+ a->error = NULL;
+ return;
+ }
+
+ archive_string_empty(&(a->error_string));
+ va_start(ap, fmt);
+ archive_string_vsprintf(&(a->error_string), fmt, ap);
+ va_end(ap);
+ a->error = a->error_string.s;
+}
+
+void
+archive_copy_error(struct archive *dest, struct archive *src)
+{
+ dest->archive_error_number = src->archive_error_number;
+
+ archive_string_copy(&dest->error_string, &src->error_string);
+ dest->error = dest->error_string.s;
+}
+
+void
+__archive_errx(int retvalue, const char *msg)
+{
+ static const char msg1[] = "Fatal Internal Error in libarchive: ";
+ size_t s;
+
+ s = write(2, msg1, strlen(msg1));
+ (void)s; /* UNUSED */
+ s = write(2, msg, strlen(msg));
+ (void)s; /* UNUSED */
+ s = write(2, "\n", 1);
+ (void)s; /* UNUSED */
+ exit(retvalue);
+}
+
+/*
+ * Create a temporary file
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * Do not use Windows tmpfile() function.
+ * It will make a temporary file under the root directory
+ * and it'll cause permission error if a user who is
+ * non-Administrator creates temporary files.
+ * Also Windows version of mktemp family including _mktemp_s
+ * are not secure.
+ */
+static int
+__archive_mktempx(const char *tmpdir, wchar_t *template)
+{
+ static const wchar_t prefix[] = L"libarchive_";
+ static const wchar_t suffix[] = L"XXXXXXXXXX";
+ static const wchar_t num[] = {
+ L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7',
+ L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F',
+ L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N',
+ L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V',
+ L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd',
+ L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l',
+ L'm', L'n', L'o', L'p', L'q', L'r', L's', L't',
+ L'u', L'v', L'w', L'x', L'y', L'z'
+ };
+ struct archive_wstring temp_name;
+ wchar_t *ws;
+ DWORD attr;
+ wchar_t *xp, *ep;
+ int fd;
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ BCRYPT_ALG_HANDLE hAlg = NULL;
+#else
+ HCRYPTPROV hProv = (HCRYPTPROV)NULL;
+#endif
+ fd = -1;
+ ws = NULL;
+
+ if (template == NULL) {
+ archive_string_init(&temp_name);
+
+ /* Get a temporary directory. */
+ if (tmpdir == NULL) {
+ size_t l;
+ wchar_t *tmp;
+
+ l = GetTempPathW(0, NULL);
+ if (l == 0) {
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+ tmp = malloc(l*sizeof(wchar_t));
+ if (tmp == NULL) {
+ errno = ENOMEM;
+ goto exit_tmpfile;
+ }
+ GetTempPathW((DWORD)l, tmp);
+ archive_wstrcpy(&temp_name, tmp);
+ free(tmp);
+ } else {
+ if (archive_wstring_append_from_mbs(&temp_name, tmpdir,
+ strlen(tmpdir)) < 0)
+ goto exit_tmpfile;
+ if (temp_name.s[temp_name.length-1] != L'/')
+ archive_wstrappend_wchar(&temp_name, L'/');
+ }
+
+ /* Check if temp_name is a directory. */
+ attr = GetFileAttributesW(temp_name.s);
+ if (attr == (DWORD)-1) {
+ if (GetLastError() != ERROR_FILE_NOT_FOUND) {
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+ ws = __la_win_permissive_name_w(temp_name.s);
+ if (ws == NULL) {
+ errno = EINVAL;
+ goto exit_tmpfile;
+ }
+ attr = GetFileAttributesW(ws);
+ if (attr == (DWORD)-1) {
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+ }
+ if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
+ errno = ENOTDIR;
+ goto exit_tmpfile;
+ }
+
+ /*
+ * Create a temporary file.
+ */
+ archive_wstrcat(&temp_name, prefix);
+ archive_wstrcat(&temp_name, suffix);
+ ep = temp_name.s + archive_strlen(&temp_name);
+ xp = ep - wcslen(suffix);
+ template = temp_name.s;
+ } else {
+ xp = wcschr(template, L'X');
+ if (xp == NULL) /* No X, programming error */
+ abort();
+ for (ep = xp; *ep == L'X'; ep++)
+ continue;
+ if (*ep) /* X followed by non X, programming error */
+ abort();
+ }
+
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM,
+ NULL, 0))) {
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+#else
+ if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT)) {
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+#endif
+
+ for (;;) {
+ wchar_t *p;
+ HANDLE h;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+ /* Generate a random file name through CryptGenRandom(). */
+ p = xp;
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ if (!BCRYPT_SUCCESS(BCryptGenRandom(hAlg, (PUCHAR)p,
+ (DWORD)(ep - p)*sizeof(wchar_t), 0))) {
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+#else
+ if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t),
+ (BYTE*)p)) {
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+#endif
+ for (; p < ep; p++)
+ *p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
+
+ free(ws);
+ ws = __la_win_permissive_name_w(template);
+ if (ws == NULL) {
+ errno = EINVAL;
+ goto exit_tmpfile;
+ }
+ if (template == temp_name.s) {
+ attr = FILE_ATTRIBUTE_TEMPORARY |
+ FILE_FLAG_DELETE_ON_CLOSE;
+ } else {
+ /* mkstemp */
+ attr = FILE_ATTRIBUTE_NORMAL;
+ }
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileAttributes = attr & 0xFFFF;
+ createExParams.dwFileFlags = attr & 0xFFF00000;
+ h = CreateFile2(ws,
+ GENERIC_READ | GENERIC_WRITE | DELETE,
+ 0,/* Not share */
+ CREATE_NEW,
+ &createExParams);
+#else
+ h = CreateFileW(ws,
+ GENERIC_READ | GENERIC_WRITE | DELETE,
+ 0,/* Not share */
+ NULL,
+ CREATE_NEW,/* Create a new file only */
+ attr,
+ NULL);
+#endif
+ if (h == INVALID_HANDLE_VALUE) {
+ /* The same file already exists. retry with
+ * a new filename. */
+ if (GetLastError() == ERROR_FILE_EXISTS)
+ continue;
+ /* Otherwise, fail creation temporary file. */
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+ fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
+ if (fd == -1) {
+ la_dosmaperr(GetLastError());
+ CloseHandle(h);
+ goto exit_tmpfile;
+ } else
+ break;/* success! */
+ }
+exit_tmpfile:
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ if (hAlg != NULL)
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+#else
+ if (hProv != (HCRYPTPROV)NULL)
+ CryptReleaseContext(hProv, 0);
+#endif
+ free(ws);
+ if (template == temp_name.s)
+ archive_wstring_free(&temp_name);
+ return (fd);
+}
+
+int
+__archive_mktemp(const char *tmpdir)
+{
+ return __archive_mktempx(tmpdir, NULL);
+}
+
+int
+__archive_mkstemp(wchar_t *template)
+{
+ return __archive_mktempx(NULL, template);
+}
+
+#else
+
+static int
+get_tempdir(struct archive_string *temppath)
+{
+ const char *tmp;
+
+ tmp = getenv("TMPDIR");
+ if (tmp == NULL)
+#ifdef _PATH_TMP
+ tmp = _PATH_TMP;
+#else
+ tmp = "/tmp";
+#endif
+ archive_strcpy(temppath, tmp);
+ if (temppath->s[temppath->length-1] != '/')
+ archive_strappend_char(temppath, '/');
+ return (ARCHIVE_OK);
+}
+
+#if defined(HAVE_MKSTEMP)
+
+/*
+ * We can use mkstemp().
+ */
+
+int
+__archive_mktemp(const char *tmpdir)
+{
+ struct archive_string temp_name;
+ int fd = -1;
+
+ archive_string_init(&temp_name);
+ if (tmpdir == NULL) {
+ if (get_tempdir(&temp_name) != ARCHIVE_OK)
+ goto exit_tmpfile;
+ } else {
+ archive_strcpy(&temp_name, tmpdir);
+ if (temp_name.s[temp_name.length-1] != '/')
+ archive_strappend_char(&temp_name, '/');
+ }
+#ifdef O_TMPFILE
+ fd = open(temp_name.s, O_RDWR|O_CLOEXEC|O_TMPFILE|O_EXCL, 0600);
+ if(fd >= 0)
+ goto exit_tmpfile;
+#endif
+ archive_strcat(&temp_name, "libarchive_XXXXXX");
+ fd = mkstemp(temp_name.s);
+ if (fd < 0)
+ goto exit_tmpfile;
+ __archive_ensure_cloexec_flag(fd);
+ unlink(temp_name.s);
+exit_tmpfile:
+ archive_string_free(&temp_name);
+ return (fd);
+}
+
+int
+__archive_mkstemp(char *template)
+{
+ int fd = -1;
+ fd = mkstemp(template);
+ if (fd >= 0)
+ __archive_ensure_cloexec_flag(fd);
+ return (fd);
+}
+
+#else /* !HAVE_MKSTEMP */
+
+/*
+ * We use a private routine.
+ */
+
+static int
+__archive_mktempx(const char *tmpdir, char *template)
+{
+ static const char num[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', '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'
+ };
+ struct archive_string temp_name;
+ struct stat st;
+ int fd;
+ char *tp, *ep;
+
+ fd = -1;
+ if (template == NULL) {
+ archive_string_init(&temp_name);
+ if (tmpdir == NULL) {
+ if (get_tempdir(&temp_name) != ARCHIVE_OK)
+ goto exit_tmpfile;
+ } else
+ archive_strcpy(&temp_name, tmpdir);
+ if (temp_name.s[temp_name.length-1] == '/') {
+ temp_name.s[temp_name.length-1] = '\0';
+ temp_name.length --;
+ }
+ if (la_stat(temp_name.s, &st) < 0)
+ goto exit_tmpfile;
+ if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ goto exit_tmpfile;
+ }
+ archive_strcat(&temp_name, "/libarchive_");
+ tp = temp_name.s + archive_strlen(&temp_name);
+ archive_strcat(&temp_name, "XXXXXXXXXX");
+ ep = temp_name.s + archive_strlen(&temp_name);
+ template = temp_name.s;
+ } else {
+ tp = strchr(template, 'X');
+ if (tp == NULL) /* No X, programming error */
+ abort();
+ for (ep = tp; *ep == 'X'; ep++)
+ continue;
+ if (*ep) /* X followed by non X, programming error */
+ abort();
+ }
+
+ do {
+ char *p;
+
+ p = tp;
+ archive_random(p, ep - p);
+ while (p < ep) {
+ int d = *((unsigned char *)p) % sizeof(num);
+ *p++ = num[d];
+ }
+ fd = open(template, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
+ 0600);
+ } while (fd < 0 && errno == EEXIST);
+ if (fd < 0)
+ goto exit_tmpfile;
+ __archive_ensure_cloexec_flag(fd);
+ if (template == temp_name.s)
+ unlink(temp_name.s);
+exit_tmpfile:
+ if (template == temp_name.s)
+ archive_string_free(&temp_name);
+ return (fd);
+}
+
+int
+__archive_mktemp(const char *tmpdir)
+{
+ return __archive_mktempx(tmpdir, NULL);
+}
+
+int
+__archive_mkstemp(char *template)
+{
+ return __archive_mktempx(NULL, template);
+}
+
+#endif /* !HAVE_MKSTEMP */
+#endif /* !_WIN32 || __CYGWIN__ */
+
+/*
+ * Set FD_CLOEXEC flag to a file descriptor if it is not set.
+ * We have to set the flag if the platform does not provide O_CLOEXEC
+ * or F_DUPFD_CLOEXEC flags.
+ *
+ * Note: This function is absolutely called after creating a new file
+ * descriptor even if the platform seemingly provides O_CLOEXEC or
+ * F_DUPFD_CLOEXEC macros because it is possible that the platform
+ * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it.
+ */
+void
+__archive_ensure_cloexec_flag(int fd)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ (void)fd; /* UNUSED */
+#else
+ int flags;
+
+ if (fd >= 0) {
+ flags = fcntl(fd, F_GETFD);
+ if (flags != -1 && (flags & FD_CLOEXEC) == 0)
+ fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+ }
+#endif
+}
+
+/*
+ * Utility function to sort a group of strings using quicksort.
+ */
+static int
+archive_utility_string_sort_helper(char **strings, unsigned int n)
+{
+ unsigned int i, lesser_count, greater_count;
+ char **lesser, **greater, **tmp, *pivot;
+ int retval1, retval2;
+
+ /* A list of 0 or 1 elements is already sorted */
+ if (n <= 1)
+ return (ARCHIVE_OK);
+
+ lesser_count = greater_count = 0;
+ lesser = greater = NULL;
+ pivot = strings[0];
+ for (i = 1; i < n; i++)
+ {
+ if (strcmp(strings[i], pivot) < 0)
+ {
+ lesser_count++;
+ tmp = (char **)realloc(lesser,
+ lesser_count * sizeof(char *));
+ if (!tmp) {
+ free(greater);
+ free(lesser);
+ return (ARCHIVE_FATAL);
+ }
+ lesser = tmp;
+ lesser[lesser_count - 1] = strings[i];
+ }
+ else
+ {
+ greater_count++;
+ tmp = (char **)realloc(greater,
+ greater_count * sizeof(char *));
+ if (!tmp) {
+ free(greater);
+ free(lesser);
+ return (ARCHIVE_FATAL);
+ }
+ greater = tmp;
+ greater[greater_count - 1] = strings[i];
+ }
+ }
+
+ /* quicksort(lesser) */
+ retval1 = archive_utility_string_sort_helper(lesser, lesser_count);
+ for (i = 0; i < lesser_count; i++)
+ strings[i] = lesser[i];
+ free(lesser);
+
+ /* pivot */
+ strings[lesser_count] = pivot;
+
+ /* quicksort(greater) */
+ retval2 = archive_utility_string_sort_helper(greater, greater_count);
+ for (i = 0; i < greater_count; i++)
+ strings[lesser_count + 1 + i] = greater[i];
+ free(greater);
+
+ return (retval1 < retval2) ? retval1 : retval2;
+}
+
+int
+archive_utility_string_sort(char **strings)
+{
+ unsigned int size = 0;
+ while (strings[size] != NULL)
+ size++;
+ return archive_utility_string_sort_helper(strings, size);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_version_details.c b/contrib/libs/libarchive/libarchive/archive_version_details.c
new file mode 100644
index 00000000000..2a143388fc0
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_version_details.c
@@ -0,0 +1,151 @@
+/*-
+ * Copyright (c) 2009-2012,2014 Michihiro NAKAJIMA
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:14Z kientzle $");
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+#ifdef HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#ifdef HAVE_LZ4_H
+#include <lz4.h>
+#endif
+#ifdef HAVE_ZSTD_H
+#include <zstd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+
+const char *
+archive_version_details(void)
+{
+ static struct archive_string str;
+ static int init = 0;
+ const char *zlib = archive_zlib_version();
+ const char *liblzma = archive_liblzma_version();
+ const char *bzlib = archive_bzlib_version();
+ const char *liblz4 = archive_liblz4_version();
+ const char *libzstd = archive_libzstd_version();
+
+ if (!init) {
+ archive_string_init(&str);
+
+ archive_strcat(&str, ARCHIVE_VERSION_STRING);
+ if (zlib != NULL) {
+ archive_strcat(&str, " zlib/");
+ archive_strcat(&str, zlib);
+ }
+ if (liblzma) {
+ archive_strcat(&str, " liblzma/");
+ archive_strcat(&str, liblzma);
+ }
+ if (bzlib) {
+ const char *p = bzlib;
+ const char *sep = strchr(p, ',');
+ if (sep == NULL)
+ sep = p + strlen(p);
+ archive_strcat(&str, " bz2lib/");
+ archive_strncat(&str, p, sep - p);
+ }
+ if (liblz4) {
+ archive_strcat(&str, " liblz4/");
+ archive_strcat(&str, liblz4);
+ }
+ if (libzstd) {
+ archive_strcat(&str, " libzstd/");
+ archive_strcat(&str, libzstd);
+ }
+ }
+ return str.s;
+}
+
+const char *
+archive_zlib_version(void)
+{
+#ifdef HAVE_ZLIB_H
+ return ZLIB_VERSION;
+#else
+ return NULL;
+#endif
+}
+
+const char *
+archive_liblzma_version(void)
+{
+#ifdef HAVE_LZMA_H
+ return LZMA_VERSION_STRING;
+#else
+ return NULL;
+#endif
+}
+
+const char *
+archive_bzlib_version(void)
+{
+#ifdef HAVE_BZLIB_H
+ return BZ2_bzlibVersion();
+#else
+ return NULL;
+#endif
+}
+
+const char *
+archive_liblz4_version(void)
+{
+#if defined(HAVE_LZ4_H) && defined(HAVE_LIBLZ4)
+#define str(s) #s
+#define NUMBER(x) str(x)
+ return NUMBER(LZ4_VERSION_MAJOR) "." NUMBER(LZ4_VERSION_MINOR) "." NUMBER(LZ4_VERSION_RELEASE);
+#undef NUMBER
+#undef str
+#else
+ return NULL;
+#endif
+}
+
+const char *
+archive_libzstd_version(void)
+{
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+ return ZSTD_VERSION_STRING;
+#else
+ return NULL;
+#endif
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_virtual.c b/contrib/libs/libarchive/libarchive/archive_virtual.c
new file mode 100644
index 00000000000..f509ee5c672
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_virtual.c
@@ -0,0 +1,163 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_virtual.c 201098 2009-12-28 02:58:14Z kientzle $");
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+
+int
+archive_filter_code(struct archive *a, int n)
+{
+ return ((a->vtable->archive_filter_code)(a, n));
+}
+
+int
+archive_filter_count(struct archive *a)
+{
+ return ((a->vtable->archive_filter_count)(a));
+}
+
+const char *
+archive_filter_name(struct archive *a, int n)
+{
+ return ((a->vtable->archive_filter_name)(a, n));
+}
+
+la_int64_t
+archive_filter_bytes(struct archive *a, int n)
+{
+ return ((a->vtable->archive_filter_bytes)(a, n));
+}
+
+int
+archive_free(struct archive *a)
+{
+ if (a == NULL)
+ return (ARCHIVE_OK);
+ return ((a->vtable->archive_free)(a));
+}
+
+int
+archive_write_close(struct archive *a)
+{
+ return ((a->vtable->archive_close)(a));
+}
+
+int
+archive_read_close(struct archive *a)
+{
+ return ((a->vtable->archive_close)(a));
+}
+
+int
+archive_write_fail(struct archive *a)
+{
+ a->state = ARCHIVE_STATE_FATAL;
+ return a->state;
+}
+
+int
+archive_write_free(struct archive *a)
+{
+ return archive_free(a);
+}
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* For backwards compatibility; will be removed with libarchive 4.0. */
+int
+archive_write_finish(struct archive *a)
+{
+ return archive_write_free(a);
+}
+#endif
+
+int
+archive_read_free(struct archive *a)
+{
+ return archive_free(a);
+}
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* For backwards compatibility; will be removed with libarchive 4.0. */
+int
+archive_read_finish(struct archive *a)
+{
+ return archive_read_free(a);
+}
+#endif
+
+int
+archive_write_header(struct archive *a, struct archive_entry *entry)
+{
+ ++a->file_count;
+ return ((a->vtable->archive_write_header)(a, entry));
+}
+
+int
+archive_write_finish_entry(struct archive *a)
+{
+ return ((a->vtable->archive_write_finish_entry)(a));
+}
+
+la_ssize_t
+archive_write_data(struct archive *a, const void *buff, size_t s)
+{
+ return ((a->vtable->archive_write_data)(a, buff, s));
+}
+
+la_ssize_t
+archive_write_data_block(struct archive *a, const void *buff, size_t s,
+ la_int64_t o)
+{
+ if (a->vtable->archive_write_data_block == NULL) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "archive_write_data_block not supported");
+ a->state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ return ((a->vtable->archive_write_data_block)(a, buff, s, o));
+}
+
+int
+archive_read_next_header(struct archive *a, struct archive_entry **entry)
+{
+ return ((a->vtable->archive_read_next_header)(a, entry));
+}
+
+int
+archive_read_next_header2(struct archive *a, struct archive_entry *entry)
+{
+ return ((a->vtable->archive_read_next_header2)(a, entry));
+}
+
+int
+archive_read_data_block(struct archive *a,
+ const void **buff, size_t *s, la_int64_t *o)
+{
+ return ((a->vtable->archive_read_data_block)(a, buff, s, o));
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_windows.c b/contrib/libs/libarchive/libarchive/archive_windows.c
new file mode 100644
index 00000000000..ebc5eefb800
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_windows.c
@@ -0,0 +1,938 @@
+/*-
+ * Copyright (c) 2009-2011 Michihiro NAKAJIMA
+ * Copyright (c) 2003-2007 Kees Zeelenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * A set of compatibility glue for building libarchive on Windows platforms.
+ *
+ * Originally created as "libarchive-nonposix.c" by Kees Zeelenberg
+ * for the GnuWin32 project, trimmed significantly by Tim Kientzle.
+ *
+ * Much of the original file was unnecessary for libarchive, because
+ * many of the features it emulated were not strictly necessary for
+ * libarchive. I hope for this to shrink further as libarchive
+ * internals are gradually reworked to sit more naturally on both
+ * POSIX and Windows. Any ideas for this are greatly appreciated.
+ *
+ * The biggest remaining issue is the dev/ino emulation; libarchive
+ * has a couple of public APIs that rely on dev/ino uniquely
+ * identifying a file. This doesn't match well with Windows. I'm
+ * considering alternative APIs.
+ */
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+#include "archive_platform.h"
+#include "archive_private.h"
+#include "archive_entry.h"
+#include <ctype.h>
+#include <errno.h>
+#include <stddef.h>
+#ifdef HAVE_SYS_UTIME_H
+#include <sys/utime.h>
+#endif
+#include <sys/stat.h>
+#include <locale.h>
+#include <process.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <windows.h>
+#include <share.h>
+
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+
+#if defined(__LA_LSEEK_NEEDED)
+static BOOL SetFilePointerEx_perso(HANDLE hFile,
+ LARGE_INTEGER liDistanceToMove,
+ PLARGE_INTEGER lpNewFilePointer,
+ DWORD dwMoveMethod)
+{
+ LARGE_INTEGER li;
+ li.QuadPart = liDistanceToMove.QuadPart;
+ li.LowPart = SetFilePointer(
+ hFile, li.LowPart, &li.HighPart, dwMoveMethod);
+ if(lpNewFilePointer) {
+ lpNewFilePointer->QuadPart = li.QuadPart;
+ }
+ return li.LowPart != -1 || GetLastError() == NO_ERROR;
+}
+#endif
+
+struct ustat {
+ int64_t st_atime;
+ uint32_t st_atime_nsec;
+ int64_t st_ctime;
+ uint32_t st_ctime_nsec;
+ int64_t st_mtime;
+ uint32_t st_mtime_nsec;
+ gid_t st_gid;
+ /* 64bits ino */
+ int64_t st_ino;
+ mode_t st_mode;
+ uint32_t st_nlink;
+ uint64_t st_size;
+ uid_t st_uid;
+ dev_t st_dev;
+ dev_t st_rdev;
+};
+
+/* Transform 64-bits ino into 32-bits by hashing.
+ * You do not forget that really unique number size is 64-bits.
+ */
+#define INOSIZE (8*sizeof(ino_t)) /* 32 */
+static __inline ino_t
+getino(struct ustat *ub)
+{
+ ULARGE_INTEGER ino64;
+ ino64.QuadPart = ub->st_ino;
+ /* I don't know this hashing is correct way */
+ return ((ino_t)(ino64.LowPart ^ (ino64.LowPart >> INOSIZE)));
+}
+
+/*
+ * Prepend "\\?\" to the path name and convert it to unicode to permit
+ * an extended-length path for a maximum total path length of 32767
+ * characters.
+ * see also http://msdn.microsoft.com/en-us/library/aa365247.aspx
+ */
+wchar_t *
+__la_win_permissive_name(const char *name)
+{
+ wchar_t *wn;
+ wchar_t *ws;
+ size_t ll;
+
+ ll = strlen(name);
+ wn = malloc((ll + 1) * sizeof(wchar_t));
+ if (wn == NULL)
+ return (NULL);
+ ll = mbstowcs(wn, name, ll);
+ if (ll == (size_t)-1) {
+ free(wn);
+ return (NULL);
+ }
+ wn[ll] = L'\0';
+ ws = __la_win_permissive_name_w(wn);
+ free(wn);
+ return (ws);
+}
+
+wchar_t *
+__la_win_permissive_name_w(const wchar_t *wname)
+{
+ wchar_t *wn, *wnp;
+ wchar_t *ws, *wsp;
+ DWORD l, len, slen;
+ int unc;
+
+ /* Get a full-pathname. */
+ l = GetFullPathNameW(wname, 0, NULL, NULL);
+ if (l == 0)
+ return (NULL);
+ /* NOTE: GetFullPathNameW has a bug that if the length of the file
+ * name is just 1 then it returns incomplete buffer size. Thus, we
+ * have to add three to the size to allocate a sufficient buffer
+ * size for the full-pathname of the file name. */
+ l += 3;
+ wnp = malloc(l * sizeof(wchar_t));
+ if (wnp == NULL)
+ return (NULL);
+ len = GetFullPathNameW(wname, l, wnp, NULL);
+ wn = wnp;
+
+ if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
+ wnp[2] == L'?' && wnp[3] == L'\\')
+ /* We have already a permissive name. */
+ return (wn);
+
+ if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
+ wnp[2] == L'.' && wnp[3] == L'\\') {
+ /* This is a device name */
+ if (((wnp[4] >= L'a' && wnp[4] <= L'z') ||
+ (wnp[4] >= L'A' && wnp[4] <= L'Z')) &&
+ wnp[5] == L':' && wnp[6] == L'\\')
+ wnp[2] = L'?';/* Not device name. */
+ return (wn);
+ }
+
+ unc = 0;
+ if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') {
+ wchar_t *p = &wnp[2];
+
+ /* Skip server-name letters. */
+ while (*p != L'\\' && *p != L'\0')
+ ++p;
+ if (*p == L'\\') {
+ wchar_t *rp = ++p;
+ /* Skip share-name letters. */
+ while (*p != L'\\' && *p != L'\0')
+ ++p;
+ if (*p == L'\\' && p != rp) {
+ /* Now, match patterns such as
+ * "\\server-name\share-name\" */
+ wnp += 2;
+ len -= 2;
+ unc = 1;
+ }
+ }
+ }
+
+ slen = 4 + (unc * 4) + len + 1;
+ ws = wsp = malloc(slen * sizeof(wchar_t));
+ if (ws == NULL) {
+ free(wn);
+ return (NULL);
+ }
+ /* prepend "\\?\" */
+ wcsncpy(wsp, L"\\\\?\\", 4);
+ wsp += 4;
+ slen -= 4;
+ if (unc) {
+ /* append "UNC\" ---> "\\?\UNC\" */
+ wcsncpy(wsp, L"UNC\\", 4);
+ wsp += 4;
+ slen -= 4;
+ }
+ wcsncpy(wsp, wnp, slen);
+ wsp[slen - 1] = L'\0'; /* Ensure null termination. */
+ free(wn);
+ return (ws);
+}
+
+/*
+ * Create a file handle.
+ * This can exceed MAX_PATH limitation.
+ */
+static HANDLE
+la_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
+ DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
+{
+ wchar_t *wpath;
+ HANDLE handle;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
+ handle = CreateFileA(path, dwDesiredAccess, dwShareMode,
+ lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
+ hTemplateFile);
+ if (handle != INVALID_HANDLE_VALUE)
+ return (handle);
+ if (GetLastError() != ERROR_PATH_NOT_FOUND)
+ return (handle);
+#endif
+ wpath = __la_win_permissive_name(path);
+ if (wpath == NULL)
+ return INVALID_HANDLE_VALUE;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileAttributes = dwFlagsAndAttributes & 0xFFFF;
+ createExParams.dwFileFlags = dwFlagsAndAttributes & 0xFFF00000;
+ createExParams.dwSecurityQosFlags = dwFlagsAndAttributes & 0x000F00000;
+ createExParams.lpSecurityAttributes = lpSecurityAttributes;
+ createExParams.hTemplateFile = hTemplateFile;
+ handle = CreateFile2(wpath, dwDesiredAccess, dwShareMode,
+ dwCreationDisposition, &createExParams);
+#else /* !WINAPI_PARTITION_DESKTOP */
+ handle = CreateFileW(wpath, dwDesiredAccess, dwShareMode,
+ lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
+ hTemplateFile);
+#endif /* !WINAPI_PARTITION_DESKTOP */
+ free(wpath);
+ return (handle);
+}
+
+#if defined(__LA_LSEEK_NEEDED)
+__int64
+__la_lseek(int fd, __int64 offset, int whence)
+{
+ LARGE_INTEGER distance;
+ LARGE_INTEGER newpointer;
+ HANDLE handle;
+
+ if (fd < 0) {
+ errno = EBADF;
+ return (-1);
+ }
+ handle = (HANDLE)_get_osfhandle(fd);
+ if (GetFileType(handle) != FILE_TYPE_DISK) {
+ errno = EBADF;
+ return (-1);
+ }
+ distance.QuadPart = offset;
+ if (!SetFilePointerEx_perso(handle, distance, &newpointer, whence)) {
+ DWORD lasterr;
+
+ lasterr = GetLastError();
+ if (lasterr == ERROR_BROKEN_PIPE)
+ return (0);
+ if (lasterr == ERROR_ACCESS_DENIED)
+ errno = EBADF;
+ else
+ la_dosmaperr(lasterr);
+ return (-1);
+ }
+ return (newpointer.QuadPart);
+}
+#endif
+
+/* This can exceed MAX_PATH limitation. */
+int
+__la_open(const char *path, int flags, ...)
+{
+ va_list ap;
+ wchar_t *ws;
+ int r, pmode;
+ DWORD attr;
+
+ va_start(ap, flags);
+ pmode = va_arg(ap, int);
+ va_end(ap);
+ ws = NULL;
+ if ((flags & ~O_BINARY) == O_RDONLY) {
+ /*
+ * When we open a directory, _open function returns
+ * "Permission denied" error.
+ */
+ attr = GetFileAttributesA(path);
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
+ if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND)
+#endif
+ {
+ ws = __la_win_permissive_name(path);
+ if (ws == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+ attr = GetFileAttributesW(ws);
+ }
+ if (attr == (DWORD)-1) {
+ la_dosmaperr(GetLastError());
+ free(ws);
+ return (-1);
+ }
+ if (attr & FILE_ATTRIBUTE_DIRECTORY) {
+ HANDLE handle;
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
+ if (ws != NULL)
+ handle = CreateFileW(ws, 0, 0, NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS |
+ FILE_ATTRIBUTE_READONLY,
+ NULL);
+ else
+ handle = CreateFileA(path, 0, 0, NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS |
+ FILE_ATTRIBUTE_READONLY,
+ NULL);
+#else /* !WINAPI_PARTITION_DESKTOP */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileAttributes = FILE_ATTRIBUTE_READONLY;
+ createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+ handle = CreateFile2(ws, 0, 0,
+ OPEN_EXISTING, &createExParams);
+#endif /* !WINAPI_PARTITION_DESKTOP */
+ free(ws);
+ if (handle == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+ r = _open_osfhandle((intptr_t)handle, _O_RDONLY);
+ return (r);
+ }
+ }
+ if (ws == NULL) {
+#if defined(__BORLANDC__)
+ /* Borland has no mode argument.
+ TODO: Fix mode of new file. */
+ r = _open(path, flags);
+#else
+ r = _open(path, flags, pmode);
+#endif
+ if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) {
+ /* Simulate other POSIX system action to pass our test suite. */
+ attr = GetFileAttributesA(path);
+ if (attr == (DWORD)-1)
+ la_dosmaperr(GetLastError());
+ else if (attr & FILE_ATTRIBUTE_DIRECTORY)
+ errno = EISDIR;
+ else
+ errno = EACCES;
+ return (-1);
+ }
+ if (r >= 0 || errno != ENOENT)
+ return (r);
+ ws = __la_win_permissive_name(path);
+ if (ws == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+ }
+ r = _wopen(ws, flags, pmode);
+ if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) {
+ /* Simulate other POSIX system action to pass our test suite. */
+ attr = GetFileAttributesW(ws);
+ if (attr == (DWORD)-1)
+ la_dosmaperr(GetLastError());
+ else if (attr & FILE_ATTRIBUTE_DIRECTORY)
+ errno = EISDIR;
+ else
+ errno = EACCES;
+ }
+ free(ws);
+ return (r);
+}
+
+ssize_t
+__la_read(int fd, void *buf, size_t nbytes)
+{
+ HANDLE handle;
+ DWORD bytes_read, lasterr;
+ int r;
+
+#ifdef _WIN64
+ if (nbytes > UINT32_MAX)
+ nbytes = UINT32_MAX;
+#endif
+ if (fd < 0) {
+ errno = EBADF;
+ return (-1);
+ }
+ /* Do not pass 0 to third parameter of ReadFile(), read bytes.
+ * This will not return to application side. */
+ if (nbytes == 0)
+ return (0);
+ handle = (HANDLE)_get_osfhandle(fd);
+ r = ReadFile(handle, buf, (uint32_t)nbytes,
+ &bytes_read, NULL);
+ if (r == 0) {
+ lasterr = GetLastError();
+ if (lasterr == ERROR_NO_DATA) {
+ errno = EAGAIN;
+ return (-1);
+ }
+ if (lasterr == ERROR_BROKEN_PIPE)
+ return (0);
+ if (lasterr == ERROR_ACCESS_DENIED)
+ errno = EBADF;
+ else
+ la_dosmaperr(lasterr);
+ return (-1);
+ }
+ return ((ssize_t)bytes_read);
+}
+
+/* Convert Windows FILETIME to UTC */
+__inline static void
+fileTimeToUTC(const FILETIME *filetime, time_t *t, long *ns)
+{
+ ULARGE_INTEGER utc;
+
+ utc.HighPart = filetime->dwHighDateTime;
+ utc.LowPart = filetime->dwLowDateTime;
+ if (utc.QuadPart >= EPOC_TIME) {
+ utc.QuadPart -= EPOC_TIME;
+ *t = (time_t)(utc.QuadPart / 10000000); /* milli seconds base */
+ *ns = (long)(utc.QuadPart % 10000000) * 100;/* nano seconds base */
+ } else {
+ *t = 0;
+ *ns = 0;
+ }
+}
+
+/* Stat by handle
+ * Windows' stat() does not accept the path added "\\?\" especially "?"
+ * character.
+ * It means we cannot access the long name path longer than MAX_PATH.
+ * So I've implemented a function similar to Windows' stat() to access the
+ * long name path.
+ * And I've added some feature.
+ * 1. set st_ino by nFileIndexHigh and nFileIndexLow of
+ * BY_HANDLE_FILE_INFORMATION.
+ * 2. set st_nlink by nNumberOfLinks of BY_HANDLE_FILE_INFORMATION.
+ * 3. set st_dev by dwVolumeSerialNumber by BY_HANDLE_FILE_INFORMATION.
+ */
+static int
+__hstat(HANDLE handle, struct ustat *st)
+{
+ BY_HANDLE_FILE_INFORMATION info;
+ ULARGE_INTEGER ino64;
+ DWORD ftype;
+ mode_t mode;
+ time_t t;
+ long ns;
+
+ switch (ftype = GetFileType(handle)) {
+ case FILE_TYPE_UNKNOWN:
+ errno = EBADF;
+ return (-1);
+ case FILE_TYPE_CHAR:
+ case FILE_TYPE_PIPE:
+ if (ftype == FILE_TYPE_CHAR) {
+ st->st_mode = S_IFCHR;
+ st->st_size = 0;
+ } else {
+ DWORD avail;
+
+ st->st_mode = S_IFIFO;
+ if (PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL))
+ st->st_size = avail;
+ else
+ st->st_size = 0;
+ }
+ st->st_atime = 0;
+ st->st_atime_nsec = 0;
+ st->st_mtime = 0;
+ st->st_mtime_nsec = 0;
+ st->st_ctime = 0;
+ st->st_ctime_nsec = 0;
+ st->st_ino = 0;
+ st->st_nlink = 1;
+ st->st_uid = 0;
+ st->st_gid = 0;
+ st->st_rdev = 0;
+ st->st_dev = 0;
+ return (0);
+ case FILE_TYPE_DISK:
+ break;
+ default:
+ /* This ftype is undocumented type. */
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+
+ ZeroMemory(&info, sizeof(info));
+ if (!GetFileInformationByHandle (handle, &info)) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+
+ mode = S_IRUSR | S_IRGRP | S_IROTH;
+ if ((info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
+ mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+ if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ else
+ mode |= S_IFREG;
+ st->st_mode = mode;
+
+ fileTimeToUTC(&info.ftLastAccessTime, &t, &ns);
+ st->st_atime = t;
+ st->st_atime_nsec = ns;
+ fileTimeToUTC(&info.ftLastWriteTime, &t, &ns);
+ st->st_mtime = t;
+ st->st_mtime_nsec = ns;
+ fileTimeToUTC(&info.ftCreationTime, &t, &ns);
+ st->st_ctime = t;
+ st->st_ctime_nsec = ns;
+ st->st_size =
+ ((int64_t)(info.nFileSizeHigh) * ((int64_t)MAXDWORD + 1))
+ + (int64_t)(info.nFileSizeLow);
+#ifdef SIMULATE_WIN_STAT
+ st->st_ino = 0;
+ st->st_nlink = 1;
+ st->st_dev = 0;
+#else
+ /* Getting FileIndex as i-node. We should remove a sequence which
+ * is high-16-bits of nFileIndexHigh. */
+ ino64.HighPart = info.nFileIndexHigh & 0x0000FFFFUL;
+ ino64.LowPart = info.nFileIndexLow;
+ st->st_ino = ino64.QuadPart;
+ st->st_nlink = info.nNumberOfLinks;
+ if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ ++st->st_nlink;/* Add parent directory. */
+ st->st_dev = info.dwVolumeSerialNumber;
+#endif
+ st->st_uid = 0;
+ st->st_gid = 0;
+ st->st_rdev = 0;
+ return (0);
+}
+
+static void
+copy_stat(struct stat *st, struct ustat *us)
+{
+ st->st_atime = us->st_atime;
+ st->st_ctime = us->st_ctime;
+ st->st_mtime = us->st_mtime;
+ st->st_gid = us->st_gid;
+ st->st_ino = getino(us);
+ st->st_mode = us->st_mode;
+ st->st_nlink = us->st_nlink;
+ st->st_size = (off_t)us->st_size;
+ st->st_uid = us->st_uid;
+ st->st_dev = us->st_dev;
+ st->st_rdev = us->st_rdev;
+}
+
+/*
+ * TODO: Remove a use of __la_fstat and __la_stat.
+ * We should use GetFileInformationByHandle in place
+ * where We still use the *stat functions.
+ */
+int
+__la_fstat(int fd, struct stat *st)
+{
+ struct ustat u;
+ int ret;
+
+ if (fd < 0) {
+ errno = EBADF;
+ return (-1);
+ }
+ ret = __hstat((HANDLE)_get_osfhandle(fd), &u);
+ if (ret >= 0) {
+ copy_stat(st, &u);
+ if (u.st_mode & (S_IFCHR | S_IFIFO)) {
+ st->st_dev = fd;
+ st->st_rdev = fd;
+ }
+ }
+ return (ret);
+}
+
+/* This can exceed MAX_PATH limitation. */
+int
+__la_stat(const char *path, struct stat *st)
+{
+ HANDLE handle;
+ struct ustat u;
+ int ret;
+
+ handle = la_CreateFile(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (handle == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+ ret = __hstat(handle, &u);
+ CloseHandle(handle);
+ if (ret >= 0) {
+ char *p;
+
+ copy_stat(st, &u);
+ p = strrchr(path, '.');
+ if (p != NULL && strlen(p) == 4) {
+ char exttype[4];
+
+ ++ p;
+ exttype[0] = toupper(*p++);
+ exttype[1] = toupper(*p++);
+ exttype[2] = toupper(*p++);
+ exttype[3] = '\0';
+ if (!strcmp(exttype, "EXE") || !strcmp(exttype, "CMD") ||
+ !strcmp(exttype, "BAT") || !strcmp(exttype, "COM"))
+ st->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * This waitpid is limited implementation.
+ */
+pid_t
+__la_waitpid(HANDLE child, int *status, int option)
+{
+ DWORD cs;
+
+ (void)option;/* UNUSED */
+ do {
+ if (GetExitCodeProcess(child, &cs) == 0) {
+ CloseHandle(child);
+ la_dosmaperr(GetLastError());
+ *status = 0;
+ return (-1);
+ }
+ } while (cs == STILL_ACTIVE);
+
+ *status = (int)(cs & 0xff);
+ return (0);
+}
+
+ssize_t
+__la_write(int fd, const void *buf, size_t nbytes)
+{
+ DWORD bytes_written;
+
+#ifdef _WIN64
+ if (nbytes > UINT32_MAX)
+ nbytes = UINT32_MAX;
+#endif
+ if (fd < 0) {
+ errno = EBADF;
+ return (-1);
+ }
+ if (!WriteFile((HANDLE)_get_osfhandle(fd), buf, (uint32_t)nbytes,
+ &bytes_written, NULL)) {
+ DWORD lasterr;
+
+ lasterr = GetLastError();
+ if (lasterr == ERROR_ACCESS_DENIED)
+ errno = EBADF;
+ else
+ la_dosmaperr(lasterr);
+ return (-1);
+ }
+ return (bytes_written);
+}
+
+/*
+ * Replace the Windows path separator '\' with '/'.
+ */
+static int
+replace_pathseparator(struct archive_wstring *ws, const wchar_t *wp)
+{
+ wchar_t *w;
+ size_t path_length;
+
+ if (wp == NULL)
+ return(0);
+ if (wcschr(wp, L'\\') == NULL)
+ return(0);
+ path_length = wcslen(wp);
+ if (archive_wstring_ensure(ws, path_length) == NULL)
+ return(-1);
+ archive_wstrncpy(ws, wp, path_length);
+ for (w = ws->s; *w; w++) {
+ if (*w == L'\\')
+ *w = L'/';
+ }
+ return(1);
+}
+
+static int
+fix_pathseparator(struct archive_entry *entry)
+{
+ struct archive_wstring ws;
+ const wchar_t *wp;
+ int ret = ARCHIVE_OK;
+
+ archive_string_init(&ws);
+ wp = archive_entry_pathname_w(entry);
+ switch (replace_pathseparator(&ws, wp)) {
+ case 0: /* Not replaced. */
+ break;
+ case 1: /* Replaced. */
+ archive_entry_copy_pathname_w(entry, ws.s);
+ break;
+ default:
+ ret = ARCHIVE_FAILED;
+ }
+ wp = archive_entry_hardlink_w(entry);
+ switch (replace_pathseparator(&ws, wp)) {
+ case 0: /* Not replaced. */
+ break;
+ case 1: /* Replaced. */
+ archive_entry_copy_hardlink_w(entry, ws.s);
+ break;
+ default:
+ ret = ARCHIVE_FAILED;
+ }
+ wp = archive_entry_symlink_w(entry);
+ switch (replace_pathseparator(&ws, wp)) {
+ case 0: /* Not replaced. */
+ break;
+ case 1: /* Replaced. */
+ archive_entry_copy_symlink_w(entry, ws.s);
+ break;
+ default:
+ ret = ARCHIVE_FAILED;
+ }
+ archive_wstring_free(&ws);
+ return(ret);
+}
+
+struct archive_entry *
+__la_win_entry_in_posix_pathseparator(struct archive_entry *entry)
+{
+ struct archive_entry *entry_main;
+ const wchar_t *wp;
+ int has_backslash = 0;
+ int ret;
+
+ wp = archive_entry_pathname_w(entry);
+ if (wp != NULL && wcschr(wp, L'\\') != NULL)
+ has_backslash = 1;
+ if (!has_backslash) {
+ wp = archive_entry_hardlink_w(entry);
+ if (wp != NULL && wcschr(wp, L'\\') != NULL)
+ has_backslash = 1;
+ }
+ if (!has_backslash) {
+ wp = archive_entry_symlink_w(entry);
+ if (wp != NULL && wcschr(wp, L'\\') != NULL)
+ has_backslash = 1;
+ }
+ /*
+ * If there is no backslash chars, return the original.
+ */
+ if (!has_backslash)
+ return (entry);
+
+ /* Copy entry so we can modify it as needed. */
+ entry_main = archive_entry_clone(entry);
+ if (entry_main == NULL)
+ return (NULL);
+ /* Replace the Windows path-separator '\' with '/'. */
+ ret = fix_pathseparator(entry_main);
+ if (ret < ARCHIVE_WARN) {
+ archive_entry_free(entry_main);
+ return (NULL);
+ }
+ return (entry_main);
+}
+
+
+/*
+ * The following function was modified from PostgreSQL sources and is
+ * subject to the copyright below.
+ */
+/*-------------------------------------------------------------------------
+ *
+ * win32error.c
+ * Map win32 error codes to errno values
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/port/win32error.c,v 1.4 2008/01/01 19:46:00 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+PostgreSQL Database Management System
+(formerly known as Postgres, then as Postgres95)
+
+Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+
+Portions Copyright (c) 1994, The Regents of the University of California
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose, without fee, and without a written agreement
+is hereby granted, provided that the above copyright notice and this
+paragraph and the following two paragraphs appear in all copies.
+
+IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
+DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
+PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+*/
+
+static const struct {
+ DWORD winerr;
+ int doserr;
+} doserrors[] =
+{
+ { ERROR_INVALID_FUNCTION, EINVAL },
+ { ERROR_FILE_NOT_FOUND, ENOENT },
+ { ERROR_PATH_NOT_FOUND, ENOENT },
+ { ERROR_TOO_MANY_OPEN_FILES, EMFILE },
+ { ERROR_ACCESS_DENIED, EACCES },
+ { ERROR_INVALID_HANDLE, EBADF },
+ { ERROR_ARENA_TRASHED, ENOMEM },
+ { ERROR_NOT_ENOUGH_MEMORY, ENOMEM },
+ { ERROR_INVALID_BLOCK, ENOMEM },
+ { ERROR_BAD_ENVIRONMENT, E2BIG },
+ { ERROR_BAD_FORMAT, ENOEXEC },
+ { ERROR_INVALID_ACCESS, EINVAL },
+ { ERROR_INVALID_DATA, EINVAL },
+ { ERROR_INVALID_DRIVE, ENOENT },
+ { ERROR_CURRENT_DIRECTORY, EACCES },
+ { ERROR_NOT_SAME_DEVICE, EXDEV },
+ { ERROR_NO_MORE_FILES, ENOENT },
+ { ERROR_LOCK_VIOLATION, EACCES },
+ { ERROR_SHARING_VIOLATION, EACCES },
+ { ERROR_BAD_NETPATH, ENOENT },
+ { ERROR_NETWORK_ACCESS_DENIED, EACCES },
+ { ERROR_BAD_NET_NAME, ENOENT },
+ { ERROR_FILE_EXISTS, EEXIST },
+ { ERROR_CANNOT_MAKE, EACCES },
+ { ERROR_FAIL_I24, EACCES },
+ { ERROR_INVALID_PARAMETER, EINVAL },
+ { ERROR_NO_PROC_SLOTS, EAGAIN },
+ { ERROR_DRIVE_LOCKED, EACCES },
+ { ERROR_BROKEN_PIPE, EPIPE },
+ { ERROR_DISK_FULL, ENOSPC },
+ { ERROR_INVALID_TARGET_HANDLE, EBADF },
+ { ERROR_INVALID_HANDLE, EINVAL },
+ { ERROR_WAIT_NO_CHILDREN, ECHILD },
+ { ERROR_CHILD_NOT_COMPLETE, ECHILD },
+ { ERROR_DIRECT_ACCESS_HANDLE, EBADF },
+ { ERROR_NEGATIVE_SEEK, EINVAL },
+ { ERROR_SEEK_ON_DEVICE, EACCES },
+ { ERROR_DIR_NOT_EMPTY, ENOTEMPTY },
+ { ERROR_NOT_LOCKED, EACCES },
+ { ERROR_BAD_PATHNAME, ENOENT },
+ { ERROR_MAX_THRDS_REACHED, EAGAIN },
+ { ERROR_LOCK_FAILED, EACCES },
+ { ERROR_ALREADY_EXISTS, EEXIST },
+ { ERROR_FILENAME_EXCED_RANGE, ENOENT },
+ { ERROR_NESTING_NOT_ALLOWED, EAGAIN },
+ { ERROR_NOT_ENOUGH_QUOTA, ENOMEM }
+};
+
+void
+__la_dosmaperr(unsigned long e)
+{
+ int i;
+
+ if (e == 0)
+ {
+ errno = 0;
+ return;
+ }
+
+ for (i = 0; i < (int)(sizeof(doserrors)/sizeof(doserrors[0])); i++)
+ {
+ if (doserrors[i].winerr == e)
+ {
+ errno = doserrors[i].doserr;
+ return;
+ }
+ }
+
+ /* fprintf(stderr, "unrecognized win32 error code: %lu", e); */
+ errno = EINVAL;
+ return;
+}
+
+#endif /* _WIN32 && !__CYGWIN__ */
diff --git a/contrib/libs/libarchive/libarchive/archive_windows.h b/contrib/libs/libarchive/libarchive/archive_windows.h
new file mode 100644
index 00000000000..47b7cb8e379
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_windows.h
@@ -0,0 +1,315 @@
+/*-
+ * Copyright (c) 2009-2011 Michihiro NAKAJIMA
+ * Copyright (c) 2003-2006 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * TODO: A lot of stuff in here isn't actually used by libarchive and
+ * can be trimmed out. Note that this file is used by libarchive and
+ * libarchive_test but nowhere else. (But note that it gets compiled
+ * with many different Windows environments, including MinGW, Visual
+ * Studio, and Cygwin. Significant changes should be tested in all three.)
+ */
+
+/*
+ * TODO: Don't use off_t in here. Use __int64 instead. Note that
+ * Visual Studio and the Windows SDK define off_t as 32 bits; Win32's
+ * more modern file handling APIs all use __int64 instead of off_t.
+ */
+
+#ifndef LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED
+#define LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+/* Start of configuration for native Win32 */
+#ifndef MINGW_HAS_SECURE_API
+#define MINGW_HAS_SECURE_API 1
+#endif
+
+#include <errno.h>
+#define set_errno(val) ((errno)=val)
+#include <io.h>
+#include <stdlib.h> //brings in NULL
+#if defined(HAVE_STDINT_H)
+#include <stdint.h>
+#endif
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <process.h>
+#include <direct.h>
+#if defined(__MINGW32__) && defined(HAVE_UNISTD_H)
+/* Prevent build error from a type mismatch of ftruncate().
+ * This unistd.h defines it as ftruncate(int, off_t). */
+#include <unistd.h>
+#endif
+#define NOCRYPT
+#include <windows.h>
+//#define EFTYPE 7
+
+#if defined(__BORLANDC__)
+#pragma warn -8068 /* Constant out of range in comparison. */
+#pragma warn -8072 /* Suspicious pointer arithmetic. */
+#endif
+
+#ifndef NULL
+#ifdef __cplusplus
+#define NULL 0
+#else
+#define NULL ((void *)0)
+#endif
+#endif
+
+/* Alias the Windows _function to the POSIX equivalent. */
+#define close _close
+#define fcntl(fd, cmd, flg) /* No operation. */
+#ifndef fileno
+#define fileno _fileno
+#endif
+#ifdef fstat
+#undef fstat
+#endif
+#define fstat __la_fstat
+#if !defined(__BORLANDC__)
+#ifdef lseek
+#undef lseek
+#endif
+#define lseek _lseeki64
+#else
+#define lseek __la_lseek
+#define __LA_LSEEK_NEEDED
+#endif
+#define lstat __la_stat
+#define open __la_open
+#define read __la_read
+#if !defined(__BORLANDC__) && !defined(__WATCOMC__)
+#define setmode _setmode
+#endif
+#define la_stat(path,stref) __la_stat(path,stref)
+#if !defined(__WATCOMC__)
+#if !defined(__BORLANDC__)
+#define strdup _strdup
+#endif
+#define tzset _tzset
+#if !defined(__BORLANDC__)
+#define umask _umask
+#endif
+#endif
+#define waitpid __la_waitpid
+#define write __la_write
+
+#if !defined(__WATCOMC__)
+
+#ifndef O_RDONLY
+#define O_RDONLY _O_RDONLY
+#define O_WRONLY _O_WRONLY
+#define O_TRUNC _O_TRUNC
+#define O_CREAT _O_CREAT
+#define O_EXCL _O_EXCL
+#define O_BINARY _O_BINARY
+#endif
+
+#ifndef _S_IFIFO
+ #define _S_IFIFO 0010000 /* pipe */
+#endif
+#ifndef _S_IFCHR
+ #define _S_IFCHR 0020000 /* character special */
+#endif
+#ifndef _S_IFDIR
+ #define _S_IFDIR 0040000 /* directory */
+#endif
+#ifndef _S_IFBLK
+ #define _S_IFBLK 0060000 /* block special */
+#endif
+#ifndef _S_IFLNK
+ #define _S_IFLNK 0120000 /* symbolic link */
+#endif
+#ifndef _S_IFSOCK
+ #define _S_IFSOCK 0140000 /* socket */
+#endif
+#ifndef _S_IFREG
+ #define _S_IFREG 0100000 /* regular */
+#endif
+#ifndef _S_IFMT
+ #define _S_IFMT 0170000 /* file type mask */
+#endif
+
+#ifndef S_IFIFO
+#define S_IFIFO _S_IFIFO
+#endif
+//#define S_IFCHR _S_IFCHR
+//#define S_IFDIR _S_IFDIR
+#ifndef S_IFBLK
+#define S_IFBLK _S_IFBLK
+#endif
+#ifndef S_IFLNK
+#define S_IFLNK _S_IFLNK
+#endif
+#ifndef S_IFSOCK
+#define S_IFSOCK _S_IFSOCK
+#endif
+//#define S_IFREG _S_IFREG
+//#define S_IFMT _S_IFMT
+
+#ifndef S_ISBLK
+#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) /* block special */
+#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) /* fifo or socket */
+#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) /* char special */
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) /* directory */
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) /* regular file */
+#endif
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) /* Symbolic link */
+#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) /* Socket */
+
+#define _S_ISUID 0004000 /* set user id on execution */
+#define _S_ISGID 0002000 /* set group id on execution */
+#define _S_ISVTX 0001000 /* save swapped text even after use */
+
+#define S_ISUID _S_ISUID
+#define S_ISGID _S_ISGID
+#define S_ISVTX _S_ISVTX
+
+#define _S_IRWXU (_S_IREAD | _S_IWRITE | _S_IEXEC)
+#define _S_IXUSR _S_IEXEC /* read permission, user */
+#define _S_IWUSR _S_IWRITE /* write permission, user */
+#define _S_IRUSR _S_IREAD /* execute/search permission, user */
+#define _S_IRWXG (_S_IRWXU >> 3)
+#define _S_IXGRP (_S_IXUSR >> 3) /* read permission, group */
+#define _S_IWGRP (_S_IWUSR >> 3) /* write permission, group */
+#define _S_IRGRP (_S_IRUSR >> 3) /* execute/search permission, group */
+#define _S_IRWXO (_S_IRWXG >> 3)
+#define _S_IXOTH (_S_IXGRP >> 3) /* read permission, other */
+#define _S_IWOTH (_S_IWGRP >> 3) /* write permission, other */
+#define _S_IROTH (_S_IRGRP >> 3) /* execute/search permission, other */
+
+#ifndef S_IRWXU
+#define S_IRWXU _S_IRWXU
+#define S_IXUSR _S_IXUSR
+#define S_IWUSR _S_IWUSR
+#define S_IRUSR _S_IRUSR
+#endif
+#ifndef S_IRWXG
+#define S_IRWXG _S_IRWXG
+#define S_IXGRP _S_IXGRP
+#define S_IWGRP _S_IWGRP
+#endif
+#ifndef S_IRGRP
+#define S_IRGRP _S_IRGRP
+#endif
+#ifndef S_IRWXO
+#define S_IRWXO _S_IRWXO
+#define S_IXOTH _S_IXOTH
+#define S_IWOTH _S_IWOTH
+#define S_IROTH _S_IROTH
+#endif
+
+#endif
+
+#define F_DUPFD 0 /* Duplicate file descriptor. */
+#define F_GETFD 1 /* Get file descriptor flags. */
+#define F_SETFD 2 /* Set file descriptor flags. */
+#define F_GETFL 3 /* Get file status flags. */
+#define F_SETFL 4 /* Set file status flags. */
+#define F_GETOWN 5 /* Get owner (receiver of SIGIO). */
+#define F_SETOWN 6 /* Set owner (receiver of SIGIO). */
+#define F_GETLK 7 /* Get record locking info. */
+#define F_SETLK 8 /* Set record locking info (non-blocking). */
+#define F_SETLKW 9 /* Set record locking info (blocking). */
+
+/* XXX missing */
+#define F_GETLK64 7 /* Get record locking info. */
+#define F_SETLK64 8 /* Set record locking info (non-blocking). */
+#define F_SETLKW64 9 /* Set record locking info (blocking). */
+
+/* File descriptor flags used with F_GETFD and F_SETFD. */
+#define FD_CLOEXEC 1 /* Close on exec. */
+
+//NOT SURE IF O_NONBLOCK is OK here but at least the 0x0004 flag is not used by anything else...
+#define O_NONBLOCK 0x0004 /* Non-blocking I/O. */
+//#define O_NDELAY O_NONBLOCK
+
+/* Symbolic constants for the access() function */
+#if !defined(F_OK)
+ #define R_OK 4 /* Test for read permission */
+ #define W_OK 2 /* Test for write permission */
+ #define X_OK 1 /* Test for execute permission */
+ #define F_OK 0 /* Test for existence of file */
+#endif
+
+
+/* Replacement POSIX function */
+extern int __la_fstat(int fd, struct stat *st);
+extern int __la_lstat(const char *path, struct stat *st);
+#if defined(__LA_LSEEK_NEEDED)
+extern __int64 __la_lseek(int fd, __int64 offset, int whence);
+#endif
+extern int __la_open(const char *path, int flags, ...);
+extern ssize_t __la_read(int fd, void *buf, size_t nbytes);
+extern int __la_stat(const char *path, struct stat *st);
+extern pid_t __la_waitpid(HANDLE child, int *status, int option);
+extern ssize_t __la_write(int fd, const void *buf, size_t nbytes);
+
+#define _stat64i32(path, st) __la_stat(path, st)
+#define _stat64(path, st) __la_stat(path, st)
+/* for status returned by la_waitpid */
+#define WIFEXITED(sts) ((sts & 0x100) == 0)
+#define WEXITSTATUS(sts) (sts & 0x0FF)
+
+extern wchar_t *__la_win_permissive_name(const char *name);
+extern wchar_t *__la_win_permissive_name_w(const wchar_t *wname);
+extern void __la_dosmaperr(unsigned long e);
+#define la_dosmaperr(e) __la_dosmaperr(e)
+extern struct archive_entry *__la_win_entry_in_posix_pathseparator(
+ struct archive_entry *);
+
+#if defined(HAVE_WCRTOMB) && defined(__BORLANDC__)
+typedef int mbstate_t;
+size_t wcrtomb(char *, wchar_t, mbstate_t *);
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+WINBASEAPI BOOL WINAPI GetVolumePathNameW(
+ LPCWSTR lpszFileName,
+ LPWSTR lpszVolumePathName,
+ DWORD cchBufferLength
+ );
+# if _WIN32_WINNT < 0x0500 /* windows.h not providing 0x500 API */
+typedef struct _FILE_ALLOCATED_RANGE_BUFFER {
+ LARGE_INTEGER FileOffset;
+ LARGE_INTEGER Length;
+} FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER;
+# define FSCTL_SET_SPARSE \
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, METHOD_BUFFERED, FILE_WRITE_DATA)
+# define FSCTL_QUERY_ALLOCATED_RANGES \
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 51, METHOD_NEITHER, FILE_READ_DATA)
+# endif
+#endif
+
+#endif /* LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_write.c b/contrib/libs/libarchive/libarchive/archive_write.c
new file mode 100644
index 00000000000..ec3c95c5668
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write.c
@@ -0,0 +1,859 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write.c 201099 2009-12-28 03:03:00Z kientzle $");
+
+/*
+ * This file contains the "essential" portions of the write API, that
+ * is, stuff that will essentially always be used by any client that
+ * actually needs to write an archive. Optional pieces have been, as
+ * far as possible, separated out into separate files to reduce
+ * needlessly bloating statically-linked clients.
+ */
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include <time.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+
+static int _archive_filter_code(struct archive *, int);
+static const char *_archive_filter_name(struct archive *, int);
+static int64_t _archive_filter_bytes(struct archive *, int);
+static int _archive_write_filter_count(struct archive *);
+static int _archive_write_close(struct archive *);
+static int _archive_write_free(struct archive *);
+static int _archive_write_header(struct archive *, struct archive_entry *);
+static int _archive_write_finish_entry(struct archive *);
+static ssize_t _archive_write_data(struct archive *, const void *, size_t);
+
+struct archive_none {
+ size_t buffer_size;
+ size_t avail;
+ char *buffer;
+ char *next;
+};
+
+static const struct archive_vtable
+archive_write_vtable = {
+ .archive_close = _archive_write_close,
+ .archive_filter_bytes = _archive_filter_bytes,
+ .archive_filter_code = _archive_filter_code,
+ .archive_filter_name = _archive_filter_name,
+ .archive_filter_count = _archive_write_filter_count,
+ .archive_free = _archive_write_free,
+ .archive_write_header = _archive_write_header,
+ .archive_write_finish_entry = _archive_write_finish_entry,
+ .archive_write_data = _archive_write_data,
+};
+
+/*
+ * Allocate, initialize and return an archive object.
+ */
+struct archive *
+archive_write_new(void)
+{
+ struct archive_write *a;
+ unsigned char *nulls;
+
+ a = (struct archive_write *)calloc(1, sizeof(*a));
+ if (a == NULL)
+ return (NULL);
+ a->archive.magic = ARCHIVE_WRITE_MAGIC;
+ a->archive.state = ARCHIVE_STATE_NEW;
+ a->archive.vtable = &archive_write_vtable;
+ /*
+ * The value 10240 here matches the traditional tar default,
+ * but is otherwise arbitrary.
+ * TODO: Set the default block size from the format selected.
+ */
+ a->bytes_per_block = 10240;
+ a->bytes_in_last_block = -1; /* Default */
+
+ /* Initialize a block of nulls for padding purposes. */
+ a->null_length = 1024;
+ nulls = (unsigned char *)calloc(1, a->null_length);
+ if (nulls == NULL) {
+ free(a);
+ return (NULL);
+ }
+ a->nulls = nulls;
+ return (&a->archive);
+}
+
+/*
+ * Set the block size. Returns 0 if successful.
+ */
+int
+archive_write_set_bytes_per_block(struct archive *_a, int bytes_per_block)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_bytes_per_block");
+ a->bytes_per_block = bytes_per_block;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Get the current block size. -1 if it has never been set.
+ */
+int
+archive_write_get_bytes_per_block(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_get_bytes_per_block");
+ return (a->bytes_per_block);
+}
+
+/*
+ * Set the size for the last block.
+ * Returns 0 if successful.
+ */
+int
+archive_write_set_bytes_in_last_block(struct archive *_a, int bytes)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_set_bytes_in_last_block");
+ a->bytes_in_last_block = bytes;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Return the value set above. -1 indicates it has not been set.
+ */
+int
+archive_write_get_bytes_in_last_block(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_get_bytes_in_last_block");
+ return (a->bytes_in_last_block);
+}
+
+/*
+ * dev/ino of a file to be rejected. Used to prevent adding
+ * an archive to itself recursively.
+ */
+int
+archive_write_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_set_skip_file");
+ a->skip_file_set = 1;
+ a->skip_file_dev = d;
+ a->skip_file_ino = i;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Allocate and return the next filter structure.
+ */
+struct archive_write_filter *
+__archive_write_allocate_filter(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f;
+
+ f = calloc(1, sizeof(*f));
+
+ if (f == NULL)
+ return (NULL);
+
+ f->archive = _a;
+ f->state = ARCHIVE_WRITE_FILTER_STATE_NEW;
+ if (a->filter_first == NULL)
+ a->filter_first = f;
+ else
+ a->filter_last->next_filter = f;
+ a->filter_last = f;
+ return f;
+}
+
+/*
+ * Write data to a particular filter.
+ */
+int
+__archive_write_filter(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ int r;
+ /* Never write to non-open filters */
+ if (f->state != ARCHIVE_WRITE_FILTER_STATE_OPEN)
+ return(ARCHIVE_FATAL);
+ if (length == 0)
+ return(ARCHIVE_OK);
+ if (f->write == NULL)
+ /* If unset, a fatal error has already occurred, so this filter
+ * didn't open. We cannot write anything. */
+ return(ARCHIVE_FATAL);
+ r = (f->write)(f, buff, length);
+ f->bytes_written += length;
+ return (r);
+}
+
+/*
+ * Recursive function for opening the filter chain
+ * Last filter is opened first
+ */
+static int
+__archive_write_open_filter(struct archive_write_filter *f)
+{
+ int ret;
+
+ ret = ARCHIVE_OK;
+ if (f->next_filter != NULL)
+ ret = __archive_write_open_filter(f->next_filter);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ if (f->state != ARCHIVE_WRITE_FILTER_STATE_NEW)
+ return (ARCHIVE_FATAL);
+ if (f->open == NULL) {
+ f->state = ARCHIVE_WRITE_FILTER_STATE_OPEN;
+ return (ARCHIVE_OK);
+ }
+ ret = (f->open)(f);
+ if (ret == ARCHIVE_OK)
+ f->state = ARCHIVE_WRITE_FILTER_STATE_OPEN;
+ else
+ f->state = ARCHIVE_WRITE_FILTER_STATE_FATAL;
+ return (ret);
+}
+
+/*
+ * Open all filters
+ */
+static int
+__archive_write_filters_open(struct archive_write *a)
+{
+ return (__archive_write_open_filter(a->filter_first));
+}
+
+/*
+ * Close all filtes
+ */
+static int
+__archive_write_filters_close(struct archive_write *a)
+{
+ struct archive_write_filter *f;
+ int ret, ret1;
+ ret = ARCHIVE_OK;
+ for (f = a->filter_first; f != NULL; f = f->next_filter) {
+ /* Do not close filters that are not open */
+ if (f->state == ARCHIVE_WRITE_FILTER_STATE_OPEN) {
+ if (f->close != NULL) {
+ ret1 = (f->close)(f);
+ if (ret1 < ret)
+ ret = ret1;
+ if (ret1 == ARCHIVE_OK) {
+ f->state =
+ ARCHIVE_WRITE_FILTER_STATE_CLOSED;
+ } else {
+ f->state =
+ ARCHIVE_WRITE_FILTER_STATE_FATAL;
+ }
+ } else
+ f->state = ARCHIVE_WRITE_FILTER_STATE_CLOSED;
+ }
+ }
+ return (ret);
+}
+
+int
+__archive_write_output(struct archive_write *a, const void *buff, size_t length)
+{
+ return (__archive_write_filter(a->filter_first, buff, length));
+}
+
+static int
+__archive_write_filters_flush(struct archive_write *a)
+{
+ struct archive_write_filter *f;
+ int ret, ret1;
+
+ ret = ARCHIVE_OK;
+ for (f = a->filter_first; f != NULL; f = f->next_filter) {
+ if (f->flush != NULL && f->bytes_written > 0) {
+ ret1 = (f->flush)(f);
+ if (ret1 < ret)
+ ret = ret1;
+ if (ret1 < ARCHIVE_WARN)
+ f->state = ARCHIVE_WRITE_FILTER_STATE_FATAL;
+ }
+ }
+ return (ret);
+}
+
+int
+__archive_write_nulls(struct archive_write *a, size_t length)
+{
+ if (length == 0)
+ return (ARCHIVE_OK);
+
+ while (length > 0) {
+ size_t to_write = length < a->null_length ? length : a->null_length;
+ int r = __archive_write_output(a, a->nulls, to_write);
+ if (r < ARCHIVE_OK)
+ return (r);
+ length -= to_write;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_client_open(struct archive_write_filter *f)
+{
+ struct archive_write *a = (struct archive_write *)f->archive;
+ struct archive_none *state;
+ void *buffer;
+ size_t buffer_size;
+ int ret;
+
+ f->bytes_per_block = archive_write_get_bytes_per_block(f->archive);
+ f->bytes_in_last_block =
+ archive_write_get_bytes_in_last_block(f->archive);
+ buffer_size = f->bytes_per_block;
+
+ state = (struct archive_none *)calloc(1, sizeof(*state));
+ buffer = (char *)malloc(buffer_size);
+ if (state == NULL || buffer == NULL) {
+ free(state);
+ free(buffer);
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for output buffering");
+ return (ARCHIVE_FATAL);
+ }
+
+ state->buffer_size = buffer_size;
+ state->buffer = buffer;
+ state->next = state->buffer;
+ state->avail = state->buffer_size;
+ f->data = state;
+
+ if (a->client_opener == NULL)
+ return (ARCHIVE_OK);
+ ret = a->client_opener(f->archive, a->client_data);
+ if (ret != ARCHIVE_OK) {
+ free(state->buffer);
+ free(state);
+ f->data = NULL;
+ }
+ return (ret);
+}
+
+static int
+archive_write_client_write(struct archive_write_filter *f,
+ const void *_buff, size_t length)
+{
+ struct archive_write *a = (struct archive_write *)f->archive;
+ struct archive_none *state = (struct archive_none *)f->data;
+ const char *buff = (const char *)_buff;
+ ssize_t remaining, to_copy;
+ ssize_t bytes_written;
+
+ remaining = length;
+
+ /*
+ * If there is no buffer for blocking, just pass the data
+ * straight through to the client write callback. In
+ * particular, this supports "no write delay" operation for
+ * special applications. Just set the block size to zero.
+ */
+ if (state->buffer_size == 0) {
+ while (remaining > 0) {
+ bytes_written = (a->client_writer)(&a->archive,
+ a->client_data, buff, remaining);
+ if (bytes_written <= 0)
+ return (ARCHIVE_FATAL);
+ remaining -= bytes_written;
+ buff += bytes_written;
+ }
+ return (ARCHIVE_OK);
+ }
+
+ /* If the copy buffer isn't empty, try to fill it. */
+ if (state->avail < state->buffer_size) {
+ /* If buffer is not empty... */
+ /* ... copy data into buffer ... */
+ to_copy = ((size_t)remaining > state->avail) ?
+ state->avail : (size_t)remaining;
+ memcpy(state->next, buff, to_copy);
+ state->next += to_copy;
+ state->avail -= to_copy;
+ buff += to_copy;
+ remaining -= to_copy;
+ /* ... if it's full, write it out. */
+ if (state->avail == 0) {
+ char *p = state->buffer;
+ size_t to_write = state->buffer_size;
+ while (to_write > 0) {
+ bytes_written = (a->client_writer)(&a->archive,
+ a->client_data, p, to_write);
+ if (bytes_written <= 0)
+ return (ARCHIVE_FATAL);
+ if ((size_t)bytes_written > to_write) {
+ archive_set_error(&(a->archive),
+ -1, "write overrun");
+ return (ARCHIVE_FATAL);
+ }
+ p += bytes_written;
+ to_write -= bytes_written;
+ }
+ state->next = state->buffer;
+ state->avail = state->buffer_size;
+ }
+ }
+
+ while ((size_t)remaining >= state->buffer_size) {
+ /* Write out full blocks directly to client. */
+ bytes_written = (a->client_writer)(&a->archive,
+ a->client_data, buff, state->buffer_size);
+ if (bytes_written <= 0)
+ return (ARCHIVE_FATAL);
+ buff += bytes_written;
+ remaining -= bytes_written;
+ }
+
+ if (remaining > 0) {
+ /* Copy last bit into copy buffer. */
+ memcpy(state->next, buff, remaining);
+ state->next += remaining;
+ state->avail -= remaining;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_client_free(struct archive_write_filter *f)
+{
+ struct archive_write *a = (struct archive_write *)f->archive;
+
+ if (a->client_freer)
+ (*a->client_freer)(&a->archive, a->client_data);
+ a->client_data = NULL;
+
+ /* Clear passphrase. */
+ if (a->passphrase != NULL) {
+ memset(a->passphrase, 0, strlen(a->passphrase));
+ free(a->passphrase);
+ a->passphrase = NULL;
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_client_close(struct archive_write_filter *f)
+{
+ struct archive_write *a = (struct archive_write *)f->archive;
+ struct archive_none *state = (struct archive_none *)f->data;
+ ssize_t block_length;
+ ssize_t target_block_length;
+ ssize_t bytes_written;
+ size_t to_write;
+ char *p;
+ int ret = ARCHIVE_OK;
+
+ /* If there's pending data, pad and write the last block */
+ if (state->next != state->buffer) {
+ block_length = state->buffer_size - state->avail;
+
+ /* Tricky calculation to determine size of last block */
+ if (a->bytes_in_last_block <= 0)
+ /* Default or Zero: pad to full block */
+ target_block_length = a->bytes_per_block;
+ else
+ /* Round to next multiple of bytes_in_last_block. */
+ target_block_length = a->bytes_in_last_block *
+ ( (block_length + a->bytes_in_last_block - 1) /
+ a->bytes_in_last_block);
+ if (target_block_length > a->bytes_per_block)
+ target_block_length = a->bytes_per_block;
+ if (block_length < target_block_length) {
+ memset(state->next, 0,
+ target_block_length - block_length);
+ block_length = target_block_length;
+ }
+ p = state->buffer;
+ to_write = block_length;
+ while (to_write > 0) {
+ bytes_written = (a->client_writer)(&a->archive,
+ a->client_data, p, to_write);
+ if (bytes_written <= 0) {
+ ret = ARCHIVE_FATAL;
+ break;
+ }
+ if ((size_t)bytes_written > to_write) {
+ archive_set_error(&(a->archive),
+ -1, "write overrun");
+ ret = ARCHIVE_FATAL;
+ break;
+ }
+ p += bytes_written;
+ to_write -= bytes_written;
+ }
+ }
+ if (a->client_closer)
+ (*a->client_closer)(&a->archive, a->client_data);
+ free(state->buffer);
+ free(state);
+
+ /* Clear the close handler myself not to be called again. */
+ f->state = ARCHIVE_WRITE_FILTER_STATE_CLOSED;
+ return (ret);
+}
+
+/*
+ * Open the archive using the current settings.
+ */
+int
+archive_write_open2(struct archive *_a, void *client_data,
+ archive_open_callback *opener, archive_write_callback *writer,
+ archive_close_callback *closer, archive_free_callback *freer)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *client_filter;
+ int ret, r1;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_open");
+ archive_clear_error(&a->archive);
+
+ a->client_writer = writer;
+ a->client_opener = opener;
+ a->client_closer = closer;
+ a->client_freer = freer;
+ a->client_data = client_data;
+
+ client_filter = __archive_write_allocate_filter(_a);
+
+ if (client_filter == NULL)
+ return (ARCHIVE_FATAL);
+
+ client_filter->open = archive_write_client_open;
+ client_filter->write = archive_write_client_write;
+ client_filter->close = archive_write_client_close;
+ client_filter->free = archive_write_client_free;
+
+ ret = __archive_write_filters_open(a);
+ if (ret < ARCHIVE_WARN) {
+ r1 = __archive_write_filters_close(a);
+ __archive_write_filters_free(_a);
+ return (r1 < ret ? r1 : ret);
+ }
+
+ a->archive.state = ARCHIVE_STATE_HEADER;
+ if (a->format_init)
+ ret = (a->format_init)(a);
+ return (ret);
+}
+
+int
+archive_write_open(struct archive *_a, void *client_data,
+ archive_open_callback *opener, archive_write_callback *writer,
+ archive_close_callback *closer)
+{
+ return archive_write_open2(_a, client_data, opener, writer,
+ closer, NULL);
+}
+
+/*
+ * Close out the archive.
+ */
+static int
+_archive_write_close(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int r = ARCHIVE_OK, r1 = ARCHIVE_OK;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL,
+ "archive_write_close");
+ if (a->archive.state == ARCHIVE_STATE_NEW
+ || a->archive.state == ARCHIVE_STATE_CLOSED)
+ return (ARCHIVE_OK); /* Okay to close() when not open. */
+
+ archive_clear_error(&a->archive);
+
+ /* Finish the last entry if a finish callback is specified */
+ if (a->archive.state == ARCHIVE_STATE_DATA
+ && a->format_finish_entry != NULL)
+ r = ((a->format_finish_entry)(a));
+
+ /* Finish off the archive. */
+ /* TODO: have format closers invoke compression close. */
+ if (a->format_close != NULL) {
+ r1 = (a->format_close)(a);
+ if (r1 < r)
+ r = r1;
+ }
+
+ /* Finish the compression and close the stream. */
+ r1 = __archive_write_filters_close(a);
+ if (r1 < r)
+ r = r1;
+
+ if (a->archive.state != ARCHIVE_STATE_FATAL)
+ a->archive.state = ARCHIVE_STATE_CLOSED;
+ return (r);
+}
+
+static int
+_archive_write_filter_count(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *p = a->filter_first;
+ int count = 0;
+ while(p) {
+ count++;
+ p = p->next_filter;
+ }
+ return count;
+}
+
+void
+__archive_write_filters_free(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int r = ARCHIVE_OK, r1;
+
+ while (a->filter_first != NULL) {
+ struct archive_write_filter *next
+ = a->filter_first->next_filter;
+ if (a->filter_first->free != NULL) {
+ r1 = (*a->filter_first->free)(a->filter_first);
+ if (r > r1)
+ r = r1;
+ }
+ free(a->filter_first);
+ a->filter_first = next;
+ }
+ a->filter_last = NULL;
+}
+
+/*
+ * Destroy the archive structure.
+ *
+ * Be careful: user might just call write_new and then write_free.
+ * Don't assume we actually wrote anything or performed any non-trivial
+ * initialization.
+ */
+static int
+_archive_write_free(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int r = ARCHIVE_OK, r1;
+
+ if (_a == NULL)
+ return (ARCHIVE_OK);
+ /* It is okay to call free() in state FATAL. */
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_free");
+ if (a->archive.state != ARCHIVE_STATE_FATAL)
+ r = archive_write_close(&a->archive);
+
+ /* Release format resources. */
+ if (a->format_free != NULL) {
+ r1 = (a->format_free)(a);
+ if (r1 < r)
+ r = r1;
+ }
+
+ __archive_write_filters_free(_a);
+
+ /* Release various dynamic buffers. */
+ free((void *)(uintptr_t)(const void *)a->nulls);
+ archive_string_free(&a->archive.error_string);
+ if (a->passphrase != NULL) {
+ /* A passphrase should be cleaned. */
+ memset(a->passphrase, 0, strlen(a->passphrase));
+ free(a->passphrase);
+ }
+ a->archive.magic = 0;
+ __archive_clean(&a->archive);
+ free(a);
+ return (r);
+}
+
+/*
+ * Write the appropriate header.
+ */
+static int
+_archive_write_header(struct archive *_a, struct archive_entry *entry)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int ret, r2;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_DATA | ARCHIVE_STATE_HEADER, "archive_write_header");
+ archive_clear_error(&a->archive);
+
+ if (a->format_write_header == NULL) {
+ archive_set_error(&(a->archive), -1,
+ "Format must be set before you can write to an archive.");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+
+ /* In particular, "retry" and "fatal" get returned immediately. */
+ ret = archive_write_finish_entry(&a->archive);
+ if (ret == ARCHIVE_FATAL) {
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ if (ret < ARCHIVE_OK && ret != ARCHIVE_WARN)
+ return (ret);
+
+ if (a->skip_file_set &&
+ archive_entry_dev_is_set(entry) &&
+ archive_entry_ino_is_set(entry) &&
+ archive_entry_dev(entry) == (dev_t)a->skip_file_dev &&
+ archive_entry_ino64(entry) == a->skip_file_ino) {
+ archive_set_error(&a->archive, 0,
+ "Can't add archive to itself");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Flush filters at boundary. */
+ r2 = __archive_write_filters_flush(a);
+ if (r2 == ARCHIVE_FAILED) {
+ return (ARCHIVE_FAILED);
+ }
+ if (r2 == ARCHIVE_FATAL) {
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ if (r2 < ret)
+ ret = r2;
+
+ /* Format and write header. */
+ r2 = ((a->format_write_header)(a, entry));
+ if (r2 == ARCHIVE_FAILED) {
+ return (ARCHIVE_FAILED);
+ }
+ if (r2 == ARCHIVE_FATAL) {
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ if (r2 < ret)
+ ret = r2;
+
+ a->archive.state = ARCHIVE_STATE_DATA;
+ return (ret);
+}
+
+static int
+_archive_write_finish_entry(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int ret = ARCHIVE_OK;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_finish_entry");
+ if (a->archive.state & ARCHIVE_STATE_DATA
+ && a->format_finish_entry != NULL)
+ ret = (a->format_finish_entry)(a);
+ a->archive.state = ARCHIVE_STATE_HEADER;
+ return (ret);
+}
+
+/*
+ * Note that the compressor is responsible for blocking.
+ */
+static ssize_t
+_archive_write_data(struct archive *_a, const void *buff, size_t s)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ const size_t max_write = INT_MAX;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_DATA, "archive_write_data");
+ /* In particular, this catches attempts to pass negative values. */
+ if (s > max_write)
+ s = max_write;
+ archive_clear_error(&a->archive);
+ return ((a->format_write_data)(a, buff, s));
+}
+
+static struct archive_write_filter *
+filter_lookup(struct archive *_a, int n)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f = a->filter_first;
+ if (n == -1)
+ return a->filter_last;
+ if (n < 0)
+ return NULL;
+ while (n > 0 && f != NULL) {
+ f = f->next_filter;
+ --n;
+ }
+ return f;
+}
+
+static int
+_archive_filter_code(struct archive *_a, int n)
+{
+ struct archive_write_filter *f = filter_lookup(_a, n);
+ return f == NULL ? -1 : f->code;
+}
+
+static const char *
+_archive_filter_name(struct archive *_a, int n)
+{
+ struct archive_write_filter *f = filter_lookup(_a, n);
+ return f != NULL ? f->name : NULL;
+}
+
+static int64_t
+_archive_filter_bytes(struct archive *_a, int n)
+{
+ struct archive_write_filter *f = filter_lookup(_a, n);
+ return f == NULL ? -1 : f->bytes_written;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter.c
new file mode 100644
index 00000000000..203f4142b5c
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter.c
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 2012 Ondrej Holy
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+/* A table that maps filter codes to functions. */
+static const
+struct { int code; int (*setter)(struct archive *); } codes[] =
+{
+ { ARCHIVE_FILTER_NONE, archive_write_add_filter_none },
+ { ARCHIVE_FILTER_GZIP, archive_write_add_filter_gzip },
+ { ARCHIVE_FILTER_BZIP2, archive_write_add_filter_bzip2 },
+ { ARCHIVE_FILTER_COMPRESS, archive_write_add_filter_compress },
+ { ARCHIVE_FILTER_GRZIP, archive_write_add_filter_grzip },
+ { ARCHIVE_FILTER_LRZIP, archive_write_add_filter_lrzip },
+ { ARCHIVE_FILTER_LZ4, archive_write_add_filter_lz4 },
+ { ARCHIVE_FILTER_LZIP, archive_write_add_filter_lzip },
+ { ARCHIVE_FILTER_LZMA, archive_write_add_filter_lzma },
+ { ARCHIVE_FILTER_LZOP, archive_write_add_filter_lzip },
+ { ARCHIVE_FILTER_UU, archive_write_add_filter_uuencode },
+ { ARCHIVE_FILTER_XZ, archive_write_add_filter_xz },
+ { ARCHIVE_FILTER_ZSTD, archive_write_add_filter_zstd },
+ { -1, NULL }
+};
+
+int
+archive_write_add_filter(struct archive *a, int code)
+{
+ int i;
+
+ for (i = 0; codes[i].code != -1; i++) {
+ if (code == codes[i].code)
+ return ((codes[i].setter)(a));
+ }
+
+ archive_set_error(a, EINVAL, "No such filter");
+ return (ARCHIVE_FATAL);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_b64encode.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_b64encode.c
new file mode 100644
index 00000000000..87fdb73ecb0
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_b64encode.c
@@ -0,0 +1,304 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+#define LBYTES 57
+
+struct private_b64encode {
+ int mode;
+ struct archive_string name;
+ struct archive_string encoded_buff;
+ size_t bs;
+ size_t hold_len;
+ unsigned char hold[LBYTES];
+};
+
+static int archive_filter_b64encode_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_filter_b64encode_open(struct archive_write_filter *);
+static int archive_filter_b64encode_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_filter_b64encode_close(struct archive_write_filter *);
+static int archive_filter_b64encode_free(struct archive_write_filter *);
+static void la_b64_encode(struct archive_string *, const unsigned char *, size_t);
+static int64_t atol8(const char *, size_t);
+
+static const char base64[] = {
+ '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', '+', '/'
+};
+
+/*
+ * Add a compress filter to this write handle.
+ */
+int
+archive_write_add_filter_b64encode(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct private_b64encode *state;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
+
+ state = (struct private_b64encode *)calloc(1, sizeof(*state));
+ if (state == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for b64encode filter");
+ return (ARCHIVE_FATAL);
+ }
+ archive_strcpy(&state->name, "-");
+ state->mode = 0644;
+
+ f->data = state;
+ f->name = "b64encode";
+ f->code = ARCHIVE_FILTER_UU;
+ f->open = archive_filter_b64encode_open;
+ f->options = archive_filter_b64encode_options;
+ f->write = archive_filter_b64encode_write;
+ f->close = archive_filter_b64encode_close;
+ f->free = archive_filter_b64encode_free;
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_filter_b64encode_options(struct archive_write_filter *f, const char *key,
+ const char *value)
+{
+ struct private_b64encode *state = (struct private_b64encode *)f->data;
+
+ if (strcmp(key, "mode") == 0) {
+ if (value == NULL) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "mode option requires octal digits");
+ return (ARCHIVE_FAILED);
+ }
+ state->mode = (int)atol8(value, strlen(value)) & 0777;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "name") == 0) {
+ if (value == NULL) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "name option requires a string");
+ return (ARCHIVE_FAILED);
+ }
+ archive_strcpy(&state->name, value);
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Setup callback.
+ */
+static int
+archive_filter_b64encode_open(struct archive_write_filter *f)
+{
+ struct private_b64encode *state = (struct private_b64encode *)f->data;
+ size_t bs = 65536, bpb;
+
+ if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+ /* Buffer size should be a multiple number of the of bytes
+ * per block for performance. */
+ bpb = archive_write_get_bytes_per_block(f->archive);
+ if (bpb > bs)
+ bs = bpb;
+ else if (bpb != 0)
+ bs -= bs % bpb;
+ }
+
+ state->bs = bs;
+ if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for b64encode buffer");
+ return (ARCHIVE_FATAL);
+ }
+
+ archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n",
+ state->mode, state->name.s);
+
+ f->data = state;
+ return (0);
+}
+
+static void
+la_b64_encode(struct archive_string *as, const unsigned char *p, size_t len)
+{
+ int c;
+
+ for (; len >= 3; p += 3, len -= 3) {
+ c = p[0] >> 2;
+ archive_strappend_char(as, base64[c]);
+ c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
+ archive_strappend_char(as, base64[c]);
+ c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
+ archive_strappend_char(as, base64[c]);
+ c = p[2] & 0x3f;
+ archive_strappend_char(as, base64[c]);
+ }
+ if (len > 0) {
+ c = p[0] >> 2;
+ archive_strappend_char(as, base64[c]);
+ c = (p[0] & 0x03) << 4;
+ if (len == 1) {
+ archive_strappend_char(as, base64[c]);
+ archive_strappend_char(as, '=');
+ archive_strappend_char(as, '=');
+ } else {
+ c |= (p[1] & 0xf0) >> 4;
+ archive_strappend_char(as, base64[c]);
+ c = (p[1] & 0x0f) << 2;
+ archive_strappend_char(as, base64[c]);
+ archive_strappend_char(as, '=');
+ }
+ }
+ archive_strappend_char(as, '\n');
+}
+
+/*
+ * Write data to the encoded stream.
+ */
+static int
+archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_b64encode *state = (struct private_b64encode *)f->data;
+ const unsigned char *p = buff;
+ int ret = ARCHIVE_OK;
+
+ if (length == 0)
+ return (ret);
+
+ if (state->hold_len) {
+ while (state->hold_len < LBYTES && length > 0) {
+ state->hold[state->hold_len++] = *p++;
+ length--;
+ }
+ if (state->hold_len < LBYTES)
+ return (ret);
+ la_b64_encode(&state->encoded_buff, state->hold, LBYTES);
+ state->hold_len = 0;
+ }
+
+ for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
+ la_b64_encode(&state->encoded_buff, p, LBYTES);
+
+ /* Save remaining bytes. */
+ if (length > 0) {
+ memcpy(state->hold, p, length);
+ state->hold_len = length;
+ }
+ while (archive_strlen(&state->encoded_buff) >= state->bs) {
+ ret = __archive_write_filter(f->next_filter,
+ state->encoded_buff.s, state->bs);
+ memmove(state->encoded_buff.s,
+ state->encoded_buff.s + state->bs,
+ state->encoded_buff.length - state->bs);
+ state->encoded_buff.length -= state->bs;
+ }
+
+ return (ret);
+}
+
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_filter_b64encode_close(struct archive_write_filter *f)
+{
+ struct private_b64encode *state = (struct private_b64encode *)f->data;
+
+ /* Flush remaining bytes. */
+ if (state->hold_len != 0)
+ la_b64_encode(&state->encoded_buff, state->hold, state->hold_len);
+ archive_string_sprintf(&state->encoded_buff, "====\n");
+ /* Write the last block */
+ archive_write_set_bytes_in_last_block(f->archive, 1);
+ return __archive_write_filter(f->next_filter,
+ state->encoded_buff.s, archive_strlen(&state->encoded_buff));
+}
+
+static int
+archive_filter_b64encode_free(struct archive_write_filter *f)
+{
+ struct private_b64encode *state = (struct private_b64encode *)f->data;
+
+ archive_string_free(&state->name);
+ archive_string_free(&state->encoded_buff);
+ free(state);
+ return (ARCHIVE_OK);
+}
+
+static int64_t
+atol8(const char *p, size_t char_cnt)
+{
+ int64_t l;
+ int digit;
+
+ l = 0;
+ while (char_cnt-- > 0) {
+ if (*p >= '0' && *p <= '7')
+ digit = *p - '0';
+ else
+ break;
+ p++;
+ l <<= 3;
+ l |= digit;
+ }
+ return (l);
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_by_name.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_by_name.c
new file mode 100644
index 00000000000..ffa633c9637
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_by_name.c
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+/* A table that maps names to functions. */
+static const
+struct { const char *name; int (*setter)(struct archive *); } names[] =
+{
+ { "b64encode", archive_write_add_filter_b64encode },
+ { "bzip2", archive_write_add_filter_bzip2 },
+ { "compress", archive_write_add_filter_compress },
+ { "grzip", archive_write_add_filter_grzip },
+ { "gzip", archive_write_add_filter_gzip },
+ { "lrzip", archive_write_add_filter_lrzip },
+ { "lz4", archive_write_add_filter_lz4 },
+ { "lzip", archive_write_add_filter_lzip },
+ { "lzma", archive_write_add_filter_lzma },
+ { "lzop", archive_write_add_filter_lzop },
+ { "uuencode", archive_write_add_filter_uuencode },
+ { "xz", archive_write_add_filter_xz },
+ { "zstd", archive_write_add_filter_zstd },
+ { NULL, NULL }
+};
+
+int
+archive_write_add_filter_by_name(struct archive *a, const char *name)
+{
+ int i;
+
+ for (i = 0; names[i].name != NULL; i++) {
+ if (strcmp(name, names[i].name) == 0)
+ return ((names[i].setter)(a));
+ }
+
+ archive_set_error(a, EINVAL, "No such filter '%s'", name);
+ a->state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_bzip2.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_bzip2.c
new file mode 100644
index 00000000000..3e5c0891ae8
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_bzip2.c
@@ -0,0 +1,401 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_bzip2.c 201091 2009-12-28 02:22:41Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_write_set_compression_bzip2(struct archive *a)
+{
+ __archive_write_filters_free(a);
+ return (archive_write_add_filter_bzip2(a));
+}
+#endif
+
+struct private_data {
+ int compression_level;
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ bz_stream stream;
+ int64_t total_in;
+ char *compressed;
+ size_t compressed_buffer_size;
+#else
+ struct archive_write_program_data *pdata;
+#endif
+};
+
+static int archive_compressor_bzip2_close(struct archive_write_filter *);
+static int archive_compressor_bzip2_free(struct archive_write_filter *);
+static int archive_compressor_bzip2_open(struct archive_write_filter *);
+static int archive_compressor_bzip2_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_compressor_bzip2_write(struct archive_write_filter *,
+ const void *, size_t);
+
+/*
+ * Add a bzip2 compression filter to this write handle.
+ */
+int
+archive_write_add_filter_bzip2(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct private_data *data;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_bzip2");
+
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ data->compression_level = 9; /* default */
+
+ f->data = data;
+ f->options = &archive_compressor_bzip2_options;
+ f->close = &archive_compressor_bzip2_close;
+ f->free = &archive_compressor_bzip2_free;
+ f->open = &archive_compressor_bzip2_open;
+ f->code = ARCHIVE_FILTER_BZIP2;
+ f->name = "bzip2";
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ return (ARCHIVE_OK);
+#else
+ data->pdata = __archive_write_program_allocate("bzip2");
+ if (data->pdata == NULL) {
+ free(data);
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ data->compression_level = 0;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Using external bzip2 program");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_compressor_bzip2_options(struct archive_write_filter *f,
+ const char *key, const char *value)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (strcmp(key, "compression-level") == 0) {
+ if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
+ value[1] != '\0')
+ return (ARCHIVE_WARN);
+ data->compression_level = value[0] - '0';
+ /* Make '0' be a synonym for '1'. */
+ /* This way, bzip2 compressor supports the same 0..9
+ * range of levels as gzip. */
+ if (data->compression_level < 1)
+ data->compression_level = 1;
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+/* Don't compile this if we don't have bzlib. */
+
+/*
+ * Yuck. bzlib.h is not const-correct, so I need this one bit
+ * of ugly hackery to convert a const * pointer to a non-const pointer.
+ */
+#define SET_NEXT_IN(st,src) \
+ (st)->stream.next_in = (char *)(uintptr_t)(const void *)(src)
+static int drive_compressor(struct archive_write_filter *,
+ struct private_data *, int finishing);
+
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_bzip2_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ if (data->compressed == NULL) {
+ size_t bs = 65536, bpb;
+ if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+ /* Buffer size should be a multiple number of the of bytes
+ * per block for performance. */
+ bpb = archive_write_get_bytes_per_block(f->archive);
+ if (bpb > bs)
+ bs = bpb;
+ else if (bpb != 0)
+ bs -= bs % bpb;
+ }
+ data->compressed_buffer_size = bs;
+ data->compressed
+ = (char *)malloc(data->compressed_buffer_size);
+ if (data->compressed == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ memset(&data->stream, 0, sizeof(data->stream));
+ data->stream.next_out = data->compressed;
+ data->stream.avail_out = (uint32_t)data->compressed_buffer_size;
+ f->write = archive_compressor_bzip2_write;
+
+ /* Initialize compression library */
+ ret = BZ2_bzCompressInit(&(data->stream),
+ data->compression_level, 0, 30);
+ if (ret == BZ_OK) {
+ f->data = data;
+ return (ARCHIVE_OK);
+ }
+
+ /* Library setup failed: clean up. */
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library");
+
+ /* Override the error message if we know what really went wrong. */
+ switch (ret) {
+ case BZ_PARAM_ERROR:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ "invalid setup parameter");
+ break;
+ case BZ_MEM_ERROR:
+ archive_set_error(f->archive, ENOMEM,
+ "Internal error initializing compression library: "
+ "out of memory");
+ break;
+ case BZ_CONFIG_ERROR:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ "mis-compiled library");
+ break;
+ }
+
+ return (ARCHIVE_FATAL);
+
+}
+
+/*
+ * Write data to the compressed stream.
+ *
+ * Returns ARCHIVE_OK if all data written, error otherwise.
+ */
+static int
+archive_compressor_bzip2_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ /* Update statistics */
+ data->total_in += length;
+
+ /* Compress input data to output buffer */
+ SET_NEXT_IN(data, buff);
+ data->stream.avail_in = (uint32_t)length;
+ if (drive_compressor(f, data, 0))
+ return (ARCHIVE_FATAL);
+ return (ARCHIVE_OK);
+}
+
+
+/*
+ * Finish the compression.
+ */
+static int
+archive_compressor_bzip2_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ /* Finish compression cycle. */
+ ret = drive_compressor(f, data, 1);
+ if (ret == ARCHIVE_OK) {
+ /* Write the last block */
+ ret = __archive_write_filter(f->next_filter,
+ data->compressed,
+ data->compressed_buffer_size - data->stream.avail_out);
+ }
+
+ switch (BZ2_bzCompressEnd(&(data->stream))) {
+ case BZ_OK:
+ break;
+ default:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Failed to clean up compressor");
+ ret = ARCHIVE_FATAL;
+ }
+ return ret;
+}
+
+static int
+archive_compressor_bzip2_free(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ free(data->compressed);
+ free(data);
+ f->data = NULL;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Utility function to push input data through compressor, writing
+ * full output blocks as necessary.
+ *
+ * Note that this handles both the regular write case (finishing ==
+ * false) and the end-of-archive case (finishing == true).
+ */
+static int
+drive_compressor(struct archive_write_filter *f,
+ struct private_data *data, int finishing)
+{
+ int ret;
+
+ for (;;) {
+ if (data->stream.avail_out == 0) {
+ ret = __archive_write_filter(f->next_filter,
+ data->compressed,
+ data->compressed_buffer_size);
+ if (ret != ARCHIVE_OK) {
+ /* TODO: Handle this write failure */
+ return (ARCHIVE_FATAL);
+ }
+ data->stream.next_out = data->compressed;
+ data->stream.avail_out = (uint32_t)data->compressed_buffer_size;
+ }
+
+ /* If there's nothing to do, we're done. */
+ if (!finishing && data->stream.avail_in == 0)
+ return (ARCHIVE_OK);
+
+ ret = BZ2_bzCompress(&(data->stream),
+ finishing ? BZ_FINISH : BZ_RUN);
+
+ switch (ret) {
+ case BZ_RUN_OK:
+ /* In non-finishing case, did compressor
+ * consume everything? */
+ if (!finishing && data->stream.avail_in == 0)
+ return (ARCHIVE_OK);
+ break;
+ case BZ_FINISH_OK: /* Finishing: There's more work to do */
+ break;
+ case BZ_STREAM_END: /* Finishing: all done */
+ /* Only occurs in finishing case */
+ return (ARCHIVE_OK);
+ default:
+ /* Any other return value indicates an error */
+ archive_set_error(f->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Bzip2 compression failed;"
+ " BZ2_bzCompress() returned %d",
+ ret);
+ return (ARCHIVE_FATAL);
+ }
+ }
+}
+
+#else /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
+
+static int
+archive_compressor_bzip2_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ struct archive_string as;
+ int r;
+
+ archive_string_init(&as);
+ archive_strcpy(&as, "bzip2");
+
+ /* Specify compression level. */
+ if (data->compression_level > 0) {
+ archive_strcat(&as, " -");
+ archive_strappend_char(&as, '0' + data->compression_level);
+ }
+ f->write = archive_compressor_bzip2_write;
+
+ r = __archive_write_program_open(f, data->pdata, as.s);
+ archive_string_free(&as);
+ return (r);
+}
+
+static int
+archive_compressor_bzip2_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_compressor_bzip2_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_close(f, data->pdata);
+}
+
+static int
+archive_compressor_bzip2_free(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ __archive_write_program_free(data->pdata);
+ free(data);
+ return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_compress.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_compress.c
new file mode 100644
index 00000000000..3ed269fce94
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_compress.c
@@ -0,0 +1,447 @@
+/*-
+ * Copyright (c) 2008 Joerg Sonnenberger
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 1985, 1986, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis and James A. Woods, derived from original
+ * work by Spencer Thomas and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_compress.c 201111 2009-12-28 03:33:05Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+
+#define HSIZE 69001 /* 95% occupancy */
+#define HSHIFT 8 /* 8 - trunc(log2(HSIZE / 65536)) */
+#define CHECK_GAP 10000 /* Ratio check interval. */
+
+#define MAXCODE(bits) ((1 << (bits)) - 1)
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define FIRST 257 /* First free entry. */
+#define CLEAR 256 /* Table clear output code. */
+
+struct private_data {
+ int64_t in_count, out_count, checkpoint;
+
+ int code_len; /* Number of bits/code. */
+ int cur_maxcode; /* Maximum code, given n_bits. */
+ int max_maxcode; /* Should NEVER generate this code. */
+ int hashtab [HSIZE];
+ unsigned short codetab [HSIZE];
+ int first_free; /* First unused entry. */
+ int compress_ratio;
+
+ int cur_code, cur_fcode;
+
+ int bit_offset;
+ unsigned char bit_buf;
+
+ unsigned char *compressed;
+ size_t compressed_buffer_size;
+ size_t compressed_offset;
+};
+
+static int archive_compressor_compress_open(struct archive_write_filter *);
+static int archive_compressor_compress_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_compressor_compress_close(struct archive_write_filter *);
+static int archive_compressor_compress_free(struct archive_write_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_write_set_compression_compress(struct archive *a)
+{
+ __archive_write_filters_free(a);
+ return (archive_write_add_filter_compress(a));
+}
+#endif
+
+/*
+ * Add a compress filter to this write handle.
+ */
+int
+archive_write_add_filter_compress(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_compress");
+ f->open = &archive_compressor_compress_open;
+ f->code = ARCHIVE_FILTER_COMPRESS;
+ f->name = "compress";
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_compress_open(struct archive_write_filter *f)
+{
+ struct private_data *state;
+ size_t bs = 65536, bpb;
+
+ f->code = ARCHIVE_FILTER_COMPRESS;
+ f->name = "compress";
+
+ state = (struct private_data *)calloc(1, sizeof(*state));
+ if (state == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+ /* Buffer size should be a multiple number of the of bytes
+ * per block for performance. */
+ bpb = archive_write_get_bytes_per_block(f->archive);
+ if (bpb > bs)
+ bs = bpb;
+ else if (bpb != 0)
+ bs -= bs % bpb;
+ }
+ state->compressed_buffer_size = bs;
+ state->compressed = malloc(state->compressed_buffer_size);
+
+ if (state->compressed == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ free(state);
+ return (ARCHIVE_FATAL);
+ }
+
+ f->write = archive_compressor_compress_write;
+ f->close = archive_compressor_compress_close;
+ f->free = archive_compressor_compress_free;
+
+ state->max_maxcode = 0x10000; /* Should NEVER generate this code. */
+ state->in_count = 0; /* Length of input. */
+ state->bit_buf = 0;
+ state->bit_offset = 0;
+ state->out_count = 3; /* Includes 3-byte header mojo. */
+ state->compress_ratio = 0;
+ state->checkpoint = CHECK_GAP;
+ state->code_len = 9;
+ state->cur_maxcode = MAXCODE(state->code_len);
+ state->first_free = FIRST;
+
+ memset(state->hashtab, 0xff, sizeof(state->hashtab));
+
+ /* Prime output buffer with a gzip header. */
+ state->compressed[0] = 0x1f; /* Compress */
+ state->compressed[1] = 0x9d;
+ state->compressed[2] = 0x90; /* Block mode, 16bit max */
+ state->compressed_offset = 3;
+
+ f->data = state;
+ return (0);
+}
+
+/*-
+ * Output the given code.
+ * Inputs:
+ * code: A n_bits-bit integer. If == -1, then EOF. This assumes
+ * that n_bits <= (long)wordsize - 1.
+ * Outputs:
+ * Outputs code to the file.
+ * Assumptions:
+ * Chars are 8 bits long.
+ * Algorithm:
+ * Maintain a BITS character long buffer (so that 8 codes will
+ * fit in it exactly). Use the VAX insv instruction to insert each
+ * code in turn. When the buffer fills up empty it and start over.
+ */
+
+static const unsigned char rmask[9] =
+ {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
+
+static int
+output_byte(struct archive_write_filter *f, unsigned char c)
+{
+ struct private_data *state = f->data;
+
+ state->compressed[state->compressed_offset++] = c;
+ ++state->out_count;
+
+ if (state->compressed_buffer_size == state->compressed_offset) {
+ int ret = __archive_write_filter(f->next_filter,
+ state->compressed, state->compressed_buffer_size);
+ if (ret != ARCHIVE_OK)
+ return ARCHIVE_FATAL;
+ state->compressed_offset = 0;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int
+output_code(struct archive_write_filter *f, int ocode)
+{
+ struct private_data *state = f->data;
+ int bits, ret, clear_flg, bit_offset;
+
+ clear_flg = ocode == CLEAR;
+
+ /*
+ * Since ocode is always >= 8 bits, only need to mask the first
+ * hunk on the left.
+ */
+ bit_offset = state->bit_offset % 8;
+ state->bit_buf |= (ocode << bit_offset) & 0xff;
+ output_byte(f, state->bit_buf);
+
+ bits = state->code_len - (8 - bit_offset);
+ ocode >>= 8 - bit_offset;
+ /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
+ if (bits >= 8) {
+ output_byte(f, ocode & 0xff);
+ ocode >>= 8;
+ bits -= 8;
+ }
+ /* Last bits. */
+ state->bit_offset += state->code_len;
+ state->bit_buf = ocode & rmask[bits];
+ if (state->bit_offset == state->code_len * 8)
+ state->bit_offset = 0;
+
+ /*
+ * If the next entry is going to be too big for the ocode size,
+ * then increase it, if possible.
+ */
+ if (clear_flg || state->first_free > state->cur_maxcode) {
+ /*
+ * Write the whole buffer, because the input side won't
+ * discover the size increase until after it has read it.
+ */
+ if (state->bit_offset > 0) {
+ while (state->bit_offset < state->code_len * 8) {
+ ret = output_byte(f, state->bit_buf);
+ if (ret != ARCHIVE_OK)
+ return ret;
+ state->bit_offset += 8;
+ state->bit_buf = 0;
+ }
+ }
+ state->bit_buf = 0;
+ state->bit_offset = 0;
+
+ if (clear_flg) {
+ state->code_len = 9;
+ state->cur_maxcode = MAXCODE(state->code_len);
+ } else {
+ state->code_len++;
+ if (state->code_len == 16)
+ state->cur_maxcode = state->max_maxcode;
+ else
+ state->cur_maxcode = MAXCODE(state->code_len);
+ }
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int
+output_flush(struct archive_write_filter *f)
+{
+ struct private_data *state = f->data;
+ int ret;
+
+ /* At EOF, write the rest of the buffer. */
+ if (state->bit_offset % 8) {
+ state->code_len = (state->bit_offset % 8 + 7) / 8;
+ ret = output_byte(f, state->bit_buf);
+ if (ret != ARCHIVE_OK)
+ return ret;
+ }
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Write data to the compressed stream.
+ */
+static int
+archive_compressor_compress_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct private_data *state = (struct private_data *)f->data;
+ int i;
+ int ratio;
+ int c, disp, ret;
+ const unsigned char *bp;
+
+ if (length == 0)
+ return ARCHIVE_OK;
+
+ bp = buff;
+
+ if (state->in_count == 0) {
+ state->cur_code = *bp++;
+ ++state->in_count;
+ --length;
+ }
+
+ while (length--) {
+ c = *bp++;
+ state->in_count++;
+ state->cur_fcode = (c << 16) | state->cur_code;
+ i = ((c << HSHIFT) ^ state->cur_code); /* Xor hashing. */
+
+ if (state->hashtab[i] == state->cur_fcode) {
+ state->cur_code = state->codetab[i];
+ continue;
+ }
+ if (state->hashtab[i] < 0) /* Empty slot. */
+ goto nomatch;
+ /* Secondary hash (after G. Knott). */
+ if (i == 0)
+ disp = 1;
+ else
+ disp = HSIZE - i;
+ probe:
+ if ((i -= disp) < 0)
+ i += HSIZE;
+
+ if (state->hashtab[i] == state->cur_fcode) {
+ state->cur_code = state->codetab[i];
+ continue;
+ }
+ if (state->hashtab[i] >= 0)
+ goto probe;
+ nomatch:
+ ret = output_code(f, state->cur_code);
+ if (ret != ARCHIVE_OK)
+ return ret;
+ state->cur_code = c;
+ if (state->first_free < state->max_maxcode) {
+ state->codetab[i] = state->first_free++; /* code -> hashtable */
+ state->hashtab[i] = state->cur_fcode;
+ continue;
+ }
+ if (state->in_count < state->checkpoint)
+ continue;
+
+ state->checkpoint = state->in_count + CHECK_GAP;
+
+ if (state->in_count <= 0x007fffff && state->out_count != 0)
+ ratio = (int)(state->in_count * 256 / state->out_count);
+ else if ((ratio = (int)(state->out_count / 256)) == 0)
+ ratio = 0x7fffffff;
+ else
+ ratio = (int)(state->in_count / ratio);
+
+ if (ratio > state->compress_ratio)
+ state->compress_ratio = ratio;
+ else {
+ state->compress_ratio = 0;
+ memset(state->hashtab, 0xff, sizeof(state->hashtab));
+ state->first_free = FIRST;
+ ret = output_code(f, CLEAR);
+ if (ret != ARCHIVE_OK)
+ return ret;
+ }
+ }
+
+ return (ARCHIVE_OK);
+}
+
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_compressor_compress_close(struct archive_write_filter *f)
+{
+ struct private_data *state = (struct private_data *)f->data;
+ int ret;
+
+ ret = output_code(f, state->cur_code);
+ if (ret != ARCHIVE_OK)
+ return ret;
+ ret = output_flush(f);
+ if (ret != ARCHIVE_OK)
+ return ret;
+
+ /* Write the last block */
+ ret = __archive_write_filter(f->next_filter,
+ state->compressed, state->compressed_offset);
+ return (ret);
+}
+
+static int
+archive_compressor_compress_free(struct archive_write_filter *f)
+{
+ struct private_data *state = (struct private_data *)f->data;
+
+ free(state->compressed);
+ free(state);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_grzip.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_grzip.c
new file mode 100644
index 00000000000..371102d74c0
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_grzip.c
@@ -0,0 +1,135 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_write_private.h"
+
+struct write_grzip {
+ struct archive_write_program_data *pdata;
+};
+
+static int archive_write_grzip_open(struct archive_write_filter *);
+static int archive_write_grzip_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_write_grzip_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_write_grzip_close(struct archive_write_filter *);
+static int archive_write_grzip_free(struct archive_write_filter *);
+
+int
+archive_write_add_filter_grzip(struct archive *_a)
+{
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct write_grzip *data;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_grzip");
+
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ archive_set_error(_a, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ data->pdata = __archive_write_program_allocate("grzip");
+ if (data->pdata == NULL) {
+ free(data);
+ archive_set_error(_a, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ f->name = "grzip";
+ f->code = ARCHIVE_FILTER_GRZIP;
+ f->data = data;
+ f->open = archive_write_grzip_open;
+ f->options = archive_write_grzip_options;
+ f->write = archive_write_grzip_write;
+ f->close = archive_write_grzip_close;
+ f->free = archive_write_grzip_free;
+
+ /* Note: This filter always uses an external program, so we
+ * return "warn" to inform of the fact. */
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external grzip program for grzip compression");
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_grzip_options(struct archive_write_filter *f, const char *key,
+ const char *value)
+{
+ (void)f; /* UNUSED */
+ (void)key; /* UNUSED */
+ (void)value; /* UNUSED */
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_grzip_open(struct archive_write_filter *f)
+{
+ struct write_grzip *data = (struct write_grzip *)f->data;
+
+ return __archive_write_program_open(f, data->pdata, "grzip");
+}
+
+static int
+archive_write_grzip_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct write_grzip *data = (struct write_grzip *)f->data;
+
+ return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_write_grzip_close(struct archive_write_filter *f)
+{
+ struct write_grzip *data = (struct write_grzip *)f->data;
+
+ return __archive_write_program_close(f, data->pdata);
+}
+
+static int
+archive_write_grzip_free(struct archive_write_filter *f)
+{
+ struct write_grzip *data = (struct write_grzip *)f->data;
+
+ __archive_write_program_free(data->pdata);
+ free(data);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_gzip.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_gzip.c
new file mode 100644
index 00000000000..8670d5ca740
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_gzip.c
@@ -0,0 +1,442 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_gzip.c 201081 2009-12-28 02:04:42Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include <time.h>
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_write_set_compression_gzip(struct archive *a)
+{
+ __archive_write_filters_free(a);
+ return (archive_write_add_filter_gzip(a));
+}
+#endif
+
+/* Don't compile this if we don't have zlib. */
+
+struct private_data {
+ int compression_level;
+ int timestamp;
+#ifdef HAVE_ZLIB_H
+ z_stream stream;
+ int64_t total_in;
+ unsigned char *compressed;
+ size_t compressed_buffer_size;
+ unsigned long crc;
+#else
+ struct archive_write_program_data *pdata;
+#endif
+};
+
+/*
+ * Yuck. zlib.h is not const-correct, so I need this one bit
+ * of ugly hackery to convert a const * pointer to a non-const pointer.
+ */
+#define SET_NEXT_IN(st,src) \
+ (st)->stream.next_in = (Bytef *)(uintptr_t)(const void *)(src)
+
+static int archive_compressor_gzip_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_compressor_gzip_open(struct archive_write_filter *);
+static int archive_compressor_gzip_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_compressor_gzip_close(struct archive_write_filter *);
+static int archive_compressor_gzip_free(struct archive_write_filter *);
+#ifdef HAVE_ZLIB_H
+static int drive_compressor(struct archive_write_filter *,
+ struct private_data *, int finishing);
+#endif
+
+
+/*
+ * Add a gzip compression filter to this write handle.
+ */
+int
+archive_write_add_filter_gzip(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct private_data *data;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_gzip");
+
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ f->data = data;
+ f->open = &archive_compressor_gzip_open;
+ f->options = &archive_compressor_gzip_options;
+ f->close = &archive_compressor_gzip_close;
+ f->free = &archive_compressor_gzip_free;
+ f->code = ARCHIVE_FILTER_GZIP;
+ f->name = "gzip";
+#ifdef HAVE_ZLIB_H
+ data->compression_level = Z_DEFAULT_COMPRESSION;
+ return (ARCHIVE_OK);
+#else
+ data->pdata = __archive_write_program_allocate("gzip");
+ if (data->pdata == NULL) {
+ free(data);
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ data->compression_level = 0;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Using external gzip program");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+static int
+archive_compressor_gzip_free(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+#ifdef HAVE_ZLIB_H
+ free(data->compressed);
+#else
+ __archive_write_program_free(data->pdata);
+#endif
+ free(data);
+ f->data = NULL;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_compressor_gzip_options(struct archive_write_filter *f, const char *key,
+ const char *value)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (strcmp(key, "compression-level") == 0) {
+ if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
+ value[1] != '\0')
+ return (ARCHIVE_WARN);
+ data->compression_level = value[0] - '0';
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "timestamp") == 0) {
+ data->timestamp = (value == NULL)?-1:1;
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+#ifdef HAVE_ZLIB_H
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_gzip_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ if (data->compressed == NULL) {
+ size_t bs = 65536, bpb;
+ if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+ /* Buffer size should be a multiple number of
+ * the of bytes per block for performance. */
+ bpb = archive_write_get_bytes_per_block(f->archive);
+ if (bpb > bs)
+ bs = bpb;
+ else if (bpb != 0)
+ bs -= bs % bpb;
+ }
+ data->compressed_buffer_size = bs;
+ data->compressed
+ = (unsigned char *)malloc(data->compressed_buffer_size);
+ if (data->compressed == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ data->crc = crc32(0L, NULL, 0);
+ data->stream.next_out = data->compressed;
+ data->stream.avail_out = (uInt)data->compressed_buffer_size;
+
+ /* Prime output buffer with a gzip header. */
+ data->compressed[0] = 0x1f; /* GZip signature bytes */
+ data->compressed[1] = 0x8b;
+ data->compressed[2] = 0x08; /* "Deflate" compression */
+ data->compressed[3] = 0; /* No options */
+ if (data->timestamp >= 0) {
+ time_t t = time(NULL);
+ data->compressed[4] = (uint8_t)(t)&0xff; /* Timestamp */
+ data->compressed[5] = (uint8_t)(t>>8)&0xff;
+ data->compressed[6] = (uint8_t)(t>>16)&0xff;
+ data->compressed[7] = (uint8_t)(t>>24)&0xff;
+ } else
+ memset(&data->compressed[4], 0, 4);
+ if (data->compression_level == 9)
+ data->compressed[8] = 2;
+ else if(data->compression_level == 1)
+ data->compressed[8] = 4;
+ else
+ data->compressed[8] = 0;
+ data->compressed[9] = 3; /* OS=Unix */
+ data->stream.next_out += 10;
+ data->stream.avail_out -= 10;
+
+ f->write = archive_compressor_gzip_write;
+
+ /* Initialize compression library. */
+ ret = deflateInit2(&(data->stream),
+ data->compression_level,
+ Z_DEFLATED,
+ -15 /* < 0 to suppress zlib header */,
+ 8,
+ Z_DEFAULT_STRATEGY);
+
+ if (ret == Z_OK) {
+ f->data = data;
+ return (ARCHIVE_OK);
+ }
+
+ /* Library setup failed: clean up. */
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "Internal error "
+ "initializing compression library");
+
+ /* Override the error message if we know what really went wrong. */
+ switch (ret) {
+ case Z_STREAM_ERROR:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing "
+ "compression library: invalid setup parameter");
+ break;
+ case Z_MEM_ERROR:
+ archive_set_error(f->archive, ENOMEM,
+ "Internal error initializing compression library");
+ break;
+ case Z_VERSION_ERROR:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing "
+ "compression library: invalid library version");
+ break;
+ }
+
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Write data to the compressed stream.
+ */
+static int
+archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ /* Update statistics */
+ data->crc = crc32(data->crc, (const Bytef *)buff, (uInt)length);
+ data->total_in += length;
+
+ /* Compress input data to output buffer */
+ SET_NEXT_IN(data, buff);
+ data->stream.avail_in = (uInt)length;
+ if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK)
+ return (ret);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_compressor_gzip_close(struct archive_write_filter *f)
+{
+ unsigned char trailer[8];
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ /* Finish compression cycle */
+ ret = drive_compressor(f, data, 1);
+ if (ret == ARCHIVE_OK) {
+ /* Write the last compressed data. */
+ ret = __archive_write_filter(f->next_filter,
+ data->compressed,
+ data->compressed_buffer_size - data->stream.avail_out);
+ }
+ if (ret == ARCHIVE_OK) {
+ /* Build and write out 8-byte trailer. */
+ trailer[0] = (uint8_t)(data->crc)&0xff;
+ trailer[1] = (uint8_t)(data->crc >> 8)&0xff;
+ trailer[2] = (uint8_t)(data->crc >> 16)&0xff;
+ trailer[3] = (uint8_t)(data->crc >> 24)&0xff;
+ trailer[4] = (uint8_t)(data->total_in)&0xff;
+ trailer[5] = (uint8_t)(data->total_in >> 8)&0xff;
+ trailer[6] = (uint8_t)(data->total_in >> 16)&0xff;
+ trailer[7] = (uint8_t)(data->total_in >> 24)&0xff;
+ ret = __archive_write_filter(f->next_filter, trailer, 8);
+ }
+
+ switch (deflateEnd(&(data->stream))) {
+ case Z_OK:
+ break;
+ default:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up compressor");
+ ret = ARCHIVE_FATAL;
+ }
+ return ret;
+}
+
+/*
+ * Utility function to push input data through compressor,
+ * writing full output blocks as necessary.
+ *
+ * Note that this handles both the regular write case (finishing ==
+ * false) and the end-of-archive case (finishing == true).
+ */
+static int
+drive_compressor(struct archive_write_filter *f,
+ struct private_data *data, int finishing)
+{
+ int ret;
+
+ for (;;) {
+ if (data->stream.avail_out == 0) {
+ ret = __archive_write_filter(f->next_filter,
+ data->compressed,
+ data->compressed_buffer_size);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ data->stream.next_out = data->compressed;
+ data->stream.avail_out =
+ (uInt)data->compressed_buffer_size;
+ }
+
+ /* If there's nothing to do, we're done. */
+ if (!finishing && data->stream.avail_in == 0)
+ return (ARCHIVE_OK);
+
+ ret = deflate(&(data->stream),
+ finishing ? Z_FINISH : Z_NO_FLUSH );
+
+ switch (ret) {
+ case Z_OK:
+ /* In non-finishing case, check if compressor
+ * consumed everything */
+ if (!finishing && data->stream.avail_in == 0)
+ return (ARCHIVE_OK);
+ /* In finishing case, this return always means
+ * there's more work */
+ break;
+ case Z_STREAM_END:
+ /* This return can only occur in finishing case. */
+ return (ARCHIVE_OK);
+ default:
+ /* Any other return value indicates an error. */
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "GZip compression failed:"
+ " deflate() call returned status %d",
+ ret);
+ return (ARCHIVE_FATAL);
+ }
+ }
+}
+
+#else /* HAVE_ZLIB_H */
+
+static int
+archive_compressor_gzip_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ struct archive_string as;
+ int r;
+
+ archive_string_init(&as);
+ archive_strcpy(&as, "gzip");
+
+ /* Specify compression level. */
+ if (data->compression_level > 0) {
+ archive_strcat(&as, " -");
+ archive_strappend_char(&as, '0' + data->compression_level);
+ }
+ if (data->timestamp < 0)
+ /* Do not save timestamp. */
+ archive_strcat(&as, " -n");
+ else if (data->timestamp > 0)
+ /* Save timestamp. */
+ archive_strcat(&as, " -N");
+
+ f->write = archive_compressor_gzip_write;
+ r = __archive_write_program_open(f, data->pdata, as.s);
+ archive_string_free(&as);
+ return (r);
+}
+
+static int
+archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_compressor_gzip_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_close(f, data->pdata);
+}
+
+#endif /* HAVE_ZLIB_H */
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_lrzip.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_lrzip.c
new file mode 100644
index 00000000000..e215f890325
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_lrzip.c
@@ -0,0 +1,197 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+struct write_lrzip {
+ struct archive_write_program_data *pdata;
+ int compression_level;
+ enum { lzma = 0, bzip2, gzip, lzo, none, zpaq } compression;
+};
+
+static int archive_write_lrzip_open(struct archive_write_filter *);
+static int archive_write_lrzip_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_write_lrzip_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_write_lrzip_close(struct archive_write_filter *);
+static int archive_write_lrzip_free(struct archive_write_filter *);
+
+int
+archive_write_add_filter_lrzip(struct archive *_a)
+{
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct write_lrzip *data;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_lrzip");
+
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ archive_set_error(_a, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ data->pdata = __archive_write_program_allocate("lrzip");
+ if (data->pdata == NULL) {
+ free(data);
+ archive_set_error(_a, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ f->name = "lrzip";
+ f->code = ARCHIVE_FILTER_LRZIP;
+ f->data = data;
+ f->open = archive_write_lrzip_open;
+ f->options = archive_write_lrzip_options;
+ f->write = archive_write_lrzip_write;
+ f->close = archive_write_lrzip_close;
+ f->free = archive_write_lrzip_free;
+
+ /* Note: This filter always uses an external program, so we
+ * return "warn" to inform of the fact. */
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external lrzip program for lrzip compression");
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_lrzip_options(struct archive_write_filter *f, const char *key,
+ const char *value)
+{
+ struct write_lrzip *data = (struct write_lrzip *)f->data;
+
+ if (strcmp(key, "compression") == 0) {
+ if (value == NULL)
+ return (ARCHIVE_WARN);
+ else if (strcmp(value, "bzip2") == 0)
+ data->compression = bzip2;
+ else if (strcmp(value, "gzip") == 0)
+ data->compression = gzip;
+ else if (strcmp(value, "lzo") == 0)
+ data->compression = lzo;
+ else if (strcmp(value, "none") == 0)
+ data->compression = none;
+ else if (strcmp(value, "zpaq") == 0)
+ data->compression = zpaq;
+ else
+ return (ARCHIVE_WARN);
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "compression-level") == 0) {
+ if (value == NULL || !(value[0] >= '1' && value[0] <= '9') ||
+ value[1] != '\0')
+ return (ARCHIVE_WARN);
+ data->compression_level = value[0] - '0';
+ return (ARCHIVE_OK);
+ }
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_lrzip_open(struct archive_write_filter *f)
+{
+ struct write_lrzip *data = (struct write_lrzip *)f->data;
+ struct archive_string as;
+ int r;
+
+ archive_string_init(&as);
+ archive_strcpy(&as, "lrzip -q");
+
+ /* Specify compression type. */
+ switch (data->compression) {
+ case lzma:/* default compression */
+ break;
+ case bzip2:
+ archive_strcat(&as, " -b");
+ break;
+ case gzip:
+ archive_strcat(&as, " -g");
+ break;
+ case lzo:
+ archive_strcat(&as, " -l");
+ break;
+ case none:
+ archive_strcat(&as, " -n");
+ break;
+ case zpaq:
+ archive_strcat(&as, " -z");
+ break;
+ }
+
+ /* Specify compression level. */
+ if (data->compression_level > 0) {
+ archive_strcat(&as, " -L ");
+ archive_strappend_char(&as, '0' + data->compression_level);
+ }
+
+ r = __archive_write_program_open(f, data->pdata, as.s);
+ archive_string_free(&as);
+ return (r);
+}
+
+static int
+archive_write_lrzip_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct write_lrzip *data = (struct write_lrzip *)f->data;
+
+ return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_write_lrzip_close(struct archive_write_filter *f)
+{
+ struct write_lrzip *data = (struct write_lrzip *)f->data;
+
+ return __archive_write_program_close(f, data->pdata);
+}
+
+static int
+archive_write_lrzip_free(struct archive_write_filter *f)
+{
+ struct write_lrzip *data = (struct write_lrzip *)f->data;
+
+ __archive_write_program_free(data->pdata);
+ free(data);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_lz4.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_lz4.c
new file mode 100644
index 00000000000..6ac450357d2
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_lz4.c
@@ -0,0 +1,700 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_LZ4_H
+#include <lz4.h>
+#endif
+#ifdef HAVE_LZ4HC_H
+#include <lz4hc.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_xxhash.h"
+
+#define LZ4_MAGICNUMBER 0x184d2204
+
+struct private_data {
+ int compression_level;
+ unsigned header_written:1;
+ unsigned version_number:1;
+ unsigned block_independence:1;
+ unsigned block_checksum:1;
+ unsigned stream_size:1;
+ unsigned stream_checksum:1;
+ unsigned preset_dictionary:1;
+ unsigned block_maximum_size:3;
+#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
+ int64_t total_in;
+ char *out;
+ char *out_buffer;
+ size_t out_buffer_size;
+ size_t out_block_size;
+ char *in;
+ char *in_buffer_allocated;
+ char *in_buffer;
+ size_t in_buffer_size;
+ size_t block_size;
+
+ void *xxh32_state;
+ void *lz4_stream;
+#else
+ struct archive_write_program_data *pdata;
+#endif
+};
+
+static int archive_filter_lz4_close(struct archive_write_filter *);
+static int archive_filter_lz4_free(struct archive_write_filter *);
+static int archive_filter_lz4_open(struct archive_write_filter *);
+static int archive_filter_lz4_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_filter_lz4_write(struct archive_write_filter *,
+ const void *, size_t);
+
+/*
+ * Add a lz4 compression filter to this write handle.
+ */
+int
+archive_write_add_filter_lz4(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct private_data *data;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_lz4");
+
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Setup default settings.
+ */
+ data->compression_level = 1;
+ data->version_number = 0x01;
+ data->block_independence = 1;
+ data->block_checksum = 0;
+ data->stream_size = 0;
+ data->stream_checksum = 1;
+ data->preset_dictionary = 0;
+ data->block_maximum_size = 7;
+
+ /*
+ * Setup a filter setting.
+ */
+ f->data = data;
+ f->options = &archive_filter_lz4_options;
+ f->close = &archive_filter_lz4_close;
+ f->free = &archive_filter_lz4_free;
+ f->open = &archive_filter_lz4_open;
+ f->code = ARCHIVE_FILTER_LZ4;
+ f->name = "lz4";
+#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
+ return (ARCHIVE_OK);
+#else
+ /*
+ * We don't have lz4 library, and execute external lz4 program
+ * instead.
+ */
+ data->pdata = __archive_write_program_allocate("lz4");
+ if (data->pdata == NULL) {
+ free(data);
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ data->compression_level = 0;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Using external lz4 program");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_filter_lz4_options(struct archive_write_filter *f,
+ const char *key, const char *value)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (strcmp(key, "compression-level") == 0) {
+ int val;
+ if (value == NULL || !((val = value[0] - '0') >= 1 && val <= 9) ||
+ value[1] != '\0')
+ return (ARCHIVE_WARN);
+
+#ifndef HAVE_LZ4HC_H
+ if(val >= 3)
+ {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "High compression not included in this build");
+ return (ARCHIVE_FATAL);
+ }
+#endif
+ data->compression_level = val;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "stream-checksum") == 0) {
+ data->stream_checksum = value != NULL;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "block-checksum") == 0) {
+ data->block_checksum = value != NULL;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "block-size") == 0) {
+ if (value == NULL || !(value[0] >= '4' && value[0] <= '7') ||
+ value[1] != '\0')
+ return (ARCHIVE_WARN);
+ data->block_maximum_size = value[0] - '0';
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "block-dependence") == 0) {
+ data->block_independence = value == NULL;
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
+/* Don't compile this if we don't have liblz4. */
+
+static int drive_compressor(struct archive_write_filter *, const char *,
+ size_t);
+static int drive_compressor_independence(struct archive_write_filter *,
+ const char *, size_t);
+static int drive_compressor_dependence(struct archive_write_filter *,
+ const char *, size_t);
+static int lz4_write_stream_descriptor(struct archive_write_filter *);
+static ssize_t lz4_write_one_block(struct archive_write_filter *, const char *,
+ size_t);
+
+
+/*
+ * Setup callback.
+ */
+static int
+archive_filter_lz4_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ size_t required_size;
+ static size_t const bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024,
+ 4 * 1024 * 1024 };
+ size_t pre_block_size;
+
+ if (data->block_maximum_size < 4)
+ data->block_size = bkmap[0];
+ else
+ data->block_size = bkmap[data->block_maximum_size - 4];
+
+ required_size = 4 + 15 + 4 + data->block_size + 4 + 4;
+ if (data->out_buffer_size < required_size) {
+ size_t bs = required_size, bpb;
+ free(data->out_buffer);
+ if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+ /* Buffer size should be a multiple number of
+ * the of bytes per block for performance. */
+ bpb = archive_write_get_bytes_per_block(f->archive);
+ if (bpb > bs)
+ bs = bpb;
+ else if (bpb != 0) {
+ bs += bpb;
+ bs -= bs % bpb;
+ }
+ }
+ data->out_block_size = bs;
+ bs += required_size;
+ data->out_buffer = malloc(bs);
+ data->out = data->out_buffer;
+ data->out_buffer_size = bs;
+ }
+
+ pre_block_size = (data->block_independence)? 0: 64 * 1024;
+ if (data->in_buffer_size < data->block_size + pre_block_size) {
+ free(data->in_buffer_allocated);
+ data->in_buffer_size = data->block_size;
+ data->in_buffer_allocated =
+ malloc(data->in_buffer_size + pre_block_size);
+ data->in_buffer = data->in_buffer_allocated + pre_block_size;
+ if (!data->block_independence && data->compression_level >= 3)
+ data->in_buffer = data->in_buffer_allocated;
+ data->in = data->in_buffer;
+ data->in_buffer_size = data->block_size;
+ }
+
+ if (data->out_buffer == NULL || data->in_buffer_allocated == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+
+ f->write = archive_filter_lz4_write;
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Write data to the out stream.
+ *
+ * Returns ARCHIVE_OK if all data written, error otherwise.
+ */
+static int
+archive_filter_lz4_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret = ARCHIVE_OK;
+ const char *p;
+ size_t remaining;
+ ssize_t size;
+
+ /* If we haven't written a stream descriptor, we have to do it first. */
+ if (!data->header_written) {
+ ret = lz4_write_stream_descriptor(f);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ data->header_written = 1;
+ }
+
+ /* Update statistics */
+ data->total_in += length;
+
+ p = (const char *)buff;
+ remaining = length;
+ while (remaining) {
+ size_t l;
+ /* Compress input data to output buffer */
+ size = lz4_write_one_block(f, p, remaining);
+ if (size < ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ l = data->out - data->out_buffer;
+ if (l >= data->out_block_size) {
+ ret = __archive_write_filter(f->next_filter,
+ data->out_buffer, data->out_block_size);
+ l -= data->out_block_size;
+ memcpy(data->out_buffer,
+ data->out_buffer + data->out_block_size, l);
+ data->out = data->out_buffer + l;
+ if (ret < ARCHIVE_WARN)
+ break;
+ }
+ p += size;
+ remaining -= size;
+ }
+
+ return (ret);
+}
+
+/*
+ * Finish the compression.
+ */
+static int
+archive_filter_lz4_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ /* Finish compression cycle. */
+ ret = (int)lz4_write_one_block(f, NULL, 0);
+ if (ret >= 0) {
+ /*
+ * Write the last block and the end of the stream data.
+ */
+
+ /* Write End Of Stream. */
+ memset(data->out, 0, 4); data->out += 4;
+ /* Write Stream checksum if needed. */
+ if (data->stream_checksum) {
+ unsigned int checksum;
+ checksum = __archive_xxhash.XXH32_digest(
+ data->xxh32_state);
+ data->xxh32_state = NULL;
+ archive_le32enc(data->out, checksum);
+ data->out += 4;
+ }
+ ret = __archive_write_filter(f->next_filter,
+ data->out_buffer, data->out - data->out_buffer);
+ }
+ return ret;
+}
+
+static int
+archive_filter_lz4_free(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (data->lz4_stream != NULL) {
+#ifdef HAVE_LZ4HC_H
+ if (data->compression_level >= 3)
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+ LZ4_freeStreamHC(data->lz4_stream);
+#else
+ LZ4_freeHC(data->lz4_stream);
+#endif
+ else
+#endif
+#if LZ4_VERSION_MINOR >= 3
+ LZ4_freeStream(data->lz4_stream);
+#else
+ LZ4_free(data->lz4_stream);
+#endif
+ }
+ free(data->out_buffer);
+ free(data->in_buffer_allocated);
+ free(data->xxh32_state);
+ free(data);
+ f->data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+lz4_write_stream_descriptor(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ uint8_t *sd;
+
+ sd = (uint8_t *)data->out;
+ /* Write Magic Number. */
+ archive_le32enc(&sd[0], LZ4_MAGICNUMBER);
+ /* FLG */
+ sd[4] = (data->version_number << 6)
+ | (data->block_independence << 5)
+ | (data->block_checksum << 4)
+ | (data->stream_size << 3)
+ | (data->stream_checksum << 2)
+ | (data->preset_dictionary << 0);
+ /* BD */
+ sd[5] = (data->block_maximum_size << 4);
+ sd[6] = (__archive_xxhash.XXH32(&sd[4], 2, 0) >> 8) & 0xff;
+ data->out += 7;
+ if (data->stream_checksum)
+ data->xxh32_state = __archive_xxhash.XXH32_init(0);
+ else
+ data->xxh32_state = NULL;
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+lz4_write_one_block(struct archive_write_filter *f, const char *p,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ ssize_t r;
+
+ if (p == NULL) {
+ /* Compress remaining uncompressed data. */
+ if (data->in_buffer == data->in)
+ return 0;
+ else {
+ size_t l = data->in - data->in_buffer;
+ r = drive_compressor(f, data->in_buffer, l);
+ if (r == ARCHIVE_OK)
+ r = (ssize_t)l;
+ }
+ } else if ((data->block_independence || data->compression_level < 3) &&
+ data->in_buffer == data->in && length >= data->block_size) {
+ r = drive_compressor(f, p, data->block_size);
+ if (r == ARCHIVE_OK)
+ r = (ssize_t)data->block_size;
+ } else {
+ size_t remaining_size = data->in_buffer_size -
+ (data->in - data->in_buffer);
+ size_t l = (remaining_size > length)? length: remaining_size;
+ memcpy(data->in, p, l);
+ data->in += l;
+ if (l == remaining_size) {
+ r = drive_compressor(f, data->in_buffer,
+ data->block_size);
+ if (r == ARCHIVE_OK)
+ r = (ssize_t)l;
+ data->in = data->in_buffer;
+ } else
+ r = (ssize_t)l;
+ }
+
+ return (r);
+}
+
+
+/*
+ * Utility function to push input data through compressor, writing
+ * full output blocks as necessary.
+ *
+ * Note that this handles both the regular write case (finishing ==
+ * false) and the end-of-archive case (finishing == true).
+ */
+static int
+drive_compressor(struct archive_write_filter *f, const char *p, size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (data->stream_checksum)
+ __archive_xxhash.XXH32_update(data->xxh32_state,
+ p, (int)length);
+ if (data->block_independence)
+ return drive_compressor_independence(f, p, length);
+ else
+ return drive_compressor_dependence(f, p, length);
+}
+
+static int
+drive_compressor_independence(struct archive_write_filter *f, const char *p,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ unsigned int outsize;
+
+#ifdef HAVE_LZ4HC_H
+ if (data->compression_level >= 3)
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+ outsize = LZ4_compress_HC(p, data->out + 4,
+ (int)length, (int)data->block_size,
+ data->compression_level);
+#else
+ outsize = LZ4_compressHC2_limitedOutput(p, data->out + 4,
+ (int)length, (int)data->block_size,
+ data->compression_level);
+#endif
+ else
+#endif
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+ outsize = LZ4_compress_default(p, data->out + 4,
+ (int)length, (int)data->block_size);
+#else
+ outsize = LZ4_compress_limitedOutput(p, data->out + 4,
+ (int)length, (int)data->block_size);
+#endif
+
+ if (outsize) {
+ /* The buffer is compressed. */
+ archive_le32enc(data->out, outsize);
+ data->out += 4;
+ } else {
+ /* The buffer is not compressed. The compressed size was
+ * bigger than its uncompressed size. */
+ archive_le32enc(data->out, (uint32_t)(length | 0x80000000));
+ data->out += 4;
+ memcpy(data->out, p, length);
+ outsize = (uint32_t)length;
+ }
+ data->out += outsize;
+ if (data->block_checksum) {
+ unsigned int checksum =
+ __archive_xxhash.XXH32(data->out - outsize, outsize, 0);
+ archive_le32enc(data->out, checksum);
+ data->out += 4;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+drive_compressor_dependence(struct archive_write_filter *f, const char *p,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int outsize;
+
+#define DICT_SIZE (64 * 1024)
+#ifdef HAVE_LZ4HC_H
+ if (data->compression_level >= 3) {
+ if (data->lz4_stream == NULL) {
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+ data->lz4_stream = LZ4_createStreamHC();
+ LZ4_resetStreamHC(data->lz4_stream, data->compression_level);
+#else
+ data->lz4_stream =
+ LZ4_createHC(data->in_buffer_allocated);
+#endif
+ if (data->lz4_stream == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression"
+ " buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ else
+ LZ4_loadDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
+
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+ outsize = LZ4_compress_HC_continue(
+ data->lz4_stream, p, data->out + 4, (int)length,
+ (int)data->block_size);
+#else
+ outsize = LZ4_compressHC2_limitedOutput_continue(
+ data->lz4_stream, p, data->out + 4, (int)length,
+ (int)data->block_size, data->compression_level);
+#endif
+ } else
+#endif
+ {
+ if (data->lz4_stream == NULL) {
+ data->lz4_stream = LZ4_createStream();
+ if (data->lz4_stream == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression"
+ " buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ else
+ LZ4_loadDict(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
+
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+ outsize = LZ4_compress_fast_continue(
+ data->lz4_stream, p, data->out + 4, (int)length,
+ (int)data->block_size, 1);
+#else
+ outsize = LZ4_compress_limitedOutput_continue(
+ data->lz4_stream, p, data->out + 4, (int)length,
+ (int)data->block_size);
+#endif
+ }
+
+ if (outsize) {
+ /* The buffer is compressed. */
+ archive_le32enc(data->out, outsize);
+ data->out += 4;
+ } else {
+ /* The buffer is not compressed. The compressed size was
+ * bigger than its uncompressed size. */
+ archive_le32enc(data->out, (uint32_t)(length | 0x80000000));
+ data->out += 4;
+ memcpy(data->out, p, length);
+ outsize = (uint32_t)length;
+ }
+ data->out += outsize;
+ if (data->block_checksum) {
+ unsigned int checksum =
+ __archive_xxhash.XXH32(data->out - outsize, outsize, 0);
+ archive_le32enc(data->out, checksum);
+ data->out += 4;
+ }
+
+ if (length == data->block_size) {
+#ifdef HAVE_LZ4HC_H
+ if (data->compression_level >= 3) {
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+ LZ4_saveDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
+#else
+ LZ4_slideInputBufferHC(data->lz4_stream);
+#endif
+ data->in_buffer = data->in_buffer_allocated + DICT_SIZE;
+ }
+ else
+#endif
+ LZ4_saveDict(data->lz4_stream,
+ data->in_buffer_allocated, DICT_SIZE);
+#undef DICT_SIZE
+ }
+ return (ARCHIVE_OK);
+}
+
+#else /* HAVE_LIBLZ4 */
+
+static int
+archive_filter_lz4_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ struct archive_string as;
+ int r;
+
+ archive_string_init(&as);
+ archive_strcpy(&as, "lz4 -z -q -q");
+
+ /* Specify a compression level. */
+ if (data->compression_level > 0) {
+ archive_strcat(&as, " -");
+ archive_strappend_char(&as, '0' + data->compression_level);
+ }
+ /* Specify a block size. */
+ archive_strcat(&as, " -B");
+ archive_strappend_char(&as, '0' + data->block_maximum_size);
+
+ if (data->block_checksum)
+ archive_strcat(&as, " -BX");
+ if (data->stream_checksum == 0)
+ archive_strcat(&as, " --no-frame-crc");
+ if (data->block_independence == 0)
+ archive_strcat(&as, " -BD");
+
+ f->write = archive_filter_lz4_write;
+
+ r = __archive_write_program_open(f, data->pdata, as.s);
+ archive_string_free(&as);
+ return (r);
+}
+
+static int
+archive_filter_lz4_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_filter_lz4_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_close(f, data->pdata);
+}
+
+static int
+archive_filter_lz4_free(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ __archive_write_program_free(data->pdata);
+ free(data);
+ return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_LIBLZ4 */
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_lzop.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_lzop.c
new file mode 100644
index 00000000000..c4cb9ed733e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_lzop.c
@@ -0,0 +1,478 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+//#undef HAVE_LZO_LZOCONF_H
+//#undef HAVE_LZO_LZO1X_H
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include <time.h>
+#ifdef HAVE_LZO_LZOCONF_H
+#error #include <lzo/lzoconf.h>
+#endif
+#ifdef HAVE_LZO_LZO1X_H
+#error #include <lzo/lzo1x.h>
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_endian.h"
+#include "archive_write_private.h"
+
+enum lzo_method {
+ METHOD_LZO1X_1 = 1,
+ METHOD_LZO1X_1_15 = 2,
+ METHOD_LZO1X_999 = 3
+};
+struct write_lzop {
+ int compression_level;
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+ unsigned char *uncompressed;
+ size_t uncompressed_buffer_size;
+ size_t uncompressed_avail_bytes;
+ unsigned char *compressed;
+ size_t compressed_buffer_size;
+ enum lzo_method method;
+ unsigned char level;
+ lzo_voidp work_buffer;
+ lzo_uint32 work_buffer_size;
+ char header_written;
+#else
+ struct archive_write_program_data *pdata;
+#endif
+};
+
+static int archive_write_lzop_open(struct archive_write_filter *);
+static int archive_write_lzop_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_write_lzop_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_write_lzop_close(struct archive_write_filter *);
+static int archive_write_lzop_free(struct archive_write_filter *);
+
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+/* Maximum block size. */
+#define BLOCK_SIZE (256 * 1024)
+/* Block information is composed of uncompressed size(4 bytes),
+ * compressed size(4 bytes) and the checksum of uncompressed data(4 bytes)
+ * in this lzop writer. */
+#define BLOCK_INfO_SIZE 12
+
+#define HEADER_VERSION 9
+#define HEADER_LIBVERSION 11
+#define HEADER_METHOD 15
+#define HEADER_LEVEL 16
+#define HEADER_MTIME_LOW 25
+#define HEADER_MTIME_HIGH 29
+#define HEADER_H_CHECKSUM 34
+
+/*
+ * Header template.
+ */
+static const unsigned char header[] = {
+ /* LZOP Magic code 9 bytes */
+ 0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a,
+ /* LZOP utility version(fake data) 2 bytes */
+ 0x10, 0x30,
+ /* LZO library version 2 bytes */
+ 0x09, 0x40,
+ /* Minimum required LZO library version 2 bytes */
+ 0x09, 0x40,
+ /* Method */
+ 1,
+ /* Level */
+ 5,
+ /* Flags 4 bytes
+ * -OS Unix
+ * -Stdout
+ * -Stdin
+ * -Adler32 used for uncompressed data 4 bytes */
+ 0x03, 0x00, 0x00, 0x0d,
+ /* Mode (AE_IFREG | 0644) 4 bytes */
+ 0x00, 0x00, 0x81, 0xa4,
+ /* Mtime low 4 bytes */
+ 0x00, 0x00, 0x00, 0x00,
+ /* Mtime high 4 bytes */
+ 0x00, 0x00, 0x00, 0x00,
+ /* Filename length */
+ 0x00,
+ /* Header checksum 4 bytes */
+ 0x00, 0x00, 0x00, 0x00,
+};
+#endif
+
+int
+archive_write_add_filter_lzop(struct archive *_a)
+{
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct write_lzop *data;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_lzop");
+
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ archive_set_error(_a, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ f->name = "lzop";
+ f->code = ARCHIVE_FILTER_LZOP;
+ f->data = data;
+ f->open = archive_write_lzop_open;
+ f->options = archive_write_lzop_options;
+ f->write = archive_write_lzop_write;
+ f->close = archive_write_lzop_close;
+ f->free = archive_write_lzop_free;
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+ if (lzo_init() != LZO_E_OK) {
+ free(data);
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "lzo_init(type check) failed");
+ return (ARCHIVE_FATAL);
+ }
+ if (lzo_version() < 0x940) {
+ free(data);
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "liblzo library is too old(%s < 0.940)",
+ lzo_version_string());
+ return (ARCHIVE_FATAL);
+ }
+ data->compression_level = 5;
+ return (ARCHIVE_OK);
+#else
+ data->pdata = __archive_write_program_allocate("lzop");
+ if (data->pdata == NULL) {
+ free(data);
+ archive_set_error(_a, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ data->compression_level = 0;
+ /* Note: We return "warn" to inform of using an external lzop
+ * program. */
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external lzop program for lzop compression");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+static int
+archive_write_lzop_free(struct archive_write_filter *f)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+ free(data->uncompressed);
+ free(data->compressed);
+ free(data->work_buffer);
+#else
+ __archive_write_program_free(data->pdata);
+#endif
+ free(data);
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_lzop_options(struct archive_write_filter *f, const char *key,
+ const char *value)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+
+ if (strcmp(key, "compression-level") == 0) {
+ if (value == NULL || !(value[0] >= '1' && value[0] <= '9') ||
+ value[1] != '\0')
+ return (ARCHIVE_WARN);
+ data->compression_level = value[0] - '0';
+ return (ARCHIVE_OK);
+ }
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+static int
+archive_write_lzop_open(struct archive_write_filter *f)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+
+ switch (data->compression_level) {
+ case 1:
+ data->method = METHOD_LZO1X_1_15; data->level = 1; break;
+ default:
+ case 2: case 3: case 4: case 5: case 6:
+ data->method = METHOD_LZO1X_1; data->level = 5; break;
+ case 7:
+ data->method = METHOD_LZO1X_999; data->level = 7; break;
+ case 8:
+ data->method = METHOD_LZO1X_999; data->level = 8; break;
+ case 9:
+ data->method = METHOD_LZO1X_999; data->level = 9; break;
+ }
+ switch (data->method) {
+ case METHOD_LZO1X_1:
+ data->work_buffer_size = LZO1X_1_MEM_COMPRESS; break;
+ case METHOD_LZO1X_1_15:
+ data->work_buffer_size = LZO1X_1_15_MEM_COMPRESS; break;
+ case METHOD_LZO1X_999:
+ data->work_buffer_size = LZO1X_999_MEM_COMPRESS; break;
+ }
+ if (data->work_buffer == NULL) {
+ data->work_buffer = (lzo_voidp)malloc(data->work_buffer_size);
+ if (data->work_buffer == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ if (data->compressed == NULL) {
+ data->compressed_buffer_size = sizeof(header) +
+ BLOCK_SIZE + (BLOCK_SIZE >> 4) + 64 + 3;
+ data->compressed = (unsigned char *)
+ malloc(data->compressed_buffer_size);
+ if (data->compressed == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ if (data->uncompressed == NULL) {
+ data->uncompressed_buffer_size = BLOCK_SIZE;
+ data->uncompressed = (unsigned char *)
+ malloc(data->uncompressed_buffer_size);
+ if (data->uncompressed == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+ data->uncompressed_avail_bytes = BLOCK_SIZE;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+make_header(struct archive_write_filter *f)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+ int64_t t;
+ uint32_t checksum;
+
+ memcpy(data->compressed, header, sizeof(header));
+ /* Overwrite library version. */
+ data->compressed[HEADER_LIBVERSION] = (unsigned char )
+ (lzo_version() >> 8) & 0xff;
+ data->compressed[HEADER_LIBVERSION + 1] = (unsigned char )
+ lzo_version() & 0xff;
+ /* Overwrite method and level. */
+ data->compressed[HEADER_METHOD] = (unsigned char)data->method;
+ data->compressed[HEADER_LEVEL] = data->level;
+ /* Overwrite mtime with current time. */
+ t = (int64_t)time(NULL);
+ archive_be32enc(&data->compressed[HEADER_MTIME_LOW],
+ (uint32_t)(t & 0xffffffff));
+ archive_be32enc(&data->compressed[HEADER_MTIME_HIGH],
+ (uint32_t)((t >> 32) & 0xffffffff));
+ /* Overwrite header checksum with calculated value. */
+ checksum = lzo_adler32(1, data->compressed + HEADER_VERSION,
+ (lzo_uint)(HEADER_H_CHECKSUM - HEADER_VERSION));
+ archive_be32enc(&data->compressed[HEADER_H_CHECKSUM], checksum);
+ return (sizeof(header));
+}
+
+static int
+drive_compressor(struct archive_write_filter *f)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+ unsigned char *p;
+ const int block_info_bytes = 12;
+ int header_bytes, r;
+ lzo_uint usize, csize;
+ uint32_t checksum;
+
+ if (!data->header_written) {
+ header_bytes = make_header(f);
+ data->header_written = 1;
+ } else
+ header_bytes = 0;
+ p = data->compressed;
+
+ usize = (lzo_uint)
+ (data->uncompressed_buffer_size - data->uncompressed_avail_bytes);
+ csize = 0;
+ switch (data->method) {
+ default:
+ case METHOD_LZO1X_1:
+ r = lzo1x_1_compress(data->uncompressed, usize,
+ p + header_bytes + block_info_bytes, &csize,
+ data->work_buffer);
+ break;
+ case METHOD_LZO1X_1_15:
+ r = lzo1x_1_15_compress(data->uncompressed, usize,
+ p + header_bytes + block_info_bytes, &csize,
+ data->work_buffer);
+ break;
+ case METHOD_LZO1X_999:
+ r = lzo1x_999_compress_level(data->uncompressed, usize,
+ p + header_bytes + block_info_bytes, &csize,
+ data->work_buffer, NULL, 0, 0, data->level);
+ break;
+ }
+ if (r != LZO_E_OK) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Lzop compression failed: returned status %d", r);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Store uncompressed size. */
+ archive_be32enc(p + header_bytes, (uint32_t)usize);
+ /* Store the checksum of the uncompressed data. */
+ checksum = lzo_adler32(1, data->uncompressed, usize);
+ archive_be32enc(p + header_bytes + 8, checksum);
+
+ if (csize < usize) {
+ /* Store compressed size. */
+ archive_be32enc(p + header_bytes + 4, (uint32_t)csize);
+ r = __archive_write_filter(f->next_filter, data->compressed,
+ header_bytes + block_info_bytes + csize);
+ } else {
+ /*
+ * This case, we output uncompressed data instead.
+ */
+ /* Store uncompressed size as compressed size. */
+ archive_be32enc(p + header_bytes + 4, (uint32_t)usize);
+ r = __archive_write_filter(f->next_filter, data->compressed,
+ header_bytes + block_info_bytes);
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ r = __archive_write_filter(f->next_filter, data->uncompressed,
+ usize);
+ }
+
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_lzop_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+ const char *p = buff;
+ int r;
+
+ do {
+ if (data->uncompressed_avail_bytes > length) {
+ memcpy(data->uncompressed
+ + data->uncompressed_buffer_size
+ - data->uncompressed_avail_bytes,
+ p, length);
+ data->uncompressed_avail_bytes -= length;
+ return (ARCHIVE_OK);
+ }
+
+ memcpy(data->uncompressed + data->uncompressed_buffer_size
+ - data->uncompressed_avail_bytes,
+ p, data->uncompressed_avail_bytes);
+ length -= data->uncompressed_avail_bytes;
+ p += data->uncompressed_avail_bytes;
+ data->uncompressed_avail_bytes = 0;
+
+ r = drive_compressor(f);
+ if (r != ARCHIVE_OK) return (r);
+ data->uncompressed_avail_bytes = BLOCK_SIZE;
+ } while (length);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_lzop_close(struct archive_write_filter *f)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+ const uint32_t endmark = 0;
+ int r;
+
+ if (data->uncompressed_avail_bytes < BLOCK_SIZE) {
+ /* Compress and output remaining data. */
+ r = drive_compressor(f);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ /* Write a zero uncompressed size as the end mark of the series of
+ * compressed block. */
+ return __archive_write_filter(f->next_filter, &endmark, sizeof(endmark));
+}
+
+#else
+static int
+archive_write_lzop_open(struct archive_write_filter *f)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+ struct archive_string as;
+ int r;
+
+ archive_string_init(&as);
+ archive_strcpy(&as, "lzop");
+ /* Specify compression level. */
+ if (data->compression_level > 0) {
+ archive_strappend_char(&as, ' ');
+ archive_strappend_char(&as, '-');
+ archive_strappend_char(&as, '0' + data->compression_level);
+ }
+
+ r = __archive_write_program_open(f, data->pdata, as.s);
+ archive_string_free(&as);
+ return (r);
+}
+
+static int
+archive_write_lzop_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+
+ return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_write_lzop_close(struct archive_write_filter *f)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+
+ return __archive_write_program_close(f, data->pdata);
+}
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_none.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_none.c
new file mode 100644
index 00000000000..3c06c642e73
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_none.c
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_none.c 201080 2009-12-28 02:03:54Z kientzle $");
+
+#include "archive.h"
+
+int
+archive_write_set_compression_none(struct archive *a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+int
+archive_write_add_filter_none(struct archive *a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_program.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_program.c
new file mode 100644
index 00000000000..c096e7227ba
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_program.c
@@ -0,0 +1,391 @@
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_program.c 201104 2009-12-28 03:14:30Z kientzle $");
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifdef HAVE_ERRNO_H
+# include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+#include "filter_fork.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_write_set_compression_program(struct archive *a, const char *cmd)
+{
+ __archive_write_filters_free(a);
+ return (archive_write_add_filter_program(a, cmd));
+}
+#endif
+
+struct archive_write_program_data {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ HANDLE child;
+#else
+ pid_t child;
+#endif
+ int child_stdin, child_stdout;
+
+ char *child_buf;
+ size_t child_buf_len, child_buf_avail;
+ char *program_name;
+};
+
+struct private_data {
+ struct archive_write_program_data *pdata;
+ struct archive_string description;
+ char *cmd;
+};
+
+static int archive_compressor_program_open(struct archive_write_filter *);
+static int archive_compressor_program_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_compressor_program_close(struct archive_write_filter *);
+static int archive_compressor_program_free(struct archive_write_filter *);
+
+/*
+ * Add a filter to this write handle that passes all data through an
+ * external program.
+ */
+int
+archive_write_add_filter_program(struct archive *_a, const char *cmd)
+{
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct private_data *data;
+ static const char prefix[] = "Program: ";
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_program");
+
+ f->data = calloc(1, sizeof(*data));
+ if (f->data == NULL)
+ goto memerr;
+ data = (struct private_data *)f->data;
+
+ data->cmd = strdup(cmd);
+ if (data->cmd == NULL)
+ goto memerr;
+
+ data->pdata = __archive_write_program_allocate(cmd);
+ if (data->pdata == NULL)
+ goto memerr;
+
+ /* Make up a description string. */
+ if (archive_string_ensure(&data->description,
+ strlen(prefix) + strlen(cmd) + 1) == NULL)
+ goto memerr;
+ archive_strcpy(&data->description, prefix);
+ archive_strcat(&data->description, cmd);
+
+ f->name = data->description.s;
+ f->code = ARCHIVE_FILTER_PROGRAM;
+ f->open = archive_compressor_program_open;
+ f->write = archive_compressor_program_write;
+ f->close = archive_compressor_program_close;
+ f->free = archive_compressor_program_free;
+ return (ARCHIVE_OK);
+memerr:
+ archive_compressor_program_free(f);
+ archive_set_error(_a, ENOMEM,
+ "Can't allocate memory for filter program");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+archive_compressor_program_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_open(f, data->pdata, data->cmd);
+}
+
+static int
+archive_compressor_program_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_compressor_program_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_close(f, data->pdata);
+}
+
+static int
+archive_compressor_program_free(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (data) {
+ free(data->cmd);
+ archive_string_free(&data->description);
+ __archive_write_program_free(data->pdata);
+ free(data);
+ f->data = NULL;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Allocate resources for executing an external program.
+ */
+struct archive_write_program_data *
+__archive_write_program_allocate(const char *program)
+{
+ struct archive_write_program_data *data;
+
+ data = calloc(1, sizeof(struct archive_write_program_data));
+ if (data == NULL)
+ return (data);
+ data->child_stdin = -1;
+ data->child_stdout = -1;
+ data->program_name = strdup(program);
+ return (data);
+}
+
+/*
+ * Release the resources.
+ */
+int
+__archive_write_program_free(struct archive_write_program_data *data)
+{
+
+ if (data) {
+ free(data->program_name);
+ free(data->child_buf);
+ free(data);
+ }
+ return (ARCHIVE_OK);
+}
+
+int
+__archive_write_program_open(struct archive_write_filter *f,
+ struct archive_write_program_data *data, const char *cmd)
+{
+ int ret;
+
+ if (data->child_buf == NULL) {
+ data->child_buf_len = 65536;
+ data->child_buf_avail = 0;
+ data->child_buf = malloc(data->child_buf_len);
+
+ if (data->child_buf == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ ret = __archive_create_child(cmd, &data->child_stdin,
+ &data->child_stdout, &data->child);
+ if (ret != ARCHIVE_OK) {
+ archive_set_error(f->archive, EINVAL,
+ "Can't launch external program: %s", cmd);
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+child_write(struct archive_write_filter *f,
+ struct archive_write_program_data *data, const char *buf, size_t buf_len)
+{
+ ssize_t ret;
+
+ if (data->child_stdin == -1)
+ return (-1);
+
+ if (buf_len == 0)
+ return (-1);
+
+ for (;;) {
+ do {
+ ret = write(data->child_stdin, buf, buf_len);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret > 0)
+ return (ret);
+ if (ret == 0) {
+ close(data->child_stdin);
+ data->child_stdin = -1;
+ fcntl(data->child_stdout, F_SETFL, 0);
+ return (0);
+ }
+ if (ret == -1 && errno != EAGAIN)
+ return (-1);
+
+ if (data->child_stdout == -1) {
+ fcntl(data->child_stdin, F_SETFL, 0);
+ __archive_check_child(data->child_stdin,
+ data->child_stdout);
+ continue;
+ }
+
+ do {
+ ret = read(data->child_stdout,
+ data->child_buf + data->child_buf_avail,
+ data->child_buf_len - data->child_buf_avail);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret == 0 || (ret == -1 && errno == EPIPE)) {
+ close(data->child_stdout);
+ data->child_stdout = -1;
+ fcntl(data->child_stdin, F_SETFL, 0);
+ continue;
+ }
+ if (ret == -1 && errno == EAGAIN) {
+ __archive_check_child(data->child_stdin,
+ data->child_stdout);
+ continue;
+ }
+ if (ret == -1)
+ return (-1);
+
+ data->child_buf_avail += ret;
+
+ ret = __archive_write_filter(f->next_filter,
+ data->child_buf, data->child_buf_avail);
+ if (ret != ARCHIVE_OK)
+ return (-1);
+ data->child_buf_avail = 0;
+ }
+}
+
+/*
+ * Write data to the filter stream.
+ */
+int
+__archive_write_program_write(struct archive_write_filter *f,
+ struct archive_write_program_data *data, const void *buff, size_t length)
+{
+ ssize_t ret;
+ const char *buf;
+
+ if (data->child == 0)
+ return (ARCHIVE_OK);
+
+ buf = buff;
+ while (length > 0) {
+ ret = child_write(f, data, buf, length);
+ if (ret == -1 || ret == 0) {
+ archive_set_error(f->archive, EIO,
+ "Can't write to program: %s", data->program_name);
+ return (ARCHIVE_FATAL);
+ }
+ length -= ret;
+ buf += ret;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Finish the filtering...
+ */
+int
+__archive_write_program_close(struct archive_write_filter *f,
+ struct archive_write_program_data *data)
+{
+ int ret, status;
+ ssize_t bytes_read;
+
+ if (data->child == 0)
+ return ARCHIVE_OK;
+
+ ret = 0;
+ close(data->child_stdin);
+ data->child_stdin = -1;
+ fcntl(data->child_stdout, F_SETFL, 0);
+
+ for (;;) {
+ do {
+ bytes_read = read(data->child_stdout,
+ data->child_buf + data->child_buf_avail,
+ data->child_buf_len - data->child_buf_avail);
+ } while (bytes_read == -1 && errno == EINTR);
+
+ if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE))
+ break;
+
+ if (bytes_read == -1) {
+ archive_set_error(f->archive, errno,
+ "Error reading from program: %s", data->program_name);
+ ret = ARCHIVE_FATAL;
+ goto cleanup;
+ }
+ data->child_buf_avail += bytes_read;
+
+ ret = __archive_write_filter(f->next_filter,
+ data->child_buf, data->child_buf_avail);
+ if (ret != ARCHIVE_OK) {
+ ret = ARCHIVE_FATAL;
+ goto cleanup;
+ }
+ data->child_buf_avail = 0;
+ }
+
+cleanup:
+ /* Shut down the child. */
+ if (data->child_stdin != -1)
+ close(data->child_stdin);
+ if (data->child_stdout != -1)
+ close(data->child_stdout);
+ while (waitpid(data->child, &status, 0) == -1 && errno == EINTR)
+ continue;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ CloseHandle(data->child);
+#endif
+ data->child = 0;
+
+ if (status != 0) {
+ archive_set_error(f->archive, EIO,
+ "Error closing program: %s", data->program_name);
+ ret = ARCHIVE_FATAL;
+ }
+ return ret;
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_uuencode.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_uuencode.c
new file mode 100644
index 00000000000..1ad45892192
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_uuencode.c
@@ -0,0 +1,295 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+#define LBYTES 45
+
+struct private_uuencode {
+ int mode;
+ struct archive_string name;
+ struct archive_string encoded_buff;
+ size_t bs;
+ size_t hold_len;
+ unsigned char hold[LBYTES];
+};
+
+static int archive_filter_uuencode_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_filter_uuencode_open(struct archive_write_filter *);
+static int archive_filter_uuencode_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_filter_uuencode_close(struct archive_write_filter *);
+static int archive_filter_uuencode_free(struct archive_write_filter *);
+static void uu_encode(struct archive_string *, const unsigned char *, size_t);
+static int64_t atol8(const char *, size_t);
+
+/*
+ * Add a compress filter to this write handle.
+ */
+int
+archive_write_add_filter_uuencode(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct private_uuencode *state;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
+
+ state = (struct private_uuencode *)calloc(1, sizeof(*state));
+ if (state == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for uuencode filter");
+ return (ARCHIVE_FATAL);
+ }
+ archive_strcpy(&state->name, "-");
+ state->mode = 0644;
+
+ f->data = state;
+ f->name = "uuencode";
+ f->code = ARCHIVE_FILTER_UU;
+ f->open = archive_filter_uuencode_open;
+ f->options = archive_filter_uuencode_options;
+ f->write = archive_filter_uuencode_write;
+ f->close = archive_filter_uuencode_close;
+ f->free = archive_filter_uuencode_free;
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_filter_uuencode_options(struct archive_write_filter *f, const char *key,
+ const char *value)
+{
+ struct private_uuencode *state = (struct private_uuencode *)f->data;
+
+ if (strcmp(key, "mode") == 0) {
+ if (value == NULL) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "mode option requires octal digits");
+ return (ARCHIVE_FAILED);
+ }
+ state->mode = (int)atol8(value, strlen(value)) & 0777;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "name") == 0) {
+ if (value == NULL) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "name option requires a string");
+ return (ARCHIVE_FAILED);
+ }
+ archive_strcpy(&state->name, value);
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Setup callback.
+ */
+static int
+archive_filter_uuencode_open(struct archive_write_filter *f)
+{
+ struct private_uuencode *state = (struct private_uuencode *)f->data;
+ size_t bs = 65536, bpb;
+
+ if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+ /* Buffer size should be a multiple number of the of bytes
+ * per block for performance. */
+ bpb = archive_write_get_bytes_per_block(f->archive);
+ if (bpb > bs)
+ bs = bpb;
+ else if (bpb != 0)
+ bs -= bs % bpb;
+ }
+
+ state->bs = bs;
+ if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for uuencode buffer");
+ return (ARCHIVE_FATAL);
+ }
+
+ archive_string_sprintf(&state->encoded_buff, "begin %o %s\n",
+ state->mode, state->name.s);
+
+ f->data = state;
+ return (0);
+}
+
+static void
+uu_encode(struct archive_string *as, const unsigned char *p, size_t len)
+{
+ int c;
+
+ c = (int)len;
+ archive_strappend_char(as, c?c + 0x20:'`');
+ for (; len >= 3; p += 3, len -= 3) {
+ c = p[0] >> 2;
+ archive_strappend_char(as, c?c + 0x20:'`');
+ c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
+ archive_strappend_char(as, c?c + 0x20:'`');
+ c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
+ archive_strappend_char(as, c?c + 0x20:'`');
+ c = p[2] & 0x3f;
+ archive_strappend_char(as, c?c + 0x20:'`');
+ }
+ if (len > 0) {
+ c = p[0] >> 2;
+ archive_strappend_char(as, c?c + 0x20:'`');
+ c = (p[0] & 0x03) << 4;
+ if (len == 1) {
+ archive_strappend_char(as, c?c + 0x20:'`');
+ archive_strappend_char(as, '`');
+ archive_strappend_char(as, '`');
+ } else {
+ c |= (p[1] & 0xf0) >> 4;
+ archive_strappend_char(as, c?c + 0x20:'`');
+ c = (p[1] & 0x0f) << 2;
+ archive_strappend_char(as, c?c + 0x20:'`');
+ archive_strappend_char(as, '`');
+ }
+ }
+ archive_strappend_char(as, '\n');
+}
+
+/*
+ * Write data to the encoded stream.
+ */
+static int
+archive_filter_uuencode_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_uuencode *state = (struct private_uuencode *)f->data;
+ const unsigned char *p = buff;
+ int ret = ARCHIVE_OK;
+
+ if (length == 0)
+ return (ret);
+
+ if (state->hold_len) {
+ while (state->hold_len < LBYTES && length > 0) {
+ state->hold[state->hold_len++] = *p++;
+ length--;
+ }
+ if (state->hold_len < LBYTES)
+ return (ret);
+ uu_encode(&state->encoded_buff, state->hold, LBYTES);
+ state->hold_len = 0;
+ }
+
+ for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
+ uu_encode(&state->encoded_buff, p, LBYTES);
+
+ /* Save remaining bytes. */
+ if (length > 0) {
+ memcpy(state->hold, p, length);
+ state->hold_len = length;
+ }
+ while (archive_strlen(&state->encoded_buff) >= state->bs) {
+ ret = __archive_write_filter(f->next_filter,
+ state->encoded_buff.s, state->bs);
+ memmove(state->encoded_buff.s,
+ state->encoded_buff.s + state->bs,
+ state->encoded_buff.length - state->bs);
+ state->encoded_buff.length -= state->bs;
+ }
+
+ return (ret);
+}
+
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_filter_uuencode_close(struct archive_write_filter *f)
+{
+ struct private_uuencode *state = (struct private_uuencode *)f->data;
+
+ /* Flush remaining bytes. */
+ if (state->hold_len != 0)
+ uu_encode(&state->encoded_buff, state->hold, state->hold_len);
+ archive_string_sprintf(&state->encoded_buff, "`\nend\n");
+ /* Write the last block */
+ archive_write_set_bytes_in_last_block(f->archive, 1);
+ return __archive_write_filter(f->next_filter,
+ state->encoded_buff.s, archive_strlen(&state->encoded_buff));
+}
+
+static int
+archive_filter_uuencode_free(struct archive_write_filter *f)
+{
+ struct private_uuencode *state = (struct private_uuencode *)f->data;
+
+ archive_string_free(&state->name);
+ archive_string_free(&state->encoded_buff);
+ free(state);
+ return (ARCHIVE_OK);
+}
+
+static int64_t
+atol8(const char *p, size_t char_cnt)
+{
+ int64_t l;
+ int digit;
+
+ l = 0;
+ while (char_cnt-- > 0) {
+ if (*p >= '0' && *p <= '7')
+ digit = *p - '0';
+ else
+ break;
+ p++;
+ l <<= 3;
+ l |= digit;
+ }
+ return (l);
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_xz.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_xz.c
new file mode 100644
index 00000000000..7ecf7015448
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_xz.c
@@ -0,0 +1,545 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_xz.c 201108 2009-12-28 03:28:21Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include <time.h>
+#ifdef HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_write_set_compression_lzip(struct archive *a)
+{
+ __archive_write_filters_free(a);
+ return (archive_write_add_filter_lzip(a));
+}
+
+int
+archive_write_set_compression_lzma(struct archive *a)
+{
+ __archive_write_filters_free(a);
+ return (archive_write_add_filter_lzma(a));
+}
+
+int
+archive_write_set_compression_xz(struct archive *a)
+{
+ __archive_write_filters_free(a);
+ return (archive_write_add_filter_xz(a));
+}
+
+#endif
+
+#ifndef HAVE_LZMA_H
+int
+archive_write_add_filter_xz(struct archive *a)
+{
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "xz compression not supported on this platform");
+ return (ARCHIVE_FATAL);
+}
+
+int
+archive_write_add_filter_lzma(struct archive *a)
+{
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "lzma compression not supported on this platform");
+ return (ARCHIVE_FATAL);
+}
+
+int
+archive_write_add_filter_lzip(struct archive *a)
+{
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "lzma compression not supported on this platform");
+ return (ARCHIVE_FATAL);
+}
+#else
+/* Don't compile this if we don't have liblzma. */
+
+struct private_data {
+ int compression_level;
+ uint32_t threads;
+ lzma_stream stream;
+ lzma_filter lzmafilters[2];
+ lzma_options_lzma lzma_opt;
+ int64_t total_in;
+ unsigned char *compressed;
+ size_t compressed_buffer_size;
+ int64_t total_out;
+ /* the CRC32 value of uncompressed data for lzip */
+ uint32_t crc32;
+};
+
+static int archive_compressor_xz_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_compressor_xz_open(struct archive_write_filter *);
+static int archive_compressor_xz_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_compressor_xz_close(struct archive_write_filter *);
+static int archive_compressor_xz_free(struct archive_write_filter *);
+static int drive_compressor(struct archive_write_filter *,
+ struct private_data *, int finishing);
+
+struct option_value {
+ uint32_t dict_size;
+ uint32_t nice_len;
+ lzma_match_finder mf;
+};
+static const struct option_value option_values[] = {
+ { 1 << 16, 32, LZMA_MF_HC3},
+ { 1 << 20, 32, LZMA_MF_HC3},
+ { 3 << 19, 32, LZMA_MF_HC4},
+ { 1 << 21, 32, LZMA_MF_BT4},
+ { 3 << 20, 32, LZMA_MF_BT4},
+ { 1 << 22, 32, LZMA_MF_BT4},
+ { 1 << 23, 64, LZMA_MF_BT4},
+ { 1 << 24, 64, LZMA_MF_BT4},
+ { 3 << 23, 64, LZMA_MF_BT4},
+ { 1 << 25, 64, LZMA_MF_BT4}
+};
+
+static int
+common_setup(struct archive_write_filter *f)
+{
+ struct private_data *data;
+ struct archive_write *a = (struct archive_write *)f->archive;
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ f->data = data;
+ data->compression_level = LZMA_PRESET_DEFAULT;
+ data->threads = 1;
+ f->open = &archive_compressor_xz_open;
+ f->close = archive_compressor_xz_close;
+ f->free = archive_compressor_xz_free;
+ f->options = &archive_compressor_xz_options;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Add an xz compression filter to this write handle.
+ */
+int
+archive_write_add_filter_xz(struct archive *_a)
+{
+ struct archive_write_filter *f;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_xz");
+ f = __archive_write_allocate_filter(_a);
+ r = common_setup(f);
+ if (r == ARCHIVE_OK) {
+ f->code = ARCHIVE_FILTER_XZ;
+ f->name = "xz";
+ }
+ return (r);
+}
+
+/* LZMA is handled identically, we just need a different compression
+ * code set. (The liblzma setup looks at the code to determine
+ * the one place that XZ and LZMA require different handling.) */
+int
+archive_write_add_filter_lzma(struct archive *_a)
+{
+ struct archive_write_filter *f;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_lzma");
+ f = __archive_write_allocate_filter(_a);
+ r = common_setup(f);
+ if (r == ARCHIVE_OK) {
+ f->code = ARCHIVE_FILTER_LZMA;
+ f->name = "lzma";
+ }
+ return (r);
+}
+
+int
+archive_write_add_filter_lzip(struct archive *_a)
+{
+ struct archive_write_filter *f;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_lzip");
+ f = __archive_write_allocate_filter(_a);
+ r = common_setup(f);
+ if (r == ARCHIVE_OK) {
+ f->code = ARCHIVE_FILTER_LZIP;
+ f->name = "lzip";
+ }
+ return (r);
+}
+
+static int
+archive_compressor_xz_init_stream(struct archive_write_filter *f,
+ struct private_data *data)
+{
+ static const lzma_stream lzma_stream_init_data = LZMA_STREAM_INIT;
+ int ret;
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+ lzma_mt mt_options;
+#endif
+
+ data->stream = lzma_stream_init_data;
+ data->stream.next_out = data->compressed;
+ data->stream.avail_out = data->compressed_buffer_size;
+ if (f->code == ARCHIVE_FILTER_XZ) {
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+ if (data->threads != 1) {
+ memset(&mt_options, 0, sizeof(mt_options));
+ mt_options.threads = data->threads;
+ mt_options.timeout = 300;
+ mt_options.filters = data->lzmafilters;
+ mt_options.check = LZMA_CHECK_CRC64;
+ ret = lzma_stream_encoder_mt(&(data->stream),
+ &mt_options);
+ } else
+#endif
+ ret = lzma_stream_encoder(&(data->stream),
+ data->lzmafilters, LZMA_CHECK_CRC64);
+ } else if (f->code == ARCHIVE_FILTER_LZMA) {
+ ret = lzma_alone_encoder(&(data->stream), &data->lzma_opt);
+ } else { /* ARCHIVE_FILTER_LZIP */
+ int dict_size = data->lzma_opt.dict_size;
+ int ds, log2dic, wedges;
+
+ /* Calculate a coded dictionary size */
+ if (dict_size < (1 << 12) || dict_size > (1 << 29)) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Unacceptable dictionary size for lzip: %d",
+ dict_size);
+ return (ARCHIVE_FATAL);
+ }
+ for (log2dic = 29; log2dic >= 12; log2dic--) {
+ if (dict_size & (1 << log2dic))
+ break;
+ }
+ if (dict_size > (1 << log2dic)) {
+ log2dic++;
+ wedges =
+ ((1 << log2dic) - dict_size) / (1 << (log2dic - 4));
+ } else
+ wedges = 0;
+ ds = ((wedges << 5) & 0xe0) | (log2dic & 0x1f);
+
+ data->crc32 = 0;
+ /* Make a header */
+ data->compressed[0] = 0x4C;
+ data->compressed[1] = 0x5A;
+ data->compressed[2] = 0x49;
+ data->compressed[3] = 0x50;
+ data->compressed[4] = 1;/* Version */
+ data->compressed[5] = (unsigned char)ds;
+ data->stream.next_out += 6;
+ data->stream.avail_out -= 6;
+
+ ret = lzma_raw_encoder(&(data->stream), data->lzmafilters);
+ }
+ if (ret == LZMA_OK)
+ return (ARCHIVE_OK);
+
+ switch (ret) {
+ case LZMA_MEM_ERROR:
+ archive_set_error(f->archive, ENOMEM,
+ "Internal error initializing compression library: "
+ "Cannot allocate memory");
+ break;
+ default:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ "It's a bug in liblzma");
+ break;
+ }
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_xz_open(struct archive_write_filter *f)
+{
+ struct private_data *data = f->data;
+ int ret;
+
+ if (data->compressed == NULL) {
+ size_t bs = 65536, bpb;
+ if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+ /* Buffer size should be a multiple number of the of bytes
+ * per block for performance. */
+ bpb = archive_write_get_bytes_per_block(f->archive);
+ if (bpb > bs)
+ bs = bpb;
+ else if (bpb != 0)
+ bs -= bs % bpb;
+ }
+ data->compressed_buffer_size = bs;
+ data->compressed
+ = (unsigned char *)malloc(data->compressed_buffer_size);
+ if (data->compressed == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ f->write = archive_compressor_xz_write;
+
+ /* Initialize compression library. */
+ if (f->code == ARCHIVE_FILTER_LZIP) {
+ const struct option_value *val =
+ &option_values[data->compression_level];
+
+ data->lzma_opt.dict_size = val->dict_size;
+ data->lzma_opt.preset_dict = NULL;
+ data->lzma_opt.preset_dict_size = 0;
+ data->lzma_opt.lc = LZMA_LC_DEFAULT;
+ data->lzma_opt.lp = LZMA_LP_DEFAULT;
+ data->lzma_opt.pb = LZMA_PB_DEFAULT;
+ data->lzma_opt.mode =
+ data->compression_level<= 2? LZMA_MODE_FAST:LZMA_MODE_NORMAL;
+ data->lzma_opt.nice_len = val->nice_len;
+ data->lzma_opt.mf = val->mf;
+ data->lzma_opt.depth = 0;
+ data->lzmafilters[0].id = LZMA_FILTER_LZMA1;
+ data->lzmafilters[0].options = &data->lzma_opt;
+ data->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
+ } else {
+ if (lzma_lzma_preset(&data->lzma_opt, data->compression_level)) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library");
+ }
+ data->lzmafilters[0].id = LZMA_FILTER_LZMA2;
+ data->lzmafilters[0].options = &data->lzma_opt;
+ data->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
+ }
+ ret = archive_compressor_xz_init_stream(f, data);
+ if (ret == LZMA_OK) {
+ f->data = data;
+ return (0);
+ }
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_compressor_xz_options(struct archive_write_filter *f,
+ const char *key, const char *value)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (strcmp(key, "compression-level") == 0) {
+ if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
+ value[1] != '\0')
+ return (ARCHIVE_WARN);
+ data->compression_level = value[0] - '0';
+ if (data->compression_level > 9)
+ data->compression_level = 9;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "threads") == 0) {
+ char *endptr;
+
+ if (value == NULL)
+ return (ARCHIVE_WARN);
+ errno = 0;
+ data->threads = (int)strtoul(value, &endptr, 10);
+ if (errno != 0 || *endptr != '\0') {
+ data->threads = 1;
+ return (ARCHIVE_WARN);
+ }
+ if (data->threads == 0) {
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+ data->threads = lzma_cputhreads();
+#else
+ data->threads = 1;
+#endif
+ }
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Write data to the compressed stream.
+ */
+static int
+archive_compressor_xz_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ /* Update statistics */
+ data->total_in += length;
+ if (f->code == ARCHIVE_FILTER_LZIP)
+ data->crc32 = lzma_crc32(buff, length, data->crc32);
+
+ /* Compress input data to output buffer */
+ data->stream.next_in = buff;
+ data->stream.avail_in = length;
+ if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK)
+ return (ret);
+
+ return (ARCHIVE_OK);
+}
+
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_compressor_xz_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ ret = drive_compressor(f, data, 1);
+ if (ret == ARCHIVE_OK) {
+ data->total_out +=
+ data->compressed_buffer_size - data->stream.avail_out;
+ ret = __archive_write_filter(f->next_filter,
+ data->compressed,
+ data->compressed_buffer_size - data->stream.avail_out);
+ if (f->code == ARCHIVE_FILTER_LZIP && ret == ARCHIVE_OK) {
+ archive_le32enc(data->compressed, data->crc32);
+ archive_le64enc(data->compressed+4, data->total_in);
+ archive_le64enc(data->compressed+12, data->total_out + 20);
+ ret = __archive_write_filter(f->next_filter,
+ data->compressed, 20);
+ }
+ }
+ lzma_end(&(data->stream));
+ return ret;
+}
+
+static int
+archive_compressor_xz_free(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ free(data->compressed);
+ free(data);
+ f->data = NULL;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Utility function to push input data through compressor,
+ * writing full output blocks as necessary.
+ *
+ * Note that this handles both the regular write case (finishing ==
+ * false) and the end-of-archive case (finishing == true).
+ */
+static int
+drive_compressor(struct archive_write_filter *f,
+ struct private_data *data, int finishing)
+{
+ int ret;
+
+ for (;;) {
+ if (data->stream.avail_out == 0) {
+ data->total_out += data->compressed_buffer_size;
+ ret = __archive_write_filter(f->next_filter,
+ data->compressed,
+ data->compressed_buffer_size);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ data->stream.next_out = data->compressed;
+ data->stream.avail_out = data->compressed_buffer_size;
+ }
+
+ /* If there's nothing to do, we're done. */
+ if (!finishing && data->stream.avail_in == 0)
+ return (ARCHIVE_OK);
+
+ ret = lzma_code(&(data->stream),
+ finishing ? LZMA_FINISH : LZMA_RUN );
+
+ switch (ret) {
+ case LZMA_OK:
+ /* In non-finishing case, check if compressor
+ * consumed everything */
+ if (!finishing && data->stream.avail_in == 0)
+ return (ARCHIVE_OK);
+ /* In finishing case, this return always means
+ * there's more work */
+ break;
+ case LZMA_STREAM_END:
+ /* This return can only occur in finishing case. */
+ if (finishing)
+ return (ARCHIVE_OK);
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "lzma compression data error");
+ return (ARCHIVE_FATAL);
+ case LZMA_MEMLIMIT_ERROR:
+ archive_set_error(f->archive, ENOMEM,
+ "lzma compression error: "
+ "%ju MiB would have been needed",
+ (uintmax_t)((lzma_memusage(&(data->stream))
+ + 1024 * 1024 -1)
+ / (1024 * 1024)));
+ return (ARCHIVE_FATAL);
+ default:
+ /* Any other return value indicates an error. */
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "lzma compression failed:"
+ " lzma_code() call returned status %d",
+ ret);
+ return (ARCHIVE_FATAL);
+ }
+ }
+}
+
+#endif /* HAVE_LZMA_H */
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_zstd.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_zstd.c
new file mode 100644
index 00000000000..584cfb668f0
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_zstd.c
@@ -0,0 +1,467 @@
+/*-
+ * Copyright (c) 2017 Sean Purcell
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ZSTD_H
+#include <zstd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+/* Don't compile this if we don't have zstd.h */
+
+struct private_data {
+ int compression_level;
+ int threads;
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+ enum {
+ running,
+ finishing,
+ resetting,
+ } state;
+ int frame_per_file;
+ size_t min_frame_size;
+ size_t max_frame_size;
+ size_t cur_frame;
+ size_t cur_frame_in;
+ size_t cur_frame_out;
+ size_t total_in;
+ ZSTD_CStream *cstream;
+ ZSTD_outBuffer out;
+#else
+ struct archive_write_program_data *pdata;
+#endif
+};
+
+/* If we don't have the library use default range values (zstdcli.c v1.4.0) */
+#define CLEVEL_MIN -99
+#define CLEVEL_STD_MIN 0 /* prior to 1.3.4 and more recent without using --fast */
+#define CLEVEL_DEFAULT 3
+#define CLEVEL_STD_MAX 19 /* without using --ultra */
+#define CLEVEL_MAX 22
+
+#define MINVER_NEGCLEVEL 10304
+#define MINVER_MINCLEVEL 10306
+
+static int archive_compressor_zstd_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_compressor_zstd_open(struct archive_write_filter *);
+static int archive_compressor_zstd_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_compressor_zstd_flush(struct archive_write_filter *);
+static int archive_compressor_zstd_close(struct archive_write_filter *);
+static int archive_compressor_zstd_free(struct archive_write_filter *);
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+static int drive_compressor(struct archive_write_filter *,
+ struct private_data *, int, const void *, size_t);
+#endif
+
+
+/*
+ * Add a zstd compression filter to this write handle.
+ */
+int
+archive_write_add_filter_zstd(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct private_data *data;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_zstd");
+
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ f->data = data;
+ f->open = &archive_compressor_zstd_open;
+ f->options = &archive_compressor_zstd_options;
+ f->flush = &archive_compressor_zstd_flush;
+ f->close = &archive_compressor_zstd_close;
+ f->free = &archive_compressor_zstd_free;
+ f->code = ARCHIVE_FILTER_ZSTD;
+ f->name = "zstd";
+ data->compression_level = CLEVEL_DEFAULT;
+ data->threads = 0;
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+ data->frame_per_file = 0;
+ data->min_frame_size = 0;
+ data->max_frame_size = SIZE_MAX;
+ data->cur_frame_in = 0;
+ data->cur_frame_out = 0;
+ data->cstream = ZSTD_createCStream();
+ if (data->cstream == NULL) {
+ free(data);
+ archive_set_error(&a->archive, ENOMEM,
+ "Failed to allocate zstd compressor object");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (ARCHIVE_OK);
+#else
+ data->pdata = __archive_write_program_allocate("zstd");
+ if (data->pdata == NULL) {
+ free(data);
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Using external zstd program");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+static int
+archive_compressor_zstd_free(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+ ZSTD_freeCStream(data->cstream);
+ free(data->out.dst);
+#else
+ __archive_write_program_free(data->pdata);
+#endif
+ free(data);
+ f->data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int string_to_number(const char *string, intmax_t *numberp)
+{
+ char *end;
+
+ if (string == NULL || *string == '\0')
+ return (ARCHIVE_WARN);
+ *numberp = strtoimax(string, &end, 10);
+ if (end == string || *end != '\0' || errno == EOVERFLOW) {
+ *numberp = 0;
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
+ const char *value)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (strcmp(key, "compression-level") == 0) {
+ intmax_t level;
+ if (string_to_number(value, &level) != ARCHIVE_OK) {
+ return (ARCHIVE_WARN);
+ }
+ /* If we don't have the library, hard-code the max level */
+ int minimum = CLEVEL_MIN;
+ int maximum = CLEVEL_MAX;
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+ maximum = ZSTD_maxCLevel();
+#if ZSTD_VERSION_NUMBER >= MINVER_MINCLEVEL
+ if (ZSTD_versionNumber() >= MINVER_MINCLEVEL) {
+ minimum = ZSTD_minCLevel();
+ }
+ else
+#endif
+ if (ZSTD_versionNumber() < MINVER_NEGCLEVEL) {
+ minimum = CLEVEL_STD_MIN;
+ }
+#endif
+ if (level < minimum || level > maximum) {
+ return (ARCHIVE_WARN);
+ }
+ data->compression_level = (int)level;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "threads") == 0) {
+ intmax_t threads;
+ if (string_to_number(value, &threads) != ARCHIVE_OK) {
+ return (ARCHIVE_WARN);
+ }
+ if (threads < 0) {
+ return (ARCHIVE_WARN);
+ }
+ data->threads = (int)threads;
+ return (ARCHIVE_OK);
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+ } else if (strcmp(key, "frame-per-file") == 0) {
+ data->frame_per_file = 1;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "min-frame-size") == 0) {
+ intmax_t min_frame_size;
+ if (string_to_number(value, &min_frame_size) != ARCHIVE_OK) {
+ return (ARCHIVE_WARN);
+ }
+ if (min_frame_size < 0) {
+ return (ARCHIVE_WARN);
+ }
+ data->min_frame_size = min_frame_size;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "max-frame-size") == 0) {
+ intmax_t max_frame_size;
+ if (string_to_number(value, &max_frame_size) != ARCHIVE_OK) {
+ return (ARCHIVE_WARN);
+ }
+ if (max_frame_size < 1024) {
+ return (ARCHIVE_WARN);
+ }
+ data->max_frame_size = max_frame_size;
+ return (ARCHIVE_OK);
+#endif
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_zstd_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (data->out.dst == NULL) {
+ size_t bs = ZSTD_CStreamOutSize(), bpb;
+ if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+ /* Buffer size should be a multiple number of
+ * the of bytes per block for performance. */
+ bpb = archive_write_get_bytes_per_block(f->archive);
+ if (bpb > bs)
+ bs = bpb;
+ else if (bpb != 0)
+ bs -= bs % bpb;
+ }
+ data->out.size = bs;
+ data->out.pos = 0;
+ data->out.dst
+ = (unsigned char *)malloc(data->out.size);
+ if (data->out.dst == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ f->write = archive_compressor_zstd_write;
+
+ if (ZSTD_isError(ZSTD_initCStream(data->cstream,
+ data->compression_level))) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing zstd compressor object");
+ return (ARCHIVE_FATAL);
+ }
+
+ ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_nbWorkers, data->threads);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Write data to the compressed stream.
+ */
+static int
+archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return (drive_compressor(f, data, 0, buff, length));
+}
+
+/*
+ * Flush the compressed stream.
+ */
+static int
+archive_compressor_zstd_flush(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (data->frame_per_file && data->state == running &&
+ data->cur_frame_out > data->min_frame_size)
+ data->state = finishing;
+ return (drive_compressor(f, data, 1, NULL, 0));
+}
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_compressor_zstd_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (data->state == running)
+ data->state = finishing;
+ return (drive_compressor(f, data, 1, NULL, 0));
+}
+
+/*
+ * Utility function to push input data through compressor,
+ * writing full output blocks as necessary.
+ */
+static int
+drive_compressor(struct archive_write_filter *f,
+ struct private_data *data, int flush, const void *src, size_t length)
+{
+ ZSTD_inBuffer in = { .src = src, .size = length, .pos = 0 };
+ size_t ipos, opos, zstdret = 0;
+ int ret;
+
+ for (;;) {
+ ipos = in.pos;
+ opos = data->out.pos;
+ switch (data->state) {
+ case running:
+ if (in.pos == in.size)
+ return (ARCHIVE_OK);
+ zstdret = ZSTD_compressStream(data->cstream,
+ &data->out, &in);
+ if (ZSTD_isError(zstdret))
+ goto zstd_fatal;
+ break;
+ case finishing:
+ zstdret = ZSTD_endStream(data->cstream, &data->out);
+ if (ZSTD_isError(zstdret))
+ goto zstd_fatal;
+ if (zstdret == 0)
+ data->state = resetting;
+ break;
+ case resetting:
+ ZSTD_CCtx_reset(data->cstream, ZSTD_reset_session_only);
+ data->cur_frame++;
+ data->cur_frame_in = 0;
+ data->cur_frame_out = 0;
+ data->state = running;
+ break;
+ }
+ data->total_in += in.pos - ipos;
+ data->cur_frame_in += in.pos - ipos;
+ data->cur_frame_out += data->out.pos - opos;
+ if (data->state == running &&
+ data->cur_frame_in >= data->max_frame_size) {
+ data->state = finishing;
+ }
+ if (data->out.pos == data->out.size ||
+ (flush && data->out.pos > 0)) {
+ ret = __archive_write_filter(f->next_filter,
+ data->out.dst, data->out.pos);
+ if (ret != ARCHIVE_OK)
+ goto fatal;
+ data->out.pos = 0;
+ }
+ }
+zstd_fatal:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Zstd compression failed: %s",
+ ZSTD_getErrorName(zstdret));
+fatal:
+ return (ARCHIVE_FATAL);
+}
+
+#else /* HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR */
+
+static int
+archive_compressor_zstd_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ struct archive_string as;
+ int r;
+
+ archive_string_init(&as);
+ /* --no-check matches library default */
+ archive_strcpy(&as, "zstd --no-check");
+
+ if (data->compression_level < CLEVEL_STD_MIN) {
+ archive_string_sprintf(&as, " --fast=%d", -data->compression_level);
+ } else {
+ archive_string_sprintf(&as, " -%d", data->compression_level);
+ }
+
+ if (data->compression_level > CLEVEL_STD_MAX) {
+ archive_strcat(&as, " --ultra");
+ }
+
+ if (data->threads != 0) {
+ archive_string_sprintf(&as, " --threads=%d", data->threads);
+ }
+
+ f->write = archive_compressor_zstd_write;
+ r = __archive_write_program_open(f, data->pdata, as.s);
+ archive_string_free(&as);
+ return (r);
+}
+
+static int
+archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_compressor_zstd_flush(struct archive_write_filter *f)
+{
+ (void)f; /* UNUSED */
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_compressor_zstd_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_close(f, data->pdata);
+}
+
+#endif /* HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR */
diff --git a/contrib/libs/libarchive/libarchive/archive_write_disk_posix.c b/contrib/libs/libarchive/libarchive/archive_write_disk_posix.c
new file mode 100644
index 00000000000..33aa2f4d89d
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_disk_posix.c
@@ -0,0 +1,4759 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+#ifdef HAVE_SYS_EXTATTR_H
+#include <sys/extattr.h>
+#endif
+#if HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#elif HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#endif
+#ifdef HAVE_SYS_EA_H
+#error #include <sys/ea.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_UTIME_H
+#include <sys/utime.h>
+#endif
+#ifdef HAVE_COPYFILE_H
+#include <copyfile.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h> /* for Linux file flags */
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#error #include <linux/ext2_fs.h> /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#error #include <ext2fs/ext2_fs.h> /* Linux file flags, broken on Cygwin */
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+#ifdef F_GETTIMES /* Tru64 specific */
+#error #include <sys/fcntl1.h>
+#endif
+
+/*
+ * Macro to cast st_mtime and time_t to an int64 so that 2 numbers can reliably be compared.
+ *
+ * It assumes that the input is an integer type of no more than 64 bits.
+ * If the number is less than zero, t must be a signed type, so it fits in
+ * int64_t. Otherwise, it's a nonnegative value so we can cast it to uint64_t
+ * without loss. But it could be a large unsigned value, so we have to clip it
+ * to INT64_MAX.*
+ */
+#define to_int64_time(t) \
+ ((t) < 0 ? (int64_t)(t) : (uint64_t)(t) > (uint64_t)INT64_MAX ? INT64_MAX : (int64_t)(t))
+
+#if __APPLE__
+#include <TargetConditionals.h>
+#if TARGET_OS_MAC && !TARGET_OS_EMBEDDED && HAVE_QUARANTINE_H
+#error #include <quarantine.h>
+#define HAVE_QUARANTINE 1
+#endif
+#endif
+
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+/* TODO: Support Mac OS 'quarantine' feature. This is really just a
+ * standard tag to mark files that have been downloaded as "tainted".
+ * On Mac OS, we should mark the extracted files as tainted if the
+ * archive being read was tainted. Windows has a similar feature; we
+ * should investigate ways to support this generically. */
+
+#include "archive.h"
+#include "archive_acl_private.h"
+#include "archive_string.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_write_disk_private.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+/* Ignore non-int O_NOFOLLOW constant. */
+/* gnulib's fcntl.h does this on AIX, but it seems practical everywhere */
+#if defined O_NOFOLLOW && !(INT_MIN <= O_NOFOLLOW && O_NOFOLLOW <= INT_MAX)
+#undef O_NOFOLLOW
+#endif
+
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 0
+#endif
+
+#ifndef AT_FDCWD
+#define AT_FDCWD -100
+#endif
+
+struct fixup_entry {
+ struct fixup_entry *next;
+ struct archive_acl acl;
+ mode_t mode;
+ __LA_MODE_T filetype;
+ int64_t atime;
+ int64_t birthtime;
+ int64_t mtime;
+ int64_t ctime;
+ unsigned long atime_nanos;
+ unsigned long birthtime_nanos;
+ unsigned long mtime_nanos;
+ unsigned long ctime_nanos;
+ unsigned long fflags_set;
+ size_t mac_metadata_size;
+ void *mac_metadata;
+ int fixup; /* bitmask of what needs fixing */
+ char *name;
+};
+
+/*
+ * We use a bitmask to track which operations remain to be done for
+ * this file. In particular, this helps us avoid unnecessary
+ * operations when it's possible to take care of one step as a
+ * side-effect of another. For example, mkdir() can specify the mode
+ * for the newly-created object but symlink() cannot. This means we
+ * can skip chmod() if mkdir() succeeded, but we must explicitly
+ * chmod() if we're trying to create a directory that already exists
+ * (mkdir() failed) or if we're restoring a symlink. Similarly, we
+ * need to verify UID/GID before trying to restore SUID/SGID bits;
+ * that verification can occur explicitly through a stat() call or
+ * implicitly because of a successful chown() call.
+ */
+#define TODO_MODE_FORCE 0x40000000
+#define TODO_MODE_BASE 0x20000000
+#define TODO_SUID 0x10000000
+#define TODO_SUID_CHECK 0x08000000
+#define TODO_SGID 0x04000000
+#define TODO_SGID_CHECK 0x02000000
+#define TODO_APPLEDOUBLE 0x01000000
+#define TODO_MODE (TODO_MODE_BASE|TODO_SUID|TODO_SGID)
+#define TODO_TIMES ARCHIVE_EXTRACT_TIME
+#define TODO_OWNER ARCHIVE_EXTRACT_OWNER
+#define TODO_FFLAGS ARCHIVE_EXTRACT_FFLAGS
+#define TODO_ACLS ARCHIVE_EXTRACT_ACL
+#define TODO_XATTR ARCHIVE_EXTRACT_XATTR
+#define TODO_MAC_METADATA ARCHIVE_EXTRACT_MAC_METADATA
+#define TODO_HFS_COMPRESSION ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED
+
+struct archive_write_disk {
+ struct archive archive;
+
+ mode_t user_umask;
+ struct fixup_entry *fixup_list;
+ struct fixup_entry *current_fixup;
+ int64_t user_uid;
+ int skip_file_set;
+ int64_t skip_file_dev;
+ int64_t skip_file_ino;
+ time_t start_time;
+
+ int64_t (*lookup_gid)(void *private, const char *gname, int64_t gid);
+ void (*cleanup_gid)(void *private);
+ void *lookup_gid_data;
+ int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid);
+ void (*cleanup_uid)(void *private);
+ void *lookup_uid_data;
+
+ /*
+ * Full path of last file to satisfy symlink checks.
+ */
+ struct archive_string path_safe;
+
+ /*
+ * Cached stat data from disk for the current entry.
+ * If this is valid, pst points to st. Otherwise,
+ * pst is null.
+ */
+ struct stat st;
+ struct stat *pst;
+
+ /* Information about the object being restored right now. */
+ struct archive_entry *entry; /* Entry being extracted. */
+ char *name; /* Name of entry, possibly edited. */
+ struct archive_string _name_data; /* backing store for 'name' */
+ char *tmpname; /* Temporary name * */
+ struct archive_string _tmpname_data; /* backing store for 'tmpname' */
+ /* Tasks remaining for this object. */
+ int todo;
+ /* Tasks deferred until end-of-archive. */
+ int deferred;
+ /* Options requested by the client. */
+ int flags;
+ /* Handle for the file we're restoring. */
+ int fd;
+ /* Current offset for writing data to the file. */
+ int64_t offset;
+ /* Last offset actually written to disk. */
+ int64_t fd_offset;
+ /* Total bytes actually written to files. */
+ int64_t total_bytes_written;
+ /* Maximum size of file, -1 if unknown. */
+ int64_t filesize;
+ /* Dir we were in before this restore; only for deep paths. */
+ int restore_pwd;
+ /* Mode we should use for this entry; affected by _PERM and umask. */
+ mode_t mode;
+ /* UID/GID to use in restoring this entry. */
+ int64_t uid;
+ int64_t gid;
+ /*
+ * HFS+ Compression.
+ */
+ /* Xattr "com.apple.decmpfs". */
+ uint32_t decmpfs_attr_size;
+ unsigned char *decmpfs_header_p;
+ /* ResourceFork set options used for fsetxattr. */
+ int rsrc_xattr_options;
+ /* Xattr "com.apple.ResourceFork". */
+ unsigned char *resource_fork;
+ size_t resource_fork_allocated_size;
+ unsigned int decmpfs_block_count;
+ uint32_t *decmpfs_block_info;
+ /* Buffer for compressed data. */
+ unsigned char *compressed_buffer;
+ size_t compressed_buffer_size;
+ size_t compressed_buffer_remaining;
+ /* The offset of the ResourceFork where compressed data will
+ * be placed. */
+ uint32_t compressed_rsrc_position;
+ uint32_t compressed_rsrc_position_v;
+ /* Buffer for uncompressed data. */
+ char *uncompressed_buffer;
+ size_t block_remaining_bytes;
+ size_t file_remaining_bytes;
+#ifdef HAVE_ZLIB_H
+ z_stream stream;
+ int stream_valid;
+ int decmpfs_compression_level;
+#endif
+};
+
+/*
+ * Default mode for dirs created automatically (will be modified by umask).
+ * Note that POSIX specifies 0777 for implicitly-created dirs, "modified
+ * by the process' file creation mask."
+ */
+#define DEFAULT_DIR_MODE 0777
+/*
+ * Dir modes are restored in two steps: During the extraction, the permissions
+ * in the archive are modified to match the following limits. During
+ * the post-extract fixup pass, the permissions from the archive are
+ * applied.
+ */
+#define MINIMUM_DIR_MODE 0700
+#define MAXIMUM_DIR_MODE 0775
+
+/*
+ * Maximum uncompressed size of a decmpfs block.
+ */
+#define MAX_DECMPFS_BLOCK_SIZE (64 * 1024)
+/*
+ * HFS+ compression type.
+ */
+#define CMP_XATTR 3/* Compressed data in xattr. */
+#define CMP_RESOURCE_FORK 4/* Compressed data in resource fork. */
+/*
+ * HFS+ compression resource fork.
+ */
+#define RSRC_H_SIZE 260 /* Base size of Resource fork header. */
+#define RSRC_F_SIZE 50 /* Size of Resource fork footer. */
+/* Size to write compressed data to resource fork. */
+#define COMPRESSED_W_SIZE (64 * 1024)
+/* decmpfs definitions. */
+#define MAX_DECMPFS_XATTR_SIZE 3802
+#ifndef DECMPFS_XATTR_NAME
+#define DECMPFS_XATTR_NAME "com.apple.decmpfs"
+#endif
+#define DECMPFS_MAGIC 0x636d7066
+#define DECMPFS_COMPRESSION_MAGIC 0
+#define DECMPFS_COMPRESSION_TYPE 4
+#define DECMPFS_UNCOMPRESSED_SIZE 8
+#define DECMPFS_HEADER_SIZE 16
+
+#define HFS_BLOCKS(s) ((s) >> 12)
+
+
+static int la_opendirat(int, const char *);
+static int la_mktemp(struct archive_write_disk *);
+static int la_verify_filetype(mode_t, __LA_MODE_T);
+static void fsobj_error(int *, struct archive_string *, int, const char *,
+ const char *);
+static int check_symlinks_fsobj(char *, int *, struct archive_string *,
+ int, int);
+static int check_symlinks(struct archive_write_disk *);
+static int create_filesystem_object(struct archive_write_disk *);
+static struct fixup_entry *current_fixup(struct archive_write_disk *,
+ const char *pathname);
+#if defined(HAVE_FCHDIR) && defined(PATH_MAX)
+static void edit_deep_directories(struct archive_write_disk *ad);
+#endif
+static int cleanup_pathname_fsobj(char *, int *, struct archive_string *,
+ int);
+static int cleanup_pathname(struct archive_write_disk *);
+static int create_dir(struct archive_write_disk *, char *);
+static int create_parent_dir(struct archive_write_disk *, char *);
+static ssize_t hfs_write_data_block(struct archive_write_disk *,
+ const char *, size_t);
+static int fixup_appledouble(struct archive_write_disk *, const char *);
+static int older(struct stat *, struct archive_entry *);
+static int restore_entry(struct archive_write_disk *);
+static int set_mac_metadata(struct archive_write_disk *, const char *,
+ const void *, size_t);
+static int set_xattrs(struct archive_write_disk *);
+static int clear_nochange_fflags(struct archive_write_disk *);
+static int set_fflags(struct archive_write_disk *);
+static int set_fflags_platform(struct archive_write_disk *, int fd,
+ const char *name, mode_t mode,
+ unsigned long fflags_set, unsigned long fflags_clear);
+static int set_ownership(struct archive_write_disk *);
+static int set_mode(struct archive_write_disk *, int mode);
+static int set_time(int, int, const char *, time_t, long, time_t, long);
+static int set_times(struct archive_write_disk *, int, int, const char *,
+ time_t, long, time_t, long, time_t, long, time_t, long);
+static int set_times_from_entry(struct archive_write_disk *);
+static struct fixup_entry *sort_dir_list(struct fixup_entry *p);
+static ssize_t write_data_block(struct archive_write_disk *,
+ const char *, size_t);
+static void close_file_descriptor(struct archive_write_disk *);
+
+static int _archive_write_disk_close(struct archive *);
+static int _archive_write_disk_free(struct archive *);
+static int _archive_write_disk_header(struct archive *,
+ struct archive_entry *);
+static int64_t _archive_write_disk_filter_bytes(struct archive *, int);
+static int _archive_write_disk_finish_entry(struct archive *);
+static ssize_t _archive_write_disk_data(struct archive *, const void *,
+ size_t);
+static ssize_t _archive_write_disk_data_block(struct archive *, const void *,
+ size_t, int64_t);
+
+static int
+la_mktemp(struct archive_write_disk *a)
+{
+ int oerrno, fd;
+ mode_t mode;
+
+ archive_string_empty(&a->_tmpname_data);
+ archive_string_sprintf(&a->_tmpname_data, "%s.XXXXXX", a->name);
+ a->tmpname = a->_tmpname_data.s;
+
+ fd = __archive_mkstemp(a->tmpname);
+ if (fd == -1)
+ return -1;
+
+ mode = a->mode & 0777 & ~a->user_umask;
+ if (fchmod(fd, mode) == -1) {
+ oerrno = errno;
+ close(fd);
+ errno = oerrno;
+ return -1;
+ }
+ return fd;
+}
+
+static int
+la_opendirat(int fd, const char *path) {
+ const int flags = O_CLOEXEC
+#if defined(O_BINARY)
+ | O_BINARY
+#endif
+#if defined(O_DIRECTORY)
+ | O_DIRECTORY
+#endif
+#if defined(O_PATH)
+ | O_PATH
+#elif defined(O_SEARCH)
+ | O_SEARCH
+#elif defined(__FreeBSD__) && defined(O_EXEC)
+ | O_EXEC
+#else
+ | O_RDONLY
+#endif
+ ;
+
+#if !defined(HAVE_OPENAT)
+ if (fd != AT_FDCWD) {
+ errno = ENOTSUP;
+ return (-1);
+ } else
+ return (open(path, flags));
+#else
+ return (openat(fd, path, flags));
+#endif
+}
+
+static int
+la_verify_filetype(mode_t mode, __LA_MODE_T filetype) {
+ int ret = 0;
+
+ switch (filetype) {
+ case AE_IFREG:
+ ret = (S_ISREG(mode));
+ break;
+ case AE_IFDIR:
+ ret = (S_ISDIR(mode));
+ break;
+ case AE_IFLNK:
+ ret = (S_ISLNK(mode));
+ break;
+ case AE_IFSOCK:
+ ret = (S_ISSOCK(mode));
+ break;
+ case AE_IFCHR:
+ ret = (S_ISCHR(mode));
+ break;
+ case AE_IFBLK:
+ ret = (S_ISBLK(mode));
+ break;
+ case AE_IFIFO:
+ ret = (S_ISFIFO(mode));
+ break;
+ default:
+ break;
+ }
+
+ return (ret);
+}
+
+static int
+lazy_stat(struct archive_write_disk *a)
+{
+ if (a->pst != NULL) {
+ /* Already have stat() data available. */
+ return (ARCHIVE_OK);
+ }
+#ifdef HAVE_FSTAT
+ if (a->fd >= 0 && fstat(a->fd, &a->st) == 0) {
+ a->pst = &a->st;
+ return (ARCHIVE_OK);
+ }
+#endif
+ /*
+ * XXX At this point, symlinks should not be hit, otherwise
+ * XXX a race occurred. Do we want to check explicitly for that?
+ */
+#ifdef HAVE_LSTAT
+ if (lstat(a->name, &a->st) == 0)
+#else
+ if (la_stat(a->name, &a->st) == 0)
+#endif
+ {
+ a->pst = &a->st;
+ return (ARCHIVE_OK);
+ }
+ archive_set_error(&a->archive, errno, "Couldn't stat file");
+ return (ARCHIVE_WARN);
+}
+
+static const struct archive_vtable
+archive_write_disk_vtable = {
+ .archive_close = _archive_write_disk_close,
+ .archive_filter_bytes = _archive_write_disk_filter_bytes,
+ .archive_free = _archive_write_disk_free,
+ .archive_write_header = _archive_write_disk_header,
+ .archive_write_finish_entry = _archive_write_disk_finish_entry,
+ .archive_write_data = _archive_write_disk_data,
+ .archive_write_data_block = _archive_write_disk_data_block,
+};
+
+static int64_t
+_archive_write_disk_filter_bytes(struct archive *_a, int n)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ (void)n; /* UNUSED */
+ if (n == -1 || n == 0)
+ return (a->total_bytes_written);
+ return (-1);
+}
+
+
+int
+archive_write_disk_set_options(struct archive *_a, int flags)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+
+ a->flags = flags;
+ return (ARCHIVE_OK);
+}
+
+
+/*
+ * Extract this entry to disk.
+ *
+ * TODO: Validate hardlinks. According to the standards, we're
+ * supposed to check each extracted hardlink and squawk if it refers
+ * to a file that we didn't restore. I'm not entirely convinced this
+ * is a good idea, but more importantly: Is there any way to validate
+ * hardlinks without keeping a complete list of filenames from the
+ * entire archive?? Ugh.
+ *
+ */
+static int
+_archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ struct fixup_entry *fe;
+ const char *linkname;
+ int ret, r;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_disk_header");
+ archive_clear_error(&a->archive);
+ if (a->archive.state & ARCHIVE_STATE_DATA) {
+ r = _archive_write_disk_finish_entry(&a->archive);
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ }
+
+ /* Set up for this particular entry. */
+ a->pst = NULL;
+ a->current_fixup = NULL;
+ a->deferred = 0;
+ if (a->entry) {
+ archive_entry_free(a->entry);
+ a->entry = NULL;
+ }
+ a->entry = archive_entry_clone(entry);
+ a->fd = -1;
+ a->fd_offset = 0;
+ a->offset = 0;
+ a->restore_pwd = -1;
+ a->uid = a->user_uid;
+ a->mode = archive_entry_mode(a->entry);
+ if (archive_entry_size_is_set(a->entry))
+ a->filesize = archive_entry_size(a->entry);
+ else
+ a->filesize = -1;
+ archive_strcpy(&(a->_name_data), archive_entry_pathname(a->entry));
+ a->name = a->_name_data.s;
+ archive_clear_error(&a->archive);
+
+ /*
+ * Clean up the requested path. This is necessary for correct
+ * dir restores; the dir restore logic otherwise gets messed
+ * up by nonsense like "dir/.".
+ */
+ ret = cleanup_pathname(a);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+
+ /*
+ * Check if we have a hardlink that points to itself.
+ */
+ linkname = archive_entry_hardlink(a->entry);
+ if (linkname != NULL && strcmp(a->name, linkname) == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Skipping hardlink pointing to itself: %s",
+ a->name);
+ return (ARCHIVE_WARN);
+ }
+
+ /*
+ * Query the umask so we get predictable mode settings.
+ * This gets done on every call to _write_header in case the
+ * user edits their umask during the extraction for some
+ * reason.
+ */
+ umask(a->user_umask = umask(0));
+
+ /* Figure out what we need to do for this entry. */
+ a->todo = TODO_MODE_BASE;
+ if (a->flags & ARCHIVE_EXTRACT_PERM) {
+ a->todo |= TODO_MODE_FORCE; /* Be pushy about permissions. */
+ /*
+ * SGID requires an extra "check" step because we
+ * cannot easily predict the GID that the system will
+ * assign. (Different systems assign GIDs to files
+ * based on a variety of criteria, including process
+ * credentials and the gid of the enclosing
+ * directory.) We can only restore the SGID bit if
+ * the file has the right GID, and we only know the
+ * GID if we either set it (see set_ownership) or if
+ * we've actually called stat() on the file after it
+ * was restored. Since there are several places at
+ * which we might verify the GID, we need a TODO bit
+ * to keep track.
+ */
+ if (a->mode & S_ISGID)
+ a->todo |= TODO_SGID | TODO_SGID_CHECK;
+ /*
+ * Verifying the SUID is simpler, but can still be
+ * done in multiple ways, hence the separate "check" bit.
+ */
+ if (a->mode & S_ISUID)
+ a->todo |= TODO_SUID | TODO_SUID_CHECK;
+ } else {
+ /*
+ * User didn't request full permissions, so don't
+ * restore SUID, SGID bits and obey umask.
+ */
+ a->mode &= ~S_ISUID;
+ a->mode &= ~S_ISGID;
+ a->mode &= ~S_ISVTX;
+ a->mode &= ~a->user_umask;
+ }
+ if (a->flags & ARCHIVE_EXTRACT_OWNER)
+ a->todo |= TODO_OWNER;
+ if (a->flags & ARCHIVE_EXTRACT_TIME)
+ a->todo |= TODO_TIMES;
+ if (a->flags & ARCHIVE_EXTRACT_ACL) {
+#if ARCHIVE_ACL_DARWIN
+ /*
+ * On MacOS, platform ACLs get stored in mac_metadata, too.
+ * If we intend to extract mac_metadata and it is present
+ * we skip extracting libarchive NFSv4 ACLs.
+ */
+ size_t metadata_size;
+
+ if ((a->flags & ARCHIVE_EXTRACT_MAC_METADATA) == 0 ||
+ archive_entry_mac_metadata(a->entry,
+ &metadata_size) == NULL || metadata_size == 0)
+#endif
+#if ARCHIVE_ACL_LIBRICHACL
+ /*
+ * RichACLs are stored in an extended attribute.
+ * If we intend to extract extended attributes and have this
+ * attribute we skip extracting libarchive NFSv4 ACLs.
+ */
+ short extract_acls = 1;
+ if (a->flags & ARCHIVE_EXTRACT_XATTR && (
+ archive_entry_acl_types(a->entry) &
+ ARCHIVE_ENTRY_ACL_TYPE_NFS4)) {
+ const char *attr_name;
+ const void *attr_value;
+ size_t attr_size;
+ int i = archive_entry_xattr_reset(a->entry);
+ while (i--) {
+ archive_entry_xattr_next(a->entry, &attr_name,
+ &attr_value, &attr_size);
+ if (attr_name != NULL && attr_value != NULL &&
+ attr_size > 0 && strcmp(attr_name,
+ "trusted.richacl") == 0) {
+ extract_acls = 0;
+ break;
+ }
+ }
+ }
+ if (extract_acls)
+#endif
+#if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL
+ {
+#endif
+ if (archive_entry_filetype(a->entry) == AE_IFDIR)
+ a->deferred |= TODO_ACLS;
+ else
+ a->todo |= TODO_ACLS;
+#if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL
+ }
+#endif
+ }
+ if (a->flags & ARCHIVE_EXTRACT_MAC_METADATA) {
+ if (archive_entry_filetype(a->entry) == AE_IFDIR)
+ a->deferred |= TODO_MAC_METADATA;
+ else
+ a->todo |= TODO_MAC_METADATA;
+ }
+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H)
+ if ((a->flags & ARCHIVE_EXTRACT_NO_HFS_COMPRESSION) == 0) {
+ unsigned long set, clear;
+ archive_entry_fflags(a->entry, &set, &clear);
+ if ((set & ~clear) & UF_COMPRESSED) {
+ a->todo |= TODO_HFS_COMPRESSION;
+ a->decmpfs_block_count = (unsigned)-1;
+ }
+ }
+ if ((a->flags & ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED) != 0 &&
+ (a->mode & AE_IFMT) == AE_IFREG && a->filesize > 0) {
+ a->todo |= TODO_HFS_COMPRESSION;
+ a->decmpfs_block_count = (unsigned)-1;
+ }
+ {
+ const char *p;
+
+ /* Check if the current file name is a type of the
+ * resource fork file. */
+ p = strrchr(a->name, '/');
+ if (p == NULL)
+ p = a->name;
+ else
+ p++;
+ if (p[0] == '.' && p[1] == '_') {
+ /* Do not compress "._XXX" files. */
+ a->todo &= ~TODO_HFS_COMPRESSION;
+ if (a->filesize > 0)
+ a->todo |= TODO_APPLEDOUBLE;
+ }
+ }
+#endif
+
+ if (a->flags & ARCHIVE_EXTRACT_XATTR) {
+#if ARCHIVE_XATTR_DARWIN
+ /*
+ * On MacOS, extended attributes get stored in mac_metadata,
+ * too. If we intend to extract mac_metadata and it is present
+ * we skip extracting extended attributes.
+ */
+ size_t metadata_size;
+
+ if ((a->flags & ARCHIVE_EXTRACT_MAC_METADATA) == 0 ||
+ archive_entry_mac_metadata(a->entry,
+ &metadata_size) == NULL || metadata_size == 0)
+#endif
+ a->todo |= TODO_XATTR;
+ }
+ if (a->flags & ARCHIVE_EXTRACT_FFLAGS)
+ a->todo |= TODO_FFLAGS;
+ if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) {
+ ret = check_symlinks(a);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ }
+#if defined(HAVE_FCHDIR) && defined(PATH_MAX)
+ /* If path exceeds PATH_MAX, shorten the path. */
+ edit_deep_directories(a);
+#endif
+
+ ret = restore_entry(a);
+
+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H)
+ /*
+ * Check if the filesystem the file is restoring on supports
+ * HFS+ Compression. If not, cancel HFS+ Compression.
+ */
+ if (a->todo | TODO_HFS_COMPRESSION) {
+ /*
+ * NOTE: UF_COMPRESSED is ignored even if the filesystem
+ * supports HFS+ Compression because the file should
+ * have at least an extended attribute "com.apple.decmpfs"
+ * before the flag is set to indicate that the file have
+ * been compressed. If the filesystem does not support
+ * HFS+ Compression the system call will fail.
+ */
+ if (a->fd < 0 || fchflags(a->fd, UF_COMPRESSED) != 0)
+ a->todo &= ~TODO_HFS_COMPRESSION;
+ }
+#endif
+
+ /*
+ * TODO: There are rumours that some extended attributes must
+ * be restored before file data is written. If this is true,
+ * then we either need to write all extended attributes both
+ * before and after restoring the data, or find some rule for
+ * determining which must go first and which last. Due to the
+ * many ways people are using xattrs, this may prove to be an
+ * intractable problem.
+ */
+
+#ifdef HAVE_FCHDIR
+ /* If we changed directory above, restore it here. */
+ if (a->restore_pwd >= 0) {
+ r = fchdir(a->restore_pwd);
+ if (r != 0) {
+ archive_set_error(&a->archive, errno,
+ "chdir() failure");
+ ret = ARCHIVE_FATAL;
+ }
+ close(a->restore_pwd);
+ a->restore_pwd = -1;
+ }
+#endif
+
+ /*
+ * Fixup uses the unedited pathname from archive_entry_pathname(),
+ * because it is relative to the base dir and the edited path
+ * might be relative to some intermediate dir as a result of the
+ * deep restore logic.
+ */
+ if (a->deferred & TODO_MODE) {
+ fe = current_fixup(a, archive_entry_pathname(entry));
+ if (fe == NULL)
+ return (ARCHIVE_FATAL);
+ fe->filetype = archive_entry_filetype(entry);
+ fe->fixup |= TODO_MODE_BASE;
+ fe->mode = a->mode;
+ }
+
+ if ((a->deferred & TODO_TIMES)
+ && (archive_entry_mtime_is_set(entry)
+ || archive_entry_atime_is_set(entry))) {
+ fe = current_fixup(a, archive_entry_pathname(entry));
+ if (fe == NULL)
+ return (ARCHIVE_FATAL);
+ fe->filetype = archive_entry_filetype(entry);
+ fe->mode = a->mode;
+ fe->fixup |= TODO_TIMES;
+ if (archive_entry_atime_is_set(entry)) {
+ fe->atime = archive_entry_atime(entry);
+ fe->atime_nanos = archive_entry_atime_nsec(entry);
+ } else {
+ /* If atime is unset, use start time. */
+ fe->atime = a->start_time;
+ fe->atime_nanos = 0;
+ }
+ if (archive_entry_mtime_is_set(entry)) {
+ fe->mtime = archive_entry_mtime(entry);
+ fe->mtime_nanos = archive_entry_mtime_nsec(entry);
+ } else {
+ /* If mtime is unset, use start time. */
+ fe->mtime = a->start_time;
+ fe->mtime_nanos = 0;
+ }
+ if (archive_entry_birthtime_is_set(entry)) {
+ fe->birthtime = archive_entry_birthtime(entry);
+ fe->birthtime_nanos = archive_entry_birthtime_nsec(
+ entry);
+ } else {
+ /* If birthtime is unset, use mtime. */
+ fe->birthtime = fe->mtime;
+ fe->birthtime_nanos = fe->mtime_nanos;
+ }
+ }
+
+ if (a->deferred & TODO_ACLS) {
+ fe = current_fixup(a, archive_entry_pathname(entry));
+ if (fe == NULL)
+ return (ARCHIVE_FATAL);
+ fe->filetype = archive_entry_filetype(entry);
+ fe->fixup |= TODO_ACLS;
+ archive_acl_copy(&fe->acl, archive_entry_acl(entry));
+ }
+
+ if (a->deferred & TODO_MAC_METADATA) {
+ const void *metadata;
+ size_t metadata_size;
+ metadata = archive_entry_mac_metadata(a->entry, &metadata_size);
+ if (metadata != NULL && metadata_size > 0) {
+ fe = current_fixup(a, archive_entry_pathname(entry));
+ if (fe == NULL)
+ return (ARCHIVE_FATAL);
+ fe->filetype = archive_entry_filetype(entry);
+ fe->mac_metadata = malloc(metadata_size);
+ if (fe->mac_metadata != NULL) {
+ memcpy(fe->mac_metadata, metadata,
+ metadata_size);
+ fe->mac_metadata_size = metadata_size;
+ fe->fixup |= TODO_MAC_METADATA;
+ }
+ }
+ }
+
+ if (a->deferred & TODO_FFLAGS) {
+ fe = current_fixup(a, archive_entry_pathname(entry));
+ if (fe == NULL)
+ return (ARCHIVE_FATAL);
+ fe->filetype = archive_entry_filetype(entry);
+ fe->fixup |= TODO_FFLAGS;
+ /* TODO: Complete this.. defer fflags from below. */
+ }
+
+ /* We've created the object and are ready to pour data into it. */
+ if (ret >= ARCHIVE_WARN)
+ a->archive.state = ARCHIVE_STATE_DATA;
+ /*
+ * If it's not open, tell our client not to try writing.
+ * In particular, dirs, links, etc, don't get written to.
+ */
+ if (a->fd < 0) {
+ archive_entry_set_size(entry, 0);
+ a->filesize = 0;
+ }
+
+ return (ret);
+}
+
+int
+archive_write_disk_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_set_skip_file");
+ a->skip_file_set = 1;
+ a->skip_file_dev = d;
+ a->skip_file_ino = i;
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+write_data_block(struct archive_write_disk *a, const char *buff, size_t size)
+{
+ uint64_t start_size = size;
+ ssize_t bytes_written = 0;
+ ssize_t block_size = 0, bytes_to_write;
+
+ if (size == 0)
+ return (ARCHIVE_OK);
+
+ if (a->filesize == 0 || a->fd < 0) {
+ archive_set_error(&a->archive, 0,
+ "Attempt to write to an empty file");
+ return (ARCHIVE_WARN);
+ }
+
+ if (a->flags & ARCHIVE_EXTRACT_SPARSE) {
+#if HAVE_STRUCT_STAT_ST_BLKSIZE
+ int r;
+ if ((r = lazy_stat(a)) != ARCHIVE_OK)
+ return (r);
+ block_size = a->pst->st_blksize;
+#else
+ /* XXX TODO XXX Is there a more appropriate choice here ? */
+ /* This needn't match the filesystem allocation size. */
+ block_size = 16*1024;
+#endif
+ }
+
+ /* If this write would run beyond the file size, truncate it. */
+ if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize)
+ start_size = size = (size_t)(a->filesize - a->offset);
+
+ /* Write the data. */
+ while (size > 0) {
+ if (block_size == 0) {
+ bytes_to_write = size;
+ } else {
+ /* We're sparsifying the file. */
+ const char *p, *end;
+ int64_t block_end;
+
+ /* Skip leading zero bytes. */
+ for (p = buff, end = buff + size; p < end; ++p) {
+ if (*p != '\0')
+ break;
+ }
+ a->offset += p - buff;
+ size -= p - buff;
+ buff = p;
+ if (size == 0)
+ break;
+
+ /* Calculate next block boundary after offset. */
+ block_end
+ = (a->offset / block_size + 1) * block_size;
+
+ /* If the adjusted write would cross block boundary,
+ * truncate it to the block boundary. */
+ bytes_to_write = size;
+ if (a->offset + bytes_to_write > block_end)
+ bytes_to_write = block_end - a->offset;
+ }
+ /* Seek if necessary to the specified offset. */
+ if (a->offset != a->fd_offset) {
+ if (lseek(a->fd, a->offset, SEEK_SET) < 0) {
+ archive_set_error(&a->archive, errno,
+ "Seek failed");
+ return (ARCHIVE_FATAL);
+ }
+ a->fd_offset = a->offset;
+ }
+ bytes_written = write(a->fd, buff, bytes_to_write);
+ if (bytes_written < 0) {
+ archive_set_error(&a->archive, errno, "Write failed");
+ return (ARCHIVE_WARN);
+ }
+ buff += bytes_written;
+ size -= bytes_written;
+ a->total_bytes_written += bytes_written;
+ a->offset += bytes_written;
+ a->fd_offset = a->offset;
+ }
+ return (start_size - size);
+}
+
+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\
+ && defined(HAVE_ZLIB_H)
+
+/*
+ * Set UF_COMPRESSED file flag.
+ * This have to be called after hfs_write_decmpfs() because if the
+ * file does not have "com.apple.decmpfs" xattr the flag is ignored.
+ */
+static int
+hfs_set_compressed_fflag(struct archive_write_disk *a)
+{
+ int r;
+
+ if ((r = lazy_stat(a)) != ARCHIVE_OK)
+ return (r);
+
+ a->st.st_flags |= UF_COMPRESSED;
+ if (fchflags(a->fd, a->st.st_flags) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to set UF_COMPRESSED file flag");
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * HFS+ Compression decmpfs
+ *
+ * +------------------------------+ +0
+ * | Magic(LE 4 bytes) |
+ * +------------------------------+
+ * | Type(LE 4 bytes) |
+ * +------------------------------+
+ * | Uncompressed size(LE 8 bytes)|
+ * +------------------------------+ +16
+ * | |
+ * | Compressed data |
+ * | (Placed only if Type == 3) |
+ * | |
+ * +------------------------------+ +3802 = MAX_DECMPFS_XATTR_SIZE
+ *
+ * Type is 3: decmpfs has compressed data.
+ * Type is 4: Resource Fork has compressed data.
+ */
+/*
+ * Write "com.apple.decmpfs"
+ */
+static int
+hfs_write_decmpfs(struct archive_write_disk *a)
+{
+ int r;
+ uint32_t compression_type;
+
+ r = fsetxattr(a->fd, DECMPFS_XATTR_NAME, a->decmpfs_header_p,
+ a->decmpfs_attr_size, 0, 0);
+ if (r < 0) {
+ archive_set_error(&a->archive, errno,
+ "Cannot restore xattr:%s", DECMPFS_XATTR_NAME);
+ compression_type = archive_le32dec(
+ &a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE]);
+ if (compression_type == CMP_RESOURCE_FORK)
+ fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME,
+ XATTR_SHOWCOMPRESSION);
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * HFS+ Compression Resource Fork
+ *
+ * +-----------------------------+
+ * | Header(260 bytes) |
+ * +-----------------------------+
+ * | Block count(LE 4 bytes) |
+ * +-----------------------------+ --+
+ * +-- | Offset (LE 4 bytes) | |
+ * | | [distance from Block count] | | Block 0
+ * | +-----------------------------+ |
+ * | | Compressed size(LE 4 bytes) | |
+ * | +-----------------------------+ --+
+ * | | |
+ * | | .................. |
+ * | | |
+ * | +-----------------------------+ --+
+ * | | Offset (LE 4 bytes) | |
+ * | +-----------------------------+ | Block (Block count -1)
+ * | | Compressed size(LE 4 bytes) | |
+ * +-> +-----------------------------+ --+
+ * | Compressed data(n bytes) | Block 0
+ * +-----------------------------+
+ * | |
+ * | .................. |
+ * | |
+ * +-----------------------------+
+ * | Compressed data(n bytes) | Block (Block count -1)
+ * +-----------------------------+
+ * | Footer(50 bytes) |
+ * +-----------------------------+
+ *
+ */
+/*
+ * Write the header of "com.apple.ResourceFork"
+ */
+static int
+hfs_write_resource_fork(struct archive_write_disk *a, unsigned char *buff,
+ size_t bytes, uint32_t position)
+{
+ int ret;
+
+ ret = fsetxattr(a->fd, XATTR_RESOURCEFORK_NAME, buff, bytes,
+ position, a->rsrc_xattr_options);
+ if (ret < 0) {
+ archive_set_error(&a->archive, errno,
+ "Cannot restore xattr: %s at %u pos %u bytes",
+ XATTR_RESOURCEFORK_NAME,
+ (unsigned)position,
+ (unsigned)bytes);
+ return (ARCHIVE_WARN);
+ }
+ a->rsrc_xattr_options &= ~XATTR_CREATE;
+ return (ARCHIVE_OK);
+}
+
+static int
+hfs_write_compressed_data(struct archive_write_disk *a, size_t bytes_compressed)
+{
+ int ret;
+
+ ret = hfs_write_resource_fork(a, a->compressed_buffer,
+ bytes_compressed, a->compressed_rsrc_position);
+ if (ret == ARCHIVE_OK)
+ a->compressed_rsrc_position += bytes_compressed;
+ return (ret);
+}
+
+static int
+hfs_write_resource_fork_header(struct archive_write_disk *a)
+{
+ unsigned char *buff;
+ uint32_t rsrc_bytes;
+ uint32_t rsrc_header_bytes;
+
+ /*
+ * Write resource fork header + block info.
+ */
+ buff = a->resource_fork;
+ rsrc_bytes = a->compressed_rsrc_position - RSRC_F_SIZE;
+ rsrc_header_bytes =
+ RSRC_H_SIZE + /* Header base size. */
+ 4 + /* Block count. */
+ (a->decmpfs_block_count * 8);/* Block info */
+ archive_be32enc(buff, 0x100);
+ archive_be32enc(buff + 4, rsrc_bytes);
+ archive_be32enc(buff + 8, rsrc_bytes - 256);
+ archive_be32enc(buff + 12, 0x32);
+ memset(buff + 16, 0, 240);
+ archive_be32enc(buff + 256, rsrc_bytes - 260);
+ return hfs_write_resource_fork(a, buff, rsrc_header_bytes, 0);
+}
+
+static size_t
+hfs_set_resource_fork_footer(unsigned char *buff, size_t buff_size)
+{
+ static const char rsrc_footer[RSRC_F_SIZE] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 'c', 'm',
+ 'p', 'f', 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00
+ };
+ if (buff_size < sizeof(rsrc_footer))
+ return (0);
+ memcpy(buff, rsrc_footer, sizeof(rsrc_footer));
+ return (sizeof(rsrc_footer));
+}
+
+static int
+hfs_reset_compressor(struct archive_write_disk *a)
+{
+ int ret;
+
+ if (a->stream_valid)
+ ret = deflateReset(&a->stream);
+ else
+ ret = deflateInit(&a->stream, a->decmpfs_compression_level);
+
+ if (ret != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to initialize compressor");
+ return (ARCHIVE_FATAL);
+ } else
+ a->stream_valid = 1;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+hfs_decompress(struct archive_write_disk *a)
+{
+ uint32_t *block_info;
+ unsigned int block_count;
+ uint32_t data_pos, data_size;
+ ssize_t r;
+ ssize_t bytes_written, bytes_to_write;
+ unsigned char *b;
+
+ block_info = (uint32_t *)(a->resource_fork + RSRC_H_SIZE);
+ block_count = archive_le32dec(block_info++);
+ while (block_count--) {
+ data_pos = RSRC_H_SIZE + archive_le32dec(block_info++);
+ data_size = archive_le32dec(block_info++);
+ r = fgetxattr(a->fd, XATTR_RESOURCEFORK_NAME,
+ a->compressed_buffer, data_size, data_pos, 0);
+ if (r != data_size) {
+ archive_set_error(&a->archive,
+ (r < 0)?errno:ARCHIVE_ERRNO_MISC,
+ "Failed to read resource fork");
+ return (ARCHIVE_WARN);
+ }
+ if (a->compressed_buffer[0] == 0xff) {
+ bytes_to_write = data_size -1;
+ b = a->compressed_buffer + 1;
+ } else {
+ uLong dest_len = MAX_DECMPFS_BLOCK_SIZE;
+ int zr;
+
+ zr = uncompress((Bytef *)a->uncompressed_buffer,
+ &dest_len, a->compressed_buffer, data_size);
+ if (zr != Z_OK) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to decompress resource fork");
+ return (ARCHIVE_WARN);
+ }
+ bytes_to_write = dest_len;
+ b = (unsigned char *)a->uncompressed_buffer;
+ }
+ do {
+ bytes_written = write(a->fd, b, bytes_to_write);
+ if (bytes_written < 0) {
+ archive_set_error(&a->archive, errno,
+ "Write failed");
+ return (ARCHIVE_WARN);
+ }
+ bytes_to_write -= bytes_written;
+ b += bytes_written;
+ } while (bytes_to_write > 0);
+ }
+ r = fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME, 0);
+ if (r == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to remove resource fork");
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+hfs_drive_compressor(struct archive_write_disk *a, const char *buff,
+ size_t size)
+{
+ unsigned char *buffer_compressed;
+ size_t bytes_compressed;
+ size_t bytes_used;
+ int ret;
+
+ ret = hfs_reset_compressor(a);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+
+ if (a->compressed_buffer == NULL) {
+ size_t block_size;
+
+ block_size = COMPRESSED_W_SIZE + RSRC_F_SIZE +
+ + compressBound(MAX_DECMPFS_BLOCK_SIZE);
+ a->compressed_buffer = malloc(block_size);
+ if (a->compressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Resource Fork");
+ return (ARCHIVE_FATAL);
+ }
+ a->compressed_buffer_size = block_size;
+ a->compressed_buffer_remaining = block_size;
+ }
+
+ buffer_compressed = a->compressed_buffer +
+ a->compressed_buffer_size - a->compressed_buffer_remaining;
+ a->stream.next_in = (Bytef *)(uintptr_t)(const void *)buff;
+ a->stream.avail_in = size;
+ a->stream.next_out = buffer_compressed;
+ a->stream.avail_out = a->compressed_buffer_remaining;
+ do {
+ ret = deflate(&a->stream, Z_FINISH);
+ switch (ret) {
+ case Z_OK:
+ case Z_STREAM_END:
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to compress data");
+ return (ARCHIVE_FAILED);
+ }
+ } while (ret == Z_OK);
+ bytes_compressed = a->compressed_buffer_remaining - a->stream.avail_out;
+
+ /*
+ * If the compressed size is larger than the original size,
+ * throw away compressed data, use uncompressed data instead.
+ */
+ if (bytes_compressed > size) {
+ buffer_compressed[0] = 0xFF;/* uncompressed marker. */
+ memcpy(buffer_compressed + 1, buff, size);
+ bytes_compressed = size + 1;
+ }
+ a->compressed_buffer_remaining -= bytes_compressed;
+
+ /*
+ * If the compressed size is smaller than MAX_DECMPFS_XATTR_SIZE
+ * and the block count in the file is only one, store compressed
+ * data to decmpfs xattr instead of the resource fork.
+ */
+ if (a->decmpfs_block_count == 1 &&
+ (a->decmpfs_attr_size + bytes_compressed)
+ <= MAX_DECMPFS_XATTR_SIZE) {
+ archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE],
+ CMP_XATTR);
+ memcpy(a->decmpfs_header_p + DECMPFS_HEADER_SIZE,
+ buffer_compressed, bytes_compressed);
+ a->decmpfs_attr_size += bytes_compressed;
+ a->compressed_buffer_remaining = a->compressed_buffer_size;
+ /*
+ * Finish HFS+ Compression.
+ * - Write the decmpfs xattr.
+ * - Set the UF_COMPRESSED file flag.
+ */
+ ret = hfs_write_decmpfs(a);
+ if (ret == ARCHIVE_OK)
+ ret = hfs_set_compressed_fflag(a);
+ return (ret);
+ }
+
+ /* Update block info. */
+ archive_le32enc(a->decmpfs_block_info++,
+ a->compressed_rsrc_position_v - RSRC_H_SIZE);
+ archive_le32enc(a->decmpfs_block_info++, bytes_compressed);
+ a->compressed_rsrc_position_v += bytes_compressed;
+
+ /*
+ * Write the compressed data to the resource fork.
+ */
+ bytes_used = a->compressed_buffer_size - a->compressed_buffer_remaining;
+ while (bytes_used >= COMPRESSED_W_SIZE) {
+ ret = hfs_write_compressed_data(a, COMPRESSED_W_SIZE);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ bytes_used -= COMPRESSED_W_SIZE;
+ if (bytes_used > COMPRESSED_W_SIZE)
+ memmove(a->compressed_buffer,
+ a->compressed_buffer + COMPRESSED_W_SIZE,
+ bytes_used);
+ else
+ memcpy(a->compressed_buffer,
+ a->compressed_buffer + COMPRESSED_W_SIZE,
+ bytes_used);
+ }
+ a->compressed_buffer_remaining = a->compressed_buffer_size - bytes_used;
+
+ /*
+ * If the current block is the last block, write the remaining
+ * compressed data and the resource fork footer.
+ */
+ if (a->file_remaining_bytes == 0) {
+ size_t rsrc_size;
+ int64_t bk;
+
+ /* Append the resource footer. */
+ rsrc_size = hfs_set_resource_fork_footer(
+ a->compressed_buffer + bytes_used,
+ a->compressed_buffer_remaining);
+ ret = hfs_write_compressed_data(a, bytes_used + rsrc_size);
+ a->compressed_buffer_remaining = a->compressed_buffer_size;
+
+ /* If the compressed size is not enough smaller than
+ * the uncompressed size. cancel HFS+ compression.
+ * TODO: study a behavior of ditto utility and improve
+ * the condition to fall back into no HFS+ compression. */
+ bk = HFS_BLOCKS(a->compressed_rsrc_position);
+ bk += bk >> 7;
+ if (bk > HFS_BLOCKS(a->filesize))
+ return hfs_decompress(a);
+ /*
+ * Write the resourcefork header.
+ */
+ if (ret == ARCHIVE_OK)
+ ret = hfs_write_resource_fork_header(a);
+ /*
+ * Finish HFS+ Compression.
+ * - Write the decmpfs xattr.
+ * - Set the UF_COMPRESSED file flag.
+ */
+ if (ret == ARCHIVE_OK)
+ ret = hfs_write_decmpfs(a);
+ if (ret == ARCHIVE_OK)
+ ret = hfs_set_compressed_fflag(a);
+ }
+ return (ret);
+}
+
+static ssize_t
+hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff,
+ size_t size)
+{
+ const char *buffer_to_write;
+ size_t bytes_to_write;
+ int ret;
+
+ if (a->decmpfs_block_count == (unsigned)-1) {
+ void *new_block;
+ size_t new_size;
+ unsigned int block_count;
+
+ if (a->decmpfs_header_p == NULL) {
+ new_block = malloc(MAX_DECMPFS_XATTR_SIZE
+ + sizeof(uint32_t));
+ if (new_block == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for decmpfs");
+ return (ARCHIVE_FATAL);
+ }
+ a->decmpfs_header_p = new_block;
+ }
+ a->decmpfs_attr_size = DECMPFS_HEADER_SIZE;
+ archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_MAGIC],
+ DECMPFS_MAGIC);
+ archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE],
+ CMP_RESOURCE_FORK);
+ archive_le64enc(&a->decmpfs_header_p[DECMPFS_UNCOMPRESSED_SIZE],
+ a->filesize);
+
+ /* Calculate a block count of the file. */
+ block_count =
+ (a->filesize + MAX_DECMPFS_BLOCK_SIZE -1) /
+ MAX_DECMPFS_BLOCK_SIZE;
+ /*
+ * Allocate buffer for resource fork.
+ * Set up related pointers;
+ */
+ new_size =
+ RSRC_H_SIZE + /* header */
+ 4 + /* Block count */
+ (block_count * sizeof(uint32_t) * 2) +
+ RSRC_F_SIZE; /* footer */
+ if (new_size > a->resource_fork_allocated_size) {
+ new_block = realloc(a->resource_fork, new_size);
+ if (new_block == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for ResourceFork");
+ return (ARCHIVE_FATAL);
+ }
+ a->resource_fork_allocated_size = new_size;
+ a->resource_fork = new_block;
+ }
+
+ /* Allocate uncompressed buffer */
+ if (a->uncompressed_buffer == NULL) {
+ new_block = malloc(MAX_DECMPFS_BLOCK_SIZE);
+ if (new_block == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for decmpfs");
+ return (ARCHIVE_FATAL);
+ }
+ a->uncompressed_buffer = new_block;
+ }
+ a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE;
+ a->file_remaining_bytes = a->filesize;
+ a->compressed_buffer_remaining = a->compressed_buffer_size;
+
+ /*
+ * Set up a resource fork.
+ */
+ a->rsrc_xattr_options = XATTR_CREATE;
+ /* Get the position where we are going to set a bunch
+ * of block info. */
+ a->decmpfs_block_info =
+ (uint32_t *)(a->resource_fork + RSRC_H_SIZE);
+ /* Set the block count to the resource fork. */
+ archive_le32enc(a->decmpfs_block_info++, block_count);
+ /* Get the position where we are going to set compressed
+ * data. */
+ a->compressed_rsrc_position =
+ RSRC_H_SIZE + 4 + (block_count * 8);
+ a->compressed_rsrc_position_v = a->compressed_rsrc_position;
+ a->decmpfs_block_count = block_count;
+ }
+
+ /* Ignore redundant bytes. */
+ if (a->file_remaining_bytes == 0)
+ return ((ssize_t)size);
+
+ /* Do not overrun a block size. */
+ if (size > a->block_remaining_bytes)
+ bytes_to_write = a->block_remaining_bytes;
+ else
+ bytes_to_write = size;
+ /* Do not overrun the file size. */
+ if (bytes_to_write > a->file_remaining_bytes)
+ bytes_to_write = a->file_remaining_bytes;
+
+ /* For efficiency, if a copy length is full of the uncompressed
+ * buffer size, do not copy writing data to it. */
+ if (bytes_to_write == MAX_DECMPFS_BLOCK_SIZE)
+ buffer_to_write = buff;
+ else {
+ memcpy(a->uncompressed_buffer +
+ MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes,
+ buff, bytes_to_write);
+ buffer_to_write = a->uncompressed_buffer;
+ }
+ a->block_remaining_bytes -= bytes_to_write;
+ a->file_remaining_bytes -= bytes_to_write;
+
+ if (a->block_remaining_bytes == 0 || a->file_remaining_bytes == 0) {
+ ret = hfs_drive_compressor(a, buffer_to_write,
+ MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes);
+ if (ret < 0)
+ return (ret);
+ a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE;
+ }
+ /* Ignore redundant bytes. */
+ if (a->file_remaining_bytes == 0)
+ return ((ssize_t)size);
+ return (bytes_to_write);
+}
+
+static ssize_t
+hfs_write_data_block(struct archive_write_disk *a, const char *buff,
+ size_t size)
+{
+ uint64_t start_size = size;
+ ssize_t bytes_written = 0;
+ ssize_t bytes_to_write;
+
+ if (size == 0)
+ return (ARCHIVE_OK);
+
+ if (a->filesize == 0 || a->fd < 0) {
+ archive_set_error(&a->archive, 0,
+ "Attempt to write to an empty file");
+ return (ARCHIVE_WARN);
+ }
+
+ /* If this write would run beyond the file size, truncate it. */
+ if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize)
+ start_size = size = (size_t)(a->filesize - a->offset);
+
+ /* Write the data. */
+ while (size > 0) {
+ bytes_to_write = size;
+ /* Seek if necessary to the specified offset. */
+ if (a->offset < a->fd_offset) {
+ /* Can't support backward move. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Seek failed");
+ return (ARCHIVE_FATAL);
+ } else if (a->offset > a->fd_offset) {
+ uint64_t skip = a->offset - a->fd_offset;
+ char nullblock[1024];
+
+ memset(nullblock, 0, sizeof(nullblock));
+ while (skip > 0) {
+ if (skip > sizeof(nullblock))
+ bytes_written = hfs_write_decmpfs_block(
+ a, nullblock, sizeof(nullblock));
+ else
+ bytes_written = hfs_write_decmpfs_block(
+ a, nullblock, skip);
+ if (bytes_written < 0) {
+ archive_set_error(&a->archive, errno,
+ "Write failed");
+ return (ARCHIVE_WARN);
+ }
+ skip -= bytes_written;
+ }
+
+ a->fd_offset = a->offset;
+ }
+ bytes_written =
+ hfs_write_decmpfs_block(a, buff, bytes_to_write);
+ if (bytes_written < 0)
+ return (bytes_written);
+ buff += bytes_written;
+ size -= bytes_written;
+ a->total_bytes_written += bytes_written;
+ a->offset += bytes_written;
+ a->fd_offset = a->offset;
+ }
+ return (start_size - size);
+}
+#else
+static ssize_t
+hfs_write_data_block(struct archive_write_disk *a, const char *buff,
+ size_t size)
+{
+ return (write_data_block(a, buff, size));
+}
+#endif
+
+static ssize_t
+_archive_write_disk_data_block(struct archive *_a,
+ const void *buff, size_t size, int64_t offset)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ ssize_t r;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_DATA, "archive_write_data_block");
+
+ a->offset = offset;
+ if (a->todo & TODO_HFS_COMPRESSION)
+ r = hfs_write_data_block(a, buff, size);
+ else
+ r = write_data_block(a, buff, size);
+ if (r < ARCHIVE_OK)
+ return (r);
+ if ((size_t)r < size) {
+ archive_set_error(&a->archive, 0,
+ "Too much data: Truncating file at %ju bytes",
+ (uintmax_t)a->filesize);
+ return (ARCHIVE_WARN);
+ }
+#if ARCHIVE_VERSION_NUMBER < 3999000
+ return (ARCHIVE_OK);
+#else
+ return (size);
+#endif
+}
+
+static ssize_t
+_archive_write_disk_data(struct archive *_a, const void *buff, size_t size)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_DATA, "archive_write_data");
+
+ if (a->todo & TODO_HFS_COMPRESSION)
+ return (hfs_write_data_block(a, buff, size));
+ return (write_data_block(a, buff, size));
+}
+
+static int
+_archive_write_disk_finish_entry(struct archive *_a)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ int ret = ARCHIVE_OK;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_finish_entry");
+ if (a->archive.state & ARCHIVE_STATE_HEADER)
+ return (ARCHIVE_OK);
+ archive_clear_error(&a->archive);
+
+ /* Pad or truncate file to the right size. */
+ if (a->fd < 0) {
+ /* There's no file. */
+ } else if (a->filesize < 0) {
+ /* File size is unknown, so we can't set the size. */
+ } else if (a->fd_offset == a->filesize) {
+ /* Last write ended at exactly the filesize; we're done. */
+ /* Hopefully, this is the common case. */
+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H)
+ } else if (a->todo & TODO_HFS_COMPRESSION) {
+ char null_d[1024];
+ ssize_t r;
+
+ if (a->file_remaining_bytes)
+ memset(null_d, 0, sizeof(null_d));
+ while (a->file_remaining_bytes) {
+ if (a->file_remaining_bytes > sizeof(null_d))
+ r = hfs_write_data_block(
+ a, null_d, sizeof(null_d));
+ else
+ r = hfs_write_data_block(
+ a, null_d, a->file_remaining_bytes);
+ if (r < 0) {
+ close_file_descriptor(a);
+ return ((int)r);
+ }
+ }
+#endif
+ } else {
+#if HAVE_FTRUNCATE
+ if (ftruncate(a->fd, a->filesize) == -1 &&
+ a->filesize == 0) {
+ archive_set_error(&a->archive, errno,
+ "File size could not be restored");
+ close_file_descriptor(a);
+ return (ARCHIVE_FAILED);
+ }
+#endif
+ /*
+ * Not all platforms implement the XSI option to
+ * extend files via ftruncate. Stat() the file again
+ * to see what happened.
+ */
+ a->pst = NULL;
+ if ((ret = lazy_stat(a)) != ARCHIVE_OK) {
+ close_file_descriptor(a);
+ return (ret);
+ }
+ /* We can use lseek()/write() to extend the file if
+ * ftruncate didn't work or isn't available. */
+ if (a->st.st_size < a->filesize) {
+ const char nul = '\0';
+ if (lseek(a->fd, a->filesize - 1, SEEK_SET) < 0) {
+ archive_set_error(&a->archive, errno,
+ "Seek failed");
+ close_file_descriptor(a);
+ return (ARCHIVE_FATAL);
+ }
+ if (write(a->fd, &nul, 1) < 0) {
+ archive_set_error(&a->archive, errno,
+ "Write to restore size failed");
+ close_file_descriptor(a);
+ return (ARCHIVE_FATAL);
+ }
+ a->pst = NULL;
+ }
+ }
+
+ /* Restore metadata. */
+
+ /*
+ * This is specific to Mac OS X.
+ * If the current file is an AppleDouble file, it should be
+ * linked with the data fork file and remove it.
+ */
+ if (a->todo & TODO_APPLEDOUBLE) {
+ int r2 = fixup_appledouble(a, a->name);
+ if (r2 == ARCHIVE_EOF) {
+ /* The current file has been successfully linked
+ * with the data fork file and removed. So there
+ * is nothing to do on the current file. */
+ goto finish_metadata;
+ }
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * Look up the "real" UID only if we're going to need it.
+ * TODO: the TODO_SGID condition can be dropped here, can't it?
+ */
+ if (a->todo & (TODO_OWNER | TODO_SUID | TODO_SGID)) {
+ a->uid = archive_write_disk_uid(&a->archive,
+ archive_entry_uname(a->entry),
+ archive_entry_uid(a->entry));
+ }
+ /* Look up the "real" GID only if we're going to need it. */
+ /* TODO: the TODO_SUID condition can be dropped here, can't it? */
+ if (a->todo & (TODO_OWNER | TODO_SGID | TODO_SUID)) {
+ a->gid = archive_write_disk_gid(&a->archive,
+ archive_entry_gname(a->entry),
+ archive_entry_gid(a->entry));
+ }
+
+ /*
+ * Restore ownership before set_mode tries to restore suid/sgid
+ * bits. If we set the owner, we know what it is and can skip
+ * a stat() call to examine the ownership of the file on disk.
+ */
+ if (a->todo & TODO_OWNER) {
+ int r2 = set_ownership(a);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * HYPOTHESIS:
+ * If we're not root, we won't be setting any security
+ * attributes that may be wiped by the set_mode() routine
+ * below. We also can't set xattr on non-owner-writable files,
+ * which may be the state after set_mode(). Perform
+ * set_xattrs() first based on these constraints.
+ */
+ if (a->user_uid != 0 &&
+ (a->todo & TODO_XATTR)) {
+ int r2 = set_xattrs(a);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * set_mode must precede ACLs on systems such as Solaris and
+ * FreeBSD where setting the mode implicitly clears extended ACLs
+ */
+ if (a->todo & TODO_MODE) {
+ int r2 = set_mode(a, a->mode);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * Security-related extended attributes (such as
+ * security.capability on Linux) have to be restored last,
+ * since they're implicitly removed by other file changes.
+ * We do this last only when root.
+ */
+ if (a->user_uid == 0 &&
+ (a->todo & TODO_XATTR)) {
+ int r2 = set_xattrs(a);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * Some flags prevent file modification; they must be restored after
+ * file contents are written.
+ */
+ if (a->todo & TODO_FFLAGS) {
+ int r2 = set_fflags(a);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * Time must follow most other metadata;
+ * otherwise atime will get changed.
+ */
+ if (a->todo & TODO_TIMES) {
+ int r2 = set_times_from_entry(a);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * Mac extended metadata includes ACLs.
+ */
+ if (a->todo & TODO_MAC_METADATA) {
+ const void *metadata;
+ size_t metadata_size;
+ metadata = archive_entry_mac_metadata(a->entry, &metadata_size);
+ if (metadata != NULL && metadata_size > 0) {
+ int r2 = set_mac_metadata(a, archive_entry_pathname(
+ a->entry), metadata, metadata_size);
+ if (r2 < ret) ret = r2;
+ }
+ }
+
+ /*
+ * ACLs must be restored after timestamps because there are
+ * ACLs that prevent attribute changes (including time).
+ */
+ if (a->todo & TODO_ACLS) {
+ int r2;
+ r2 = archive_write_disk_set_acls(&a->archive, a->fd,
+ archive_entry_pathname(a->entry),
+ archive_entry_acl(a->entry),
+ archive_entry_mode(a->entry));
+ if (r2 < ret) ret = r2;
+ }
+
+finish_metadata:
+ /* If there's an fd, we can close it now. */
+ if (a->fd >= 0) {
+ close(a->fd);
+ a->fd = -1;
+ if (a->tmpname) {
+ if (rename(a->tmpname, a->name) == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to rename temporary file");
+ ret = ARCHIVE_FAILED;
+ unlink(a->tmpname);
+ }
+ a->tmpname = NULL;
+ }
+ }
+ /* If there's an entry, we can release it now. */
+ archive_entry_free(a->entry);
+ a->entry = NULL;
+ a->archive.state = ARCHIVE_STATE_HEADER;
+ return (ret);
+}
+
+int
+archive_write_disk_set_group_lookup(struct archive *_a,
+ void *private_data,
+ la_int64_t (*lookup_gid)(void *private, const char *gname, la_int64_t gid),
+ void (*cleanup_gid)(void *private))
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_set_group_lookup");
+
+ if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL)
+ (a->cleanup_gid)(a->lookup_gid_data);
+
+ a->lookup_gid = lookup_gid;
+ a->cleanup_gid = cleanup_gid;
+ a->lookup_gid_data = private_data;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_write_disk_set_user_lookup(struct archive *_a,
+ void *private_data,
+ int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid),
+ void (*cleanup_uid)(void *private))
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_set_user_lookup");
+
+ if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL)
+ (a->cleanup_uid)(a->lookup_uid_data);
+
+ a->lookup_uid = lookup_uid;
+ a->cleanup_uid = cleanup_uid;
+ a->lookup_uid_data = private_data;
+ return (ARCHIVE_OK);
+}
+
+int64_t
+archive_write_disk_gid(struct archive *_a, const char *name, la_int64_t id)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_gid");
+ if (a->lookup_gid)
+ return (a->lookup_gid)(a->lookup_gid_data, name, id);
+ return (id);
+}
+
+int64_t
+archive_write_disk_uid(struct archive *_a, const char *name, la_int64_t id)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_uid");
+ if (a->lookup_uid)
+ return (a->lookup_uid)(a->lookup_uid_data, name, id);
+ return (id);
+}
+
+/*
+ * Create a new archive_write_disk object and initialize it with global state.
+ */
+struct archive *
+archive_write_disk_new(void)
+{
+ struct archive_write_disk *a;
+
+ a = (struct archive_write_disk *)calloc(1, sizeof(*a));
+ if (a == NULL)
+ return (NULL);
+ a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC;
+ /* We're ready to write a header immediately. */
+ a->archive.state = ARCHIVE_STATE_HEADER;
+ a->archive.vtable = &archive_write_disk_vtable;
+ a->start_time = time(NULL);
+ /* Query and restore the umask. */
+ umask(a->user_umask = umask(0));
+#ifdef HAVE_GETEUID
+ a->user_uid = geteuid();
+#endif /* HAVE_GETEUID */
+ if (archive_string_ensure(&a->path_safe, 512) == NULL) {
+ free(a);
+ return (NULL);
+ }
+ a->path_safe.s[0] = 0;
+
+#ifdef HAVE_ZLIB_H
+ a->decmpfs_compression_level = 5;
+#endif
+ return (&a->archive);
+}
+
+
+/*
+ * If pathname is longer than PATH_MAX, chdir to a suitable
+ * intermediate dir and edit the path down to a shorter suffix. Note
+ * that this routine never returns an error; if the chdir() attempt
+ * fails for any reason, we just go ahead with the long pathname. The
+ * object creation is likely to fail, but any error will get handled
+ * at that time.
+ */
+#if defined(HAVE_FCHDIR) && defined(PATH_MAX)
+static void
+edit_deep_directories(struct archive_write_disk *a)
+{
+ int ret;
+ char *tail = a->name;
+
+ /* If path is short, avoid the open() below. */
+ if (strlen(tail) < PATH_MAX)
+ return;
+
+ /* Try to record our starting dir. */
+ a->restore_pwd = la_opendirat(AT_FDCWD, ".");
+ __archive_ensure_cloexec_flag(a->restore_pwd);
+ if (a->restore_pwd < 0)
+ return;
+
+ /* As long as the path is too long... */
+ while (strlen(tail) >= PATH_MAX) {
+ /* Locate a dir prefix shorter than PATH_MAX. */
+ tail += PATH_MAX - 8;
+ while (tail > a->name && *tail != '/')
+ tail--;
+ /* Exit if we find a too-long path component. */
+ if (tail <= a->name)
+ return;
+ /* Create the intermediate dir and chdir to it. */
+ *tail = '\0'; /* Terminate dir portion */
+ ret = create_dir(a, a->name);
+ if (ret == ARCHIVE_OK && chdir(a->name) != 0)
+ ret = ARCHIVE_FAILED;
+ *tail = '/'; /* Restore the / we removed. */
+ if (ret != ARCHIVE_OK)
+ return;
+ tail++;
+ /* The chdir() succeeded; we've now shortened the path. */
+ a->name = tail;
+ }
+ return;
+}
+#endif
+
+/*
+ * The main restore function.
+ */
+static int
+restore_entry(struct archive_write_disk *a)
+{
+ int ret = ARCHIVE_OK, en;
+
+ if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) {
+ /*
+ * TODO: Fix this. Apparently, there are platforms
+ * that still allow root to hose the entire filesystem
+ * by unlinking a dir. The S_ISDIR() test above
+ * prevents us from using unlink() here if the new
+ * object is a dir, but that doesn't mean the old
+ * object isn't a dir.
+ */
+ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+ (void)clear_nochange_fflags(a);
+ if (unlink(a->name) == 0) {
+ /* We removed it, reset cached stat. */
+ a->pst = NULL;
+ } else if (errno == ENOENT) {
+ /* File didn't exist, that's just as good. */
+ } else if (rmdir(a->name) == 0) {
+ /* It was a dir, but now it's gone. */
+ a->pst = NULL;
+ } else {
+ /* We tried, but couldn't get rid of it. */
+ archive_set_error(&a->archive, errno,
+ "Could not unlink");
+ return(ARCHIVE_FAILED);
+ }
+ }
+
+ /* Try creating it first; if this fails, we'll try to recover. */
+ en = create_filesystem_object(a);
+
+ if ((en == ENOTDIR || en == ENOENT)
+ && !(a->flags & ARCHIVE_EXTRACT_NO_AUTODIR)) {
+ /* If the parent dir doesn't exist, try creating it. */
+ create_parent_dir(a, a->name);
+ /* Now try to create the object again. */
+ en = create_filesystem_object(a);
+ }
+
+ if ((en == ENOENT) && (archive_entry_hardlink(a->entry) != NULL)) {
+ archive_set_error(&a->archive, en,
+ "Hard-link target '%s' does not exist.",
+ archive_entry_hardlink(a->entry));
+ return (ARCHIVE_FAILED);
+ }
+
+ if ((en == EISDIR || en == EEXIST)
+ && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+ /* If we're not overwriting, we're done. */
+ if (S_ISDIR(a->mode)) {
+ /* Don't overwrite any settings on existing directories. */
+ a->todo = 0;
+ }
+ archive_entry_unset_size(a->entry);
+ return (ARCHIVE_OK);
+ }
+
+ /*
+ * Some platforms return EISDIR if you call
+ * open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some
+ * return EEXIST. POSIX is ambiguous, requiring EISDIR
+ * for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT)
+ * on an existing item.
+ */
+ if (en == EISDIR) {
+ /* A dir is in the way of a non-dir, rmdir it. */
+ if (rmdir(a->name) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't remove already-existing dir");
+ return (ARCHIVE_FAILED);
+ }
+ a->pst = NULL;
+ /* Try again. */
+ en = create_filesystem_object(a);
+ } else if (en == EEXIST) {
+ /*
+ * We know something is in the way, but we don't know what;
+ * we need to find out before we go any further.
+ */
+ int r = 0;
+ /*
+ * The SECURE_SYMLINKS logic has already removed a
+ * symlink to a dir if the client wants that. So
+ * follow the symlink if we're creating a dir.
+ */
+ if (S_ISDIR(a->mode))
+ r = la_stat(a->name, &a->st);
+ /*
+ * If it's not a dir (or it's a broken symlink),
+ * then don't follow it.
+ */
+ if (r != 0 || !S_ISDIR(a->mode))
+#ifdef HAVE_LSTAT
+ r = lstat(a->name, &a->st);
+#else
+ r = la_stat(a->name, &a->st);
+#endif
+ if (r != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't stat existing object");
+ return (ARCHIVE_FAILED);
+ }
+
+ /*
+ * NO_OVERWRITE_NEWER doesn't apply to directories.
+ */
+ if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER)
+ && !S_ISDIR(a->st.st_mode)) {
+ if (!older(&(a->st), a->entry)) {
+ archive_entry_unset_size(a->entry);
+ return (ARCHIVE_OK);
+ }
+ }
+
+ /* If it's our archive, we're done. */
+ if (a->skip_file_set &&
+ a->st.st_dev == (dev_t)a->skip_file_dev &&
+ a->st.st_ino == (ino_t)a->skip_file_ino) {
+ archive_set_error(&a->archive, 0,
+ "Refusing to overwrite archive");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (!S_ISDIR(a->st.st_mode)) {
+ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+ (void)clear_nochange_fflags(a);
+
+ if ((a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) &&
+ S_ISREG(a->st.st_mode)) {
+ /* Use a temporary file to extract */
+ if ((a->fd = la_mktemp(a)) == -1) {
+ archive_set_error(&a->archive, errno,
+ "Can't create temporary file");
+ return ARCHIVE_FAILED;
+ }
+ a->pst = NULL;
+ en = 0;
+ } else {
+ /* A non-dir is in the way, unlink it. */
+ if (unlink(a->name) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't unlink already-existing "
+ "object");
+ return (ARCHIVE_FAILED);
+ }
+ a->pst = NULL;
+ /* Try again. */
+ en = create_filesystem_object(a);
+ }
+ } else if (!S_ISDIR(a->mode)) {
+ /* A dir is in the way of a non-dir, rmdir it. */
+ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+ (void)clear_nochange_fflags(a);
+ if (rmdir(a->name) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't replace existing directory with non-directory");
+ return (ARCHIVE_FAILED);
+ }
+ /* Try again. */
+ en = create_filesystem_object(a);
+ } else {
+ /*
+ * There's a dir in the way of a dir. Don't
+ * waste time with rmdir()/mkdir(), just fix
+ * up the permissions on the existing dir.
+ * Note that we don't change perms on existing
+ * dirs unless _EXTRACT_PERM is specified.
+ */
+ if ((a->mode != a->st.st_mode)
+ && (a->todo & TODO_MODE_FORCE))
+ a->deferred |= (a->todo & TODO_MODE);
+ /* Ownership doesn't need deferred fixup. */
+ en = 0; /* Forget the EEXIST. */
+ }
+ }
+
+ if (en) {
+ /* Everything failed; give up here. */
+ if ((&a->archive)->error == NULL)
+ archive_set_error(&a->archive, en, "Can't create '%s'",
+ a->name);
+ return (ARCHIVE_FAILED);
+ }
+
+ a->pst = NULL; /* Cached stat data no longer valid. */
+ return (ret);
+}
+
+/*
+ * Returns 0 if creation succeeds, or else returns errno value from
+ * the failed system call. Note: This function should only ever perform
+ * a single system call.
+ */
+static int
+create_filesystem_object(struct archive_write_disk *a)
+{
+ /* Create the entry. */
+ const char *linkname;
+ mode_t final_mode, mode;
+ int r;
+ /* these for check_symlinks_fsobj */
+ char *linkname_copy; /* non-const copy of linkname */
+ struct stat st;
+ struct archive_string error_string;
+ int error_number;
+
+ /* We identify hard/symlinks according to the link names. */
+ /* Since link(2) and symlink(2) don't handle modes, we're done here. */
+ linkname = archive_entry_hardlink(a->entry);
+ if (linkname != NULL) {
+#if !HAVE_LINK
+ return (EPERM);
+#else
+ archive_string_init(&error_string);
+ linkname_copy = strdup(linkname);
+ if (linkname_copy == NULL) {
+ return (EPERM);
+ }
+ /*
+ * TODO: consider using the cleaned-up path as the link
+ * target?
+ */
+ r = cleanup_pathname_fsobj(linkname_copy, &error_number,
+ &error_string, a->flags);
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&a->archive, error_number, "%s",
+ error_string.s);
+ free(linkname_copy);
+ archive_string_free(&error_string);
+ /*
+ * EPERM is more appropriate than error_number for our
+ * callers
+ */
+ return (EPERM);
+ }
+ r = check_symlinks_fsobj(linkname_copy, &error_number,
+ &error_string, a->flags, 1);
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&a->archive, error_number, "%s",
+ error_string.s);
+ free(linkname_copy);
+ archive_string_free(&error_string);
+ /*
+ * EPERM is more appropriate than error_number for our
+ * callers
+ */
+ return (EPERM);
+ }
+ free(linkname_copy);
+ archive_string_free(&error_string);
+ /*
+ * Unlinking and linking here is really not atomic,
+ * but doing it right, would require us to construct
+ * an mktemplink() function, and then use rename(2).
+ */
+ if (a->flags & ARCHIVE_EXTRACT_SAFE_WRITES)
+ unlink(a->name);
+#ifdef HAVE_LINKAT
+ r = linkat(AT_FDCWD, linkname, AT_FDCWD, a->name,
+ 0) ? errno : 0;
+#else
+ r = link(linkname, a->name) ? errno : 0;
+#endif
+ /*
+ * New cpio and pax formats allow hardlink entries
+ * to carry data, so we may have to open the file
+ * for hardlink entries.
+ *
+ * If the hardlink was successfully created and
+ * the archive doesn't have carry data for it,
+ * consider it to be non-authoritative for meta data.
+ * This is consistent with GNU tar and BSD pax.
+ * If the hardlink does carry data, let the last
+ * archive entry decide ownership.
+ */
+ if (r == 0 && a->filesize <= 0) {
+ a->todo = 0;
+ a->deferred = 0;
+ } else if (r == 0 && a->filesize > 0) {
+#ifdef HAVE_LSTAT
+ r = lstat(a->name, &st);
+#else
+ r = la_stat(a->name, &st);
+#endif
+ if (r != 0)
+ r = errno;
+ else if ((st.st_mode & AE_IFMT) == AE_IFREG) {
+ a->fd = open(a->name, O_WRONLY | O_TRUNC |
+ O_BINARY | O_CLOEXEC | O_NOFOLLOW);
+ __archive_ensure_cloexec_flag(a->fd);
+ if (a->fd < 0)
+ r = errno;
+ }
+ }
+ return (r);
+#endif
+ }
+ linkname = archive_entry_symlink(a->entry);
+ if (linkname != NULL) {
+#if HAVE_SYMLINK
+ /*
+ * Unlinking and linking here is really not atomic,
+ * but doing it right, would require us to construct
+ * an mktempsymlink() function, and then use rename(2).
+ */
+ if (a->flags & ARCHIVE_EXTRACT_SAFE_WRITES)
+ unlink(a->name);
+ return symlink(linkname, a->name) ? errno : 0;
+#else
+ return (EPERM);
+#endif
+ }
+
+ /*
+ * The remaining system calls all set permissions, so let's
+ * try to take advantage of that to avoid an extra chmod()
+ * call. (Recall that umask is set to zero right now!)
+ */
+
+ /* Mode we want for the final restored object (w/o file type bits). */
+ final_mode = a->mode & 07777;
+ /*
+ * The mode that will actually be restored in this step. Note
+ * that SUID, SGID, etc, require additional work to ensure
+ * security, so we never restore them at this point.
+ */
+ mode = final_mode & 0777 & ~a->user_umask;
+
+ /*
+ * Always create writable such that [f]setxattr() works if we're not
+ * root.
+ */
+ if (a->user_uid != 0 &&
+ a->todo & (TODO_HFS_COMPRESSION | TODO_XATTR)) {
+ mode |= 0200;
+ }
+
+ switch (a->mode & AE_IFMT) {
+ default:
+ /* POSIX requires that we fall through here. */
+ /* FALLTHROUGH */
+ case AE_IFREG:
+ a->tmpname = NULL;
+ a->fd = open(a->name,
+ O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, mode);
+ __archive_ensure_cloexec_flag(a->fd);
+ r = (a->fd < 0);
+ break;
+ case AE_IFCHR:
+#ifdef HAVE_MKNOD
+ /* Note: we use AE_IFCHR for the case label, and
+ * S_IFCHR for the mknod() call. This is correct. */
+ r = mknod(a->name, mode | S_IFCHR,
+ archive_entry_rdev(a->entry));
+ break;
+#else
+ /* TODO: Find a better way to warn about our inability
+ * to restore a char device node. */
+ return (EINVAL);
+#endif /* HAVE_MKNOD */
+ case AE_IFBLK:
+#ifdef HAVE_MKNOD
+ r = mknod(a->name, mode | S_IFBLK,
+ archive_entry_rdev(a->entry));
+ break;
+#else
+ /* TODO: Find a better way to warn about our inability
+ * to restore a block device node. */
+ return (EINVAL);
+#endif /* HAVE_MKNOD */
+ case AE_IFDIR:
+ mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE;
+ r = mkdir(a->name, mode);
+ if (r == 0) {
+ /* Defer setting dir times. */
+ a->deferred |= (a->todo & TODO_TIMES);
+ a->todo &= ~TODO_TIMES;
+ /* Never use an immediate chmod(). */
+ /* We can't avoid the chmod() entirely if EXTRACT_PERM
+ * because of SysV SGID inheritance. */
+ if ((mode != final_mode)
+ || (a->flags & ARCHIVE_EXTRACT_PERM))
+ a->deferred |= (a->todo & TODO_MODE);
+ a->todo &= ~TODO_MODE;
+ }
+ break;
+ case AE_IFIFO:
+#ifdef HAVE_MKFIFO
+ r = mkfifo(a->name, mode);
+ break;
+#else
+ /* TODO: Find a better way to warn about our inability
+ * to restore a fifo. */
+ return (EINVAL);
+#endif /* HAVE_MKFIFO */
+ }
+
+ /* All the system calls above set errno on failure. */
+ if (r)
+ return (errno);
+
+ /* If we managed to set the final mode, we've avoided a chmod(). */
+ if (mode == final_mode)
+ a->todo &= ~TODO_MODE;
+ return (0);
+}
+
+/*
+ * Cleanup function for archive_extract. Mostly, this involves processing
+ * the fixup list, which is used to address a number of problems:
+ * * Dir permissions might prevent us from restoring a file in that
+ * dir, so we restore the dir with minimum 0700 permissions first,
+ * then correct the mode at the end.
+ * * Similarly, the act of restoring a file touches the directory
+ * and changes the timestamp on the dir, so we have to touch-up dir
+ * timestamps at the end as well.
+ * * Some file flags can interfere with the restore by, for example,
+ * preventing the creation of hardlinks to those files.
+ * * Mac OS extended metadata includes ACLs, so must be deferred on dirs.
+ *
+ * Note that tar/cpio do not require that archives be in a particular
+ * order; there is no way to know when the last file has been restored
+ * within a directory, so there's no way to optimize the memory usage
+ * here by fixing up the directory any earlier than the
+ * end-of-archive.
+ *
+ * XXX TODO: Directory ACLs should be restored here, for the same
+ * reason we set directory perms here. XXX
+ */
+static int
+_archive_write_disk_close(struct archive *_a)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ struct fixup_entry *next, *p;
+ struct stat st;
+ char *c;
+ int fd, ret, openflags;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_disk_close");
+ ret = _archive_write_disk_finish_entry(&a->archive);
+
+ /* Sort dir list so directories are fixed up in depth-first order. */
+ p = sort_dir_list(a->fixup_list);
+
+ while (p != NULL) {
+ fd = -1;
+ a->pst = NULL; /* Mark stat cache as out-of-date. */
+
+ /* We must strip trailing slashes from the path to avoid
+ dereferencing symbolic links to directories */
+ c = p->name;
+ while (*c != '\0')
+ c++;
+ while (c != p->name && *(c - 1) == '/') {
+ c--;
+ *c = '\0';
+ }
+
+ if (p->fixup == 0)
+ goto skip_fixup_entry;
+ else {
+ /*
+ * We need to verify if the type of the file
+ * we are going to open matches the file type
+ * of the fixup entry.
+ */
+ openflags = O_BINARY | O_NOFOLLOW | O_RDONLY
+ | O_CLOEXEC;
+#if defined(O_DIRECTORY)
+ if (p->filetype == AE_IFDIR)
+ openflags |= O_DIRECTORY;
+#endif
+ fd = open(p->name, openflags);
+
+#if defined(O_DIRECTORY)
+ /*
+ * If we support O_DIRECTORY and open was
+ * successful we can skip the file type check
+ * for directories. For other file types
+ * we need to verify via fstat() or lstat()
+ */
+ if (fd == -1 || p->filetype != AE_IFDIR) {
+#if HAVE_FSTAT
+ if (fd > 0 && (
+ fstat(fd, &st) != 0 ||
+ la_verify_filetype(st.st_mode,
+ p->filetype) == 0)) {
+ goto skip_fixup_entry;
+ } else
+#endif
+ if (
+#ifdef HAVE_LSTAT
+ lstat(p->name, &st) != 0 ||
+#else
+ la_stat(p->name, &st) != 0 ||
+#endif
+ la_verify_filetype(st.st_mode,
+ p->filetype) == 0) {
+ goto skip_fixup_entry;
+ }
+ }
+#else
+#if HAVE_FSTAT
+ if (fd > 0 && (
+ fstat(fd, &st) != 0 ||
+ la_verify_filetype(st.st_mode,
+ p->filetype) == 0)) {
+ goto skip_fixup_entry;
+ } else
+#endif
+ if (
+#ifdef HAVE_LSTAT
+ lstat(p->name, &st) != 0 ||
+#else
+ la_stat(p->name, &st) != 0 ||
+#endif
+ la_verify_filetype(st.st_mode,
+ p->filetype) == 0) {
+ goto skip_fixup_entry;
+ }
+#endif
+ }
+ if (p->fixup & TODO_TIMES) {
+ set_times(a, fd, p->mode, p->name,
+ p->atime, p->atime_nanos,
+ p->birthtime, p->birthtime_nanos,
+ p->mtime, p->mtime_nanos,
+ p->ctime, p->ctime_nanos);
+ }
+ if (p->fixup & TODO_MODE_BASE) {
+#ifdef HAVE_FCHMOD
+ if (fd >= 0)
+ fchmod(fd, p->mode & 07777);
+ else
+#endif
+#ifdef HAVE_LCHMOD
+ lchmod(p->name, p->mode & 07777);
+#else
+ chmod(p->name, p->mode & 07777);
+#endif
+ }
+ if (p->fixup & TODO_ACLS)
+ archive_write_disk_set_acls(&a->archive, fd,
+ p->name, &p->acl, p->mode);
+ if (p->fixup & TODO_FFLAGS)
+ set_fflags_platform(a, fd, p->name,
+ p->mode, p->fflags_set, 0);
+ if (p->fixup & TODO_MAC_METADATA)
+ set_mac_metadata(a, p->name, p->mac_metadata,
+ p->mac_metadata_size);
+skip_fixup_entry:
+ next = p->next;
+ archive_acl_clear(&p->acl);
+ free(p->mac_metadata);
+ free(p->name);
+ if (fd >= 0)
+ close(fd);
+ free(p);
+ p = next;
+ }
+ a->fixup_list = NULL;
+ return (ret);
+}
+
+static int
+_archive_write_disk_free(struct archive *_a)
+{
+ struct archive_write_disk *a;
+ int ret;
+ if (_a == NULL)
+ return (ARCHIVE_OK);
+ archive_check_magic(_a, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_disk_free");
+ a = (struct archive_write_disk *)_a;
+ ret = _archive_write_disk_close(&a->archive);
+ archive_write_disk_set_group_lookup(&a->archive, NULL, NULL, NULL);
+ archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL);
+ archive_entry_free(a->entry);
+ archive_string_free(&a->_name_data);
+ archive_string_free(&a->_tmpname_data);
+ archive_string_free(&a->archive.error_string);
+ archive_string_free(&a->path_safe);
+ a->archive.magic = 0;
+ __archive_clean(&a->archive);
+ free(a->decmpfs_header_p);
+ free(a->resource_fork);
+ free(a->compressed_buffer);
+ free(a->uncompressed_buffer);
+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\
+ && defined(HAVE_ZLIB_H)
+ if (a->stream_valid) {
+ switch (deflateEnd(&a->stream)) {
+ case Z_OK:
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up compressor");
+ ret = ARCHIVE_FATAL;
+ break;
+ }
+ }
+#endif
+ free(a);
+ return (ret);
+}
+
+/*
+ * Simple O(n log n) merge sort to order the fixup list. In
+ * particular, we want to restore dir timestamps depth-first.
+ */
+static struct fixup_entry *
+sort_dir_list(struct fixup_entry *p)
+{
+ struct fixup_entry *a, *b, *t;
+
+ if (p == NULL)
+ return (NULL);
+ /* A one-item list is already sorted. */
+ if (p->next == NULL)
+ return (p);
+
+ /* Step 1: split the list. */
+ t = p;
+ a = p->next->next;
+ while (a != NULL) {
+ /* Step a twice, t once. */
+ a = a->next;
+ if (a != NULL)
+ a = a->next;
+ t = t->next;
+ }
+ /* Now, t is at the mid-point, so break the list here. */
+ b = t->next;
+ t->next = NULL;
+ a = p;
+
+ /* Step 2: Recursively sort the two sub-lists. */
+ a = sort_dir_list(a);
+ b = sort_dir_list(b);
+
+ /* Step 3: Merge the returned lists. */
+ /* Pick the first element for the merged list. */
+ if (strcmp(a->name, b->name) > 0) {
+ t = p = a;
+ a = a->next;
+ } else {
+ t = p = b;
+ b = b->next;
+ }
+
+ /* Always put the later element on the list first. */
+ while (a != NULL && b != NULL) {
+ if (strcmp(a->name, b->name) > 0) {
+ t->next = a;
+ a = a->next;
+ } else {
+ t->next = b;
+ b = b->next;
+ }
+ t = t->next;
+ }
+
+ /* Only one list is non-empty, so just splice it on. */
+ if (a != NULL)
+ t->next = a;
+ if (b != NULL)
+ t->next = b;
+
+ return (p);
+}
+
+/*
+ * Returns a new, initialized fixup entry.
+ *
+ * TODO: Reduce the memory requirements for this list by using a tree
+ * structure rather than a simple list of names.
+ */
+static struct fixup_entry *
+new_fixup(struct archive_write_disk *a, const char *pathname)
+{
+ struct fixup_entry *fe;
+
+ fe = (struct fixup_entry *)calloc(1, sizeof(struct fixup_entry));
+ if (fe == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for a fixup");
+ return (NULL);
+ }
+ fe->next = a->fixup_list;
+ a->fixup_list = fe;
+ fe->fixup = 0;
+ fe->filetype = 0;
+ fe->name = strdup(pathname);
+ return (fe);
+}
+
+/*
+ * Returns a fixup structure for the current entry.
+ */
+static struct fixup_entry *
+current_fixup(struct archive_write_disk *a, const char *pathname)
+{
+ if (a->current_fixup == NULL)
+ a->current_fixup = new_fixup(a, pathname);
+ return (a->current_fixup);
+}
+
+/* Error helper for new *_fsobj functions */
+static void
+fsobj_error(int *a_eno, struct archive_string *a_estr,
+ int err, const char *errstr, const char *path)
+{
+ if (a_eno)
+ *a_eno = err;
+ if (a_estr)
+ archive_string_sprintf(a_estr, "%s%s", errstr, path);
+}
+
+/*
+ * TODO: Someday, integrate this with the deep dir support; they both
+ * scan the path and both can be optimized by comparing against other
+ * recent paths.
+ */
+/*
+ * Checks the given path to see if any elements along it are symlinks. Returns
+ * ARCHIVE_OK if there are none, otherwise puts an error in errmsg.
+ */
+static int
+check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
+ int flags, int checking_linkname)
+{
+#if !defined(HAVE_LSTAT) && \
+ !(defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT))
+ /* Platform doesn't have lstat, so we can't look for symlinks. */
+ (void)path; /* UNUSED */
+ (void)a_eno; /* UNUSED */
+ (void)a_estr; /* UNUSED */
+ (void)flags; /* UNUSED */
+ (void)checking_linkname; /* UNUSED */
+ return (ARCHIVE_OK);
+#else
+ int res = ARCHIVE_OK;
+ char *tail;
+ char *head;
+ int last;
+ char c = '\0';
+ int r;
+ struct stat st;
+ int chdir_fd;
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+ int fd;
+#endif
+
+ /* Nothing to do here if name is empty */
+ if(path[0] == '\0')
+ return (ARCHIVE_OK);
+
+ /*
+ * Guard against symlink tricks. Reject any archive entry whose
+ * destination would be altered by a symlink.
+ *
+ * Walk the filename in chunks separated by '/'. For each segment:
+ * - if it doesn't exist, continue
+ * - if it's symlink, abort or remove it
+ * - if it's a directory and it's not the last chunk, cd into it
+ * As we go:
+ * head points to the current (relative) path
+ * tail points to the temporary \0 terminating the segment we're
+ * currently examining
+ * c holds what used to be in *tail
+ * last is 1 if this is the last tail
+ */
+ chdir_fd = la_opendirat(AT_FDCWD, ".");
+ __archive_ensure_cloexec_flag(chdir_fd);
+ if (chdir_fd < 0) {
+ fsobj_error(a_eno, a_estr, errno,
+ "Could not open ", path);
+ return (ARCHIVE_FATAL);
+ }
+ head = path;
+ tail = path;
+ last = 0;
+ /* TODO: reintroduce a safe cache here? */
+ /* Skip the root directory if the path is absolute. */
+ if(tail == path && tail[0] == '/')
+ ++tail;
+ /* Keep going until we've checked the entire name.
+ * head, tail, path all alias the same string, which is
+ * temporarily zeroed at tail, so be careful restoring the
+ * stashed (c=tail[0]) for error messages.
+ * Exiting the loop with break is okay; continue is not.
+ */
+ while (!last) {
+ /*
+ * Skip the separator we just consumed, plus any adjacent ones
+ */
+ while (*tail == '/')
+ ++tail;
+ /* Skip the next path element. */
+ while (*tail != '\0' && *tail != '/')
+ ++tail;
+ /* is this the last path component? */
+ last = (tail[0] == '\0') || (tail[0] == '/' && tail[1] == '\0');
+ /* temporarily truncate the string here */
+ c = tail[0];
+ tail[0] = '\0';
+ /* Check that we haven't hit a symlink. */
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+ r = fstatat(chdir_fd, head, &st, AT_SYMLINK_NOFOLLOW);
+#elif defined(HAVE_LSTAT)
+ r = lstat(head, &st);
+#else
+ r = la_stat(head, &st);
+#endif
+ if (r != 0) {
+ tail[0] = c;
+ /* We've hit a dir that doesn't exist; stop now. */
+ if (errno == ENOENT) {
+ break;
+ } else {
+ /*
+ * Treat any other error as fatal - best to be
+ * paranoid here.
+ * Note: This effectively disables deep
+ * directory support when security checks are
+ * enabled. Otherwise, very long pathnames that
+ * trigger an error here could evade the
+ * sandbox.
+ * TODO: We could do better, but it would
+ * probably require merging the symlink checks
+ * with the deep-directory editing.
+ */
+ fsobj_error(a_eno, a_estr, errno,
+ "Could not stat ", path);
+ res = ARCHIVE_FAILED;
+ break;
+ }
+ } else if (S_ISDIR(st.st_mode)) {
+ if (!last) {
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+ fd = la_opendirat(chdir_fd, head);
+ if (fd < 0)
+ r = -1;
+ else {
+ r = 0;
+ close(chdir_fd);
+ chdir_fd = fd;
+ }
+#else
+ r = chdir(head);
+#endif
+ if (r != 0) {
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr, errno,
+ "Could not chdir ", path);
+ res = (ARCHIVE_FATAL);
+ break;
+ }
+ /* Our view is now from inside this dir: */
+ head = tail + 1;
+ }
+ } else if (S_ISLNK(st.st_mode)) {
+ if (last && checking_linkname) {
+#ifdef HAVE_LINKAT
+ /*
+ * Hardlinks to symlinks are safe to write
+ * if linkat() is supported as it does not
+ * follow symlinks.
+ */
+ res = ARCHIVE_OK;
+#else
+ /*
+ * We return ARCHIVE_FAILED here as we are
+ * not able to safely write hardlinks
+ * to symlinks.
+ */
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr, errno,
+ "Cannot write hardlink to symlink ",
+ path);
+ res = ARCHIVE_FAILED;
+#endif
+ break;
+ } else
+ if (last) {
+ /*
+ * Last element is symlink; remove it
+ * so we can overwrite it with the
+ * item being extracted.
+ */
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+ r = unlinkat(chdir_fd, head, 0);
+#else
+ r = unlink(head);
+#endif
+ if (r != 0) {
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr, errno,
+ "Could not remove symlink ",
+ path);
+ res = ARCHIVE_FAILED;
+ break;
+ }
+ /*
+ * Even if we did remove it, a warning
+ * is in order. The warning is silly,
+ * though, if we're just replacing one
+ * symlink with another symlink.
+ */
+ tail[0] = c;
+ /*
+ * FIXME: not sure how important this is to
+ * restore
+ */
+ /*
+ if (!S_ISLNK(path)) {
+ fsobj_error(a_eno, a_estr, 0,
+ "Removing symlink ", path);
+ }
+ */
+ /* Symlink gone. No more problem! */
+ res = ARCHIVE_OK;
+ break;
+ } else if (flags & ARCHIVE_EXTRACT_UNLINK) {
+ /* User asked us to remove problems. */
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+ r = unlinkat(chdir_fd, head, 0);
+#else
+ r = unlink(head);
+#endif
+ if (r != 0) {
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr, 0,
+ "Cannot remove intervening "
+ "symlink ", path);
+ res = ARCHIVE_FAILED;
+ break;
+ }
+ tail[0] = c;
+ } else if ((flags &
+ ARCHIVE_EXTRACT_SECURE_SYMLINKS) == 0) {
+ /*
+ * We are not the last element and we want to
+ * follow symlinks if they are a directory.
+ *
+ * This is needed to extract hardlinks over
+ * symlinks.
+ */
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+ r = fstatat(chdir_fd, head, &st, 0);
+#else
+ r = la_stat(head, &st);
+#endif
+ if (r != 0) {
+ tail[0] = c;
+ if (errno == ENOENT) {
+ break;
+ } else {
+ fsobj_error(a_eno, a_estr,
+ errno,
+ "Could not stat ", path);
+ res = (ARCHIVE_FAILED);
+ break;
+ }
+ } else if (S_ISDIR(st.st_mode)) {
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+ fd = la_opendirat(chdir_fd, head);
+ if (fd < 0)
+ r = -1;
+ else {
+ r = 0;
+ close(chdir_fd);
+ chdir_fd = fd;
+ }
+#else
+ r = chdir(head);
+#endif
+ if (r != 0) {
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr,
+ errno,
+ "Could not chdir ", path);
+ res = (ARCHIVE_FATAL);
+ break;
+ }
+ /*
+ * Our view is now from inside
+ * this dir:
+ */
+ head = tail + 1;
+ } else {
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr, 0,
+ "Cannot extract through "
+ "symlink ", path);
+ res = ARCHIVE_FAILED;
+ break;
+ }
+ } else {
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr, 0,
+ "Cannot extract through symlink ", path);
+ res = ARCHIVE_FAILED;
+ break;
+ }
+ }
+ /* be sure to always maintain this */
+ tail[0] = c;
+ if (tail[0] != '\0')
+ tail++; /* Advance to the next segment. */
+ }
+ /* Catches loop exits via break */
+ tail[0] = c;
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+ /* If we operate with openat(), fstatat() and unlinkat() there was
+ * no chdir(), so just close the fd */
+ if (chdir_fd >= 0)
+ close(chdir_fd);
+#elif HAVE_FCHDIR
+ /* If we changed directory above, restore it here. */
+ if (chdir_fd >= 0) {
+ r = fchdir(chdir_fd);
+ if (r != 0) {
+ fsobj_error(a_eno, a_estr, errno,
+ "chdir() failure", "");
+ }
+ close(chdir_fd);
+ chdir_fd = -1;
+ if (r != 0) {
+ res = (ARCHIVE_FATAL);
+ }
+ }
+#endif
+ /* TODO: reintroduce a safe cache here? */
+ return res;
+#endif
+}
+
+/*
+ * Check a->name for symlinks, returning ARCHIVE_OK if its clean, otherwise
+ * calls archive_set_error and returns ARCHIVE_{FATAL,FAILED}
+ */
+static int
+check_symlinks(struct archive_write_disk *a)
+{
+ struct archive_string error_string;
+ int error_number;
+ int rc;
+ archive_string_init(&error_string);
+ rc = check_symlinks_fsobj(a->name, &error_number, &error_string,
+ a->flags, 0);
+ if (rc != ARCHIVE_OK) {
+ archive_set_error(&a->archive, error_number, "%s",
+ error_string.s);
+ }
+ archive_string_free(&error_string);
+ a->pst = NULL; /* to be safe */
+ return rc;
+}
+
+
+#if defined(__CYGWIN__)
+/*
+ * 1. Convert a path separator from '\' to '/' .
+ * We shouldn't check multibyte character directly because some
+ * character-set have been using the '\' character for a part of
+ * its multibyte character code.
+ * 2. Replace unusable characters in Windows with underscore('_').
+ * See also : http://msdn.microsoft.com/en-us/library/aa365247.aspx
+ */
+static void
+cleanup_pathname_win(char *path)
+{
+ wchar_t wc;
+ char *p;
+ size_t alen, l;
+ int mb, complete, utf8;
+
+ alen = 0;
+ mb = 0;
+ complete = 1;
+ utf8 = (strcmp(nl_langinfo(CODESET), "UTF-8") == 0)? 1: 0;
+ for (p = path; *p != '\0'; p++) {
+ ++alen;
+ if (*p == '\\') {
+ /* If previous byte is smaller than 128,
+ * this is not second byte of multibyte characters,
+ * so we can replace '\' with '/'. */
+ if (utf8 || !mb)
+ *p = '/';
+ else
+ complete = 0;/* uncompleted. */
+ } else if (*(unsigned char *)p > 127)
+ mb = 1;
+ else
+ mb = 0;
+ /* Rewrite the path name if its next character is unusable. */
+ if (*p == ':' || *p == '*' || *p == '?' || *p == '"' ||
+ *p == '<' || *p == '>' || *p == '|')
+ *p = '_';
+ }
+ if (complete)
+ return;
+
+ /*
+ * Convert path separator in wide-character.
+ */
+ p = path;
+ while (*p != '\0' && alen) {
+ l = mbtowc(&wc, p, alen);
+ if (l == (size_t)-1) {
+ while (*p != '\0') {
+ if (*p == '\\')
+ *p = '/';
+ ++p;
+ }
+ break;
+ }
+ if (l == 1 && wc == L'\\')
+ *p = '/';
+ p += l;
+ alen -= l;
+ }
+}
+#endif
+
+/*
+ * Canonicalize the pathname. In particular, this strips duplicate
+ * '/' characters, '.' elements, and trailing '/'. It also raises an
+ * error for an empty path, a trailing '..', (if _SECURE_NODOTDOT is
+ * set) any '..' in the path or (if ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS
+ * is set) if the path is absolute.
+ */
+static int
+cleanup_pathname_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
+ int flags)
+{
+ char *dest, *src;
+ char separator = '\0';
+
+ dest = src = path;
+ if (*src == '\0') {
+ fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC,
+ "Invalid empty ", "pathname");
+ return (ARCHIVE_FAILED);
+ }
+
+#if defined(__CYGWIN__)
+ cleanup_pathname_win(path);
+#endif
+ /* Skip leading '/'. */
+ if (*src == '/') {
+ if (flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) {
+ fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC,
+ "Path is ", "absolute");
+ return (ARCHIVE_FAILED);
+ }
+
+ separator = *src++;
+ }
+
+ /* Scan the pathname one element at a time. */
+ for (;;) {
+ /* src points to first char after '/' */
+ if (src[0] == '\0') {
+ break;
+ } else if (src[0] == '/') {
+ /* Found '//', ignore second one. */
+ src++;
+ continue;
+ } else if (src[0] == '.') {
+ if (src[1] == '\0') {
+ /* Ignore trailing '.' */
+ break;
+ } else if (src[1] == '/') {
+ /* Skip './'. */
+ src += 2;
+ continue;
+ } else if (src[1] == '.') {
+ if (src[2] == '/' || src[2] == '\0') {
+ /* Conditionally warn about '..' */
+ if (flags
+ & ARCHIVE_EXTRACT_SECURE_NODOTDOT) {
+ fsobj_error(a_eno, a_estr,
+ ARCHIVE_ERRNO_MISC,
+ "Path contains ", "'..'");
+ return (ARCHIVE_FAILED);
+ }
+ }
+ /*
+ * Note: Under no circumstances do we
+ * remove '..' elements. In
+ * particular, restoring
+ * '/foo/../bar/' should create the
+ * 'foo' dir as a side-effect.
+ */
+ }
+ }
+
+ /* Copy current element, including leading '/'. */
+ if (separator)
+ *dest++ = '/';
+ while (*src != '\0' && *src != '/') {
+ *dest++ = *src++;
+ }
+
+ if (*src == '\0')
+ break;
+
+ /* Skip '/' separator. */
+ separator = *src++;
+ }
+ /*
+ * We've just copied zero or more path elements, not including the
+ * final '/'.
+ */
+ if (dest == path) {
+ /*
+ * Nothing got copied. The path must have been something
+ * like '.' or '/' or './' or '/././././/./'.
+ */
+ if (separator)
+ *dest++ = '/';
+ else
+ *dest++ = '.';
+ }
+ /* Terminate the result. */
+ *dest = '\0';
+ return (ARCHIVE_OK);
+}
+
+static int
+cleanup_pathname(struct archive_write_disk *a)
+{
+ struct archive_string error_string;
+ int error_number;
+ int rc;
+ archive_string_init(&error_string);
+ rc = cleanup_pathname_fsobj(a->name, &error_number, &error_string,
+ a->flags);
+ if (rc != ARCHIVE_OK) {
+ archive_set_error(&a->archive, error_number, "%s",
+ error_string.s);
+ }
+ archive_string_free(&error_string);
+ return rc;
+}
+
+/*
+ * Create the parent directory of the specified path, assuming path
+ * is already in mutable storage.
+ */
+static int
+create_parent_dir(struct archive_write_disk *a, char *path)
+{
+ char *slash;
+ int r;
+
+ /* Remove tail element to obtain parent name. */
+ slash = strrchr(path, '/');
+ if (slash == NULL)
+ return (ARCHIVE_OK);
+ *slash = '\0';
+ r = create_dir(a, path);
+ *slash = '/';
+ return (r);
+}
+
+/*
+ * Create the specified dir, recursing to create parents as necessary.
+ *
+ * Returns ARCHIVE_OK if the path exists when we're done here.
+ * Otherwise, returns ARCHIVE_FAILED.
+ * Assumes path is in mutable storage; path is unchanged on exit.
+ */
+static int
+create_dir(struct archive_write_disk *a, char *path)
+{
+ struct stat st;
+ struct fixup_entry *le;
+ char *slash, *base;
+ mode_t mode_final, mode;
+ int r;
+
+ /* Check for special names and just skip them. */
+ slash = strrchr(path, '/');
+ if (slash == NULL)
+ base = path;
+ else
+ base = slash + 1;
+
+ if (base[0] == '\0' ||
+ (base[0] == '.' && base[1] == '\0') ||
+ (base[0] == '.' && base[1] == '.' && base[2] == '\0')) {
+ /* Don't bother trying to create null path, '.', or '..'. */
+ if (slash != NULL) {
+ *slash = '\0';
+ r = create_dir(a, path);
+ *slash = '/';
+ return (r);
+ }
+ return (ARCHIVE_OK);
+ }
+
+ /*
+ * Yes, this should be stat() and not lstat(). Using lstat()
+ * here loses the ability to extract through symlinks. Also note
+ * that this should not use the a->st cache.
+ */
+ if (la_stat(path, &st) == 0) {
+ if (S_ISDIR(st.st_mode))
+ return (ARCHIVE_OK);
+ if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+ archive_set_error(&a->archive, EEXIST,
+ "Can't create directory '%s'", path);
+ return (ARCHIVE_FAILED);
+ }
+ if (unlink(path) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't create directory '%s': "
+ "Conflicting file cannot be removed",
+ path);
+ return (ARCHIVE_FAILED);
+ }
+ } else if (errno != ENOENT && errno != ENOTDIR) {
+ /* Stat failed? */
+ archive_set_error(&a->archive, errno,
+ "Can't test directory '%s'", path);
+ return (ARCHIVE_FAILED);
+ } else if (slash != NULL) {
+ *slash = '\0';
+ r = create_dir(a, path);
+ *slash = '/';
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ /*
+ * Mode we want for the final restored directory. Per POSIX,
+ * implicitly-created dirs must be created obeying the umask.
+ * There's no mention whether this is different for privileged
+ * restores (which the rest of this code handles by pretending
+ * umask=0). I've chosen here to always obey the user's umask for
+ * implicit dirs, even if _EXTRACT_PERM was specified.
+ */
+ mode_final = DEFAULT_DIR_MODE & ~a->user_umask;
+ /* Mode we want on disk during the restore process. */
+ mode = mode_final;
+ mode |= MINIMUM_DIR_MODE;
+ mode &= MAXIMUM_DIR_MODE;
+ if (mkdir(path, mode) == 0) {
+ if (mode != mode_final) {
+ le = new_fixup(a, path);
+ if (le == NULL)
+ return (ARCHIVE_FATAL);
+ le->fixup |=TODO_MODE_BASE;
+ le->mode = mode_final;
+ }
+ return (ARCHIVE_OK);
+ }
+
+ /*
+ * Without the following check, a/b/../b/c/d fails at the
+ * second visit to 'b', so 'd' can't be created. Note that we
+ * don't add it to the fixup list here, as it's already been
+ * added.
+ */
+ if (la_stat(path, &st) == 0 && S_ISDIR(st.st_mode))
+ return (ARCHIVE_OK);
+
+ archive_set_error(&a->archive, errno, "Failed to create dir '%s'",
+ path);
+ return (ARCHIVE_FAILED);
+}
+
+/*
+ * Note: Although we can skip setting the user id if the desired user
+ * id matches the current user, we cannot skip setting the group, as
+ * many systems set the gid based on the containing directory. So
+ * we have to perform a chown syscall if we want to set the SGID
+ * bit. (The alternative is to stat() and then possibly chown(); it's
+ * more efficient to skip the stat() and just always chown().) Note
+ * that a successful chown() here clears the TODO_SGID_CHECK bit, which
+ * allows set_mode to skip the stat() check for the GID.
+ */
+static int
+set_ownership(struct archive_write_disk *a)
+{
+#if !defined(__CYGWIN__) && !defined(__linux__)
+/*
+ * On Linux, a process may have the CAP_CHOWN capability.
+ * On Windows there is no 'root' user with uid 0.
+ * Elsewhere we can skip calling chown if we are not root and the desired
+ * user id does not match the current user.
+ */
+ if (a->user_uid != 0 && a->user_uid != a->uid) {
+ archive_set_error(&a->archive, errno,
+ "Can't set UID=%jd", (intmax_t)a->uid);
+ return (ARCHIVE_WARN);
+ }
+#endif
+
+#ifdef HAVE_FCHOWN
+ /* If we have an fd, we can avoid a race. */
+ if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0) {
+ /* We've set owner and know uid/gid are correct. */
+ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+ return (ARCHIVE_OK);
+ }
+#endif
+
+ /* We prefer lchown() but will use chown() if that's all we have. */
+ /* Of course, if we have neither, this will always fail. */
+#ifdef HAVE_LCHOWN
+ if (lchown(a->name, a->uid, a->gid) == 0) {
+ /* We've set owner and know uid/gid are correct. */
+ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+ return (ARCHIVE_OK);
+ }
+#elif HAVE_CHOWN
+ if (!S_ISLNK(a->mode) && chown(a->name, a->uid, a->gid) == 0) {
+ /* We've set owner and know uid/gid are correct. */
+ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+ return (ARCHIVE_OK);
+ }
+#endif
+
+ archive_set_error(&a->archive, errno,
+ "Can't set user=%jd/group=%jd for %s",
+ (intmax_t)a->uid, (intmax_t)a->gid, a->name);
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Note: Returns 0 on success, non-zero on failure.
+ */
+static int
+set_time(int fd, int mode, const char *name,
+ time_t atime, long atime_nsec,
+ time_t mtime, long mtime_nsec)
+{
+ /* Select the best implementation for this platform. */
+#if defined(HAVE_UTIMENSAT) && defined(HAVE_FUTIMENS)
+ /*
+ * utimensat() and futimens() are defined in
+ * POSIX.1-2008. They support ns resolution and setting times
+ * on fds and symlinks.
+ */
+ struct timespec ts[2];
+ (void)mode; /* UNUSED */
+ ts[0].tv_sec = atime;
+ ts[0].tv_nsec = atime_nsec;
+ ts[1].tv_sec = mtime;
+ ts[1].tv_nsec = mtime_nsec;
+ if (fd >= 0)
+ return futimens(fd, ts);
+ return utimensat(AT_FDCWD, name, ts, AT_SYMLINK_NOFOLLOW);
+
+#elif HAVE_UTIMES
+ /*
+ * The utimes()-family functions support µs-resolution and
+ * setting times fds and symlinks. utimes() is documented as
+ * LEGACY by POSIX, futimes() and lutimes() are not described
+ * in POSIX.
+ */
+ struct timeval times[2];
+
+ times[0].tv_sec = atime;
+ times[0].tv_usec = atime_nsec / 1000;
+ times[1].tv_sec = mtime;
+ times[1].tv_usec = mtime_nsec / 1000;
+
+#ifdef HAVE_FUTIMES
+ if (fd >= 0)
+ return (futimes(fd, times));
+#else
+ (void)fd; /* UNUSED */
+#endif
+#ifdef HAVE_LUTIMES
+ (void)mode; /* UNUSED */
+ return (lutimes(name, times));
+#else
+ if (S_ISLNK(mode))
+ return (0);
+ return (utimes(name, times));
+#endif
+
+#elif defined(HAVE_UTIME)
+ /*
+ * utime() is POSIX-standard but only supports 1s resolution and
+ * does not support fds or symlinks.
+ */
+ struct utimbuf times;
+ (void)fd; /* UNUSED */
+ (void)name; /* UNUSED */
+ (void)atime_nsec; /* UNUSED */
+ (void)mtime_nsec; /* UNUSED */
+ times.actime = atime;
+ times.modtime = mtime;
+ if (S_ISLNK(mode))
+ return (ARCHIVE_OK);
+ return (utime(name, &times));
+
+#else
+ /*
+ * We don't know how to set the time on this platform.
+ */
+ (void)fd; /* UNUSED */
+ (void)mode; /* UNUSED */
+ (void)name; /* UNUSED */
+ (void)atime; /* UNUSED */
+ (void)atime_nsec; /* UNUSED */
+ (void)mtime; /* UNUSED */
+ (void)mtime_nsec; /* UNUSED */
+ return (ARCHIVE_WARN);
+#endif
+}
+
+#ifdef F_SETTIMES
+static int
+set_time_tru64(int fd, int mode, const char *name,
+ time_t atime, long atime_nsec,
+ time_t mtime, long mtime_nsec,
+ time_t ctime, long ctime_nsec)
+{
+ struct attr_timbuf tstamp;
+ tstamp.atime.tv_sec = atime;
+ tstamp.mtime.tv_sec = mtime;
+ tstamp.ctime.tv_sec = ctime;
+#if defined (__hpux) && defined (__ia64)
+ tstamp.atime.tv_nsec = atime_nsec;
+ tstamp.mtime.tv_nsec = mtime_nsec;
+ tstamp.ctime.tv_nsec = ctime_nsec;
+#else
+ tstamp.atime.tv_usec = atime_nsec / 1000;
+ tstamp.mtime.tv_usec = mtime_nsec / 1000;
+ tstamp.ctime.tv_usec = ctime_nsec / 1000;
+#endif
+ return (fcntl(fd,F_SETTIMES,&tstamp));
+}
+#endif /* F_SETTIMES */
+
+static int
+set_times(struct archive_write_disk *a,
+ int fd, int mode, const char *name,
+ time_t atime, long atime_nanos,
+ time_t birthtime, long birthtime_nanos,
+ time_t mtime, long mtime_nanos,
+ time_t cctime, long ctime_nanos)
+{
+ /* Note: set_time doesn't use libarchive return conventions!
+ * It uses syscall conventions. So 0 here instead of ARCHIVE_OK. */
+ int r1 = 0, r2 = 0;
+
+#ifdef F_SETTIMES
+ /*
+ * on Tru64 try own fcntl first which can restore even the
+ * ctime, fall back to default code path below if it fails
+ * or if we are not running as root
+ */
+ if (a->user_uid == 0 &&
+ set_time_tru64(fd, mode, name,
+ atime, atime_nanos, mtime,
+ mtime_nanos, cctime, ctime_nanos) == 0) {
+ return (ARCHIVE_OK);
+ }
+#else /* Tru64 */
+ (void)cctime; /* UNUSED */
+ (void)ctime_nanos; /* UNUSED */
+#endif /* Tru64 */
+
+#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
+ /*
+ * If you have struct stat.st_birthtime, we assume BSD
+ * birthtime semantics, in which {f,l,}utimes() updates
+ * birthtime to earliest mtime. So we set the time twice,
+ * first using the birthtime, then using the mtime. If
+ * birthtime == mtime, this isn't necessary, so we skip it.
+ * If birthtime > mtime, then this won't work, so we skip it.
+ */
+ if (birthtime < mtime
+ || (birthtime == mtime && birthtime_nanos < mtime_nanos))
+ r1 = set_time(fd, mode, name,
+ atime, atime_nanos,
+ birthtime, birthtime_nanos);
+#else
+ (void)birthtime; /* UNUSED */
+ (void)birthtime_nanos; /* UNUSED */
+#endif
+ r2 = set_time(fd, mode, name,
+ atime, atime_nanos,
+ mtime, mtime_nanos);
+ if (r1 != 0 || r2 != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't restore time");
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+set_times_from_entry(struct archive_write_disk *a)
+{
+ time_t atime, birthtime, mtime, cctime;
+ long atime_nsec, birthtime_nsec, mtime_nsec, ctime_nsec;
+
+ /* Suitable defaults. */
+ atime = birthtime = mtime = cctime = a->start_time;
+ atime_nsec = birthtime_nsec = mtime_nsec = ctime_nsec = 0;
+
+ /* If no time was provided, we're done. */
+ if (!archive_entry_atime_is_set(a->entry)
+#if HAVE_STRUCT_STAT_ST_BIRTHTIME
+ && !archive_entry_birthtime_is_set(a->entry)
+#endif
+ && !archive_entry_mtime_is_set(a->entry))
+ return (ARCHIVE_OK);
+
+ if (archive_entry_atime_is_set(a->entry)) {
+ atime = archive_entry_atime(a->entry);
+ atime_nsec = archive_entry_atime_nsec(a->entry);
+ }
+ if (archive_entry_birthtime_is_set(a->entry)) {
+ birthtime = archive_entry_birthtime(a->entry);
+ birthtime_nsec = archive_entry_birthtime_nsec(a->entry);
+ }
+ if (archive_entry_mtime_is_set(a->entry)) {
+ mtime = archive_entry_mtime(a->entry);
+ mtime_nsec = archive_entry_mtime_nsec(a->entry);
+ }
+ if (archive_entry_ctime_is_set(a->entry)) {
+ cctime = archive_entry_ctime(a->entry);
+ ctime_nsec = archive_entry_ctime_nsec(a->entry);
+ }
+
+ return set_times(a, a->fd, a->mode, a->name,
+ atime, atime_nsec,
+ birthtime, birthtime_nsec,
+ mtime, mtime_nsec,
+ cctime, ctime_nsec);
+}
+
+static int
+set_mode(struct archive_write_disk *a, int mode)
+{
+ int r = ARCHIVE_OK;
+ int r2;
+ mode &= 07777; /* Strip off file type bits. */
+
+ if (a->todo & TODO_SGID_CHECK) {
+ /*
+ * If we don't know the GID is right, we must stat()
+ * to verify it. We can't just check the GID of this
+ * process, since systems sometimes set GID from
+ * the enclosing dir or based on ACLs.
+ */
+ if ((r = lazy_stat(a)) != ARCHIVE_OK)
+ return (r);
+ if (a->pst->st_gid != a->gid) {
+ mode &= ~ S_ISGID;
+ if (a->flags & ARCHIVE_EXTRACT_OWNER) {
+ /*
+ * This is only an error if you
+ * requested owner restore. If you
+ * didn't, we'll try to restore
+ * sgid/suid, but won't consider it a
+ * problem if we can't.
+ */
+ archive_set_error(&a->archive, -1,
+ "Can't restore SGID bit");
+ r = ARCHIVE_WARN;
+ }
+ }
+ /* While we're here, double-check the UID. */
+ if (a->pst->st_uid != a->uid
+ && (a->todo & TODO_SUID)) {
+ mode &= ~ S_ISUID;
+ if (a->flags & ARCHIVE_EXTRACT_OWNER) {
+ archive_set_error(&a->archive, -1,
+ "Can't restore SUID bit");
+ r = ARCHIVE_WARN;
+ }
+ }
+ a->todo &= ~TODO_SGID_CHECK;
+ a->todo &= ~TODO_SUID_CHECK;
+ } else if (a->todo & TODO_SUID_CHECK) {
+ /*
+ * If we don't know the UID is right, we can just check
+ * the user, since all systems set the file UID from
+ * the process UID.
+ */
+ if (a->user_uid != a->uid) {
+ mode &= ~ S_ISUID;
+ if (a->flags & ARCHIVE_EXTRACT_OWNER) {
+ archive_set_error(&a->archive, -1,
+ "Can't make file SUID");
+ r = ARCHIVE_WARN;
+ }
+ }
+ a->todo &= ~TODO_SUID_CHECK;
+ }
+
+ if (S_ISLNK(a->mode)) {
+#ifdef HAVE_LCHMOD
+ /*
+ * If this is a symlink, use lchmod(). If the
+ * platform doesn't support lchmod(), just skip it. A
+ * platform that doesn't provide a way to set
+ * permissions on symlinks probably ignores
+ * permissions on symlinks, so a failure here has no
+ * impact.
+ */
+ if (lchmod(a->name, mode) != 0) {
+ switch (errno) {
+ case ENOTSUP:
+ case ENOSYS:
+#if ENOTSUP != EOPNOTSUPP
+ case EOPNOTSUPP:
+#endif
+ /*
+ * if lchmod is defined but the platform
+ * doesn't support it, silently ignore
+ * error
+ */
+ break;
+ default:
+ archive_set_error(&a->archive, errno,
+ "Can't set permissions to 0%o", (int)mode);
+ r = ARCHIVE_WARN;
+ }
+ }
+#endif
+ } else if (!S_ISDIR(a->mode)) {
+ /*
+ * If it's not a symlink and not a dir, then use
+ * fchmod() or chmod(), depending on whether we have
+ * an fd. Dirs get their perms set during the
+ * post-extract fixup, which is handled elsewhere.
+ */
+#ifdef HAVE_FCHMOD
+ if (a->fd >= 0)
+ r2 = fchmod(a->fd, mode);
+ else
+#endif
+ /* If this platform lacks fchmod(), then
+ * we'll just use chmod(). */
+ r2 = chmod(a->name, mode);
+
+ if (r2 != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't set permissions to 0%o", (int)mode);
+ r = ARCHIVE_WARN;
+ }
+ }
+ return (r);
+}
+
+static int
+set_fflags(struct archive_write_disk *a)
+{
+ struct fixup_entry *le;
+ unsigned long set, clear;
+ int r;
+ mode_t mode = archive_entry_mode(a->entry);
+ /*
+ * Make 'critical_flags' hold all file flags that can't be
+ * immediately restored. For example, on BSD systems,
+ * SF_IMMUTABLE prevents hardlinks from being created, so
+ * should not be set until after any hardlinks are created. To
+ * preserve some semblance of portability, this uses #ifdef
+ * extensively. Ugly, but it works.
+ *
+ * Yes, Virginia, this does create a security race. It's mitigated
+ * somewhat by the practice of creating dirs 0700 until the extract
+ * is done, but it would be nice if we could do more than that.
+ * People restoring critical file systems should be wary of
+ * other programs that might try to muck with files as they're
+ * being restored.
+ */
+ const int critical_flags = 0
+#ifdef SF_IMMUTABLE
+ | SF_IMMUTABLE
+#endif
+#ifdef UF_IMMUTABLE
+ | UF_IMMUTABLE
+#endif
+#ifdef SF_APPEND
+ | SF_APPEND
+#endif
+#ifdef UF_APPEND
+ | UF_APPEND
+#endif
+#if defined(FS_APPEND_FL)
+ | FS_APPEND_FL
+#elif defined(EXT2_APPEND_FL)
+ | EXT2_APPEND_FL
+#endif
+#if defined(FS_IMMUTABLE_FL)
+ | FS_IMMUTABLE_FL
+#elif defined(EXT2_IMMUTABLE_FL)
+ | EXT2_IMMUTABLE_FL
+#endif
+#ifdef FS_JOURNAL_DATA_FL
+ | FS_JOURNAL_DATA_FL
+#endif
+ ;
+
+ if (a->todo & TODO_FFLAGS) {
+ archive_entry_fflags(a->entry, &set, &clear);
+
+ /*
+ * The first test encourages the compiler to eliminate
+ * all of this if it's not necessary.
+ */
+ if ((critical_flags != 0) && (set & critical_flags)) {
+ le = current_fixup(a, a->name);
+ if (le == NULL)
+ return (ARCHIVE_FATAL);
+ le->filetype = archive_entry_filetype(a->entry);
+ le->fixup |= TODO_FFLAGS;
+ le->fflags_set = set;
+ /* Store the mode if it's not already there. */
+ if ((le->fixup & TODO_MODE) == 0)
+ le->mode = mode;
+ } else {
+ r = set_fflags_platform(a, a->fd,
+ a->name, mode, set, clear);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+clear_nochange_fflags(struct archive_write_disk *a)
+{
+ mode_t mode = archive_entry_mode(a->entry);
+ const int nochange_flags = 0
+#ifdef SF_IMMUTABLE
+ | SF_IMMUTABLE
+#endif
+#ifdef UF_IMMUTABLE
+ | UF_IMMUTABLE
+#endif
+#ifdef SF_APPEND
+ | SF_APPEND
+#endif
+#ifdef UF_APPEND
+ | UF_APPEND
+#endif
+#ifdef EXT2_APPEND_FL
+ | EXT2_APPEND_FL
+#endif
+#ifdef EXT2_IMMUTABLE_FL
+ | EXT2_IMMUTABLE_FL
+#endif
+ ;
+
+ return (set_fflags_platform(a, a->fd, a->name, mode, 0,
+ nochange_flags));
+}
+
+
+#if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && defined(HAVE_STRUCT_STAT_ST_FLAGS)
+/*
+ * BSD reads flags using stat() and sets them with one of {f,l,}chflags()
+ */
+static int
+set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
+ mode_t mode, unsigned long set, unsigned long clear)
+{
+ int r;
+ const int sf_mask = 0
+#ifdef SF_APPEND
+ | SF_APPEND
+#endif
+#ifdef SF_ARCHIVED
+ | SF_ARCHIVED
+#endif
+#ifdef SF_IMMUTABLE
+ | SF_IMMUTABLE
+#endif
+#ifdef SF_NOUNLINK
+ | SF_NOUNLINK
+#endif
+ ;
+ (void)mode; /* UNUSED */
+
+ if (set == 0 && clear == 0)
+ return (ARCHIVE_OK);
+
+ /*
+ * XXX Is the stat here really necessary? Or can I just use
+ * the 'set' flags directly? In particular, I'm not sure
+ * about the correct approach if we're overwriting an existing
+ * file that already has flags on it. XXX
+ */
+ if ((r = lazy_stat(a)) != ARCHIVE_OK)
+ return (r);
+
+ a->st.st_flags &= ~clear;
+ a->st.st_flags |= set;
+
+ /* Only super-user may change SF_* flags */
+
+ if (a->user_uid != 0)
+ a->st.st_flags &= ~sf_mask;
+
+#ifdef HAVE_FCHFLAGS
+ /* If platform has fchflags() and we were given an fd, use it. */
+ if (fd >= 0 && fchflags(fd, a->st.st_flags) == 0)
+ return (ARCHIVE_OK);
+#endif
+ /*
+ * If we can't use the fd to set the flags, we'll use the
+ * pathname to set flags. We prefer lchflags() but will use
+ * chflags() if we must.
+ */
+#ifdef HAVE_LCHFLAGS
+ if (lchflags(name, a->st.st_flags) == 0)
+ return (ARCHIVE_OK);
+#elif defined(HAVE_CHFLAGS)
+ if (S_ISLNK(a->st.st_mode)) {
+ archive_set_error(&a->archive, errno,
+ "Can't set file flags on symlink.");
+ return (ARCHIVE_WARN);
+ }
+ if (chflags(name, a->st.st_flags) == 0)
+ return (ARCHIVE_OK);
+#endif
+ archive_set_error(&a->archive, errno,
+ "Failed to set file flags");
+ return (ARCHIVE_WARN);
+}
+
+#elif (defined(FS_IOC_GETFLAGS) && defined(FS_IOC_SETFLAGS) && \
+ defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \
+ (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) && \
+ defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))
+/*
+ * Linux uses ioctl() to read and write file flags.
+ */
+static int
+set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
+ mode_t mode, unsigned long set, unsigned long clear)
+{
+ int ret;
+ int myfd = fd;
+ int newflags, oldflags;
+ /*
+ * Linux has no define for the flags that are only settable by
+ * the root user. This code may seem a little complex, but
+ * there seem to be some Linux systems that lack these
+ * defines. (?) The code below degrades reasonably gracefully
+ * if sf_mask is incomplete.
+ */
+ const int sf_mask = 0
+#if defined(FS_IMMUTABLE_FL)
+ | FS_IMMUTABLE_FL
+#elif defined(EXT2_IMMUTABLE_FL)
+ | EXT2_IMMUTABLE_FL
+#endif
+#if defined(FS_APPEND_FL)
+ | FS_APPEND_FL
+#elif defined(EXT2_APPEND_FL)
+ | EXT2_APPEND_FL
+#endif
+#if defined(FS_JOURNAL_DATA_FL)
+ | FS_JOURNAL_DATA_FL
+#endif
+ ;
+
+ if (set == 0 && clear == 0)
+ return (ARCHIVE_OK);
+ /* Only regular files and dirs can have flags. */
+ if (!S_ISREG(mode) && !S_ISDIR(mode))
+ return (ARCHIVE_OK);
+
+ /* If we weren't given an fd, open it ourselves. */
+ if (myfd < 0) {
+ myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY |
+ O_CLOEXEC | O_NOFOLLOW);
+ __archive_ensure_cloexec_flag(myfd);
+ }
+ if (myfd < 0)
+ return (ARCHIVE_OK);
+
+ /*
+ * XXX As above, this would be way simpler if we didn't have
+ * to read the current flags from disk. XXX
+ */
+ ret = ARCHIVE_OK;
+
+ /* Read the current file flags. */
+ if (ioctl(myfd,
+#ifdef FS_IOC_GETFLAGS
+ FS_IOC_GETFLAGS,
+#else
+ EXT2_IOC_GETFLAGS,
+#endif
+ &oldflags) < 0)
+ goto fail;
+
+ /* Try setting the flags as given. */
+ newflags = (oldflags & ~clear) | set;
+ if (ioctl(myfd,
+#ifdef FS_IOC_SETFLAGS
+ FS_IOC_SETFLAGS,
+#else
+ EXT2_IOC_SETFLAGS,
+#endif
+ &newflags) >= 0)
+ goto cleanup;
+ if (errno != EPERM)
+ goto fail;
+
+ /* If we couldn't set all the flags, try again with a subset. */
+ newflags &= ~sf_mask;
+ oldflags &= sf_mask;
+ newflags |= oldflags;
+ if (ioctl(myfd,
+#ifdef FS_IOC_SETFLAGS
+ FS_IOC_SETFLAGS,
+#else
+ EXT2_IOC_SETFLAGS,
+#endif
+ &newflags) >= 0)
+ goto cleanup;
+
+ /* We couldn't set the flags, so report the failure. */
+fail:
+ archive_set_error(&a->archive, errno,
+ "Failed to set file flags");
+ ret = ARCHIVE_WARN;
+cleanup:
+ if (fd < 0)
+ close(myfd);
+ return (ret);
+}
+
+#else
+
+/*
+ * Of course, some systems have neither BSD chflags() nor Linux' flags
+ * support through ioctl().
+ */
+static int
+set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
+ mode_t mode, unsigned long set, unsigned long clear)
+{
+ (void)a; /* UNUSED */
+ (void)fd; /* UNUSED */
+ (void)name; /* UNUSED */
+ (void)mode; /* UNUSED */
+ (void)set; /* UNUSED */
+ (void)clear; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+#endif /* __linux */
+
+#ifndef HAVE_COPYFILE_H
+/* Default is to simply drop Mac extended metadata. */
+static int
+set_mac_metadata(struct archive_write_disk *a, const char *pathname,
+ const void *metadata, size_t metadata_size)
+{
+ (void)a; /* UNUSED */
+ (void)pathname; /* UNUSED */
+ (void)metadata; /* UNUSED */
+ (void)metadata_size; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+static int
+fixup_appledouble(struct archive_write_disk *a, const char *pathname)
+{
+ (void)a; /* UNUSED */
+ (void)pathname; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+#else
+
+/*
+ * On Mac OS, we use copyfile() to unpack the metadata and
+ * apply it to the target file.
+ */
+
+#if defined(HAVE_SYS_XATTR_H)
+static int
+copy_xattrs(struct archive_write_disk *a, int tmpfd, int dffd)
+{
+ ssize_t xattr_size;
+ char *xattr_names = NULL, *xattr_val = NULL;
+ int ret = ARCHIVE_OK, xattr_i;
+
+ xattr_size = flistxattr(tmpfd, NULL, 0, 0);
+ if (xattr_size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to read metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ goto exit_xattr;
+ }
+ xattr_names = malloc(xattr_size);
+ if (xattr_names == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for metadata(xattr)");
+ ret = ARCHIVE_FATAL;
+ goto exit_xattr;
+ }
+ xattr_size = flistxattr(tmpfd, xattr_names, xattr_size, 0);
+ if (xattr_size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to read metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ goto exit_xattr;
+ }
+ for (xattr_i = 0; xattr_i < xattr_size;
+ xattr_i += strlen(xattr_names + xattr_i) + 1) {
+ char *xattr_val_saved;
+ ssize_t s;
+ int f;
+
+ s = fgetxattr(tmpfd, xattr_names + xattr_i, NULL, 0, 0, 0);
+ if (s == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ goto exit_xattr;
+ }
+ xattr_val_saved = xattr_val;
+ xattr_val = realloc(xattr_val, s);
+ if (xattr_val == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Failed to get metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ free(xattr_val_saved);
+ goto exit_xattr;
+ }
+ s = fgetxattr(tmpfd, xattr_names + xattr_i, xattr_val, s, 0, 0);
+ if (s == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ goto exit_xattr;
+ }
+ f = fsetxattr(dffd, xattr_names + xattr_i, xattr_val, s, 0, 0);
+ if (f == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ goto exit_xattr;
+ }
+ }
+exit_xattr:
+ free(xattr_names);
+ free(xattr_val);
+ return (ret);
+}
+#endif
+
+static int
+copy_acls(struct archive_write_disk *a, int tmpfd, int dffd)
+{
+#ifndef HAVE_SYS_ACL_H
+ return 0;
+#else
+ acl_t acl, dfacl = NULL;
+ int acl_r, ret = ARCHIVE_OK;
+
+ acl = acl_get_fd(tmpfd);
+ if (acl == NULL) {
+ if (errno == ENOENT)
+ /* There are not any ACLs. */
+ return (ret);
+ archive_set_error(&a->archive, errno,
+ "Failed to get metadata(acl)");
+ ret = ARCHIVE_WARN;
+ goto exit_acl;
+ }
+ dfacl = acl_dup(acl);
+ acl_r = acl_set_fd(dffd, dfacl);
+ if (acl_r == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get metadata(acl)");
+ ret = ARCHIVE_WARN;
+ goto exit_acl;
+ }
+exit_acl:
+ if (acl)
+ acl_free(acl);
+ if (dfacl)
+ acl_free(dfacl);
+ return (ret);
+#endif
+}
+
+static int
+create_tempdatafork(struct archive_write_disk *a, const char *pathname)
+{
+ struct archive_string tmpdatafork;
+ int tmpfd;
+
+ archive_string_init(&tmpdatafork);
+ archive_strcpy(&tmpdatafork, "tar.md.XXXXXX");
+ tmpfd = mkstemp(tmpdatafork.s);
+ if (tmpfd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to mkstemp");
+ archive_string_free(&tmpdatafork);
+ return (-1);
+ }
+ if (copyfile(pathname, tmpdatafork.s, 0,
+ COPYFILE_UNPACK | COPYFILE_NOFOLLOW
+ | COPYFILE_ACL | COPYFILE_XATTR) < 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to restore metadata");
+ close(tmpfd);
+ tmpfd = -1;
+ }
+ unlink(tmpdatafork.s);
+ archive_string_free(&tmpdatafork);
+ return (tmpfd);
+}
+
+static int
+copy_metadata(struct archive_write_disk *a, const char *metadata,
+ const char *datafork, int datafork_compressed)
+{
+ int ret = ARCHIVE_OK;
+
+ if (datafork_compressed) {
+ int dffd, tmpfd;
+
+ tmpfd = create_tempdatafork(a, metadata);
+ if (tmpfd == -1)
+ return (ARCHIVE_WARN);
+
+ /*
+ * Do not open the data fork compressed by HFS+ compression
+ * with at least a writing mode(O_RDWR or O_WRONLY). it
+ * makes the data fork uncompressed.
+ */
+ dffd = open(datafork, 0);
+ if (dffd == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to open the data fork for metadata");
+ close(tmpfd);
+ return (ARCHIVE_WARN);
+ }
+
+#if defined(HAVE_SYS_XATTR_H)
+ ret = copy_xattrs(a, tmpfd, dffd);
+ if (ret == ARCHIVE_OK)
+#endif
+ ret = copy_acls(a, tmpfd, dffd);
+ close(tmpfd);
+ close(dffd);
+ } else {
+ if (copyfile(metadata, datafork, 0,
+ COPYFILE_UNPACK | COPYFILE_NOFOLLOW
+ | COPYFILE_ACL | COPYFILE_XATTR) < 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to restore metadata");
+ ret = ARCHIVE_WARN;
+ }
+ }
+ return (ret);
+}
+
+static int
+set_mac_metadata(struct archive_write_disk *a, const char *pathname,
+ const void *metadata, size_t metadata_size)
+{
+ struct archive_string tmp;
+ ssize_t written;
+ int fd;
+ int ret = ARCHIVE_OK;
+
+ /* This would be simpler if copyfile() could just accept the
+ * metadata as a block of memory; then we could sidestep this
+ * silly dance of writing the data to disk just so that
+ * copyfile() can read it back in again. */
+ archive_string_init(&tmp);
+ archive_strcpy(&tmp, pathname);
+ archive_strcat(&tmp, ".XXXXXX");
+ fd = mkstemp(tmp.s);
+
+ if (fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to restore metadata");
+ archive_string_free(&tmp);
+ return (ARCHIVE_WARN);
+ }
+ written = write(fd, metadata, metadata_size);
+ close(fd);
+ if ((size_t)written != metadata_size) {
+ archive_set_error(&a->archive, errno,
+ "Failed to restore metadata");
+ ret = ARCHIVE_WARN;
+ } else {
+ int compressed;
+
+#if defined(UF_COMPRESSED)
+ if ((a->todo & TODO_HFS_COMPRESSION) != 0 &&
+ (ret = lazy_stat(a)) == ARCHIVE_OK)
+ compressed = a->st.st_flags & UF_COMPRESSED;
+ else
+#endif
+ compressed = 0;
+ ret = copy_metadata(a, tmp.s, pathname, compressed);
+ }
+ unlink(tmp.s);
+ archive_string_free(&tmp);
+ return (ret);
+}
+
+static int
+fixup_appledouble(struct archive_write_disk *a, const char *pathname)
+{
+ char buff[8];
+ struct stat st;
+ const char *p;
+ struct archive_string datafork;
+ int fd = -1, ret = ARCHIVE_OK;
+
+ archive_string_init(&datafork);
+ /* Check if the current file name is a type of the resource
+ * fork file. */
+ p = strrchr(pathname, '/');
+ if (p == NULL)
+ p = pathname;
+ else
+ p++;
+ if (p[0] != '.' || p[1] != '_')
+ goto skip_appledouble;
+
+ /*
+ * Check if the data fork file exists.
+ *
+ * TODO: Check if this write disk object has handled it.
+ */
+ archive_strncpy(&datafork, pathname, p - pathname);
+ archive_strcat(&datafork, p + 2);
+ if (
+#ifdef HAVE_LSTAT
+ lstat(datafork.s, &st) == -1 ||
+#else
+ la_stat(datafork.s, &st) == -1 ||
+#endif
+ (st.st_mode & AE_IFMT) != AE_IFREG)
+ goto skip_appledouble;
+
+ /*
+ * Check if the file is in the AppleDouble form.
+ */
+ fd = open(pathname, O_RDONLY | O_BINARY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
+ if (fd == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to open a restoring file");
+ ret = ARCHIVE_WARN;
+ goto skip_appledouble;
+ }
+ if (read(fd, buff, 8) == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to read a restoring file");
+ close(fd);
+ ret = ARCHIVE_WARN;
+ goto skip_appledouble;
+ }
+ close(fd);
+ /* Check AppleDouble Magic Code. */
+ if (archive_be32dec(buff) != 0x00051607)
+ goto skip_appledouble;
+ /* Check AppleDouble Version. */
+ if (archive_be32dec(buff+4) != 0x00020000)
+ goto skip_appledouble;
+
+ ret = copy_metadata(a, pathname, datafork.s,
+#if defined(UF_COMPRESSED)
+ st.st_flags & UF_COMPRESSED);
+#else
+ 0);
+#endif
+ if (ret == ARCHIVE_OK) {
+ unlink(pathname);
+ ret = ARCHIVE_EOF;
+ }
+skip_appledouble:
+ archive_string_free(&datafork);
+ return (ret);
+}
+#endif
+
+#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX
+/*
+ * Restore extended attributes - Linux, Darwin and AIX implementations:
+ * AIX' ea interface is syntaxwise identical to the Linux xattr interface.
+ */
+static int
+set_xattrs(struct archive_write_disk *a)
+{
+ struct archive_entry *entry = a->entry;
+ struct archive_string errlist;
+ int ret = ARCHIVE_OK;
+ int i = archive_entry_xattr_reset(entry);
+ short fail = 0;
+
+ archive_string_init(&errlist);
+
+ while (i--) {
+ const char *name;
+ const void *value;
+ size_t size;
+ int e;
+
+ archive_entry_xattr_next(entry, &name, &value, &size);
+
+ if (name == NULL)
+ continue;
+#if ARCHIVE_XATTR_LINUX
+ /* Linux: quietly skip POSIX.1e ACL extended attributes */
+ if (strncmp(name, "system.", 7) == 0 &&
+ (strcmp(name + 7, "posix_acl_access") == 0 ||
+ strcmp(name + 7, "posix_acl_default") == 0))
+ continue;
+ if (strncmp(name, "trusted.SGI_", 12) == 0 &&
+ (strcmp(name + 12, "ACL_DEFAULT") == 0 ||
+ strcmp(name + 12, "ACL_FILE") == 0))
+ continue;
+
+ /* Linux: xfsroot namespace is obsolete and unsupported */
+ if (strncmp(name, "xfsroot.", 8) == 0) {
+ fail = 1;
+ archive_strcat(&errlist, name);
+ archive_strappend_char(&errlist, ' ');
+ continue;
+ }
+#endif
+
+ if (a->fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
+ e = fsetxattr(a->fd, name, value, size, 0);
+#elif ARCHIVE_XATTR_DARWIN
+ e = fsetxattr(a->fd, name, value, size, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+ e = fsetea(a->fd, name, value, size, 0);
+#endif
+ } else {
+#if ARCHIVE_XATTR_LINUX
+ e = lsetxattr(archive_entry_pathname(entry),
+ name, value, size, 0);
+#elif ARCHIVE_XATTR_DARWIN
+ e = setxattr(archive_entry_pathname(entry),
+ name, value, size, 0, XATTR_NOFOLLOW);
+#elif ARCHIVE_XATTR_AIX
+ e = lsetea(archive_entry_pathname(entry),
+ name, value, size, 0);
+#endif
+ }
+ if (e == -1) {
+ ret = ARCHIVE_WARN;
+ archive_strcat(&errlist, name);
+ archive_strappend_char(&errlist, ' ');
+ if (errno != ENOTSUP && errno != ENOSYS)
+ fail = 1;
+ }
+ }
+
+ if (ret == ARCHIVE_WARN) {
+ if (fail && errlist.length > 0) {
+ errlist.length--;
+ errlist.s[errlist.length] = '\0';
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot restore extended attributes: %s",
+ errlist.s);
+ } else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot restore extended "
+ "attributes on this file system.");
+ }
+
+ archive_string_free(&errlist);
+ return (ret);
+}
+#elif ARCHIVE_XATTR_FREEBSD
+/*
+ * Restore extended attributes - FreeBSD implementation
+ */
+static int
+set_xattrs(struct archive_write_disk *a)
+{
+ struct archive_entry *entry = a->entry;
+ struct archive_string errlist;
+ int ret = ARCHIVE_OK;
+ int i = archive_entry_xattr_reset(entry);
+ short fail = 0;
+
+ archive_string_init(&errlist);
+
+ while (i--) {
+ const char *name;
+ const void *value;
+ size_t size;
+ archive_entry_xattr_next(entry, &name, &value, &size);
+ if (name != NULL) {
+ int e;
+ int namespace;
+
+ namespace = EXTATTR_NAMESPACE_USER;
+
+ if (strncmp(name, "user.", 5) == 0) {
+ /* "user." attributes go to user namespace */
+ name += 5;
+ namespace = EXTATTR_NAMESPACE_USER;
+ } else if (strncmp(name, "system.", 7) == 0) {
+ name += 7;
+ namespace = EXTATTR_NAMESPACE_SYSTEM;
+ if (!strcmp(name, "nfs4.acl") ||
+ !strcmp(name, "posix1e.acl_access") ||
+ !strcmp(name, "posix1e.acl_default"))
+ continue;
+ } else {
+ /* Other namespaces are unsupported */
+ archive_strcat(&errlist, name);
+ archive_strappend_char(&errlist, ' ');
+ fail = 1;
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+
+ if (a->fd >= 0) {
+ /*
+ * On FreeBSD, extattr_set_fd does not
+ * return the same as
+ * extattr_set_file. It returns zero
+ * on success, non-zero on failure.
+ *
+ * We can detect the failure by
+ * manually setting errno prior to the
+ * call and checking after.
+ *
+ * If errno remains zero, fake the
+ * return value by setting e to size.
+ *
+ * This is a hack for now until I
+ * (Shawn Webb) get FreeBSD to fix the
+ * issue, if that's even possible.
+ */
+ errno = 0;
+ e = extattr_set_fd(a->fd, namespace, name,
+ value, size);
+ if (e == 0 && errno == 0) {
+ e = size;
+ }
+ } else {
+ e = extattr_set_link(
+ archive_entry_pathname(entry), namespace,
+ name, value, size);
+ }
+ if (e != (int)size) {
+ archive_strcat(&errlist, name);
+ archive_strappend_char(&errlist, ' ');
+ ret = ARCHIVE_WARN;
+ if (errno != ENOTSUP && errno != ENOSYS)
+ fail = 1;
+ }
+ }
+ }
+
+ if (ret == ARCHIVE_WARN) {
+ if (fail && errlist.length > 0) {
+ errlist.length--;
+ errlist.s[errlist.length] = '\0';
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot restore extended attributes: %s",
+ errlist.s);
+ } else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot restore extended "
+ "attributes on this file system.");
+ }
+
+ archive_string_free(&errlist);
+ return (ret);
+}
+#else
+/*
+ * Restore extended attributes - stub implementation for unsupported systems
+ */
+static int
+set_xattrs(struct archive_write_disk *a)
+{
+ static int warning_done = 0;
+
+ /* If there aren't any extended attributes, then it's okay not
+ * to extract them, otherwise, issue a single warning. */
+ if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) {
+ warning_done = 1;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Cannot restore extended attributes on this system");
+ return (ARCHIVE_WARN);
+ }
+ /* Warning was already emitted; suppress further warnings. */
+ return (ARCHIVE_OK);
+}
+#endif
+
+/*
+ * Test if file on disk is older than entry.
+ */
+static int
+older(struct stat *st, struct archive_entry *entry)
+{
+ /* First, test the seconds and return if we have a definite answer. */
+ /* Definitely older. */
+ if (to_int64_time(st->st_mtime) < to_int64_time(archive_entry_mtime(entry)))
+ return (1);
+ /* Definitely younger. */
+ if (to_int64_time(st->st_mtime) > to_int64_time(archive_entry_mtime(entry)))
+ return (0);
+ /* If this platform supports fractional seconds, try those. */
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+ /* Definitely older. */
+ if (st->st_mtimespec.tv_nsec < archive_entry_mtime_nsec(entry))
+ return (1);
+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+ /* Definitely older. */
+ if (st->st_mtim.tv_nsec < archive_entry_mtime_nsec(entry))
+ return (1);
+#elif HAVE_STRUCT_STAT_ST_MTIME_N
+ /* older. */
+ if (st->st_mtime_n < archive_entry_mtime_nsec(entry))
+ return (1);
+#elif HAVE_STRUCT_STAT_ST_UMTIME
+ /* older. */
+ if (st->st_umtime * 1000 < archive_entry_mtime_nsec(entry))
+ return (1);
+#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
+ /* older. */
+ if (st->st_mtime_usec * 1000 < archive_entry_mtime_nsec(entry))
+ return (1);
+#else
+ /* This system doesn't have high-res timestamps. */
+#endif
+ /* Same age or newer, so not older. */
+ return (0);
+}
+
+#ifndef ARCHIVE_ACL_SUPPORT
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+ struct archive_acl *abstract_acl, __LA_MODE_T mode)
+{
+ (void)a; /* UNUSED */
+ (void)fd; /* UNUSED */
+ (void)name; /* UNUSED */
+ (void)abstract_acl; /* UNUSED */
+ (void)mode; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+#endif
+
+/*
+ * Close the file descriptor if one is open.
+ */
+static void close_file_descriptor(struct archive_write_disk* a)
+{
+ if (a->fd >= 0) {
+ close(a->fd);
+ a->fd = -1;
+ }
+}
+
+
+#endif /* !_WIN32 || __CYGWIN__ */
+
diff --git a/contrib/libs/libarchive/libarchive/archive_write_disk_private.h b/contrib/libs/libarchive/libarchive/archive_write_disk_private.h
new file mode 100644
index 00000000000..557d7e2bf34
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_disk_private.h
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_write_disk_private.h 201086 2009-12-28 02:17:53Z kientzle $
+ */
+
+#ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED
+#define ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include "archive_platform_acl.h"
+#include "archive_acl_private.h"
+#include "archive_entry.h"
+
+struct archive_write_disk;
+
+int archive_write_disk_set_acls(struct archive *, int, const char *,
+ struct archive_acl *, __LA_MODE_T);
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_write_disk_set_standard_lookup.c b/contrib/libs/libarchive/libarchive/archive_write_disk_set_standard_lookup.c
new file mode 100644
index 00000000000..5fccdb9dc65
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_disk_set_standard_lookup.c
@@ -0,0 +1,263 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_disk_set_standard_lookup.c 201083 2009-12-28 02:09:57Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_write_disk_private.h"
+
+struct bucket {
+ char *name;
+ int hash;
+ id_t id;
+};
+
+static const size_t cache_size = 127;
+static unsigned int hash(const char *);
+static int64_t lookup_gid(void *, const char *uname, int64_t);
+static int64_t lookup_uid(void *, const char *uname, int64_t);
+static void cleanup(void *);
+
+/*
+ * Installs functions that use getpwnam()/getgrnam()---along with
+ * a simple cache to accelerate such lookups---into the archive_write_disk
+ * object. This is in a separate file because getpwnam()/getgrnam()
+ * can pull in a LOT of library code (including NIS/LDAP functions, which
+ * pull in DNS resolvers, etc). This can easily top 500kB, which makes
+ * it inappropriate for some space-constrained applications.
+ *
+ * Applications that are size-sensitive may want to just use the
+ * real default functions (defined in archive_write_disk.c) that just
+ * use the uid/gid without the lookup. Or define your own custom functions
+ * if you prefer.
+ *
+ * TODO: Replace these hash tables with simpler move-to-front LRU
+ * lists with a bounded size (128 items?). The hash is a bit faster,
+ * but has a bad pathology in which it thrashes a single bucket. Even
+ * walking a list of 128 items is a lot faster than calling
+ * getpwnam()!
+ */
+int
+archive_write_disk_set_standard_lookup(struct archive *a)
+{
+ struct bucket *ucache = calloc(cache_size, sizeof(struct bucket));
+ struct bucket *gcache = calloc(cache_size, sizeof(struct bucket));
+ if (ucache == NULL || gcache == NULL) {
+ free(ucache);
+ free(gcache);
+ return (ARCHIVE_FATAL);
+ }
+ archive_write_disk_set_group_lookup(a, gcache, lookup_gid, cleanup);
+ archive_write_disk_set_user_lookup(a, ucache, lookup_uid, cleanup);
+ return (ARCHIVE_OK);
+}
+
+static int64_t
+lookup_gid(void *private_data, const char *gname, int64_t gid)
+{
+ int h;
+ struct bucket *b;
+ struct bucket *gcache = (struct bucket *)private_data;
+
+ /* If no gname, just use the gid provided. */
+ if (gname == NULL || *gname == '\0')
+ return (gid);
+
+ /* Try to find gname in the cache. */
+ h = hash(gname);
+ b = &gcache[h % cache_size ];
+ if (b->name != NULL && b->hash == h && strcmp(gname, b->name) == 0)
+ return ((gid_t)b->id);
+
+ /* Free the cache slot for a new entry. */
+ free(b->name);
+ b->name = strdup(gname);
+ /* Note: If strdup fails, that's okay; we just won't cache. */
+ b->hash = h;
+#if HAVE_GRP_H
+# if HAVE_GETGRNAM_R
+ {
+ char _buffer[128];
+ size_t bufsize = 128;
+ char *buffer = _buffer;
+ char *allocated = NULL;
+ struct group grent, *result;
+ int r;
+
+ for (;;) {
+ result = &grent; /* Old getgrnam_r ignores last arg. */
+ r = getgrnam_r(gname, &grent, buffer, bufsize, &result);
+ if (r == 0)
+ break;
+ if (r != ERANGE)
+ break;
+ bufsize *= 2;
+ free(allocated);
+ allocated = malloc(bufsize);
+ if (allocated == NULL)
+ break;
+ buffer = allocated;
+ }
+ if (result != NULL)
+ gid = result->gr_gid;
+ free(allocated);
+ }
+# else /* HAVE_GETGRNAM_R */
+ {
+ struct group *result;
+
+ result = getgrnam(gname);
+ if (result != NULL)
+ gid = result->gr_gid;
+ }
+# endif /* HAVE_GETGRNAM_R */
+#elif defined(_WIN32) && !defined(__CYGWIN__)
+ /* TODO: do a gname->gid lookup for Windows. */
+#else
+ #error No way to perform gid lookups on this platform
+#endif
+ b->id = (gid_t)gid;
+
+ return (gid);
+}
+
+static int64_t
+lookup_uid(void *private_data, const char *uname, int64_t uid)
+{
+ int h;
+ struct bucket *b;
+ struct bucket *ucache = (struct bucket *)private_data;
+
+ /* If no uname, just use the uid provided. */
+ if (uname == NULL || *uname == '\0')
+ return (uid);
+
+ /* Try to find uname in the cache. */
+ h = hash(uname);
+ b = &ucache[h % cache_size ];
+ if (b->name != NULL && b->hash == h && strcmp(uname, b->name) == 0)
+ return ((uid_t)b->id);
+
+ /* Free the cache slot for a new entry. */
+ free(b->name);
+ b->name = strdup(uname);
+ /* Note: If strdup fails, that's okay; we just won't cache. */
+ b->hash = h;
+#if HAVE_PWD_H
+# if HAVE_GETPWNAM_R
+ {
+ char _buffer[128];
+ size_t bufsize = 128;
+ char *buffer = _buffer;
+ char *allocated = NULL;
+ struct passwd pwent, *result;
+ int r;
+
+ for (;;) {
+ result = &pwent; /* Old getpwnam_r ignores last arg. */
+ r = getpwnam_r(uname, &pwent, buffer, bufsize, &result);
+ if (r == 0)
+ break;
+ if (r != ERANGE)
+ break;
+ bufsize *= 2;
+ free(allocated);
+ allocated = malloc(bufsize);
+ if (allocated == NULL)
+ break;
+ buffer = allocated;
+ }
+ if (result != NULL)
+ uid = result->pw_uid;
+ free(allocated);
+ }
+# else /* HAVE_GETPWNAM_R */
+ {
+ struct passwd *result;
+
+ result = getpwnam(uname);
+ if (result != NULL)
+ uid = result->pw_uid;
+ }
+#endif /* HAVE_GETPWNAM_R */
+#elif defined(_WIN32) && !defined(__CYGWIN__)
+ /* TODO: do a uname->uid lookup for Windows. */
+#else
+ #error No way to look up uids on this platform
+#endif
+ b->id = (uid_t)uid;
+
+ return (uid);
+}
+
+static void
+cleanup(void *private)
+{
+ size_t i;
+ struct bucket *cache = (struct bucket *)private;
+
+ for (i = 0; i < cache_size; i++)
+ free(cache[i].name);
+ free(cache);
+}
+
+
+static unsigned int
+hash(const char *p)
+{
+ /* A 32-bit version of Peter Weinberger's (PJW) hash algorithm,
+ as used by ELF for hashing function names. */
+ unsigned g, h = 0;
+ while (*p != '\0') {
+ h = (h << 4) + *p++;
+ if ((g = h & 0xF0000000) != 0) {
+ h ^= g >> 24;
+ h &= 0x0FFFFFFF;
+ }
+ }
+ return h;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_disk_windows.c b/contrib/libs/libarchive/libarchive/archive_write_disk_windows.c
new file mode 100644
index 00000000000..7b9ea74937c
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_disk_windows.c
@@ -0,0 +1,2911 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_UTIME_H
+#include <sys/utime.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <winioctl.h>
+
+/* TODO: Support Mac OS 'quarantine' feature. This is really just a
+ * standard tag to mark files that have been downloaded as "tainted".
+ * On Mac OS, we should mark the extracted files as tainted if the
+ * archive being read was tainted. Windows has a similar feature; we
+ * should investigate ways to support this generically. */
+
+#include "archive.h"
+#include "archive_acl_private.h"
+#include "archive_string.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef IO_REPARSE_TAG_SYMLINK
+/* Old SDKs do not provide IO_REPARSE_TAG_SYMLINK */
+#define IO_REPARSE_TAG_SYMLINK 0xA000000CL
+#endif
+
+static BOOL SetFilePointerEx_perso(HANDLE hFile,
+ LARGE_INTEGER liDistanceToMove,
+ PLARGE_INTEGER lpNewFilePointer,
+ DWORD dwMoveMethod)
+{
+ LARGE_INTEGER li;
+ li.QuadPart = liDistanceToMove.QuadPart;
+ li.LowPart = SetFilePointer(
+ hFile, li.LowPart, &li.HighPart, dwMoveMethod);
+ if(lpNewFilePointer) {
+ lpNewFilePointer->QuadPart = li.QuadPart;
+ }
+ return li.LowPart != (DWORD)-1 || GetLastError() == NO_ERROR;
+}
+
+struct fixup_entry {
+ struct fixup_entry *next;
+ struct archive_acl acl;
+ mode_t mode;
+ int64_t atime;
+ int64_t birthtime;
+ int64_t mtime;
+ int64_t ctime;
+ unsigned long atime_nanos;
+ unsigned long birthtime_nanos;
+ unsigned long mtime_nanos;
+ unsigned long ctime_nanos;
+ unsigned long fflags_set;
+ int fixup; /* bitmask of what needs fixing */
+ wchar_t *name;
+};
+
+/*
+ * We use a bitmask to track which operations remain to be done for
+ * this file. In particular, this helps us avoid unnecessary
+ * operations when it's possible to take care of one step as a
+ * side-effect of another. For example, mkdir() can specify the mode
+ * for the newly-created object but symlink() cannot. This means we
+ * can skip chmod() if mkdir() succeeded, but we must explicitly
+ * chmod() if we're trying to create a directory that already exists
+ * (mkdir() failed) or if we're restoring a symlink. Similarly, we
+ * need to verify UID/GID before trying to restore SUID/SGID bits;
+ * that verification can occur explicitly through a stat() call or
+ * implicitly because of a successful chown() call.
+ */
+#define TODO_MODE_FORCE 0x40000000
+#define TODO_MODE_BASE 0x20000000
+#define TODO_SUID 0x10000000
+#define TODO_SUID_CHECK 0x08000000
+#define TODO_SGID 0x04000000
+#define TODO_SGID_CHECK 0x02000000
+#define TODO_MODE (TODO_MODE_BASE|TODO_SUID|TODO_SGID)
+#define TODO_TIMES ARCHIVE_EXTRACT_TIME
+#define TODO_OWNER ARCHIVE_EXTRACT_OWNER
+#define TODO_FFLAGS ARCHIVE_EXTRACT_FFLAGS
+#define TODO_ACLS ARCHIVE_EXTRACT_ACL
+#define TODO_XATTR ARCHIVE_EXTRACT_XATTR
+#define TODO_MAC_METADATA ARCHIVE_EXTRACT_MAC_METADATA
+
+struct archive_write_disk {
+ struct archive archive;
+
+ mode_t user_umask;
+ struct fixup_entry *fixup_list;
+ struct fixup_entry *current_fixup;
+ int64_t user_uid;
+ int skip_file_set;
+ int64_t skip_file_dev;
+ int64_t skip_file_ino;
+ time_t start_time;
+
+ int64_t (*lookup_gid)(void *private, const char *gname, int64_t gid);
+ void (*cleanup_gid)(void *private);
+ void *lookup_gid_data;
+ int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid);
+ void (*cleanup_uid)(void *private);
+ void *lookup_uid_data;
+
+ /*
+ * Full path of last file to satisfy symlink checks.
+ */
+ struct archive_wstring path_safe;
+
+ /*
+ * Cached stat data from disk for the current entry.
+ * If this is valid, pst points to st. Otherwise,
+ * pst is null.
+ */
+ BY_HANDLE_FILE_INFORMATION st;
+ BY_HANDLE_FILE_INFORMATION *pst;
+
+ /* Information about the object being restored right now. */
+ struct archive_entry *entry; /* Entry being extracted. */
+ wchar_t *name; /* Name of entry, possibly edited. */
+ struct archive_wstring _name_data; /* backing store for 'name' */
+ wchar_t *tmpname; /* Temporary name */
+ struct archive_wstring _tmpname_data; /* backing store for 'tmpname' */
+ /* Tasks remaining for this object. */
+ int todo;
+ /* Tasks deferred until end-of-archive. */
+ int deferred;
+ /* Options requested by the client. */
+ int flags;
+ /* Handle for the file we're restoring. */
+ HANDLE fh;
+ /* Current offset for writing data to the file. */
+ int64_t offset;
+ /* Last offset actually written to disk. */
+ int64_t fd_offset;
+ /* Total bytes actually written to files. */
+ int64_t total_bytes_written;
+ /* Maximum size of file, -1 if unknown. */
+ int64_t filesize;
+ /* Dir we were in before this restore; only for deep paths. */
+ int restore_pwd;
+ /* Mode we should use for this entry; affected by _PERM and umask. */
+ mode_t mode;
+ /* UID/GID to use in restoring this entry. */
+ int64_t uid;
+ int64_t gid;
+};
+
+/*
+ * Default mode for dirs created automatically (will be modified by umask).
+ * Note that POSIX specifies 0777 for implicitly-created dirs, "modified
+ * by the process' file creation mask."
+ */
+#define DEFAULT_DIR_MODE 0777
+/*
+ * Dir modes are restored in two steps: During the extraction, the permissions
+ * in the archive are modified to match the following limits. During
+ * the post-extract fixup pass, the permissions from the archive are
+ * applied.
+ */
+#define MINIMUM_DIR_MODE 0700
+#define MAXIMUM_DIR_MODE 0775
+
+static int disk_unlink(const wchar_t *);
+static int disk_rmdir(const wchar_t *);
+static int check_symlinks(struct archive_write_disk *);
+static int create_filesystem_object(struct archive_write_disk *);
+static struct fixup_entry *current_fixup(struct archive_write_disk *,
+ const wchar_t *pathname);
+static int cleanup_pathname(struct archive_write_disk *, wchar_t *);
+static int create_dir(struct archive_write_disk *, wchar_t *);
+static int create_parent_dir(struct archive_write_disk *, wchar_t *);
+static int la_chmod(const wchar_t *, mode_t);
+static int la_mktemp(struct archive_write_disk *);
+static int older(BY_HANDLE_FILE_INFORMATION *, struct archive_entry *);
+static int permissive_name_w(struct archive_write_disk *);
+static int restore_entry(struct archive_write_disk *);
+static int set_acls(struct archive_write_disk *, HANDLE h,
+ const wchar_t *, struct archive_acl *);
+static int set_xattrs(struct archive_write_disk *);
+static int clear_nochange_fflags(struct archive_write_disk *);
+static int set_fflags(struct archive_write_disk *);
+static int set_fflags_platform(const wchar_t *, unsigned long,
+ unsigned long);
+static int set_ownership(struct archive_write_disk *);
+static int set_mode(struct archive_write_disk *, int mode);
+static int set_times(struct archive_write_disk *, HANDLE, int,
+ const wchar_t *, time_t, long, time_t, long, time_t,
+ long, time_t, long);
+static int set_times_from_entry(struct archive_write_disk *);
+static struct fixup_entry *sort_dir_list(struct fixup_entry *p);
+static ssize_t write_data_block(struct archive_write_disk *,
+ const char *, size_t);
+
+static int _archive_write_disk_close(struct archive *);
+static int _archive_write_disk_free(struct archive *);
+static int _archive_write_disk_header(struct archive *,
+ struct archive_entry *);
+static int64_t _archive_write_disk_filter_bytes(struct archive *, int);
+static int _archive_write_disk_finish_entry(struct archive *);
+static ssize_t _archive_write_disk_data(struct archive *, const void *,
+ size_t);
+static ssize_t _archive_write_disk_data_block(struct archive *, const void *,
+ size_t, int64_t);
+
+#define bhfi_dev(bhfi) ((bhfi)->dwVolumeSerialNumber)
+/* Treat FileIndex as i-node. We should remove a sequence number
+ * which is high-16-bits of nFileIndexHigh. */
+#define bhfi_ino(bhfi) \
+ ((((int64_t)((bhfi)->nFileIndexHigh & 0x0000FFFFUL)) << 32) \
+ | (bhfi)->nFileIndexLow)
+#define bhfi_size(bhfi) \
+ ((((int64_t)(bhfi)->nFileSizeHigh) << 32) | (bhfi)->nFileSizeLow)
+
+static int
+file_information(struct archive_write_disk *a, wchar_t *path,
+ BY_HANDLE_FILE_INFORMATION *st, mode_t *mode, int sim_lstat)
+{
+ HANDLE h;
+ int r;
+ DWORD flag = FILE_FLAG_BACKUP_SEMANTICS;
+ WIN32_FIND_DATAW findData;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+ if (sim_lstat || mode != NULL) {
+ h = FindFirstFileW(path, &findData);
+ if (h == INVALID_HANDLE_VALUE &&
+ GetLastError() == ERROR_INVALID_NAME) {
+ wchar_t *full;
+ full = __la_win_permissive_name_w(path);
+ h = FindFirstFileW(full, &findData);
+ free(full);
+ }
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+ FindClose(h);
+ }
+
+ /* Is symlink file ? */
+ if (sim_lstat &&
+ ((findData.dwFileAttributes
+ & FILE_ATTRIBUTE_REPARSE_POINT) &&
+ (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)))
+ flag |= FILE_FLAG_OPEN_REPARSE_POINT;
+
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = flag;
+ h = CreateFile2(a->name, 0, 0,
+ OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(a->name, 0, 0, NULL,
+ OPEN_EXISTING, flag, NULL);
+#endif
+ if (h == INVALID_HANDLE_VALUE &&
+ GetLastError() == ERROR_INVALID_NAME) {
+ wchar_t *full;
+ full = __la_win_permissive_name_w(path);
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ h = CreateFile2(full, 0, 0,
+ OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(full, 0, 0, NULL,
+ OPEN_EXISTING, flag, NULL);
+#endif
+ free(full);
+ }
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+ r = GetFileInformationByHandle(h, st);
+ CloseHandle(h);
+ if (r == 0) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+
+ if (mode == NULL)
+ return (0);
+
+ *mode = S_IRUSR | S_IRGRP | S_IROTH;
+ if ((st->dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
+ *mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+ if ((st->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
+ findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)
+ *mode |= S_IFLNK;
+ else if (st->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ *mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ else {
+ const wchar_t *p;
+
+ *mode |= S_IFREG;
+ p = wcsrchr(path, L'.');
+ if (p != NULL && wcslen(p) == 4) {
+ switch (p[1]) {
+ case L'B': case L'b':
+ if ((p[2] == L'A' || p[2] == L'a' ) &&
+ (p[3] == L'T' || p[3] == L't' ))
+ *mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ case L'C': case L'c':
+ if (((p[2] == L'M' || p[2] == L'm' ) &&
+ (p[3] == L'D' || p[3] == L'd' )))
+ *mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ case L'E': case L'e':
+ if ((p[2] == L'X' || p[2] == L'x' ) &&
+ (p[3] == L'E' || p[3] == L'e' ))
+ *mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return (0);
+}
+
+/*
+ * Note: The path, for example, "aa/a/../b../c" will be converted to "aa/c"
+ * by GetFullPathNameW() W32 API, which __la_win_permissive_name_w uses.
+ * It means we cannot handle multiple dirs in one archive_entry.
+ * So we have to make the full-pathname in another way, which does not
+ * break "../" path string.
+ */
+static int
+permissive_name_w(struct archive_write_disk *a)
+{
+ wchar_t *wn, *wnp;
+ wchar_t *ws, *wsp;
+ DWORD l;
+
+ wnp = a->name;
+ if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
+ wnp[2] == L'?' && wnp[3] == L'\\')
+ /* We have already a permissive name. */
+ return (0);
+
+ if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
+ wnp[2] == L'.' && wnp[3] == L'\\') {
+ /* This is a device name */
+ if (((wnp[4] >= L'a' && wnp[4] <= L'z') ||
+ (wnp[4] >= L'A' && wnp[4] <= L'Z')) &&
+ wnp[5] == L':' && wnp[6] == L'\\') {
+ wnp[2] = L'?';/* Not device name. */
+ return (0);
+ }
+ }
+
+ /*
+ * A full-pathname starting with a drive name like "C:\abc".
+ */
+ if (((wnp[0] >= L'a' && wnp[0] <= L'z') ||
+ (wnp[0] >= L'A' && wnp[0] <= L'Z')) &&
+ wnp[1] == L':' && wnp[2] == L'\\') {
+ wn = _wcsdup(wnp);
+ if (wn == NULL)
+ return (-1);
+ archive_wstring_ensure(&(a->_name_data), 4 + wcslen(wn) + 1);
+ a->name = a->_name_data.s;
+ /* Prepend "\\?\" */
+ archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4);
+ archive_wstrcat(&(a->_name_data), wn);
+ free(wn);
+ return (0);
+ }
+
+ /*
+ * A full-pathname pointing to a network drive
+ * like "\\<server-name>\<share-name>\file".
+ */
+ if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') {
+ const wchar_t *p = &wnp[2];
+
+ /* Skip server-name letters. */
+ while (*p != L'\\' && *p != L'\0')
+ ++p;
+ if (*p == L'\\') {
+ const wchar_t *rp = ++p;
+ /* Skip share-name letters. */
+ while (*p != L'\\' && *p != L'\0')
+ ++p;
+ if (*p == L'\\' && p != rp) {
+ /* Now, match patterns such as
+ * "\\server-name\share-name\" */
+ wn = _wcsdup(wnp);
+ if (wn == NULL)
+ return (-1);
+ archive_wstring_ensure(&(a->_name_data),
+ 8 + wcslen(wn) + 1);
+ a->name = a->_name_data.s;
+ /* Prepend "\\?\UNC\" */
+ archive_wstrncpy(&(a->_name_data),
+ L"\\\\?\\UNC\\", 8);
+ archive_wstrcat(&(a->_name_data), wn+2);
+ free(wn);
+ return (0);
+ }
+ }
+ return (0);
+ }
+
+ /*
+ * Get current working directory.
+ */
+ l = GetCurrentDirectoryW(0, NULL);
+ if (l == 0)
+ return (-1);
+ ws = malloc(l * sizeof(wchar_t));
+ l = GetCurrentDirectoryW(l, ws);
+ if (l == 0) {
+ free(ws);
+ return (-1);
+ }
+ wsp = ws;
+
+ /*
+ * A full-pathname starting without a drive name like "\abc".
+ */
+ if (wnp[0] == L'\\') {
+ wn = _wcsdup(wnp);
+ if (wn == NULL)
+ return (-1);
+ archive_wstring_ensure(&(a->_name_data),
+ 4 + 2 + wcslen(wn) + 1);
+ a->name = a->_name_data.s;
+ /* Prepend "\\?\" and drive name. */
+ archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4);
+ archive_wstrncat(&(a->_name_data), wsp, 2);
+ archive_wstrcat(&(a->_name_data), wn);
+ free(wsp);
+ free(wn);
+ return (0);
+ }
+
+ wn = _wcsdup(wnp);
+ if (wn == NULL)
+ return (-1);
+ archive_wstring_ensure(&(a->_name_data), 4 + l + 1 + wcslen(wn) + 1);
+ a->name = a->_name_data.s;
+ /* Prepend "\\?\" and drive name if not already added. */
+ if (l > 3 && wsp[0] == L'\\' && wsp[1] == L'\\' &&
+ wsp[2] == L'?' && wsp[3] == L'\\')
+ {
+ archive_wstrncpy(&(a->_name_data), wsp, l);
+ }
+ else if (l > 2 && wsp[0] == L'\\' && wsp[1] == L'\\' && wsp[2] != L'\\')
+ {
+ archive_wstrncpy(&(a->_name_data), L"\\\\?\\UNC\\", 8);
+ archive_wstrncat(&(a->_name_data), wsp+2, l-2);
+ }
+ else
+ {
+ archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4);
+ archive_wstrncat(&(a->_name_data), wsp, l);
+ }
+ archive_wstrncat(&(a->_name_data), L"\\", 1);
+ archive_wstrcat(&(a->_name_data), wn);
+ a->name = a->_name_data.s;
+ free(wsp);
+ free(wn);
+ return (0);
+}
+
+static int
+la_chmod(const wchar_t *path, mode_t mode)
+{
+ DWORD attr;
+ BOOL r;
+ wchar_t *fullname;
+ int ret = 0;
+
+ fullname = NULL;
+ attr = GetFileAttributesW(path);
+ if (attr == (DWORD)-1 &&
+ GetLastError() == ERROR_INVALID_NAME) {
+ fullname = __la_win_permissive_name_w(path);
+ attr = GetFileAttributesW(fullname);
+ }
+ if (attr == (DWORD)-1) {
+ la_dosmaperr(GetLastError());
+ ret = -1;
+ goto exit_chmode;
+ }
+ if (mode & _S_IWRITE)
+ attr &= ~FILE_ATTRIBUTE_READONLY;
+ else
+ attr |= FILE_ATTRIBUTE_READONLY;
+ if (fullname != NULL)
+ r = SetFileAttributesW(fullname, attr);
+ else
+ r = SetFileAttributesW(path, attr);
+ if (r == 0) {
+ la_dosmaperr(GetLastError());
+ ret = -1;
+ }
+exit_chmode:
+ free(fullname);
+ return (ret);
+}
+
+static int
+la_mktemp(struct archive_write_disk *a)
+{
+ int fd;
+ mode_t mode;
+
+ archive_wstring_empty(&(a->_tmpname_data));
+ archive_wstrcpy(&(a->_tmpname_data), a->name);
+ archive_wstrcat(&(a->_tmpname_data), L".XXXXXX");
+ a->tmpname = a->_tmpname_data.s;
+
+ fd = __archive_mkstemp(a->tmpname);
+ if (fd == -1)
+ return -1;
+
+ mode = a->mode & 0777 & ~a->user_umask;
+ if (la_chmod(a->tmpname, mode) == -1) {
+ la_dosmaperr(GetLastError());
+ _close(fd);
+ return -1;
+ }
+ return (fd);
+}
+
+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
+static void *
+la_GetFunctionKernel32(const char *name)
+{
+ static HINSTANCE lib;
+ static int set;
+ if (!set) {
+ set = 1;
+ lib = LoadLibrary(TEXT("kernel32.dll"));
+ }
+ if (lib == NULL) {
+ fprintf(stderr, "Can't load kernel32.dll?!\n");
+ exit(1);
+ }
+ return (void *)GetProcAddress(lib, name);
+}
+#endif
+
+static int
+la_CreateHardLinkW(wchar_t *linkname, wchar_t *target)
+{
+ static BOOL (WINAPI *f)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
+ BOOL ret;
+
+#if _WIN32_WINNT < _WIN32_WINNT_XP
+ static int set;
+/* CreateHardLinkW is available since XP and always loaded */
+ if (!set) {
+ set = 1;
+ f = la_GetFunctionKernel32("CreateHardLinkW");
+ }
+#else
+ f = CreateHardLinkW;
+#endif
+ if (!f) {
+ errno = ENOTSUP;
+ return (0);
+ }
+ ret = (*f)(linkname, target, NULL);
+ if (!ret) {
+ /* Under windows 2000, it is necessary to remove
+ * the "\\?\" prefix. */
+#define IS_UNC(name) ((name[0] == L'U' || name[0] == L'u') && \
+ (name[1] == L'N' || name[1] == L'n') && \
+ (name[2] == L'C' || name[2] == L'c') && \
+ name[3] == L'\\')
+ if (!wcsncmp(linkname,L"\\\\?\\", 4)) {
+ linkname += 4;
+ if (IS_UNC(linkname))
+ linkname += 4;
+ }
+ if (!wcsncmp(target,L"\\\\?\\", 4)) {
+ target += 4;
+ if (IS_UNC(target))
+ target += 4;
+ }
+#undef IS_UNC
+ ret = (*f)(linkname, target, NULL);
+ }
+ return (ret);
+}
+
+/*
+ * Create file or directory symolic link
+ *
+ * If linktype is AE_SYMLINK_TYPE_UNDEFINED (or unknown), guess linktype from
+ * the link target
+ */
+static int
+la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target,
+ int linktype) {
+ static BOOLEAN (WINAPI *f)(LPCWSTR, LPCWSTR, DWORD);
+ wchar_t *ttarget, *p;
+ size_t len;
+ DWORD attrs = 0;
+ DWORD flags = 0;
+ DWORD newflags = 0;
+ BOOL ret = 0;
+
+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
+/* CreateSymbolicLinkW is available since Vista and always loaded */
+ static int set;
+ if (!set) {
+ set = 1;
+ f = la_GetFunctionKernel32("CreateSymbolicLinkW");
+ }
+#else
+# if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+ f = CreateSymbolicLinkW;
+# else
+ f = NULL;
+# endif
+#endif
+ if (!f)
+ return (0);
+
+ len = wcslen(target);
+ if (len == 0) {
+ errno = EINVAL;
+ return(0);
+ }
+ /*
+ * When writing path targets, we need to translate slashes
+ * to backslashes
+ */
+ ttarget = malloc((len + 1) * sizeof(wchar_t));
+ if (ttarget == NULL)
+ return(0);
+
+ p = ttarget;
+
+ while(*target != L'\0') {
+ if (*target == L'/')
+ *p = L'\\';
+ else
+ *p = *target;
+ target++;
+ p++;
+ }
+ *p = L'\0';
+
+ /*
+ * In case of undefined symlink type we guess it from the target.
+ * If the target equals ".", "..", ends with a backslash or a
+ * backslash followed by "." or ".." we assume it is a directory
+ * symlink. In all other cases we assume a file symlink.
+ */
+ if (linktype != AE_SYMLINK_TYPE_FILE && (
+ linktype == AE_SYMLINK_TYPE_DIRECTORY ||
+ *(p - 1) == L'\\' || (*(p - 1) == L'.' && (
+ len == 1 || *(p - 2) == L'\\' || ( *(p - 2) == L'.' && (
+ len == 2 || *(p - 3) == L'\\')))))) {
+#if defined(SYMBOLIC_LINK_FLAG_DIRECTORY)
+ flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
+#else
+ flags |= 0x1;
+#endif
+ }
+
+#if defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
+ newflags = flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
+#else
+ newflags = flags | 0x2;
+#endif
+
+ /*
+ * Windows won't overwrite existing links
+ */
+ attrs = GetFileAttributesW(linkname);
+ if (attrs != INVALID_FILE_ATTRIBUTES) {
+ if (attrs & FILE_ATTRIBUTE_DIRECTORY)
+ disk_rmdir(linkname);
+ else
+ disk_unlink(linkname);
+ }
+
+ ret = (*f)(linkname, ttarget, newflags);
+ /*
+ * Prior to Windows 10 calling CreateSymbolicLinkW() will fail
+ * if SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is set
+ */
+ if (!ret) {
+ ret = (*f)(linkname, ttarget, flags);
+ }
+ free(ttarget);
+ return (ret);
+}
+
+static int
+la_ftruncate(HANDLE handle, int64_t length)
+{
+ LARGE_INTEGER distance;
+
+ if (GetFileType(handle) != FILE_TYPE_DISK) {
+ errno = EBADF;
+ return (-1);
+ }
+ distance.QuadPart = length;
+ if (!SetFilePointerEx_perso(handle, distance, NULL, FILE_BEGIN)) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+ if (!SetEndOfFile(handle)) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+lazy_stat(struct archive_write_disk *a)
+{
+ if (a->pst != NULL) {
+ /* Already have stat() data available. */
+ return (ARCHIVE_OK);
+ }
+ if (a->fh != INVALID_HANDLE_VALUE &&
+ GetFileInformationByHandle(a->fh, &a->st) == 0) {
+ a->pst = &a->st;
+ return (ARCHIVE_OK);
+ }
+
+ /*
+ * XXX At this point, symlinks should not be hit, otherwise
+ * XXX a race occurred. Do we want to check explicitly for that?
+ */
+ if (file_information(a, a->name, &a->st, NULL, 1) == 0) {
+ a->pst = &a->st;
+ return (ARCHIVE_OK);
+ }
+ archive_set_error(&a->archive, errno, "Couldn't stat file");
+ return (ARCHIVE_WARN);
+}
+
+static const struct archive_vtable
+archive_write_disk_vtable = {
+ .archive_close = _archive_write_disk_close,
+ .archive_filter_bytes = _archive_write_disk_filter_bytes,
+ .archive_free = _archive_write_disk_free,
+ .archive_write_header = _archive_write_disk_header,
+ .archive_write_finish_entry = _archive_write_disk_finish_entry,
+ .archive_write_data = _archive_write_disk_data,
+ .archive_write_data_block = _archive_write_disk_data_block,
+};
+
+static int64_t
+_archive_write_disk_filter_bytes(struct archive *_a, int n)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ (void)n; /* UNUSED */
+ if (n == -1 || n == 0)
+ return (a->total_bytes_written);
+ return (-1);
+}
+
+
+int
+archive_write_disk_set_options(struct archive *_a, int flags)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+
+ a->flags = flags;
+ return (ARCHIVE_OK);
+}
+
+
+/*
+ * Extract this entry to disk.
+ *
+ * TODO: Validate hardlinks. According to the standards, we're
+ * supposed to check each extracted hardlink and squawk if it refers
+ * to a file that we didn't restore. I'm not entirely convinced this
+ * is a good idea, but more importantly: Is there any way to validate
+ * hardlinks without keeping a complete list of filenames from the
+ * entire archive?? Ugh.
+ *
+ */
+static int
+_archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ struct fixup_entry *fe;
+ int ret, r;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_disk_header");
+ archive_clear_error(&a->archive);
+ if (a->archive.state & ARCHIVE_STATE_DATA) {
+ r = _archive_write_disk_finish_entry(&a->archive);
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ }
+
+ /* Set up for this particular entry. */
+ a->pst = NULL;
+ a->current_fixup = NULL;
+ a->deferred = 0;
+ archive_entry_free(a->entry);
+ a->entry = NULL;
+ a->entry = archive_entry_clone(entry);
+ a->fh = INVALID_HANDLE_VALUE;
+ a->fd_offset = 0;
+ a->offset = 0;
+ a->restore_pwd = -1;
+ a->uid = a->user_uid;
+ a->mode = archive_entry_mode(a->entry);
+ if (archive_entry_size_is_set(a->entry))
+ a->filesize = archive_entry_size(a->entry);
+ else
+ a->filesize = -1;
+ archive_wstrcpy(&(a->_name_data), archive_entry_pathname_w(a->entry));
+ a->name = a->_name_data.s;
+ archive_clear_error(&a->archive);
+
+ /*
+ * Clean up the requested path. This is necessary for correct
+ * dir restores; the dir restore logic otherwise gets messed
+ * up by nonsense like "dir/.".
+ */
+ ret = cleanup_pathname(a, a->name);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+
+ /*
+ * Generate a full-pathname and use it from here.
+ */
+ if (permissive_name_w(a) < 0) {
+ errno = EINVAL;
+ return (ARCHIVE_FAILED);
+ }
+
+ /*
+ * Query the umask so we get predictable mode settings.
+ * This gets done on every call to _write_header in case the
+ * user edits their umask during the extraction for some
+ * reason.
+ */
+ umask(a->user_umask = umask(0));
+
+ /* Figure out what we need to do for this entry. */
+ a->todo = TODO_MODE_BASE;
+ if (a->flags & ARCHIVE_EXTRACT_PERM) {
+ a->todo |= TODO_MODE_FORCE; /* Be pushy about permissions. */
+ /*
+ * SGID requires an extra "check" step because we
+ * cannot easily predict the GID that the system will
+ * assign. (Different systems assign GIDs to files
+ * based on a variety of criteria, including process
+ * credentials and the gid of the enclosing
+ * directory.) We can only restore the SGID bit if
+ * the file has the right GID, and we only know the
+ * GID if we either set it (see set_ownership) or if
+ * we've actually called stat() on the file after it
+ * was restored. Since there are several places at
+ * which we might verify the GID, we need a TODO bit
+ * to keep track.
+ */
+ if (a->mode & S_ISGID)
+ a->todo |= TODO_SGID | TODO_SGID_CHECK;
+ /*
+ * Verifying the SUID is simpler, but can still be
+ * done in multiple ways, hence the separate "check" bit.
+ */
+ if (a->mode & S_ISUID)
+ a->todo |= TODO_SUID | TODO_SUID_CHECK;
+ } else {
+ /*
+ * User didn't request full permissions, so don't
+ * restore SUID, SGID bits and obey umask.
+ */
+ a->mode &= ~S_ISUID;
+ a->mode &= ~S_ISGID;
+ a->mode &= ~S_ISVTX;
+ a->mode &= ~a->user_umask;
+ }
+#if 0
+ if (a->flags & ARCHIVE_EXTRACT_OWNER)
+ a->todo |= TODO_OWNER;
+#endif
+ if (a->flags & ARCHIVE_EXTRACT_TIME)
+ a->todo |= TODO_TIMES;
+ if (a->flags & ARCHIVE_EXTRACT_ACL) {
+ if (archive_entry_filetype(a->entry) == AE_IFDIR)
+ a->deferred |= TODO_ACLS;
+ else
+ a->todo |= TODO_ACLS;
+ }
+ if (a->flags & ARCHIVE_EXTRACT_XATTR)
+ a->todo |= TODO_XATTR;
+ if (a->flags & ARCHIVE_EXTRACT_FFLAGS)
+ a->todo |= TODO_FFLAGS;
+ if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) {
+ ret = check_symlinks(a);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ }
+
+ ret = restore_entry(a);
+
+ /*
+ * TODO: There are rumours that some extended attributes must
+ * be restored before file data is written. If this is true,
+ * then we either need to write all extended attributes both
+ * before and after restoring the data, or find some rule for
+ * determining which must go first and which last. Due to the
+ * many ways people are using xattrs, this may prove to be an
+ * intractable problem.
+ */
+
+ /*
+ * Fixup uses the unedited pathname from archive_entry_pathname(),
+ * because it is relative to the base dir and the edited path
+ * might be relative to some intermediate dir as a result of the
+ * deep restore logic.
+ */
+ if (a->deferred & TODO_MODE) {
+ fe = current_fixup(a, archive_entry_pathname_w(entry));
+ fe->fixup |= TODO_MODE_BASE;
+ fe->mode = a->mode;
+ }
+
+ if ((a->deferred & TODO_TIMES)
+ && (archive_entry_mtime_is_set(entry)
+ || archive_entry_atime_is_set(entry))) {
+ fe = current_fixup(a, archive_entry_pathname_w(entry));
+ fe->mode = a->mode;
+ fe->fixup |= TODO_TIMES;
+ if (archive_entry_atime_is_set(entry)) {
+ fe->atime = archive_entry_atime(entry);
+ fe->atime_nanos = archive_entry_atime_nsec(entry);
+ } else {
+ /* If atime is unset, use start time. */
+ fe->atime = a->start_time;
+ fe->atime_nanos = 0;
+ }
+ if (archive_entry_mtime_is_set(entry)) {
+ fe->mtime = archive_entry_mtime(entry);
+ fe->mtime_nanos = archive_entry_mtime_nsec(entry);
+ } else {
+ /* If mtime is unset, use start time. */
+ fe->mtime = a->start_time;
+ fe->mtime_nanos = 0;
+ }
+ if (archive_entry_birthtime_is_set(entry)) {
+ fe->birthtime = archive_entry_birthtime(entry);
+ fe->birthtime_nanos = archive_entry_birthtime_nsec(entry);
+ } else {
+ /* If birthtime is unset, use mtime. */
+ fe->birthtime = fe->mtime;
+ fe->birthtime_nanos = fe->mtime_nanos;
+ }
+ }
+
+ if (a->deferred & TODO_ACLS) {
+ fe = current_fixup(a, archive_entry_pathname_w(entry));
+ archive_acl_copy(&fe->acl, archive_entry_acl(entry));
+ }
+
+ if (a->deferred & TODO_FFLAGS) {
+ unsigned long set, clear;
+
+ fe = current_fixup(a, archive_entry_pathname_w(entry));
+ archive_entry_fflags(entry, &set, &clear);
+ fe->fflags_set = set;
+ }
+
+ /*
+ * On Windows, A creating sparse file requires a special mark.
+ */
+ if (a->fh != INVALID_HANDLE_VALUE &&
+ archive_entry_sparse_count(entry) > 0) {
+ int64_t base = 0, offset, length;
+ int i, cnt = archive_entry_sparse_reset(entry);
+ int sparse = 0;
+
+ for (i = 0; i < cnt; i++) {
+ archive_entry_sparse_next(entry, &offset, &length);
+ if (offset - base >= 4096) {
+ sparse = 1;/* we have a hole. */
+ break;
+ }
+ base = offset + length;
+ }
+ if (sparse) {
+ DWORD dmy;
+ /* Mark this file as sparse. */
+ DeviceIoControl(a->fh, FSCTL_SET_SPARSE,
+ NULL, 0, NULL, 0, &dmy, NULL);
+ }
+ }
+
+ /* We've created the object and are ready to pour data into it. */
+ if (ret >= ARCHIVE_WARN)
+ a->archive.state = ARCHIVE_STATE_DATA;
+ /*
+ * If it's not open, tell our client not to try writing.
+ * In particular, dirs, links, etc, don't get written to.
+ */
+ if (a->fh == INVALID_HANDLE_VALUE) {
+ archive_entry_set_size(entry, 0);
+ a->filesize = 0;
+ }
+
+ return (ret);
+}
+
+int
+archive_write_disk_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_set_skip_file");
+ a->skip_file_set = 1;
+ a->skip_file_dev = d;
+ a->skip_file_ino = i;
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+write_data_block(struct archive_write_disk *a, const char *buff, size_t size)
+{
+ OVERLAPPED ol;
+ uint64_t start_size = size;
+ DWORD bytes_written = 0;
+ ssize_t block_size = 0, bytes_to_write;
+
+ if (size == 0)
+ return (ARCHIVE_OK);
+
+ if (a->filesize == 0 || a->fh == INVALID_HANDLE_VALUE) {
+ archive_set_error(&a->archive, 0,
+ "Attempt to write to an empty file");
+ return (ARCHIVE_WARN);
+ }
+
+ if (a->flags & ARCHIVE_EXTRACT_SPARSE) {
+ /* XXX TODO XXX Is there a more appropriate choice here ? */
+ /* This needn't match the filesystem allocation size. */
+ block_size = 16*1024;
+ }
+
+ /* If this write would run beyond the file size, truncate it. */
+ if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize)
+ start_size = size = (size_t)(a->filesize - a->offset);
+
+ /* Write the data. */
+ while (size > 0) {
+ if (block_size == 0) {
+ bytes_to_write = size;
+ } else {
+ /* We're sparsifying the file. */
+ const char *p, *end;
+ int64_t block_end;
+
+ /* Skip leading zero bytes. */
+ for (p = buff, end = buff + size; p < end; ++p) {
+ if (*p != '\0')
+ break;
+ }
+ a->offset += p - buff;
+ size -= p - buff;
+ buff = p;
+ if (size == 0)
+ break;
+
+ /* Calculate next block boundary after offset. */
+ block_end
+ = (a->offset / block_size + 1) * block_size;
+
+ /* If the adjusted write would cross block boundary,
+ * truncate it to the block boundary. */
+ bytes_to_write = size;
+ if (a->offset + bytes_to_write > block_end)
+ bytes_to_write = (DWORD)(block_end - a->offset);
+ }
+ memset(&ol, 0, sizeof(ol));
+ ol.Offset = (DWORD)(a->offset & 0xFFFFFFFF);
+ ol.OffsetHigh = (DWORD)(a->offset >> 32);
+ if (!WriteFile(a->fh, buff, (uint32_t)bytes_to_write,
+ &bytes_written, &ol)) {
+ DWORD lasterr;
+
+ lasterr = GetLastError();
+ if (lasterr == ERROR_ACCESS_DENIED)
+ errno = EBADF;
+ else
+ la_dosmaperr(lasterr);
+ archive_set_error(&a->archive, errno, "Write failed");
+ return (ARCHIVE_WARN);
+ }
+ buff += bytes_written;
+ size -= bytes_written;
+ a->total_bytes_written += bytes_written;
+ a->offset += bytes_written;
+ a->fd_offset = a->offset;
+ }
+ return ((ssize_t)(start_size - size));
+}
+
+static ssize_t
+_archive_write_disk_data_block(struct archive *_a,
+ const void *buff, size_t size, int64_t offset)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ ssize_t r;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_DATA, "archive_write_data_block");
+
+ a->offset = offset;
+ r = write_data_block(a, buff, size);
+ if (r < ARCHIVE_OK)
+ return (r);
+ if ((size_t)r < size) {
+ archive_set_error(&a->archive, 0,
+ "Write request too large");
+ return (ARCHIVE_WARN);
+ }
+#if ARCHIVE_VERSION_NUMBER < 3999000
+ return (ARCHIVE_OK);
+#else
+ return (size);
+#endif
+}
+
+static ssize_t
+_archive_write_disk_data(struct archive *_a, const void *buff, size_t size)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_DATA, "archive_write_data");
+
+ return (write_data_block(a, buff, size));
+}
+
+static int
+_archive_write_disk_finish_entry(struct archive *_a)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ int ret = ARCHIVE_OK;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_finish_entry");
+ if (a->archive.state & ARCHIVE_STATE_HEADER)
+ return (ARCHIVE_OK);
+ archive_clear_error(&a->archive);
+
+ /* Pad or truncate file to the right size. */
+ if (a->fh == INVALID_HANDLE_VALUE) {
+ /* There's no file. */
+ } else if (a->filesize < 0) {
+ /* File size is unknown, so we can't set the size. */
+ } else if (a->fd_offset == a->filesize) {
+ /* Last write ended at exactly the filesize; we're done. */
+ /* Hopefully, this is the common case. */
+ } else {
+ if (la_ftruncate(a->fh, a->filesize) == -1) {
+ archive_set_error(&a->archive, errno,
+ "File size could not be restored");
+ CloseHandle(a->fh);
+ a->fh = INVALID_HANDLE_VALUE;
+ return (ARCHIVE_FAILED);
+ }
+ }
+
+ /* Restore metadata. */
+
+ /*
+ * Look up the "real" UID only if we're going to need it.
+ * TODO: the TODO_SGID condition can be dropped here, can't it?
+ */
+ if (a->todo & (TODO_OWNER | TODO_SUID | TODO_SGID)) {
+ a->uid = archive_write_disk_uid(&a->archive,
+ archive_entry_uname(a->entry),
+ archive_entry_uid(a->entry));
+ }
+ /* Look up the "real" GID only if we're going to need it. */
+ /* TODO: the TODO_SUID condition can be dropped here, can't it? */
+ if (a->todo & (TODO_OWNER | TODO_SGID | TODO_SUID)) {
+ a->gid = archive_write_disk_gid(&a->archive,
+ archive_entry_gname(a->entry),
+ archive_entry_gid(a->entry));
+ }
+
+ /*
+ * Restore ownership before set_mode tries to restore suid/sgid
+ * bits. If we set the owner, we know what it is and can skip
+ * a stat() call to examine the ownership of the file on disk.
+ */
+ if (a->todo & TODO_OWNER)
+ ret = set_ownership(a);
+
+ /*
+ * set_mode must precede ACLs on systems such as Solaris and
+ * FreeBSD where setting the mode implicitly clears extended ACLs
+ */
+ if (a->todo & TODO_MODE) {
+ int r2 = set_mode(a, a->mode);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * Security-related extended attributes (such as
+ * security.capability on Linux) have to be restored last,
+ * since they're implicitly removed by other file changes.
+ */
+ if (a->todo & TODO_XATTR) {
+ int r2 = set_xattrs(a);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * Some flags prevent file modification; they must be restored after
+ * file contents are written.
+ */
+ if (a->todo & TODO_FFLAGS) {
+ int r2 = set_fflags(a);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * Time must follow most other metadata;
+ * otherwise atime will get changed.
+ */
+ if (a->todo & TODO_TIMES) {
+ int r2 = set_times_from_entry(a);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * ACLs must be restored after timestamps because there are
+ * ACLs that prevent attribute changes (including time).
+ */
+ if (a->todo & TODO_ACLS) {
+ int r2 = set_acls(a, a->fh,
+ archive_entry_pathname_w(a->entry),
+ archive_entry_acl(a->entry));
+ if (r2 < ret) ret = r2;
+ }
+
+ /* If there's an fd, we can close it now. */
+ if (a->fh != INVALID_HANDLE_VALUE) {
+ CloseHandle(a->fh);
+ a->fh = INVALID_HANDLE_VALUE;
+ if (a->tmpname) {
+ /* Windows does not support atomic rename */
+ disk_unlink(a->name);
+ if (_wrename(a->tmpname, a->name) != 0) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Failed to rename temporary file");
+ ret = ARCHIVE_FAILED;
+ disk_unlink(a->tmpname);
+ }
+ a->tmpname = NULL;
+ }
+ }
+ /* If there's an entry, we can release it now. */
+ archive_entry_free(a->entry);
+ a->entry = NULL;
+ a->archive.state = ARCHIVE_STATE_HEADER;
+ return (ret);
+}
+
+int
+archive_write_disk_set_group_lookup(struct archive *_a,
+ void *private_data,
+ la_int64_t (*lookup_gid)(void *private, const char *gname, la_int64_t gid),
+ void (*cleanup_gid)(void *private))
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_set_group_lookup");
+
+ if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL)
+ (a->cleanup_gid)(a->lookup_gid_data);
+
+ a->lookup_gid = lookup_gid;
+ a->cleanup_gid = cleanup_gid;
+ a->lookup_gid_data = private_data;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_write_disk_set_user_lookup(struct archive *_a,
+ void *private_data,
+ int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid),
+ void (*cleanup_uid)(void *private))
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_set_user_lookup");
+
+ if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL)
+ (a->cleanup_uid)(a->lookup_uid_data);
+
+ a->lookup_uid = lookup_uid;
+ a->cleanup_uid = cleanup_uid;
+ a->lookup_uid_data = private_data;
+ return (ARCHIVE_OK);
+}
+
+int64_t
+archive_write_disk_gid(struct archive *_a, const char *name, la_int64_t id)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_gid");
+ if (a->lookup_gid)
+ return (a->lookup_gid)(a->lookup_gid_data, name, id);
+ return (id);
+}
+
+int64_t
+archive_write_disk_uid(struct archive *_a, const char *name, la_int64_t id)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_uid");
+ if (a->lookup_uid)
+ return (a->lookup_uid)(a->lookup_uid_data, name, id);
+ return (id);
+}
+
+/*
+ * Create a new archive_write_disk object and initialize it with global state.
+ */
+struct archive *
+archive_write_disk_new(void)
+{
+ struct archive_write_disk *a;
+
+ a = (struct archive_write_disk *)calloc(1, sizeof(*a));
+ if (a == NULL)
+ return (NULL);
+ a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC;
+ /* We're ready to write a header immediately. */
+ a->archive.state = ARCHIVE_STATE_HEADER;
+ a->archive.vtable = &archive_write_disk_vtable;
+ a->start_time = time(NULL);
+ /* Query and restore the umask. */
+ umask(a->user_umask = umask(0));
+ if (archive_wstring_ensure(&a->path_safe, 512) == NULL) {
+ free(a);
+ return (NULL);
+ }
+ a->path_safe.s[0] = 0;
+ return (&a->archive);
+}
+
+static int
+disk_unlink(const wchar_t *path)
+{
+ wchar_t *fullname;
+ int r;
+
+ r = _wunlink(path);
+ if (r != 0 && GetLastError() == ERROR_INVALID_NAME) {
+ fullname = __la_win_permissive_name_w(path);
+ r = _wunlink(fullname);
+ free(fullname);
+ }
+ return (r);
+}
+
+static int
+disk_rmdir(const wchar_t *path)
+{
+ wchar_t *fullname;
+ int r;
+
+ r = _wrmdir(path);
+ if (r != 0 && GetLastError() == ERROR_INVALID_NAME) {
+ fullname = __la_win_permissive_name_w(path);
+ r = _wrmdir(fullname);
+ free(fullname);
+ }
+ return (r);
+}
+
+/*
+ * The main restore function.
+ */
+static int
+restore_entry(struct archive_write_disk *a)
+{
+ int ret = ARCHIVE_OK, en;
+
+ if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) {
+ /*
+ * TODO: Fix this. Apparently, there are platforms
+ * that still allow root to hose the entire filesystem
+ * by unlinking a dir. The S_ISDIR() test above
+ * prevents us from using unlink() here if the new
+ * object is a dir, but that doesn't mean the old
+ * object isn't a dir.
+ */
+ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+ (void)clear_nochange_fflags(a);
+ if (disk_unlink(a->name) == 0) {
+ /* We removed it, reset cached stat. */
+ a->pst = NULL;
+ } else if (errno == ENOENT) {
+ /* File didn't exist, that's just as good. */
+ } else if (disk_rmdir(a->name) == 0) {
+ /* It was a dir, but now it's gone. */
+ a->pst = NULL;
+ } else {
+ /* We tried, but couldn't get rid of it. */
+ archive_set_error(&a->archive, errno,
+ "Could not unlink");
+ return(ARCHIVE_FAILED);
+ }
+ }
+
+ /* Try creating it first; if this fails, we'll try to recover. */
+ en = create_filesystem_object(a);
+
+ if ((en == ENOTDIR || en == ENOENT)
+ && !(a->flags & ARCHIVE_EXTRACT_NO_AUTODIR)) {
+ wchar_t *full;
+ /* If the parent dir doesn't exist, try creating it. */
+ create_parent_dir(a, a->name);
+ /* Now try to create the object again. */
+ full = __la_win_permissive_name_w(a->name);
+ if (full == NULL) {
+ en = EINVAL;
+ } else {
+ /* Remove multiple directories such as "a/../b../c" */
+ archive_wstrcpy(&(a->_name_data), full);
+ a->name = a->_name_data.s;
+ free(full);
+ en = create_filesystem_object(a);
+ }
+ }
+
+ if ((en == ENOENT) && (archive_entry_hardlink(a->entry) != NULL)) {
+ archive_set_error(&a->archive, en,
+ "Hard-link target '%s' does not exist.",
+ archive_entry_hardlink(a->entry));
+ return (ARCHIVE_FAILED);
+ }
+
+ if ((en == EISDIR || en == EEXIST)
+ && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+ /* If we're not overwriting, we're done. */
+ if (S_ISDIR(a->mode)) {
+ /* Don't overwrite any settings on existing directories. */
+ a->todo = 0;
+ }
+ archive_entry_unset_size(a->entry);
+ return (ARCHIVE_OK);
+ }
+
+ /*
+ * Some platforms return EISDIR if you call
+ * open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some
+ * return EEXIST. POSIX is ambiguous, requiring EISDIR
+ * for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT)
+ * on an existing item.
+ */
+ if (en == EISDIR) {
+ /* A dir is in the way of a non-dir, rmdir it. */
+ if (disk_rmdir(a->name) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't remove already-existing dir");
+ return (ARCHIVE_FAILED);
+ }
+ a->pst = NULL;
+ /* Try again. */
+ en = create_filesystem_object(a);
+ } else if (en == EEXIST) {
+ mode_t st_mode;
+ mode_t lst_mode;
+ BY_HANDLE_FILE_INFORMATION lst;
+ /*
+ * We know something is in the way, but we don't know what;
+ * we need to find out before we go any further.
+ */
+ int r = 0;
+ int dirlnk = 0;
+
+ /*
+ * The SECURE_SYMLINK logic has already removed a
+ * symlink to a dir if the client wants that. So
+ * follow the symlink if we're creating a dir.
+ * If it's not a dir (or it's a broken symlink),
+ * then don't follow it.
+ *
+ * Windows distinguishes file and directory symlinks.
+ * A file symlink may erroneously point to a directory
+ * and a directory symlink to a file. Windows does not follow
+ * such symlinks. We always need both source and target
+ * information.
+ */
+ r = file_information(a, a->name, &lst, &lst_mode, 1);
+ if (r != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't stat existing object");
+ return (ARCHIVE_FAILED);
+ } else if (S_ISLNK(lst_mode)) {
+ if (lst.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ dirlnk = 1;
+ /* In case of a symlink we need target information */
+ r = file_information(a, a->name, &a->st, &st_mode, 0);
+ if (r != 0) {
+ a->st = lst;
+ st_mode = lst_mode;
+ }
+ } else {
+ a->st = lst;
+ st_mode = lst_mode;
+ }
+
+ /*
+ * NO_OVERWRITE_NEWER doesn't apply to directories.
+ */
+ if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER)
+ && !S_ISDIR(st_mode)) {
+ if (!older(&(a->st), a->entry)) {
+ archive_entry_unset_size(a->entry);
+ return (ARCHIVE_OK);
+ }
+ }
+
+ /* If it's our archive, we're done. */
+ if (a->skip_file_set &&
+ bhfi_dev(&a->st) == a->skip_file_dev &&
+ bhfi_ino(&a->st) == a->skip_file_ino) {
+ archive_set_error(&a->archive, 0,
+ "Refusing to overwrite archive");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (!S_ISDIR(st_mode)) {
+ if (a->flags &
+ ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {
+ (void)clear_nochange_fflags(a);
+ }
+ if ((a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) &&
+ S_ISREG(st_mode)) {
+ int fd = la_mktemp(a);
+
+ if (fd == -1) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Can't create temporary file");
+ return (ARCHIVE_FAILED);
+ }
+ a->fh = (HANDLE)_get_osfhandle(fd);
+ if (a->fh == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ return (ARCHIVE_FAILED);
+ }
+ a->pst = NULL;
+ en = 0;
+ } else {
+ if (dirlnk) {
+ /* Edge case: dir symlink pointing
+ * to a file */
+ if (disk_rmdir(a->name) != 0) {
+ archive_set_error(&a->archive,
+ errno, "Can't unlink "
+ "directory symlink");
+ return (ARCHIVE_FAILED);
+ }
+ } else {
+ if (disk_unlink(a->name) != 0) {
+ /* A non-dir is in the way,
+ * unlink it. */
+ archive_set_error(&a->archive,
+ errno, "Can't unlink "
+ "already-existing object");
+ return (ARCHIVE_FAILED);
+ }
+ }
+ a->pst = NULL;
+ /* Try again. */
+ en = create_filesystem_object(a);
+ }
+ } else if (!S_ISDIR(a->mode)) {
+ /* A dir is in the way of a non-dir, rmdir it. */
+ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+ (void)clear_nochange_fflags(a);
+ if (disk_rmdir(a->name) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't remove already-existing dir");
+ return (ARCHIVE_FAILED);
+ }
+ /* Try again. */
+ en = create_filesystem_object(a);
+ } else {
+ /*
+ * There's a dir in the way of a dir. Don't
+ * waste time with rmdir()/mkdir(), just fix
+ * up the permissions on the existing dir.
+ * Note that we don't change perms on existing
+ * dirs unless _EXTRACT_PERM is specified.
+ */
+ if ((a->mode != st_mode)
+ && (a->todo & TODO_MODE_FORCE))
+ a->deferred |= (a->todo & TODO_MODE);
+ /* Ownership doesn't need deferred fixup. */
+ en = 0; /* Forget the EEXIST. */
+ }
+ }
+
+ if (en) {
+ /* Everything failed; give up here. */
+ archive_set_error(&a->archive, en, "Can't create '%ls'",
+ a->name);
+ return (ARCHIVE_FAILED);
+ }
+
+ a->pst = NULL; /* Cached stat data no longer valid. */
+ return (ret);
+}
+
+/*
+ * Returns 0 if creation succeeds, or else returns errno value from
+ * the failed system call. Note: This function should only ever perform
+ * a single system call.
+ */
+static int
+create_filesystem_object(struct archive_write_disk *a)
+{
+ /* Create the entry. */
+ const wchar_t *linkname;
+ wchar_t *fullname;
+ mode_t final_mode, mode;
+ int r;
+ DWORD attrs = 0;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+ /* We identify hard/symlinks according to the link names. */
+ /* Since link(2) and symlink(2) don't handle modes, we're done here. */
+ linkname = archive_entry_hardlink_w(a->entry);
+ if (linkname != NULL) {
+ wchar_t *linksanitized, *linkfull, *namefull;
+ size_t l = (wcslen(linkname) + 1) * sizeof(wchar_t);
+ linksanitized = malloc(l);
+ if (linksanitized == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for hardlink target");
+ return (-1);
+ }
+ memcpy(linksanitized, linkname, l);
+ r = cleanup_pathname(a, linksanitized);
+ if (r != ARCHIVE_OK) {
+ free(linksanitized);
+ return (r);
+ }
+ linkfull = __la_win_permissive_name_w(linksanitized);
+ free(linksanitized);
+ namefull = __la_win_permissive_name_w(a->name);
+ if (linkfull == NULL || namefull == NULL) {
+ errno = EINVAL;
+ r = -1;
+ } else {
+ /*
+ * Unlinking and linking here is really not atomic,
+ * but doing it right, would require us to construct
+ * an mktemplink() function, and then use _wrename().
+ */
+ if (a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) {
+ attrs = GetFileAttributesW(namefull);
+ if (attrs != INVALID_FILE_ATTRIBUTES) {
+ if (attrs & FILE_ATTRIBUTE_DIRECTORY)
+ disk_rmdir(namefull);
+ else
+ disk_unlink(namefull);
+ }
+ }
+ r = la_CreateHardLinkW(namefull, linkfull);
+ if (r == 0) {
+ la_dosmaperr(GetLastError());
+ r = errno;
+ } else
+ r = 0;
+ }
+ /*
+ * New cpio and pax formats allow hardlink entries
+ * to carry data, so we may have to open the file
+ * for hardlink entries.
+ *
+ * If the hardlink was successfully created and
+ * the archive doesn't have carry data for it,
+ * consider it to be non-authoritative for meta data.
+ * This is consistent with GNU tar and BSD pax.
+ * If the hardlink does carry data, let the last
+ * archive entry decide ownership.
+ */
+ if (r == 0 && a->filesize <= 0) {
+ a->todo = 0;
+ a->deferred = 0;
+ } else if (r == 0 && a->filesize > 0) {
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
+ a->fh = CreateFile2(namefull, GENERIC_WRITE, 0,
+ TRUNCATE_EXISTING, &createExParams);
+#else
+ a->fh = CreateFileW(namefull, GENERIC_WRITE, 0, NULL,
+ TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+#endif
+ if (a->fh == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ r = errno;
+ }
+ }
+ free(linkfull);
+ free(namefull);
+ return (r);
+ }
+ linkname = archive_entry_symlink_w(a->entry);
+ if (linkname != NULL) {
+ /*
+ * Unlinking and linking here is really not atomic,
+ * but doing it right, would require us to construct
+ * an mktemplink() function, and then use _wrename().
+ */
+ attrs = GetFileAttributesW(a->name);
+ if (attrs != INVALID_FILE_ATTRIBUTES) {
+ if (attrs & FILE_ATTRIBUTE_DIRECTORY)
+ disk_rmdir(a->name);
+ else
+ disk_unlink(a->name);
+ }
+#if HAVE_SYMLINK
+ return symlink(linkname, a->name) ? errno : 0;
+#else
+ errno = 0;
+ r = la_CreateSymbolicLinkW((const wchar_t *)a->name, linkname,
+ archive_entry_symlink_type(a->entry));
+ if (r == 0) {
+ if (errno == 0)
+ la_dosmaperr(GetLastError());
+ r = errno;
+ } else
+ r = 0;
+ return (r);
+#endif
+ }
+
+ /*
+ * The remaining system calls all set permissions, so let's
+ * try to take advantage of that to avoid an extra chmod()
+ * call. (Recall that umask is set to zero right now!)
+ */
+
+ /* Mode we want for the final restored object (w/o file type bits). */
+ final_mode = a->mode & 07777;
+ /*
+ * The mode that will actually be restored in this step. Note
+ * that SUID, SGID, etc, require additional work to ensure
+ * security, so we never restore them at this point.
+ */
+ mode = final_mode & 0777 & ~a->user_umask;
+
+ switch (a->mode & AE_IFMT) {
+ default:
+ /* POSIX requires that we fall through here. */
+ /* FALLTHROUGH */
+ case AE_IFREG:
+ a->tmpname = NULL;
+ fullname = a->name;
+ /* O_WRONLY | O_CREAT | O_EXCL */
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
+ a->fh = CreateFile2(fullname, GENERIC_WRITE, 0,
+ CREATE_NEW, &createExParams);
+#else
+ a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,
+ CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+#endif
+ if (a->fh == INVALID_HANDLE_VALUE &&
+ GetLastError() == ERROR_INVALID_NAME &&
+ fullname == a->name) {
+ fullname = __la_win_permissive_name_w(a->name);
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ a->fh = CreateFile2(fullname, GENERIC_WRITE, 0,
+ CREATE_NEW, &createExParams);
+#else
+ a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,
+ CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+#endif
+ }
+ if (a->fh == INVALID_HANDLE_VALUE) {
+ if (GetLastError() == ERROR_ACCESS_DENIED) {
+ DWORD attr;
+ /* Simulate an errno of POSIX system. */
+ attr = GetFileAttributesW(fullname);
+ if (attr == (DWORD)-1)
+ la_dosmaperr(GetLastError());
+ else if (attr & FILE_ATTRIBUTE_DIRECTORY)
+ errno = EISDIR;
+ else
+ errno = EACCES;
+ } else
+ la_dosmaperr(GetLastError());
+ r = 1;
+ } else
+ r = 0;
+ if (fullname != a->name)
+ free(fullname);
+ break;
+ case AE_IFCHR:
+ case AE_IFBLK:
+ /* TODO: Find a better way to warn about our inability
+ * to restore a block device node. */
+ return (EINVAL);
+ case AE_IFDIR:
+ mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE;
+ fullname = a->name;
+ r = CreateDirectoryW(fullname, NULL);
+ if (r == 0 && GetLastError() == ERROR_INVALID_NAME &&
+ fullname == a->name) {
+ fullname = __la_win_permissive_name_w(a->name);
+ r = CreateDirectoryW(fullname, NULL);
+ }
+ if (r != 0) {
+ r = 0;
+ /* Defer setting dir times. */
+ a->deferred |= (a->todo & TODO_TIMES);
+ a->todo &= ~TODO_TIMES;
+ /* Never use an immediate chmod(). */
+ /* We can't avoid the chmod() entirely if EXTRACT_PERM
+ * because of SysV SGID inheritance. */
+ if ((mode != final_mode)
+ || (a->flags & ARCHIVE_EXTRACT_PERM))
+ a->deferred |= (a->todo & TODO_MODE);
+ a->todo &= ~TODO_MODE;
+ } else {
+ la_dosmaperr(GetLastError());
+ r = -1;
+ }
+ if (fullname != a->name)
+ free(fullname);
+ break;
+ case AE_IFIFO:
+ /* TODO: Find a better way to warn about our inability
+ * to restore a fifo. */
+ return (EINVAL);
+ }
+
+ /* All the system calls above set errno on failure. */
+ if (r)
+ return (errno);
+
+ /* If we managed to set the final mode, we've avoided a chmod(). */
+ if (mode == final_mode)
+ a->todo &= ~TODO_MODE;
+ return (0);
+}
+
+/*
+ * Cleanup function for archive_extract. Mostly, this involves processing
+ * the fixup list, which is used to address a number of problems:
+ * * Dir permissions might prevent us from restoring a file in that
+ * dir, so we restore the dir with minimum 0700 permissions first,
+ * then correct the mode at the end.
+ * * Similarly, the act of restoring a file touches the directory
+ * and changes the timestamp on the dir, so we have to touch-up dir
+ * timestamps at the end as well.
+ * * Some file flags can interfere with the restore by, for example,
+ * preventing the creation of hardlinks to those files.
+ * * Mac OS extended metadata includes ACLs, so must be deferred on dirs.
+ *
+ * Note that tar/cpio do not require that archives be in a particular
+ * order; there is no way to know when the last file has been restored
+ * within a directory, so there's no way to optimize the memory usage
+ * here by fixing up the directory any earlier than the
+ * end-of-archive.
+ *
+ * XXX TODO: Directory ACLs should be restored here, for the same
+ * reason we set directory perms here. XXX
+ */
+static int
+_archive_write_disk_close(struct archive *_a)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ struct fixup_entry *next, *p;
+ int ret;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_disk_close");
+ ret = _archive_write_disk_finish_entry(&a->archive);
+
+ /* Sort dir list so directories are fixed up in depth-first order. */
+ p = sort_dir_list(a->fixup_list);
+
+ while (p != NULL) {
+ a->pst = NULL; /* Mark stat cache as out-of-date. */
+ if (p->fixup & TODO_TIMES) {
+ set_times(a, INVALID_HANDLE_VALUE, p->mode, p->name,
+ p->atime, p->atime_nanos,
+ p->birthtime, p->birthtime_nanos,
+ p->mtime, p->mtime_nanos,
+ p->ctime, p->ctime_nanos);
+ }
+ if (p->fixup & TODO_MODE_BASE)
+ la_chmod(p->name, p->mode);
+ if (p->fixup & TODO_ACLS)
+ set_acls(a, INVALID_HANDLE_VALUE, p->name, &p->acl);
+ if (p->fixup & TODO_FFLAGS)
+ set_fflags_platform(p->name, p->fflags_set, 0);
+ next = p->next;
+ archive_acl_clear(&p->acl);
+ free(p->name);
+ free(p);
+ p = next;
+ }
+ a->fixup_list = NULL;
+ return (ret);
+}
+
+static int
+_archive_write_disk_free(struct archive *_a)
+{
+ struct archive_write_disk *a;
+ int ret;
+ if (_a == NULL)
+ return (ARCHIVE_OK);
+ archive_check_magic(_a, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_disk_free");
+ a = (struct archive_write_disk *)_a;
+ ret = _archive_write_disk_close(&a->archive);
+ archive_write_disk_set_group_lookup(&a->archive, NULL, NULL, NULL);
+ archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL);
+ archive_entry_free(a->entry);
+ archive_wstring_free(&a->_name_data);
+ archive_wstring_free(&a->_tmpname_data);
+ archive_string_free(&a->archive.error_string);
+ archive_wstring_free(&a->path_safe);
+ a->archive.magic = 0;
+ __archive_clean(&a->archive);
+ free(a);
+ return (ret);
+}
+
+/*
+ * Simple O(n log n) merge sort to order the fixup list. In
+ * particular, we want to restore dir timestamps depth-first.
+ */
+static struct fixup_entry *
+sort_dir_list(struct fixup_entry *p)
+{
+ struct fixup_entry *a, *b, *t;
+
+ if (p == NULL)
+ return (NULL);
+ /* A one-item list is already sorted. */
+ if (p->next == NULL)
+ return (p);
+
+ /* Step 1: split the list. */
+ t = p;
+ a = p->next->next;
+ while (a != NULL) {
+ /* Step a twice, t once. */
+ a = a->next;
+ if (a != NULL)
+ a = a->next;
+ t = t->next;
+ }
+ /* Now, t is at the mid-point, so break the list here. */
+ b = t->next;
+ t->next = NULL;
+ a = p;
+
+ /* Step 2: Recursively sort the two sub-lists. */
+ a = sort_dir_list(a);
+ b = sort_dir_list(b);
+
+ /* Step 3: Merge the returned lists. */
+ /* Pick the first element for the merged list. */
+ if (wcscmp(a->name, b->name) > 0) {
+ t = p = a;
+ a = a->next;
+ } else {
+ t = p = b;
+ b = b->next;
+ }
+
+ /* Always put the later element on the list first. */
+ while (a != NULL && b != NULL) {
+ if (wcscmp(a->name, b->name) > 0) {
+ t->next = a;
+ a = a->next;
+ } else {
+ t->next = b;
+ b = b->next;
+ }
+ t = t->next;
+ }
+
+ /* Only one list is non-empty, so just splice it on. */
+ if (a != NULL)
+ t->next = a;
+ if (b != NULL)
+ t->next = b;
+
+ return (p);
+}
+
+/*
+ * Returns a new, initialized fixup entry.
+ *
+ * TODO: Reduce the memory requirements for this list by using a tree
+ * structure rather than a simple list of names.
+ */
+static struct fixup_entry *
+new_fixup(struct archive_write_disk *a, const wchar_t *pathname)
+{
+ struct fixup_entry *fe;
+
+ fe = (struct fixup_entry *)calloc(1, sizeof(struct fixup_entry));
+ if (fe == NULL)
+ return (NULL);
+ fe->next = a->fixup_list;
+ a->fixup_list = fe;
+ fe->fixup = 0;
+ fe->name = _wcsdup(pathname);
+ fe->fflags_set = 0;
+ return (fe);
+}
+
+/*
+ * Returns a fixup structure for the current entry.
+ */
+static struct fixup_entry *
+current_fixup(struct archive_write_disk *a, const wchar_t *pathname)
+{
+ if (a->current_fixup == NULL)
+ a->current_fixup = new_fixup(a, pathname);
+ return (a->current_fixup);
+}
+
+/*
+ * TODO: The deep-directory support bypasses this; disable deep directory
+ * support if we're doing symlink checks.
+ */
+/*
+ * TODO: Someday, integrate this with the deep dir support; they both
+ * scan the path and both can be optimized by comparing against other
+ * recent paths.
+ */
+static int
+check_symlinks(struct archive_write_disk *a)
+{
+ wchar_t *pn, *p;
+ wchar_t c;
+ int r;
+ BY_HANDLE_FILE_INFORMATION st;
+ mode_t st_mode;
+
+ /*
+ * Guard against symlink tricks. Reject any archive entry whose
+ * destination would be altered by a symlink.
+ */
+ /* Whatever we checked last time doesn't need to be re-checked. */
+ pn = a->name;
+ p = a->path_safe.s;
+ while ((*pn != '\0') && (*p == *pn))
+ ++p, ++pn;
+ /* Skip leading backslashes */
+ while (*pn == '\\')
+ ++pn;
+ c = pn[0];
+ /* Keep going until we've checked the entire name. */
+ while (pn[0] != '\0' && (pn[0] != '\\' || pn[1] != '\0')) {
+ /* Skip the next path element. */
+ while (*pn != '\0' && *pn != '\\')
+ ++pn;
+ c = pn[0];
+ pn[0] = '\0';
+ /* Check that we haven't hit a symlink. */
+ r = file_information(a, a->name, &st, &st_mode, 1);
+ if (r != 0) {
+ /* We've hit a dir that doesn't exist; stop now. */
+ if (errno == ENOENT)
+ break;
+ } else if (S_ISLNK(st_mode)) {
+ if (c == '\0') {
+ /*
+ * Last element is a file or directory symlink.
+ * Remove it so we can overwrite it with the
+ * item being extracted.
+ */
+ if (a->flags &
+ ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {
+ (void)clear_nochange_fflags(a);
+ }
+ if (st.dwFileAttributes &
+ FILE_ATTRIBUTE_DIRECTORY) {
+ r = disk_rmdir(a->name);
+ } else {
+ r = disk_unlink(a->name);
+ }
+ if (r) {
+ archive_set_error(&a->archive, errno,
+ "Could not remove symlink %ls",
+ a->name);
+ pn[0] = c;
+ return (ARCHIVE_FAILED);
+ }
+ a->pst = NULL;
+ /*
+ * Even if we did remove it, a warning
+ * is in order. The warning is silly,
+ * though, if we're just replacing one
+ * symlink with another symlink.
+ */
+ if (!S_ISLNK(a->mode)) {
+ archive_set_error(&a->archive, 0,
+ "Removing symlink %ls",
+ a->name);
+ }
+ /* Symlink gone. No more problem! */
+ pn[0] = c;
+ return (0);
+ } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) {
+ /* User asked us to remove problems. */
+ if (a->flags &
+ ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {
+ (void)clear_nochange_fflags(a);
+ }
+ if (st.dwFileAttributes &
+ FILE_ATTRIBUTE_DIRECTORY) {
+ r = disk_rmdir(a->name);
+ } else {
+ r = disk_unlink(a->name);
+ }
+ if (r != 0) {
+ archive_set_error(&a->archive, 0,
+ "Cannot remove intervening "
+ "symlink %ls", a->name);
+ pn[0] = c;
+ return (ARCHIVE_FAILED);
+ }
+ a->pst = NULL;
+ } else {
+ archive_set_error(&a->archive, 0,
+ "Cannot extract through symlink %ls",
+ a->name);
+ pn[0] = c;
+ return (ARCHIVE_FAILED);
+ }
+ }
+ if (!c)
+ break;
+ pn[0] = c;
+ pn++;
+ }
+ pn[0] = c;
+ /* We've checked and/or cleaned the whole path, so remember it. */
+ archive_wstrcpy(&a->path_safe, a->name);
+ return (ARCHIVE_OK);
+}
+
+static int
+guidword(wchar_t *p, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if ((*p >= L'0' && *p <= L'9') ||
+ (*p >= L'a' && *p <= L'f') ||
+ (*p >= L'A' && *p <= L'F'))
+ p++;
+ else
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Canonicalize the pathname. In particular, this strips duplicate
+ * '\' characters, '.' elements, and trailing '\'. It also raises an
+ * error for an empty path, a trailing '..' or (if _SECURE_NODOTDOT is
+ * set) any '..' in the path.
+ */
+static int
+cleanup_pathname(struct archive_write_disk *a, wchar_t *name)
+{
+ wchar_t *dest, *src, *p, *top;
+ wchar_t separator = L'\0';
+
+ p = name;
+ if (*p == L'\0') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid empty pathname");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Replace '/' by '\' */
+ for (; *p != L'\0'; p++) {
+ if (*p == L'/')
+ *p = L'\\';
+ }
+ p = name;
+
+ /* Skip leading "\\.\" or "\\?\" or "\\?\UNC\" or
+ * "\\?\Volume{GUID}\"
+ * (absolute path prefixes used by Windows API) */
+ if (p[0] == L'\\' && p[1] == L'\\' &&
+ (p[2] == L'.' || p[2] == L'?') && p[3] == L'\\')
+ {
+ /* A path begin with "\\?\UNC\" */
+ if (p[2] == L'?' &&
+ (p[4] == L'U' || p[4] == L'u') &&
+ (p[5] == L'N' || p[5] == L'n') &&
+ (p[6] == L'C' || p[6] == L'c') &&
+ p[7] == L'\\')
+ p += 8;
+ /* A path begin with "\\?\Volume{GUID}\" */
+ else if (p[2] == L'?' &&
+ (p[4] == L'V' || p[4] == L'v') &&
+ (p[5] == L'O' || p[5] == L'o') &&
+ (p[6] == L'L' || p[6] == L'l') &&
+ (p[7] == L'U' || p[7] == L'u') &&
+ (p[8] == L'M' || p[8] == L'm') &&
+ (p[9] == L'E' || p[9] == L'e') &&
+ p[10] == L'{') {
+ if (guidword(p+11, 8) == 0 && p[19] == L'-' &&
+ guidword(p+20, 4) == 0 && p[24] == L'-' &&
+ guidword(p+25, 4) == 0 && p[29] == L'-' &&
+ guidword(p+30, 4) == 0 && p[34] == L'-' &&
+ guidword(p+35, 12) == 0 && p[47] == L'}' &&
+ p[48] == L'\\')
+ p += 49;
+ else
+ p += 4;
+ /* A path begin with "\\.\PhysicalDriveX" */
+ } else if (p[2] == L'.' &&
+ (p[4] == L'P' || p[4] == L'p') &&
+ (p[5] == L'H' || p[5] == L'h') &&
+ (p[6] == L'Y' || p[6] == L'y') &&
+ (p[7] == L'S' || p[7] == L's') &&
+ (p[8] == L'I' || p[8] == L'i') &&
+ (p[9] == L'C' || p[9] == L'c') &&
+ (p[9] == L'A' || p[9] == L'a') &&
+ (p[9] == L'L' || p[9] == L'l') &&
+ (p[9] == L'D' || p[9] == L'd') &&
+ (p[9] == L'R' || p[9] == L'r') &&
+ (p[9] == L'I' || p[9] == L'i') &&
+ (p[9] == L'V' || p[9] == L'v') &&
+ (p[9] == L'E' || p[9] == L'e') &&
+ (p[10] >= L'0' && p[10] <= L'9') &&
+ p[11] == L'\0') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Path is a physical drive name");
+ return (ARCHIVE_FAILED);
+ } else
+ p += 4;
+ /* Network drive path like "\\<server-name>\<share-name>\file" */
+ } else if (p[0] == L'\\' && p[1] == L'\\') {
+ p += 2;
+ }
+
+ /* Skip leading drive letter from archives created
+ * on Windows. */
+ if (((p[0] >= L'a' && p[0] <= L'z') ||
+ (p[0] >= L'A' && p[0] <= L'Z')) &&
+ p[1] == L':') {
+ if (p[2] == L'\0') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Path is a drive name");
+ return (ARCHIVE_FAILED);
+ }
+ if (p[2] == L'\\')
+ p += 2;
+ }
+
+ top = dest = src = p;
+ /* Rewrite the path name if its character is a unusable. */
+ for (; *p != L'\0'; p++) {
+ if (*p == L':' || *p == L'*' || *p == L'?' || *p == L'"' ||
+ *p == L'<' || *p == L'>' || *p == L'|')
+ *p = L'_';
+ }
+ /* Skip leading '\'. */
+ if (*src == L'\\')
+ separator = *src++;
+
+ /* Scan the pathname one element at a time. */
+ for (;;) {
+ /* src points to first char after '\' */
+ if (src[0] == L'\0') {
+ break;
+ } else if (src[0] == L'\\') {
+ /* Found '\\'('//'), ignore second one. */
+ src++;
+ continue;
+ } else if (src[0] == L'.') {
+ if (src[1] == L'\0') {
+ /* Ignore trailing '.' */
+ break;
+ } else if (src[1] == L'\\') {
+ /* Skip '.\'. */
+ src += 2;
+ continue;
+ } else if (src[1] == L'.') {
+ if (src[2] == L'\\' || src[2] == L'\0') {
+ /* Conditionally warn about '..' */
+ if (a->flags &
+ ARCHIVE_EXTRACT_SECURE_NODOTDOT) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Path contains '..'");
+ return (ARCHIVE_FAILED);
+ }
+ }
+ /*
+ * Note: Under no circumstances do we
+ * remove '..' elements. In
+ * particular, restoring
+ * '\foo\..\bar\' should create the
+ * 'foo' dir as a side-effect.
+ */
+ }
+ }
+
+ /* Copy current element, including leading '\'. */
+ if (separator)
+ *dest++ = L'\\';
+ while (*src != L'\0' && *src != L'\\') {
+ *dest++ = *src++;
+ }
+
+ if (*src == L'\0')
+ break;
+
+ /* Skip '\' separator. */
+ separator = *src++;
+ }
+ /*
+ * We've just copied zero or more path elements, not including the
+ * final '\'.
+ */
+ if (dest == top) {
+ /*
+ * Nothing got copied. The path must have been something
+ * like '.' or '\' or './' or '/././././/./'.
+ */
+ if (separator)
+ *dest++ = L'\\';
+ else
+ *dest++ = L'.';
+ }
+ /* Terminate the result. */
+ *dest = L'\0';
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Create the parent directory of the specified path, assuming path
+ * is already in mutable storage.
+ */
+static int
+create_parent_dir(struct archive_write_disk *a, wchar_t *path)
+{
+ wchar_t *slash;
+ int r;
+
+ /* Remove tail element to obtain parent name. */
+ slash = wcsrchr(path, L'\\');
+ if (slash == NULL)
+ return (ARCHIVE_OK);
+ *slash = L'\0';
+ r = create_dir(a, path);
+ *slash = L'\\';
+ return (r);
+}
+
+/*
+ * Create the specified dir, recursing to create parents as necessary.
+ *
+ * Returns ARCHIVE_OK if the path exists when we're done here.
+ * Otherwise, returns ARCHIVE_FAILED.
+ * Assumes path is in mutable storage; path is unchanged on exit.
+ */
+static int
+create_dir(struct archive_write_disk *a, wchar_t *path)
+{
+ BY_HANDLE_FILE_INFORMATION st;
+ struct fixup_entry *le;
+ wchar_t *slash, *base, *full;
+ mode_t mode_final, mode, st_mode;
+ int r;
+
+ /* Check for special names and just skip them. */
+ slash = wcsrchr(path, L'\\');
+ if (slash == NULL)
+ base = path;
+ else
+ base = slash + 1;
+
+ if (base[0] == L'\0' ||
+ (base[0] == L'.' && base[1] == L'\0') ||
+ (base[0] == L'.' && base[1] == L'.' && base[2] == L'\0')) {
+ /* Don't bother trying to create null path, '.', or '..'. */
+ if (slash != NULL) {
+ *slash = L'\0';
+ r = create_dir(a, path);
+ *slash = L'\\';
+ return (r);
+ }
+ return (ARCHIVE_OK);
+ }
+
+ /*
+ * Yes, this should be stat() and not lstat(). Using lstat()
+ * here loses the ability to extract through symlinks. Also note
+ * that this should not use the a->st cache.
+ */
+ if (file_information(a, path, &st, &st_mode, 0) == 0) {
+ if (S_ISDIR(st_mode))
+ return (ARCHIVE_OK);
+ if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+ archive_set_error(&a->archive, EEXIST,
+ "Can't create directory '%ls'", path);
+ return (ARCHIVE_FAILED);
+ }
+ if (disk_unlink(path) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't create directory '%ls': "
+ "Conflicting file cannot be removed",
+ path);
+ return (ARCHIVE_FAILED);
+ }
+ } else if (errno != ENOENT && errno != ENOTDIR) {
+ /* Stat failed? */
+ archive_set_error(&a->archive, errno,
+ "Can't test directory '%ls'", path);
+ return (ARCHIVE_FAILED);
+ } else if (slash != NULL) {
+ *slash = '\0';
+ r = create_dir(a, path);
+ *slash = '\\';
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ /*
+ * Mode we want for the final restored directory. Per POSIX,
+ * implicitly-created dirs must be created obeying the umask.
+ * There's no mention whether this is different for privileged
+ * restores (which the rest of this code handles by pretending
+ * umask=0). I've chosen here to always obey the user's umask for
+ * implicit dirs, even if _EXTRACT_PERM was specified.
+ */
+ mode_final = DEFAULT_DIR_MODE & ~a->user_umask;
+ /* Mode we want on disk during the restore process. */
+ mode = mode_final;
+ mode |= MINIMUM_DIR_MODE;
+ mode &= MAXIMUM_DIR_MODE;
+ /*
+ * Apply __la_win_permissive_name_w to path in order to
+ * remove '../' path string.
+ */
+ full = __la_win_permissive_name_w(path);
+ if (full == NULL)
+ errno = EINVAL;
+ else if (CreateDirectoryW(full, NULL) != 0) {
+ if (mode != mode_final) {
+ le = new_fixup(a, path);
+ le->fixup |=TODO_MODE_BASE;
+ le->mode = mode_final;
+ }
+ free(full);
+ return (ARCHIVE_OK);
+ } else {
+ la_dosmaperr(GetLastError());
+ }
+ free(full);
+
+ /*
+ * Without the following check, a/b/../b/c/d fails at the
+ * second visit to 'b', so 'd' can't be created. Note that we
+ * don't add it to the fixup list here, as it's already been
+ * added.
+ */
+ if (file_information(a, path, &st, &st_mode, 0) == 0 &&
+ S_ISDIR(st_mode))
+ return (ARCHIVE_OK);
+
+ archive_set_error(&a->archive, errno, "Failed to create dir '%ls'",
+ path);
+ return (ARCHIVE_FAILED);
+}
+
+/*
+ * Note: Although we can skip setting the user id if the desired user
+ * id matches the current user, we cannot skip setting the group, as
+ * many systems set the gid based on the containing directory. So
+ * we have to perform a chown syscall if we want to set the SGID
+ * bit. (The alternative is to stat() and then possibly chown(); it's
+ * more efficient to skip the stat() and just always chown().) Note
+ * that a successful chown() here clears the TODO_SGID_CHECK bit, which
+ * allows set_mode to skip the stat() check for the GID.
+ */
+static int
+set_ownership(struct archive_write_disk *a)
+{
+/* unfortunately, on win32 there is no 'root' user with uid 0,
+ so we just have to try the chown and see if it works */
+
+ /* If we know we can't change it, don't bother trying. */
+ if (a->user_uid != 0 && a->user_uid != a->uid) {
+ archive_set_error(&a->archive, errno,
+ "Can't set UID=%jd", (intmax_t)a->uid);
+ return (ARCHIVE_WARN);
+ }
+
+ archive_set_error(&a->archive, errno,
+ "Can't set user=%jd/group=%jd for %ls",
+ (intmax_t)a->uid, (intmax_t)a->gid, a->name);
+ return (ARCHIVE_WARN);
+}
+
+static int
+set_times(struct archive_write_disk *a,
+ HANDLE h, int mode, const wchar_t *name,
+ time_t atime, long atime_nanos,
+ time_t birthtime, long birthtime_nanos,
+ time_t mtime, long mtime_nanos,
+ time_t ctime_sec, long ctime_nanos)
+{
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+#define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\
+ + (((nsec)/1000)*10))
+
+ HANDLE hw = 0;
+ ULARGE_INTEGER wintm;
+ FILETIME *pfbtime;
+ FILETIME fatime, fbtime, fmtime;
+
+ (void)ctime_sec; /* UNUSED */
+ (void)ctime_nanos; /* UNUSED */
+
+ if (h != INVALID_HANDLE_VALUE) {
+ hw = NULL;
+ } else {
+ wchar_t *ws;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+ if (S_ISLNK(mode))
+ return (ARCHIVE_OK);
+ ws = __la_win_permissive_name_w(name);
+ if (ws == NULL)
+ goto settimes_failed;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+ hw = CreateFile2(ws, FILE_WRITE_ATTRIBUTES, 0,
+ OPEN_EXISTING, &createExParams);
+#else
+ hw = CreateFileW(ws, FILE_WRITE_ATTRIBUTES,
+ 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+#endif
+ free(ws);
+ if (hw == INVALID_HANDLE_VALUE)
+ goto settimes_failed;
+ h = hw;
+ }
+
+ wintm.QuadPart = WINTIME(atime, atime_nanos);
+ fatime.dwLowDateTime = wintm.LowPart;
+ fatime.dwHighDateTime = wintm.HighPart;
+ wintm.QuadPart = WINTIME(mtime, mtime_nanos);
+ fmtime.dwLowDateTime = wintm.LowPart;
+ fmtime.dwHighDateTime = wintm.HighPart;
+ /*
+ * SetFileTime() supports birthtime.
+ */
+ if (birthtime > 0 || birthtime_nanos > 0) {
+ wintm.QuadPart = WINTIME(birthtime, birthtime_nanos);
+ fbtime.dwLowDateTime = wintm.LowPart;
+ fbtime.dwHighDateTime = wintm.HighPart;
+ pfbtime = &fbtime;
+ } else
+ pfbtime = NULL;
+ if (SetFileTime(h, pfbtime, &fatime, &fmtime) == 0)
+ goto settimes_failed;
+ CloseHandle(hw);
+ return (ARCHIVE_OK);
+
+settimes_failed:
+ CloseHandle(hw);
+ archive_set_error(&a->archive, EINVAL, "Can't restore time");
+ return (ARCHIVE_WARN);
+}
+
+static int
+set_times_from_entry(struct archive_write_disk *a)
+{
+ time_t atime, birthtime, mtime, ctime_sec;
+ long atime_nsec, birthtime_nsec, mtime_nsec, ctime_nsec;
+
+ /* Suitable defaults. */
+ atime = birthtime = mtime = ctime_sec = a->start_time;
+ atime_nsec = birthtime_nsec = mtime_nsec = ctime_nsec = 0;
+
+ /* If no time was provided, we're done. */
+ if (!archive_entry_atime_is_set(a->entry)
+ && !archive_entry_birthtime_is_set(a->entry)
+ && !archive_entry_mtime_is_set(a->entry))
+ return (ARCHIVE_OK);
+
+ if (archive_entry_atime_is_set(a->entry)) {
+ atime = archive_entry_atime(a->entry);
+ atime_nsec = archive_entry_atime_nsec(a->entry);
+ }
+ if (archive_entry_birthtime_is_set(a->entry)) {
+ birthtime = archive_entry_birthtime(a->entry);
+ birthtime_nsec = archive_entry_birthtime_nsec(a->entry);
+ }
+ if (archive_entry_mtime_is_set(a->entry)) {
+ mtime = archive_entry_mtime(a->entry);
+ mtime_nsec = archive_entry_mtime_nsec(a->entry);
+ }
+ if (archive_entry_ctime_is_set(a->entry)) {
+ ctime_sec = archive_entry_ctime(a->entry);
+ ctime_nsec = archive_entry_ctime_nsec(a->entry);
+ }
+
+ return set_times(a, a->fh, a->mode, a->name,
+ atime, atime_nsec,
+ birthtime, birthtime_nsec,
+ mtime, mtime_nsec,
+ ctime_sec, ctime_nsec);
+}
+
+static int
+set_mode(struct archive_write_disk *a, int mode)
+{
+ int r = ARCHIVE_OK;
+ mode &= 07777; /* Strip off file type bits. */
+
+ if (a->todo & TODO_SGID_CHECK) {
+ /*
+ * If we don't know the GID is right, we must stat()
+ * to verify it. We can't just check the GID of this
+ * process, since systems sometimes set GID from
+ * the enclosing dir or based on ACLs.
+ */
+ if ((r = lazy_stat(a)) != ARCHIVE_OK)
+ return (r);
+ if (0 != a->gid) {
+ mode &= ~ S_ISGID;
+ }
+ /* While we're here, double-check the UID. */
+ if (0 != a->uid
+ && (a->todo & TODO_SUID)) {
+ mode &= ~ S_ISUID;
+ }
+ a->todo &= ~TODO_SGID_CHECK;
+ a->todo &= ~TODO_SUID_CHECK;
+ } else if (a->todo & TODO_SUID_CHECK) {
+ /*
+ * If we don't know the UID is right, we can just check
+ * the user, since all systems set the file UID from
+ * the process UID.
+ */
+ if (a->user_uid != a->uid) {
+ mode &= ~ S_ISUID;
+ }
+ a->todo &= ~TODO_SUID_CHECK;
+ }
+
+ if (S_ISLNK(a->mode)) {
+#ifdef HAVE_LCHMOD
+ /*
+ * If this is a symlink, use lchmod(). If the
+ * platform doesn't support lchmod(), just skip it. A
+ * platform that doesn't provide a way to set
+ * permissions on symlinks probably ignores
+ * permissions on symlinks, so a failure here has no
+ * impact.
+ */
+ if (lchmod(a->name, mode) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't set permissions to 0%o", (int)mode);
+ r = ARCHIVE_WARN;
+ }
+#endif
+ } else if (!S_ISDIR(a->mode)) {
+ /*
+ * If it's not a symlink and not a dir, then use
+ * fchmod() or chmod(), depending on whether we have
+ * an fd. Dirs get their perms set during the
+ * post-extract fixup, which is handled elsewhere.
+ */
+#ifdef HAVE_FCHMOD
+ if (a->fd >= 0) {
+ if (fchmod(a->fd, mode) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't set permissions to 0%o", (int)mode);
+ r = ARCHIVE_WARN;
+ }
+ } else
+#endif
+ /* If this platform lacks fchmod(), then
+ * we'll just use chmod(). */
+ if (la_chmod(a->name, mode) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't set permissions to 0%o", (int)mode);
+ r = ARCHIVE_WARN;
+ }
+ }
+ return (r);
+}
+
+static int set_fflags_platform(const wchar_t *name, unsigned long fflags_set,
+ unsigned long fflags_clear)
+{
+ DWORD oldflags, newflags;
+ wchar_t *fullname;
+
+ const DWORD settable_flags =
+ FILE_ATTRIBUTE_ARCHIVE |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_NORMAL |
+ FILE_ATTRIBUTE_NOT_CONTENT_INDEXED |
+ FILE_ATTRIBUTE_OFFLINE |
+ FILE_ATTRIBUTE_READONLY |
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_TEMPORARY;
+
+ oldflags = GetFileAttributesW(name);
+ if (oldflags == (DWORD)-1 &&
+ GetLastError() == ERROR_INVALID_NAME) {
+ fullname = __la_win_permissive_name_w(name);
+ oldflags = GetFileAttributesW(fullname);
+ }
+ if (oldflags == (DWORD)-1) {
+ la_dosmaperr(GetLastError());
+ return (ARCHIVE_WARN);
+ }
+ newflags = ((oldflags & ~fflags_clear) | fflags_set) & settable_flags;
+ if(SetFileAttributesW(name, newflags) == 0)
+ return (ARCHIVE_WARN);
+ return (ARCHIVE_OK);
+}
+
+static int
+clear_nochange_fflags(struct archive_write_disk *a)
+{
+ return (set_fflags_platform(a->name, 0, FILE_ATTRIBUTE_READONLY));
+}
+
+static int
+set_fflags(struct archive_write_disk *a)
+{
+ unsigned long set, clear;
+
+ if (a->todo & TODO_FFLAGS) {
+ archive_entry_fflags(a->entry, &set, &clear);
+ if (set == 0 && clear == 0)
+ return (ARCHIVE_OK);
+ return (set_fflags_platform(a->name, set, clear));
+
+ }
+ return (ARCHIVE_OK);
+}
+
+/* Default empty function body to satisfy mainline code. */
+static int
+set_acls(struct archive_write_disk *a, HANDLE h, const wchar_t *name,
+ struct archive_acl *acl)
+{
+ (void)a; /* UNUSED */
+ (void)h; /* UNUSED */
+ (void)name; /* UNUSED */
+ (void)acl; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Restore extended attributes - stub implementation for unsupported systems
+ */
+static int
+set_xattrs(struct archive_write_disk *a)
+{
+ static int warning_done = 0;
+
+ /* If there aren't any extended attributes, then it's okay not
+ * to extract them, otherwise, issue a single warning. */
+ if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) {
+ warning_done = 1;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Cannot restore extended attributes on this system");
+ return (ARCHIVE_WARN);
+ }
+ /* Warning was already emitted; suppress further warnings. */
+ return (ARCHIVE_OK);
+}
+
+static void
+fileTimeToUtc(const FILETIME *filetime, time_t *t, long *ns)
+{
+ ULARGE_INTEGER utc;
+
+ utc.HighPart = filetime->dwHighDateTime;
+ utc.LowPart = filetime->dwLowDateTime;
+ if (utc.QuadPart >= EPOC_TIME) {
+ utc.QuadPart -= EPOC_TIME;
+ /* milli seconds base */
+ *t = (time_t)(utc.QuadPart / 10000000);
+ /* nano seconds base */
+ *ns = (long)(utc.QuadPart % 10000000) * 100;
+ } else {
+ *t = 0;
+ *ns = 0;
+ }
+}
+/*
+ * Test if file on disk is older than entry.
+ */
+static int
+older(BY_HANDLE_FILE_INFORMATION *st, struct archive_entry *entry)
+{
+ time_t sec;
+ long nsec;
+
+ fileTimeToUtc(&st->ftLastWriteTime, &sec, &nsec);
+ /* First, test the seconds and return if we have a definite answer. */
+ /* Definitely older. */
+ if (sec < archive_entry_mtime(entry))
+ return (1);
+ /* Definitely younger. */
+ if (sec > archive_entry_mtime(entry))
+ return (0);
+ if (nsec < archive_entry_mtime_nsec(entry))
+ return (1);
+ /* Same age or newer, so not older. */
+ return (0);
+}
+
+#endif /* _WIN32 && !__CYGWIN__ */
+
diff --git a/contrib/libs/libarchive/libarchive/archive_write_open_fd.c b/contrib/libs/libarchive/libarchive/archive_write_open_fd.c
new file mode 100644
index 00000000000..b8d491faa27
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_open_fd.c
@@ -0,0 +1,146 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_open_fd.c 201093 2009-12-28 02:28:44Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+
+struct write_fd_data {
+ int fd;
+};
+
+static int file_free(struct archive *, void *);
+static int file_open(struct archive *, void *);
+static ssize_t file_write(struct archive *, void *, const void *buff, size_t);
+
+int
+archive_write_open_fd(struct archive *a, int fd)
+{
+ struct write_fd_data *mine;
+
+ mine = (struct write_fd_data *)malloc(sizeof(*mine));
+ if (mine == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ mine->fd = fd;
+#if defined(__CYGWIN__) || defined(_WIN32)
+ setmode(mine->fd, O_BINARY);
+#endif
+ return (archive_write_open2(a, mine,
+ file_open, file_write, NULL, file_free));
+}
+
+static int
+file_open(struct archive *a, void *client_data)
+{
+ struct write_fd_data *mine;
+ struct stat st;
+
+ mine = (struct write_fd_data *)client_data;
+
+ if (fstat(mine->fd, &st) != 0) {
+ archive_set_error(a, errno, "Couldn't stat fd %d", mine->fd);
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * If this is a regular file, don't add it to itself.
+ */
+ if (S_ISREG(st.st_mode))
+ archive_write_set_skip_file(a, st.st_dev, st.st_ino);
+
+ /*
+ * If client hasn't explicitly set the last block handling,
+ * then set it here.
+ */
+ if (archive_write_get_bytes_in_last_block(a) < 0) {
+ /* If the output is a block or character device, fifo,
+ * or stdout, pad the last block, otherwise leave it
+ * unpadded. */
+ if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) ||
+ S_ISFIFO(st.st_mode) || (mine->fd == 1))
+ /* Last block will be fully padded. */
+ archive_write_set_bytes_in_last_block(a, 0);
+ else
+ archive_write_set_bytes_in_last_block(a, 1);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+file_write(struct archive *a, void *client_data, const void *buff, size_t length)
+{
+ struct write_fd_data *mine;
+ ssize_t bytesWritten;
+
+ mine = (struct write_fd_data *)client_data;
+ for (;;) {
+ bytesWritten = write(mine->fd, buff, length);
+ if (bytesWritten <= 0) {
+ if (errno == EINTR)
+ continue;
+ archive_set_error(a, errno, "Write error");
+ return (-1);
+ }
+ return (bytesWritten);
+ }
+}
+
+static int
+file_free(struct archive *a, void *client_data)
+{
+ struct write_fd_data *mine = (struct write_fd_data *)client_data;
+
+ (void)a; /* UNUSED */
+ if (mine == NULL)
+ return (ARCHIVE_OK);
+ free(mine);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_open_file.c b/contrib/libs/libarchive/libarchive/archive_write_open_file.c
new file mode 100644
index 00000000000..bf5b55a672e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_open_file.c
@@ -0,0 +1,111 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_file.c,v 1.19 2007/01/09 08:05:56 kientzle Exp $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+
+struct write_FILE_data {
+ FILE *f;
+};
+
+static int file_free(struct archive *, void *);
+static int file_open(struct archive *, void *);
+static ssize_t file_write(struct archive *, void *, const void *buff, size_t);
+
+int
+archive_write_open_FILE(struct archive *a, FILE *f)
+{
+ struct write_FILE_data *mine;
+
+ mine = (struct write_FILE_data *)malloc(sizeof(*mine));
+ if (mine == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ mine->f = f;
+ return (archive_write_open2(a, mine, file_open, file_write,
+ NULL, file_free));
+}
+
+static int
+file_open(struct archive *a, void *client_data)
+{
+ (void)a; /* UNUSED */
+ (void)client_data; /* UNUSED */
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+file_write(struct archive *a, void *client_data, const void *buff, size_t length)
+{
+ struct write_FILE_data *mine;
+ size_t bytesWritten;
+
+ mine = client_data;
+ for (;;) {
+ bytesWritten = fwrite(buff, 1, length, mine->f);
+ if (bytesWritten <= 0) {
+ if (errno == EINTR)
+ continue;
+ archive_set_error(a, errno, "Write error");
+ return (-1);
+ }
+ return (bytesWritten);
+ }
+}
+
+static int
+file_free(struct archive *a, void *client_data)
+{
+ struct write_FILE_data *mine = client_data;
+
+ (void)a; /* UNUSED */
+ if (mine == NULL)
+ return (ARCHIVE_OK);
+ free(mine);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_open_filename.c b/contrib/libs/libarchive/libarchive/archive_write_open_filename.c
new file mode 100644
index 00000000000..9ceefb19bc9
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_open_filename.c
@@ -0,0 +1,270 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_open_filename.c 191165 2009-04-17 00:39:35Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+struct write_file_data {
+ int fd;
+ struct archive_mstring filename;
+};
+
+static int file_close(struct archive *, void *);
+static int file_free(struct archive *, void *);
+static int file_open(struct archive *, void *);
+static ssize_t file_write(struct archive *, void *, const void *buff, size_t);
+static int open_filename(struct archive *, int, const void *);
+
+int
+archive_write_open_file(struct archive *a, const char *filename)
+{
+ return (archive_write_open_filename(a, filename));
+}
+
+int
+archive_write_open_filename(struct archive *a, const char *filename)
+{
+
+ if (filename == NULL || filename[0] == '\0')
+ return (archive_write_open_fd(a, 1));
+
+ return (open_filename(a, 1, filename));
+}
+
+int
+archive_write_open_filename_w(struct archive *a, const wchar_t *filename)
+{
+
+ if (filename == NULL || filename[0] == L'\0')
+ return (archive_write_open_fd(a, 1));
+
+ return (open_filename(a, 0, filename));
+}
+
+static int
+open_filename(struct archive *a, int mbs_fn, const void *filename)
+{
+ struct write_file_data *mine;
+ int r;
+
+ mine = (struct write_file_data *)calloc(1, sizeof(*mine));
+ if (mine == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ if (mbs_fn)
+ r = archive_mstring_copy_mbs(&mine->filename, filename);
+ else
+ r = archive_mstring_copy_wcs(&mine->filename, filename);
+ if (r < 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ if (mbs_fn)
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Can't convert '%s' to WCS",
+ (const char *)filename);
+ else
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Can't convert '%S' to MBS",
+ (const wchar_t *)filename);
+ return (ARCHIVE_FAILED);
+ }
+ mine->fd = -1;
+ return (archive_write_open2(a, mine,
+ file_open, file_write, file_close, file_free));
+}
+
+static int
+file_open(struct archive *a, void *client_data)
+{
+ int flags;
+ struct write_file_data *mine;
+ struct stat st;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ wchar_t *fullpath;
+#endif
+ const wchar_t *wcs;
+ const char *mbs;
+
+ mine = (struct write_file_data *)client_data;
+ flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC;
+
+ /*
+ * Open the file.
+ */
+ mbs = NULL; wcs = NULL;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (archive_mstring_get_wcs(a, &mine->filename, &wcs) != 0) {
+ if (errno == ENOMEM)
+ archive_set_error(a, errno, "No memory");
+ else {
+ archive_mstring_get_mbs(a, &mine->filename, &mbs);
+ archive_set_error(a, errno,
+ "Can't convert '%s' to WCS", mbs);
+ }
+ return (ARCHIVE_FATAL);
+ }
+ fullpath = __la_win_permissive_name_w(wcs);
+ if (fullpath != NULL) {
+ mine->fd = _wopen(fullpath, flags, 0666);
+ free(fullpath);
+ } else
+ mine->fd = _wopen(wcs, flags, 0666);
+#else
+ if (archive_mstring_get_mbs(a, &mine->filename, &mbs) != 0) {
+ if (errno == ENOMEM)
+ archive_set_error(a, errno, "No memory");
+ else {
+ archive_mstring_get_wcs(a, &mine->filename, &wcs);
+ archive_set_error(a, errno,
+ "Can't convert '%S' to MBS", wcs);
+ }
+ return (ARCHIVE_FATAL);
+ }
+ mine->fd = open(mbs, flags, 0666);
+ __archive_ensure_cloexec_flag(mine->fd);
+#endif
+ if (mine->fd < 0) {
+ if (mbs != NULL)
+ archive_set_error(a, errno, "Failed to open '%s'", mbs);
+ else
+ archive_set_error(a, errno, "Failed to open '%S'", wcs);
+ return (ARCHIVE_FATAL);
+ }
+
+ if (fstat(mine->fd, &st) != 0) {
+ if (mbs != NULL)
+ archive_set_error(a, errno, "Couldn't stat '%s'", mbs);
+ else
+ archive_set_error(a, errno, "Couldn't stat '%S'", wcs);
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Set up default last block handling.
+ */
+ if (archive_write_get_bytes_in_last_block(a) < 0) {
+ if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) ||
+ S_ISFIFO(st.st_mode))
+ /* Pad last block when writing to device or FIFO. */
+ archive_write_set_bytes_in_last_block(a, 0);
+ else
+ /* Don't pad last block otherwise. */
+ archive_write_set_bytes_in_last_block(a, 1);
+ }
+
+ /*
+ * If the output file is a regular file, don't add it to
+ * itself. If it's a device file, it's okay to add the device
+ * entry to the output archive.
+ */
+ if (S_ISREG(st.st_mode))
+ archive_write_set_skip_file(a, st.st_dev, st.st_ino);
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+file_write(struct archive *a, void *client_data, const void *buff,
+ size_t length)
+{
+ struct write_file_data *mine;
+ ssize_t bytesWritten;
+
+ mine = (struct write_file_data *)client_data;
+ for (;;) {
+ bytesWritten = write(mine->fd, buff, length);
+ if (bytesWritten <= 0) {
+ if (errno == EINTR)
+ continue;
+ archive_set_error(a, errno, "Write error");
+ return (-1);
+ }
+ return (bytesWritten);
+ }
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+ struct write_file_data *mine = (struct write_file_data *)client_data;
+
+ (void)a; /* UNUSED */
+
+ if (mine == NULL)
+ return (ARCHIVE_FATAL);
+
+ if (mine->fd >= 0)
+ close(mine->fd);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+file_free(struct archive *a, void *client_data)
+{
+ struct write_file_data *mine = (struct write_file_data *)client_data;
+
+ (void)a; /* UNUSED */
+
+ if (mine == NULL)
+ return (ARCHIVE_OK);
+
+ archive_mstring_clean(&mine->filename);
+ free(mine);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_open_memory.c b/contrib/libs/libarchive/libarchive/archive_write_open_memory.c
new file mode 100644
index 00000000000..a8a0b817fc2
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_open_memory.c
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_memory.c,v 1.3 2007/01/09 08:05:56 kientzle Exp $");
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "archive.h"
+
+struct write_memory_data {
+ size_t used;
+ size_t size;
+ size_t * client_size;
+ unsigned char * buff;
+};
+
+static int memory_write_free(struct archive *, void *);
+static int memory_write_open(struct archive *, void *);
+static ssize_t memory_write(struct archive *, void *, const void *buff, size_t);
+
+/*
+ * Client provides a pointer to a block of memory to receive
+ * the data. The 'size' param both tells us the size of the
+ * client buffer and lets us tell the client the final size.
+ */
+int
+archive_write_open_memory(struct archive *a, void *buff, size_t buffSize, size_t *used)
+{
+ struct write_memory_data *mine;
+
+ mine = (struct write_memory_data *)calloc(1, sizeof(*mine));
+ if (mine == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ mine->buff = buff;
+ mine->size = buffSize;
+ mine->client_size = used;
+ return (archive_write_open2(a, mine,
+ memory_write_open, memory_write, NULL, memory_write_free));
+}
+
+static int
+memory_write_open(struct archive *a, void *client_data)
+{
+ struct write_memory_data *mine;
+ mine = client_data;
+ mine->used = 0;
+ if (mine->client_size != NULL)
+ *mine->client_size = mine->used;
+ /* Disable padding if it hasn't been set explicitly. */
+ if (-1 == archive_write_get_bytes_in_last_block(a))
+ archive_write_set_bytes_in_last_block(a, 1);
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Copy the data into the client buffer.
+ * Note that we update mine->client_size on every write.
+ * In particular, this means the client can follow exactly
+ * how much has been written into their buffer at any time.
+ */
+static ssize_t
+memory_write(struct archive *a, void *client_data, const void *buff, size_t length)
+{
+ struct write_memory_data *mine;
+ mine = client_data;
+
+ if (mine->used + length > mine->size) {
+ archive_set_error(a, ENOMEM, "Buffer exhausted");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(mine->buff + mine->used, buff, length);
+ mine->used += length;
+ if (mine->client_size != NULL)
+ *mine->client_size = mine->used;
+ return (length);
+}
+
+static int
+memory_write_free(struct archive *a, void *client_data)
+{
+ struct write_memory_data *mine;
+ (void)a; /* UNUSED */
+ mine = client_data;
+ if (mine == NULL)
+ return (ARCHIVE_OK);
+ free(mine);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_private.h b/contrib/libs/libarchive/libarchive/archive_write_private.h
new file mode 100644
index 00000000000..6522e6521be
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_private.h
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_write_private.h 201155 2009-12-29 05:20:12Z kientzle $
+ */
+
+#ifndef ARCHIVE_WRITE_PRIVATE_H_INCLUDED
+#define ARCHIVE_WRITE_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_private.h"
+
+#define ARCHIVE_WRITE_FILTER_STATE_NEW 1U
+#define ARCHIVE_WRITE_FILTER_STATE_OPEN 2U
+#define ARCHIVE_WRITE_FILTER_STATE_CLOSED 4U
+#define ARCHIVE_WRITE_FILTER_STATE_FATAL 0x8000U
+
+struct archive_write;
+
+struct archive_write_filter {
+ int64_t bytes_written;
+ struct archive *archive; /* Associated archive. */
+ struct archive_write_filter *next_filter; /* Who I write to. */
+ int (*options)(struct archive_write_filter *,
+ const char *key, const char *value);
+ int (*open)(struct archive_write_filter *);
+ int (*write)(struct archive_write_filter *, const void *, size_t);
+ int (*flush)(struct archive_write_filter *);
+ int (*close)(struct archive_write_filter *);
+ int (*free)(struct archive_write_filter *);
+ void *data;
+ const char *name;
+ int code;
+ int bytes_per_block;
+ int bytes_in_last_block;
+ int state;
+};
+
+#if ARCHIVE_VERSION < 4000000
+void __archive_write_filters_free(struct archive *);
+#endif
+
+struct archive_write_filter *__archive_write_allocate_filter(struct archive *);
+
+int __archive_write_output(struct archive_write *, const void *, size_t);
+int __archive_write_nulls(struct archive_write *, size_t);
+int __archive_write_filter(struct archive_write_filter *, const void *, size_t);
+
+struct archive_write {
+ struct archive archive;
+
+ /* Dev/ino of the archive being written. */
+ int skip_file_set;
+ int64_t skip_file_dev;
+ int64_t skip_file_ino;
+
+ /* Utility: Pointer to a block of nulls. */
+ const unsigned char *nulls;
+ size_t null_length;
+
+ /* Callbacks to open/read/write/close archive stream. */
+ archive_open_callback *client_opener;
+ archive_write_callback *client_writer;
+ archive_close_callback *client_closer;
+ archive_free_callback *client_freer;
+ void *client_data;
+
+ /*
+ * Blocking information. Note that bytes_in_last_block is
+ * misleadingly named; I should find a better name. These
+ * control the final output from all compressors, including
+ * compression_none.
+ */
+ int bytes_per_block;
+ int bytes_in_last_block;
+
+ /*
+ * First and last write filters in the pipeline.
+ */
+ struct archive_write_filter *filter_first;
+ struct archive_write_filter *filter_last;
+
+ /*
+ * Pointers to format-specific functions for writing. They're
+ * initialized by archive_write_set_format_XXX() calls.
+ */
+ void *format_data;
+ const char *format_name;
+ int (*format_init)(struct archive_write *);
+ int (*format_options)(struct archive_write *,
+ const char *key, const char *value);
+ int (*format_finish_entry)(struct archive_write *);
+ int (*format_write_header)(struct archive_write *,
+ struct archive_entry *);
+ ssize_t (*format_write_data)(struct archive_write *,
+ const void *buff, size_t);
+ int (*format_close)(struct archive_write *);
+ int (*format_free)(struct archive_write *);
+
+
+ /*
+ * Encryption passphrase.
+ */
+ char *passphrase;
+ archive_passphrase_callback *passphrase_callback;
+ void *passphrase_client_data;
+};
+
+/*
+ * Utility function to format a USTAR header into a buffer. If
+ * "strict" is set, this tries to create the absolutely most portable
+ * version of a ustar header. If "strict" is set to 0, then it will
+ * relax certain requirements.
+ *
+ * Generally, format-specific declarations don't belong in this
+ * header; this is a rare example of a function that is shared by
+ * two very similar formats (ustar and pax).
+ */
+int
+__archive_write_format_header_ustar(struct archive_write *, char buff[512],
+ struct archive_entry *, int tartype, int strict,
+ struct archive_string_conv *);
+
+struct archive_write_program_data;
+struct archive_write_program_data * __archive_write_program_allocate(const char *program_name);
+int __archive_write_program_free(struct archive_write_program_data *);
+int __archive_write_program_open(struct archive_write_filter *,
+ struct archive_write_program_data *, const char *);
+int __archive_write_program_close(struct archive_write_filter *,
+ struct archive_write_program_data *);
+int __archive_write_program_write(struct archive_write_filter *,
+ struct archive_write_program_data *, const void *, size_t);
+
+/*
+ * Get a encryption passphrase.
+ */
+const char * __archive_write_get_passphrase(struct archive_write *a);
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format.c b/contrib/libs/libarchive/libarchive/archive_write_set_format.c
new file mode 100644
index 00000000000..1f65fa4a77e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format.c
@@ -0,0 +1,125 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format.c 201168 2009-12-29 06:15:32Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_write_set_format_private.h"
+
+/* A table that maps format codes to functions. */
+static const
+struct { int code; int (*setter)(struct archive *); } codes[] =
+{
+ { ARCHIVE_FORMAT_7ZIP, archive_write_set_format_7zip },
+ { ARCHIVE_FORMAT_CPIO, archive_write_set_format_cpio },
+ { ARCHIVE_FORMAT_CPIO_BIN_LE, archive_write_set_format_cpio_bin },
+ { ARCHIVE_FORMAT_CPIO_PWB, archive_write_set_format_cpio_pwb },
+ { ARCHIVE_FORMAT_CPIO_POSIX, archive_write_set_format_cpio_odc },
+ { ARCHIVE_FORMAT_CPIO_SVR4_NOCRC, archive_write_set_format_cpio_newc },
+ { ARCHIVE_FORMAT_ISO9660, archive_write_set_format_iso9660 },
+ { ARCHIVE_FORMAT_MTREE, archive_write_set_format_mtree },
+ { ARCHIVE_FORMAT_RAW, archive_write_set_format_raw },
+ { ARCHIVE_FORMAT_SHAR, archive_write_set_format_shar },
+ { ARCHIVE_FORMAT_SHAR_BASE, archive_write_set_format_shar },
+ { ARCHIVE_FORMAT_SHAR_DUMP, archive_write_set_format_shar_dump },
+ { ARCHIVE_FORMAT_TAR, archive_write_set_format_pax_restricted },
+ { ARCHIVE_FORMAT_TAR_GNUTAR, archive_write_set_format_gnutar },
+ { ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE, archive_write_set_format_pax },
+ { ARCHIVE_FORMAT_TAR_PAX_RESTRICTED,
+ archive_write_set_format_pax_restricted },
+ { ARCHIVE_FORMAT_TAR_USTAR, archive_write_set_format_ustar },
+ { ARCHIVE_FORMAT_WARC, archive_write_set_format_warc },
+ { ARCHIVE_FORMAT_XAR, archive_write_set_format_xar },
+ { ARCHIVE_FORMAT_ZIP, archive_write_set_format_zip },
+ { 0, NULL }
+};
+
+int
+archive_write_set_format(struct archive *a, int code)
+{
+ int i;
+
+ for (i = 0; codes[i].code != 0; i++) {
+ if (code == codes[i].code)
+ return ((codes[i].setter)(a));
+ }
+
+ archive_set_error(a, EINVAL, "No such format");
+ return (ARCHIVE_FATAL);
+}
+
+void
+__archive_write_entry_filetype_unsupported(struct archive *a,
+ struct archive_entry *entry, const char *format)
+{
+ const char *name = NULL;
+
+ switch (archive_entry_filetype(entry)) {
+ /*
+ * All formats should be able to archive regular files (AE_IFREG)
+ */
+ case AE_IFDIR:
+ name = "directories";
+ break;
+ case AE_IFLNK:
+ name = "symbolic links";
+ break;
+ case AE_IFCHR:
+ name = "character devices";
+ break;
+ case AE_IFBLK:
+ name = "block devices";
+ break;
+ case AE_IFIFO:
+ name = "named pipes";
+ break;
+ case AE_IFSOCK:
+ name = "sockets";
+ break;
+ default:
+ break;
+ }
+
+ if (name != NULL) {
+ archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+ "%s: %s format cannot archive %s",
+ archive_entry_pathname(entry), format, name);
+ } else {
+ archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+ "%s: %s format cannot archive files with mode 0%lo",
+ archive_entry_pathname(entry), format,
+ (unsigned long)archive_entry_mode(entry));
+ }
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_7zip.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_7zip.c
new file mode 100644
index 00000000000..3e97b25de74
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_7zip.c
@@ -0,0 +1,2322 @@
+/*-
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdlib.h>
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#if HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#ifndef HAVE_ZLIB_H
+#error #include "archive_crc32.h"
+#endif
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_ppmd7_private.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+/*
+ * Codec ID
+ */
+#define _7Z_COPY 0
+#define _7Z_LZMA1 0x030101
+#define _7Z_LZMA2 0x21
+#define _7Z_DEFLATE 0x040108
+#define _7Z_BZIP2 0x040202
+#define _7Z_PPMD 0x030401
+
+/*
+ * 7-Zip header property IDs.
+ */
+#define kEnd 0x00
+#define kHeader 0x01
+#define kArchiveProperties 0x02
+#define kAdditionalStreamsInfo 0x03
+#define kMainStreamsInfo 0x04
+#define kFilesInfo 0x05
+#define kPackInfo 0x06
+#define kUnPackInfo 0x07
+#define kSubStreamsInfo 0x08
+#define kSize 0x09
+#define kCRC 0x0A
+#define kFolder 0x0B
+#define kCodersUnPackSize 0x0C
+#define kNumUnPackStream 0x0D
+#define kEmptyStream 0x0E
+#define kEmptyFile 0x0F
+#define kAnti 0x10
+#define kName 0x11
+#define kCTime 0x12
+#define kATime 0x13
+#define kMTime 0x14
+#define kAttributes 0x15
+#define kEncodedHeader 0x17
+
+enum la_zaction {
+ ARCHIVE_Z_FINISH,
+ ARCHIVE_Z_RUN
+};
+
+/*
+ * A stream object of universal compressor.
+ */
+struct la_zstream {
+ const uint8_t *next_in;
+ size_t avail_in;
+ uint64_t total_in;
+
+ uint8_t *next_out;
+ size_t avail_out;
+ uint64_t total_out;
+
+ uint32_t prop_size;
+ uint8_t *props;
+
+ int valid;
+ void *real_stream;
+ int (*code) (struct archive *a,
+ struct la_zstream *lastrm,
+ enum la_zaction action);
+ int (*end)(struct archive *a,
+ struct la_zstream *lastrm);
+};
+
+#define PPMD7_DEFAULT_ORDER 6
+#define PPMD7_DEFAULT_MEM_SIZE (1 << 24)
+
+struct ppmd_stream {
+ int stat;
+ CPpmd7 ppmd7_context;
+ CPpmd7z_RangeEnc range_enc;
+ IByteOut byteout;
+ uint8_t *buff;
+ uint8_t *buff_ptr;
+ uint8_t *buff_end;
+ size_t buff_bytes;
+};
+
+struct coder {
+ unsigned codec;
+ size_t prop_size;
+ uint8_t *props;
+};
+
+struct file {
+ struct archive_rb_node rbnode;
+
+ struct file *next;
+ unsigned name_len;
+ uint8_t *utf16name;/* UTF16-LE name. */
+ uint64_t size;
+ unsigned flg;
+#define MTIME_IS_SET (1<<0)
+#define ATIME_IS_SET (1<<1)
+#define CTIME_IS_SET (1<<2)
+#define CRC32_IS_SET (1<<3)
+#define HAS_STREAM (1<<4)
+
+ struct {
+ time_t time;
+ long time_ns;
+ } times[3];
+#define MTIME 0
+#define ATIME 1
+#define CTIME 2
+
+ mode_t mode;
+ uint32_t crc32;
+
+ unsigned dir:1;
+};
+
+struct _7zip {
+ int temp_fd;
+ uint64_t temp_offset;
+
+ struct file *cur_file;
+ size_t total_number_entry;
+ size_t total_number_nonempty_entry;
+ size_t total_number_empty_entry;
+ size_t total_number_dir_entry;
+ size_t total_bytes_entry_name;
+ size_t total_number_time_defined[3];
+ uint64_t total_bytes_compressed;
+ uint64_t total_bytes_uncompressed;
+ uint64_t entry_bytes_remaining;
+ uint32_t entry_crc32;
+ uint32_t precode_crc32;
+ uint32_t encoded_crc32;
+ int crc32flg;
+#define PRECODE_CRC32 1
+#define ENCODED_CRC32 2
+
+ unsigned opt_compression;
+ int opt_compression_level;
+
+ struct la_zstream stream;
+ struct coder coder;
+
+ struct archive_string_conv *sconv;
+
+ /*
+ * Compressed data buffer.
+ */
+ unsigned char wbuff[512 * 20 * 6];
+ size_t wbuff_remaining;
+
+ /*
+ * The list of the file entries which has its contents is used to
+ * manage struct file objects.
+ * We use 'next' (a member of struct file) to chain.
+ */
+ struct {
+ struct file *first;
+ struct file **last;
+ } file_list, empty_list;
+ struct archive_rb_tree rbtree;/* for empty files */
+};
+
+static int _7z_options(struct archive_write *,
+ const char *, const char *);
+static int _7z_write_header(struct archive_write *,
+ struct archive_entry *);
+static ssize_t _7z_write_data(struct archive_write *,
+ const void *, size_t);
+static int _7z_finish_entry(struct archive_write *);
+static int _7z_close(struct archive_write *);
+static int _7z_free(struct archive_write *);
+static int file_cmp_node(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+static int file_cmp_key(const struct archive_rb_node *, const void *);
+static int file_new(struct archive_write *a, struct archive_entry *,
+ struct file **);
+static void file_free(struct file *);
+static void file_register(struct _7zip *, struct file *);
+static void file_register_empty(struct _7zip *, struct file *);
+static void file_init_register(struct _7zip *);
+static void file_init_register_empty(struct _7zip *);
+static void file_free_register(struct _7zip *);
+static ssize_t compress_out(struct archive_write *, const void *, size_t ,
+ enum la_zaction);
+static int compression_init_encoder_copy(struct archive *,
+ struct la_zstream *);
+static int compression_code_copy(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end_copy(struct archive *, struct la_zstream *);
+static int compression_init_encoder_deflate(struct archive *,
+ struct la_zstream *, int, int);
+#ifdef HAVE_ZLIB_H
+static int compression_code_deflate(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end_deflate(struct archive *, struct la_zstream *);
+#endif
+static int compression_init_encoder_bzip2(struct archive *,
+ struct la_zstream *, int);
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+static int compression_code_bzip2(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end_bzip2(struct archive *, struct la_zstream *);
+#endif
+static int compression_init_encoder_lzma1(struct archive *,
+ struct la_zstream *, int);
+static int compression_init_encoder_lzma2(struct archive *,
+ struct la_zstream *, int);
+#if defined(HAVE_LZMA_H)
+static int compression_code_lzma(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end_lzma(struct archive *, struct la_zstream *);
+#endif
+static int compression_init_encoder_ppmd(struct archive *,
+ struct la_zstream *, unsigned, uint32_t);
+static int compression_code_ppmd(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end_ppmd(struct archive *, struct la_zstream *);
+static int _7z_compression_init_encoder(struct archive_write *, unsigned,
+ int);
+static int compression_code(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end(struct archive *,
+ struct la_zstream *);
+static int enc_uint64(struct archive_write *, uint64_t);
+static int make_header(struct archive_write *, uint64_t, uint64_t,
+ uint64_t, int, struct coder *);
+static int make_streamsInfo(struct archive_write *, uint64_t, uint64_t,
+ uint64_t, int, struct coder *, int, uint32_t);
+
+int
+archive_write_set_format_7zip(struct archive *_a)
+{
+ static const struct archive_rb_tree_ops rb_ops = {
+ file_cmp_node, file_cmp_key
+ };
+ struct archive_write *a = (struct archive_write *)_a;
+ struct _7zip *zip;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_7zip");
+
+ /* If another format was already registered, unregister it. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ zip = calloc(1, sizeof(*zip));
+ if (zip == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate 7-Zip data");
+ return (ARCHIVE_FATAL);
+ }
+ zip->temp_fd = -1;
+ __archive_rb_tree_init(&(zip->rbtree), &rb_ops);
+ file_init_register(zip);
+ file_init_register_empty(zip);
+
+ /* Set default compression type and its level. */
+#if HAVE_LZMA_H
+ zip->opt_compression = _7Z_LZMA1;
+#elif defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ zip->opt_compression = _7Z_BZIP2;
+#elif defined(HAVE_ZLIB_H)
+ zip->opt_compression = _7Z_DEFLATE;
+#else
+ zip->opt_compression = _7Z_COPY;
+#endif
+ zip->opt_compression_level = 6;
+
+ a->format_data = zip;
+
+ a->format_name = "7zip";
+ a->format_options = _7z_options;
+ a->format_write_header = _7z_write_header;
+ a->format_write_data = _7z_write_data;
+ a->format_finish_entry = _7z_finish_entry;
+ a->format_close = _7z_close;
+ a->format_free = _7z_free;
+ a->archive.archive_format = ARCHIVE_FORMAT_7ZIP;
+ a->archive.archive_format_name = "7zip";
+
+ return (ARCHIVE_OK);
+}
+
+static int
+_7z_options(struct archive_write *a, const char *key, const char *value)
+{
+ struct _7zip *zip;
+
+ zip = (struct _7zip *)a->format_data;
+
+ if (strcmp(key, "compression") == 0) {
+ const char *name = NULL;
+
+ if (value == NULL || strcmp(value, "copy") == 0 ||
+ strcmp(value, "COPY") == 0 ||
+ strcmp(value, "store") == 0 ||
+ strcmp(value, "STORE") == 0)
+ zip->opt_compression = _7Z_COPY;
+ else if (strcmp(value, "deflate") == 0 ||
+ strcmp(value, "DEFLATE") == 0)
+#if HAVE_ZLIB_H
+ zip->opt_compression = _7Z_DEFLATE;
+#else
+ name = "deflate";
+#endif
+ else if (strcmp(value, "bzip2") == 0 ||
+ strcmp(value, "BZIP2") == 0)
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ zip->opt_compression = _7Z_BZIP2;
+#else
+ name = "bzip2";
+#endif
+ else if (strcmp(value, "lzma1") == 0 ||
+ strcmp(value, "LZMA1") == 0)
+#if HAVE_LZMA_H
+ zip->opt_compression = _7Z_LZMA1;
+#else
+ name = "lzma1";
+#endif
+ else if (strcmp(value, "lzma2") == 0 ||
+ strcmp(value, "LZMA2") == 0)
+#if HAVE_LZMA_H
+ zip->opt_compression = _7Z_LZMA2;
+#else
+ name = "lzma2";
+#endif
+ else if (strcmp(value, "ppmd") == 0 ||
+ strcmp(value, "PPMD") == 0 ||
+ strcmp(value, "PPMd") == 0)
+ zip->opt_compression = _7Z_PPMD;
+ else {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Unknown compression name: `%s'",
+ value);
+ return (ARCHIVE_FAILED);
+ }
+ if (name != NULL) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "`%s' compression not supported "
+ "on this platform",
+ name);
+ return (ARCHIVE_FAILED);
+ }
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "compression-level") == 0) {
+ if (value == NULL ||
+ !(value[0] >= '0' && value[0] <= '9') ||
+ value[1] != '\0') {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Illegal value `%s'",
+ value);
+ return (ARCHIVE_FAILED);
+ }
+ zip->opt_compression_level = value[0] - '0';
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+_7z_write_header(struct archive_write *a, struct archive_entry *entry)
+{
+ struct _7zip *zip;
+ struct file *file;
+ int r;
+
+ zip = (struct _7zip *)a->format_data;
+ zip->cur_file = NULL;
+ zip->entry_bytes_remaining = 0;
+
+ if (zip->sconv == NULL) {
+ zip->sconv = archive_string_conversion_to_charset(
+ &a->archive, "UTF-16LE", 1);
+ if (zip->sconv == NULL)
+ return (ARCHIVE_FATAL);
+ }
+
+ r = file_new(a, entry, &file);
+ if (r < ARCHIVE_WARN) {
+ if (file != NULL)
+ file_free(file);
+ return (r);
+ }
+ if (file->size == 0 && file->dir) {
+ if (!__archive_rb_tree_insert_node(&(zip->rbtree),
+ (struct archive_rb_node *)file)) {
+ /* We have already had the same file. */
+ file_free(file);
+ return (ARCHIVE_OK);
+ }
+ }
+
+ if (file->flg & MTIME_IS_SET)
+ zip->total_number_time_defined[MTIME]++;
+ if (file->flg & CTIME_IS_SET)
+ zip->total_number_time_defined[CTIME]++;
+ if (file->flg & ATIME_IS_SET)
+ zip->total_number_time_defined[ATIME]++;
+
+ zip->total_number_entry++;
+ zip->total_bytes_entry_name += file->name_len + 2;
+ if (file->size == 0) {
+ /* Count up the number of empty files. */
+ zip->total_number_empty_entry++;
+ if (file->dir)
+ zip->total_number_dir_entry++;
+ else
+ file_register_empty(zip, file);
+ return (r);
+ }
+
+ /*
+ * Init compression.
+ */
+ if ((zip->total_number_entry - zip->total_number_empty_entry) == 1) {
+ r = _7z_compression_init_encoder(a, zip->opt_compression,
+ zip->opt_compression_level);
+ if (r < 0) {
+ file_free(file);
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ /* Register a non-empty file. */
+ file_register(zip, file);
+
+ /*
+ * Set the current file to cur_file to read its contents.
+ */
+ zip->cur_file = file;
+
+
+ /* Save a offset of current file in temporary file. */
+ zip->entry_bytes_remaining = file->size;
+ zip->entry_crc32 = 0;
+
+ /*
+ * Store a symbolic link name as file contents.
+ */
+ if (archive_entry_filetype(entry) == AE_IFLNK) {
+ ssize_t bytes;
+ const void *p = (const void *)archive_entry_symlink(entry);
+ bytes = compress_out(a, p, (size_t)file->size, ARCHIVE_Z_RUN);
+ if (bytes < 0)
+ return ((int)bytes);
+ zip->entry_crc32 = crc32(zip->entry_crc32, p, (unsigned)bytes);
+ zip->entry_bytes_remaining -= bytes;
+ }
+
+ return (r);
+}
+
+/*
+ * Write data to a temporary file.
+ */
+static int
+write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+ struct _7zip *zip;
+ const unsigned char *p;
+ ssize_t ws;
+
+ zip = (struct _7zip *)a->format_data;
+
+ /*
+ * Open a temporary file.
+ */
+ if (zip->temp_fd == -1) {
+ zip->temp_offset = 0;
+ zip->temp_fd = __archive_mktemp(NULL);
+ if (zip->temp_fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't create temporary file");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ p = (const unsigned char *)buff;
+ while (s) {
+ ws = write(zip->temp_fd, p, s);
+ if (ws < 0) {
+ archive_set_error(&(a->archive), errno,
+ "fwrite function failed");
+ return (ARCHIVE_FATAL);
+ }
+ s -= ws;
+ p += ws;
+ zip->temp_offset += ws;
+ }
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+compress_out(struct archive_write *a, const void *buff, size_t s,
+ enum la_zaction run)
+{
+ struct _7zip *zip = (struct _7zip *)a->format_data;
+ int r;
+
+ if (run == ARCHIVE_Z_FINISH && zip->stream.total_in == 0 && s == 0)
+ return (0);
+
+ if ((zip->crc32flg & PRECODE_CRC32) && s)
+ zip->precode_crc32 = crc32(zip->precode_crc32, buff,
+ (unsigned)s);
+ zip->stream.next_in = (const unsigned char *)buff;
+ zip->stream.avail_in = s;
+ for (;;) {
+ /* Compress file data. */
+ r = compression_code(&(a->archive), &(zip->stream), run);
+ if (r != ARCHIVE_OK && r != ARCHIVE_EOF)
+ return (ARCHIVE_FATAL);
+ if (zip->stream.avail_out == 0) {
+ if (write_to_temp(a, zip->wbuff, sizeof(zip->wbuff))
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->stream.next_out = zip->wbuff;
+ zip->stream.avail_out = sizeof(zip->wbuff);
+ if (zip->crc32flg & ENCODED_CRC32)
+ zip->encoded_crc32 = crc32(zip->encoded_crc32,
+ zip->wbuff, sizeof(zip->wbuff));
+ if (run == ARCHIVE_Z_FINISH && r != ARCHIVE_EOF)
+ continue;
+ }
+ if (zip->stream.avail_in == 0)
+ break;
+ }
+ if (run == ARCHIVE_Z_FINISH) {
+ uint64_t bytes = sizeof(zip->wbuff) - zip->stream.avail_out;
+ if (write_to_temp(a, zip->wbuff, (size_t)bytes) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ if ((zip->crc32flg & ENCODED_CRC32) && bytes)
+ zip->encoded_crc32 = crc32(zip->encoded_crc32,
+ zip->wbuff, (unsigned)bytes);
+ }
+
+ return (s);
+}
+
+static ssize_t
+_7z_write_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct _7zip *zip;
+ ssize_t bytes;
+
+ zip = (struct _7zip *)a->format_data;
+
+ if (s > zip->entry_bytes_remaining)
+ s = (size_t)zip->entry_bytes_remaining;
+ if (s == 0 || zip->cur_file == NULL)
+ return (0);
+ bytes = compress_out(a, buff, s, ARCHIVE_Z_RUN);
+ if (bytes < 0)
+ return (bytes);
+ zip->entry_crc32 = crc32(zip->entry_crc32, buff, (unsigned)bytes);
+ zip->entry_bytes_remaining -= bytes;
+ return (bytes);
+}
+
+static int
+_7z_finish_entry(struct archive_write *a)
+{
+ struct _7zip *zip;
+ size_t s;
+ ssize_t r;
+
+ zip = (struct _7zip *)a->format_data;
+ if (zip->cur_file == NULL)
+ return (ARCHIVE_OK);
+
+ while (zip->entry_bytes_remaining > 0) {
+ s = (size_t)zip->entry_bytes_remaining;
+ if (s > a->null_length)
+ s = a->null_length;
+ r = _7z_write_data(a, a->nulls, s);
+ if (r < 0)
+ return ((int)r);
+ }
+ zip->total_bytes_compressed += zip->stream.total_in;
+ zip->total_bytes_uncompressed += zip->stream.total_out;
+ zip->cur_file->crc32 = zip->entry_crc32;
+ zip->cur_file = NULL;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+flush_wbuff(struct archive_write *a)
+{
+ struct _7zip *zip;
+ int r;
+ size_t s;
+
+ zip = (struct _7zip *)a->format_data;
+ s = sizeof(zip->wbuff) - zip->wbuff_remaining;
+ r = __archive_write_output(a, zip->wbuff, s);
+ if (r != ARCHIVE_OK)
+ return (r);
+ zip->wbuff_remaining = sizeof(zip->wbuff);
+ return (r);
+}
+
+static int
+copy_out(struct archive_write *a, uint64_t offset, uint64_t length)
+{
+ struct _7zip *zip;
+ int r;
+
+ zip = (struct _7zip *)a->format_data;
+ if (zip->temp_offset > 0 &&
+ lseek(zip->temp_fd, offset, SEEK_SET) < 0) {
+ archive_set_error(&(a->archive), errno, "lseek failed");
+ return (ARCHIVE_FATAL);
+ }
+ while (length) {
+ size_t rsize;
+ ssize_t rs;
+ unsigned char *wb;
+
+ if (length > zip->wbuff_remaining)
+ rsize = zip->wbuff_remaining;
+ else
+ rsize = (size_t)length;
+ wb = zip->wbuff + (sizeof(zip->wbuff) - zip->wbuff_remaining);
+ rs = read(zip->temp_fd, wb, rsize);
+ if (rs < 0) {
+ archive_set_error(&(a->archive), errno,
+ "Can't read temporary file(%jd)",
+ (intmax_t)rs);
+ return (ARCHIVE_FATAL);
+ }
+ if (rs == 0) {
+ archive_set_error(&(a->archive), 0,
+ "Truncated 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+ zip->wbuff_remaining -= rs;
+ length -= rs;
+ if (zip->wbuff_remaining == 0) {
+ r = flush_wbuff(a);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+_7z_close(struct archive_write *a)
+{
+ struct _7zip *zip;
+ unsigned char *wb;
+ uint64_t header_offset, header_size, header_unpacksize;
+ uint64_t length;
+ uint32_t header_crc32;
+ int r;
+
+ zip = (struct _7zip *)a->format_data;
+
+ if (zip->total_number_entry > 0) {
+ struct archive_rb_node *n;
+ uint64_t data_offset, data_size, data_unpacksize;
+ unsigned header_compression;
+
+ r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH);
+ if (r < 0)
+ return (r);
+ data_offset = 0;
+ data_size = zip->stream.total_out;
+ data_unpacksize = zip->stream.total_in;
+ zip->coder.codec = zip->opt_compression;
+ zip->coder.prop_size = zip->stream.prop_size;
+ zip->coder.props = zip->stream.props;
+ zip->stream.prop_size = 0;
+ zip->stream.props = NULL;
+ zip->total_number_nonempty_entry =
+ zip->total_number_entry - zip->total_number_empty_entry;
+
+ /* Connect an empty file list. */
+ if (zip->empty_list.first != NULL) {
+ *zip->file_list.last = zip->empty_list.first;
+ zip->file_list.last = zip->empty_list.last;
+ }
+ /* Connect a directory file list. */
+ ARCHIVE_RB_TREE_FOREACH(n, &(zip->rbtree)) {
+ file_register(zip, (struct file *)n);
+ }
+
+ /*
+ * NOTE: 7z command supports just LZMA1, LZMA2 and COPY for
+ * the compression type for encoding the header.
+ */
+#if HAVE_LZMA_H
+ header_compression = _7Z_LZMA1;
+ if(zip->opt_compression == _7Z_LZMA2 ||
+ zip->opt_compression == _7Z_COPY)
+ header_compression = zip->opt_compression;
+
+ /* If the stored file is only one, do not encode the header.
+ * This is the same way 7z command does. */
+ if (zip->total_number_entry == 1)
+ header_compression = _7Z_COPY;
+#else
+ header_compression = _7Z_COPY;
+#endif
+ r = _7z_compression_init_encoder(a, header_compression,
+ zip->opt_compression_level);
+ if (r < 0)
+ return (r);
+ zip->crc32flg = PRECODE_CRC32;
+ zip->precode_crc32 = 0;
+ r = make_header(a, data_offset, data_size, data_unpacksize,
+ 1, &(zip->coder));
+ if (r < 0)
+ return (r);
+ r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH);
+ if (r < 0)
+ return (r);
+ header_offset = data_offset + data_size;
+ header_size = zip->stream.total_out;
+ header_crc32 = zip->precode_crc32;
+ header_unpacksize = zip->stream.total_in;
+
+ if (header_compression != _7Z_COPY) {
+ /*
+ * Encode the header in order to reduce the size
+ * of the archive.
+ */
+ free(zip->coder.props);
+ zip->coder.codec = header_compression;
+ zip->coder.prop_size = zip->stream.prop_size;
+ zip->coder.props = zip->stream.props;
+ zip->stream.prop_size = 0;
+ zip->stream.props = NULL;
+
+ r = _7z_compression_init_encoder(a, _7Z_COPY, 0);
+ if (r < 0)
+ return (r);
+ zip->crc32flg = ENCODED_CRC32;
+ zip->encoded_crc32 = 0;
+
+ /*
+ * Make EncodedHeader.
+ */
+ r = enc_uint64(a, kEncodedHeader);
+ if (r < 0)
+ return (r);
+ r = make_streamsInfo(a, header_offset, header_size,
+ header_unpacksize, 1, &(zip->coder), 0,
+ header_crc32);
+ if (r < 0)
+ return (r);
+ r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH);
+ if (r < 0)
+ return (r);
+ header_offset = header_offset + header_size;
+ header_size = zip->stream.total_out;
+ header_crc32 = zip->encoded_crc32;
+ }
+ zip->crc32flg = 0;
+ } else {
+ header_offset = header_size = 0;
+ header_crc32 = 0;
+ }
+
+ length = zip->temp_offset;
+
+ /*
+ * Make the zip header on wbuff(write buffer).
+ */
+ wb = zip->wbuff;
+ zip->wbuff_remaining = sizeof(zip->wbuff);
+ memcpy(&wb[0], "7z\xBC\xAF\x27\x1C", 6);
+ wb[6] = 0;/* Major version. */
+ wb[7] = 3;/* Minor version. */
+ archive_le64enc(&wb[12], header_offset);/* Next Header Offset */
+ archive_le64enc(&wb[20], header_size);/* Next Header Size */
+ archive_le32enc(&wb[28], header_crc32);/* Next Header CRC */
+ archive_le32enc(&wb[8], crc32(0, &wb[12], 20));/* Start Header CRC */
+ zip->wbuff_remaining -= 32;
+
+ /*
+ * Read all file contents and an encoded header from the temporary
+ * file and write out it.
+ */
+ r = copy_out(a, 0, length);
+ if (r != ARCHIVE_OK)
+ return (r);
+ r = flush_wbuff(a);
+ return (r);
+}
+
+/*
+ * Encode 64 bits value into 7-Zip's encoded UINT64 value.
+ */
+static int
+enc_uint64(struct archive_write *a, uint64_t val)
+{
+ unsigned mask = 0x80;
+ uint8_t numdata[9];
+ int i;
+
+ numdata[0] = 0;
+ for (i = 1; i < (int)sizeof(numdata); i++) {
+ if (val < mask) {
+ numdata[0] |= (uint8_t)val;
+ break;
+ }
+ numdata[i] = (uint8_t)val;
+ val >>= 8;
+ numdata[0] |= mask;
+ mask >>= 1;
+ }
+ return ((int)compress_out(a, numdata, i, ARCHIVE_Z_RUN));
+}
+
+static int
+make_substreamsInfo(struct archive_write *a, struct coder *coders)
+{
+ struct _7zip *zip = (struct _7zip *)a->format_data;
+ struct file *file;
+ int r;
+
+ /*
+ * Make SubStreamsInfo.
+ */
+ r = enc_uint64(a, kSubStreamsInfo);
+ if (r < 0)
+ return (r);
+
+ if (zip->total_number_nonempty_entry > 1 && coders->codec != _7Z_COPY) {
+ /*
+ * Make NumUnPackStream.
+ */
+ r = enc_uint64(a, kNumUnPackStream);
+ if (r < 0)
+ return (r);
+
+ /* Write numUnpackStreams */
+ r = enc_uint64(a, zip->total_number_nonempty_entry);
+ if (r < 0)
+ return (r);
+
+ /*
+ * Make kSize.
+ */
+ r = enc_uint64(a, kSize);
+ if (r < 0)
+ return (r);
+ file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ if (file->next == NULL ||
+ file->next->size == 0)
+ break;
+ r = enc_uint64(a, file->size);
+ if (r < 0)
+ return (r);
+ }
+ }
+
+ /*
+ * Make CRC.
+ */
+ r = enc_uint64(a, kCRC);
+ if (r < 0)
+ return (r);
+
+
+ /* All are defined */
+ r = enc_uint64(a, 1);
+ if (r < 0)
+ return (r);
+ file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ uint8_t crc[4];
+ if (file->size == 0)
+ break;
+ archive_le32enc(crc, file->crc32);
+ r = (int)compress_out(a, crc, 4, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+
+ /* Write End. */
+ r = enc_uint64(a, kEnd);
+ if (r < 0)
+ return (r);
+ return (ARCHIVE_OK);
+}
+
+static int
+make_streamsInfo(struct archive_write *a, uint64_t offset, uint64_t pack_size,
+ uint64_t unpack_size, int num_coder, struct coder *coders, int substrm,
+ uint32_t header_crc)
+{
+ struct _7zip *zip = (struct _7zip *)a->format_data;
+ uint8_t codec_buff[8];
+ int numFolders, fi;
+ int codec_size;
+ int i, r;
+
+ if (coders->codec == _7Z_COPY)
+ numFolders = (int)zip->total_number_nonempty_entry;
+ else
+ numFolders = 1;
+
+ /*
+ * Make PackInfo.
+ */
+ r = enc_uint64(a, kPackInfo);
+ if (r < 0)
+ return (r);
+
+ /* Write PackPos. */
+ r = enc_uint64(a, offset);
+ if (r < 0)
+ return (r);
+
+ /* Write NumPackStreams. */
+ r = enc_uint64(a, numFolders);
+ if (r < 0)
+ return (r);
+
+ /* Make Size. */
+ r = enc_uint64(a, kSize);
+ if (r < 0)
+ return (r);
+
+ if (numFolders > 1) {
+ struct file *file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ if (file->size == 0)
+ break;
+ r = enc_uint64(a, file->size);
+ if (r < 0)
+ return (r);
+ }
+ } else {
+ /* Write size. */
+ r = enc_uint64(a, pack_size);
+ if (r < 0)
+ return (r);
+ }
+
+ r = enc_uint64(a, kEnd);
+ if (r < 0)
+ return (r);
+
+ /*
+ * Make UnPackInfo.
+ */
+ r = enc_uint64(a, kUnPackInfo);
+ if (r < 0)
+ return (r);
+
+ /*
+ * Make Folder.
+ */
+ r = enc_uint64(a, kFolder);
+ if (r < 0)
+ return (r);
+
+ /* Write NumFolders. */
+ r = enc_uint64(a, numFolders);
+ if (r < 0)
+ return (r);
+
+ /* Write External. */
+ r = enc_uint64(a, 0);
+ if (r < 0)
+ return (r);
+
+ for (fi = 0; fi < numFolders; fi++) {
+ /* Write NumCoders. */
+ r = enc_uint64(a, num_coder);
+ if (r < 0)
+ return (r);
+
+ for (i = 0; i < num_coder; i++) {
+ unsigned codec_id = coders[i].codec;
+
+ /* Write Codec flag. */
+ archive_be64enc(codec_buff, codec_id);
+ for (codec_size = 8; codec_size > 0; codec_size--) {
+ if (codec_buff[8 - codec_size])
+ break;
+ }
+ if (codec_size == 0)
+ codec_size = 1;
+ if (coders[i].prop_size)
+ r = enc_uint64(a, codec_size | 0x20);
+ else
+ r = enc_uint64(a, codec_size);
+ if (r < 0)
+ return (r);
+
+ /* Write Codec ID. */
+ codec_size &= 0x0f;
+ r = (int)compress_out(a, &codec_buff[8-codec_size],
+ codec_size, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+
+ if (coders[i].prop_size) {
+ /* Write Codec property size. */
+ r = enc_uint64(a, coders[i].prop_size);
+ if (r < 0)
+ return (r);
+
+ /* Write Codec properties. */
+ r = (int)compress_out(a, coders[i].props,
+ coders[i].prop_size, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+ }
+ }
+
+ /*
+ * Make CodersUnPackSize.
+ */
+ r = enc_uint64(a, kCodersUnPackSize);
+ if (r < 0)
+ return (r);
+
+ if (numFolders > 1) {
+ struct file *file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ if (file->size == 0)
+ break;
+ r = enc_uint64(a, file->size);
+ if (r < 0)
+ return (r);
+ }
+
+ } else {
+ /* Write UnPackSize. */
+ r = enc_uint64(a, unpack_size);
+ if (r < 0)
+ return (r);
+ }
+
+ if (!substrm) {
+ uint8_t crc[4];
+ /*
+ * Make CRC.
+ */
+ r = enc_uint64(a, kCRC);
+ if (r < 0)
+ return (r);
+
+ /* All are defined */
+ r = enc_uint64(a, 1);
+ if (r < 0)
+ return (r);
+ archive_le32enc(crc, header_crc);
+ r = (int)compress_out(a, crc, 4, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+
+ /* Write End. */
+ r = enc_uint64(a, kEnd);
+ if (r < 0)
+ return (r);
+
+ if (substrm) {
+ /*
+ * Make SubStreamsInfo.
+ */
+ r = make_substreamsInfo(a, coders);
+ if (r < 0)
+ return (r);
+ }
+
+
+ /* Write End. */
+ r = enc_uint64(a, kEnd);
+ if (r < 0)
+ return (r);
+
+ return (ARCHIVE_OK);
+}
+
+
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+static uint64_t
+utcToFiletime(time_t t, long ns)
+{
+ uint64_t fileTime;
+
+ fileTime = t;
+ fileTime *= 10000000;
+ fileTime += ns / 100;
+ fileTime += EPOC_TIME;
+ return (fileTime);
+}
+
+static int
+make_time(struct archive_write *a, uint8_t type, unsigned flg, int ti)
+{
+ uint8_t filetime[8];
+ struct _7zip *zip = (struct _7zip *)a->format_data;
+ struct file *file;
+ int r;
+ uint8_t b, mask;
+
+ /*
+ * Make Time Bools.
+ */
+ if (zip->total_number_time_defined[ti] == zip->total_number_entry) {
+ /* Write Time Type. */
+ r = enc_uint64(a, type);
+ if (r < 0)
+ return (r);
+ /* Write EmptyStream Size. */
+ r = enc_uint64(a, 2 + zip->total_number_entry * 8);
+ if (r < 0)
+ return (r);
+ /* All are defined. */
+ r = enc_uint64(a, 1);
+ if (r < 0)
+ return (r);
+ } else {
+ if (zip->total_number_time_defined[ti] == 0)
+ return (ARCHIVE_OK);
+
+ /* Write Time Type. */
+ r = enc_uint64(a, type);
+ if (r < 0)
+ return (r);
+ /* Write EmptyStream Size. */
+ r = enc_uint64(a, 2 + ((zip->total_number_entry + 7) >> 3)
+ + zip->total_number_time_defined[ti] * 8);
+ if (r < 0)
+ return (r);
+
+ /* All are not defined. */
+ r = enc_uint64(a, 0);
+ if (r < 0)
+ return (r);
+
+ b = 0;
+ mask = 0x80;
+ file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ if (file->flg & flg)
+ b |= mask;
+ mask >>= 1;
+ if (mask == 0) {
+ r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ mask = 0x80;
+ b = 0;
+ }
+ }
+ if (mask != 0x80) {
+ r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+ }
+
+ /* External. */
+ r = enc_uint64(a, 0);
+ if (r < 0)
+ return (r);
+
+
+ /*
+ * Make Times.
+ */
+ file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ if ((file->flg & flg) == 0)
+ continue;
+ archive_le64enc(filetime, utcToFiletime(file->times[ti].time,
+ file->times[ti].time_ns));
+ r = (int)compress_out(a, filetime, 8, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int
+make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size,
+ uint64_t unpack_size, int codernum, struct coder *coders)
+{
+ struct _7zip *zip = (struct _7zip *)a->format_data;
+ struct file *file;
+ int r;
+ uint8_t b, mask;
+
+ /*
+ * Make FilesInfo.
+ */
+ r = enc_uint64(a, kHeader);
+ if (r < 0)
+ return (r);
+
+ /*
+ * If there are empty files only, do not write MainStreamInfo.
+ */
+ if (zip->total_number_nonempty_entry) {
+ /*
+ * Make MainStreamInfo.
+ */
+ r = enc_uint64(a, kMainStreamsInfo);
+ if (r < 0)
+ return (r);
+ r = make_streamsInfo(a, offset, pack_size, unpack_size,
+ codernum, coders, 1, 0);
+ if (r < 0)
+ return (r);
+ }
+
+ /*
+ * Make FilesInfo.
+ */
+ r = enc_uint64(a, kFilesInfo);
+ if (r < 0)
+ return (r);
+
+ /* Write numFiles. */
+ r = enc_uint64(a, zip->total_number_entry);
+ if (r < 0)
+ return (r);
+
+ if (zip->total_number_empty_entry > 0) {
+ /* Make EmptyStream. */
+ r = enc_uint64(a, kEmptyStream);
+ if (r < 0)
+ return (r);
+
+ /* Write EmptyStream Size. */
+ r = enc_uint64(a, (zip->total_number_entry+7)>>3);
+ if (r < 0)
+ return (r);
+
+ b = 0;
+ mask = 0x80;
+ file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ if (file->size == 0)
+ b |= mask;
+ mask >>= 1;
+ if (mask == 0) {
+ r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ mask = 0x80;
+ b = 0;
+ }
+ }
+ if (mask != 0x80) {
+ r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+ }
+
+ if (zip->total_number_empty_entry > zip->total_number_dir_entry) {
+ /* Make EmptyFile. */
+ r = enc_uint64(a, kEmptyFile);
+ if (r < 0)
+ return (r);
+
+ /* Write EmptyFile Size. */
+ r = enc_uint64(a, (zip->total_number_empty_entry + 7) >> 3);
+ if (r < 0)
+ return (r);
+
+ b = 0;
+ mask = 0x80;
+ file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ if (file->size)
+ continue;
+ if (!file->dir)
+ b |= mask;
+ mask >>= 1;
+ if (mask == 0) {
+ r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ mask = 0x80;
+ b = 0;
+ }
+ }
+ if (mask != 0x80) {
+ r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+ }
+
+ /* Make Name. */
+ r = enc_uint64(a, kName);
+ if (r < 0)
+ return (r);
+
+ /* Write Name size. */
+ r = enc_uint64(a, zip->total_bytes_entry_name+1);
+ if (r < 0)
+ return (r);
+
+ /* Write dmy byte. */
+ r = enc_uint64(a, 0);
+ if (r < 0)
+ return (r);
+
+ file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ r = (int)compress_out(a, file->utf16name, file->name_len+2,
+ ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+
+ /* Make MTime. */
+ r = make_time(a, kMTime, MTIME_IS_SET, MTIME);
+ if (r < 0)
+ return (r);
+
+ /* Make CTime. */
+ r = make_time(a, kCTime, CTIME_IS_SET, CTIME);
+ if (r < 0)
+ return (r);
+
+ /* Make ATime. */
+ r = make_time(a, kATime, ATIME_IS_SET, ATIME);
+ if (r < 0)
+ return (r);
+
+ /* Make Attributes. */
+ r = enc_uint64(a, kAttributes);
+ if (r < 0)
+ return (r);
+
+ /* Write Attributes size. */
+ r = enc_uint64(a, 2 + zip->total_number_entry * 4);
+ if (r < 0)
+ return (r);
+
+ /* Write "All Are Defined". */
+ r = enc_uint64(a, 1);
+ if (r < 0)
+ return (r);
+
+ /* Write dmy byte. */
+ r = enc_uint64(a, 0);
+ if (r < 0)
+ return (r);
+
+ file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ /*
+ * High 16bits is unix mode.
+ * Low 16bits is Windows attributes.
+ */
+ uint32_t encattr, attr;
+ if (file->dir)
+ attr = 0x8010;
+ else
+ attr = 0x8020;
+ if ((file->mode & 0222) == 0)
+ attr |= 1;/* Read Only. */
+ attr |= ((uint32_t)file->mode) << 16;
+ archive_le32enc(&encattr, attr);
+ r = (int)compress_out(a, &encattr, 4, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+
+ /* Write End. */
+ r = enc_uint64(a, kEnd);
+ if (r < 0)
+ return (r);
+
+ /* Write End. */
+ r = enc_uint64(a, kEnd);
+ if (r < 0)
+ return (r);
+
+ return (ARCHIVE_OK);
+}
+
+
+static int
+_7z_free(struct archive_write *a)
+{
+ struct _7zip *zip = (struct _7zip *)a->format_data;
+
+ /* Close the temporary file. */
+ if (zip->temp_fd >= 0)
+ close(zip->temp_fd);
+
+ file_free_register(zip);
+ compression_end(&(a->archive), &(zip->stream));
+ free(zip->coder.props);
+ free(zip);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+file_cmp_node(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct file *f1 = (const struct file *)n1;
+ const struct file *f2 = (const struct file *)n2;
+
+ if (f1->name_len == f2->name_len)
+ return (memcmp(f1->utf16name, f2->utf16name, f1->name_len));
+ return (f1->name_len > f2->name_len)?1:-1;
+}
+
+static int
+file_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ const struct file *f = (const struct file *)n;
+
+ return (f->name_len - *(const char *)key);
+}
+
+static int
+file_new(struct archive_write *a, struct archive_entry *entry,
+ struct file **newfile)
+{
+ struct _7zip *zip;
+ struct file *file;
+ const char *u16;
+ size_t u16len;
+ int ret = ARCHIVE_OK;
+
+ zip = (struct _7zip *)a->format_data;
+ *newfile = NULL;
+
+ file = calloc(1, sizeof(*file));
+ if (file == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (0 > archive_entry_pathname_l(entry, &u16, &u16len, zip->sconv)) {
+ if (errno == ENOMEM) {
+ free(file);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for UTF-16LE");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "A filename cannot be converted to UTF-16LE;"
+ "You should disable making Joliet extension");
+ ret = ARCHIVE_WARN;
+ }
+ file->utf16name = malloc(u16len + 2);
+ if (file->utf16name == NULL) {
+ free(file);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Name");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(file->utf16name, u16, u16len);
+ file->utf16name[u16len+0] = 0;
+ file->utf16name[u16len+1] = 0;
+ file->name_len = (unsigned)u16len;
+ file->mode = archive_entry_mode(entry);
+ if (archive_entry_filetype(entry) == AE_IFREG)
+ file->size = archive_entry_size(entry);
+ else
+ archive_entry_set_size(entry, 0);
+ if (archive_entry_filetype(entry) == AE_IFDIR)
+ file->dir = 1;
+ else if (archive_entry_filetype(entry) == AE_IFLNK)
+ file->size = strlen(archive_entry_symlink(entry));
+ if (archive_entry_mtime_is_set(entry)) {
+ file->flg |= MTIME_IS_SET;
+ file->times[MTIME].time = archive_entry_mtime(entry);
+ file->times[MTIME].time_ns = archive_entry_mtime_nsec(entry);
+ }
+ if (archive_entry_atime_is_set(entry)) {
+ file->flg |= ATIME_IS_SET;
+ file->times[ATIME].time = archive_entry_atime(entry);
+ file->times[ATIME].time_ns = archive_entry_atime_nsec(entry);
+ }
+ if (archive_entry_ctime_is_set(entry)) {
+ file->flg |= CTIME_IS_SET;
+ file->times[CTIME].time = archive_entry_ctime(entry);
+ file->times[CTIME].time_ns = archive_entry_ctime_nsec(entry);
+ }
+
+ *newfile = file;
+ return (ret);
+}
+
+static void
+file_free(struct file *file)
+{
+ free(file->utf16name);
+ free(file);
+}
+
+static void
+file_register(struct _7zip *zip, struct file *file)
+{
+ file->next = NULL;
+ *zip->file_list.last = file;
+ zip->file_list.last = &(file->next);
+}
+
+static void
+file_init_register(struct _7zip *zip)
+{
+ zip->file_list.first = NULL;
+ zip->file_list.last = &(zip->file_list.first);
+}
+
+static void
+file_free_register(struct _7zip *zip)
+{
+ struct file *file, *file_next;
+
+ file = zip->file_list.first;
+ while (file != NULL) {
+ file_next = file->next;
+ file_free(file);
+ file = file_next;
+ }
+}
+
+static void
+file_register_empty(struct _7zip *zip, struct file *file)
+{
+ file->next = NULL;
+ *zip->empty_list.last = file;
+ zip->empty_list.last = &(file->next);
+}
+
+static void
+file_init_register_empty(struct _7zip *zip)
+{
+ zip->empty_list.first = NULL;
+ zip->empty_list.last = &(zip->empty_list.first);
+}
+
+#if !defined(HAVE_ZLIB_H) || !defined(HAVE_BZLIB_H) ||\
+ !defined(BZ_CONFIG_ERROR) || !defined(HAVE_LZMA_H)
+static int
+compression_unsupported_encoder(struct archive *a,
+ struct la_zstream *lastrm, const char *name)
+{
+
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "%s compression not supported on this platform", name);
+ lastrm->valid = 0;
+ lastrm->real_stream = NULL;
+ return (ARCHIVE_FAILED);
+}
+#endif
+
+/*
+ * _7_COPY compressor.
+ */
+static int
+compression_init_encoder_copy(struct archive *a, struct la_zstream *lastrm)
+{
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ lastrm->valid = 1;
+ lastrm->code = compression_code_copy;
+ lastrm->end = compression_end_copy;
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_code_copy(struct archive *a,
+ struct la_zstream *lastrm, enum la_zaction action)
+{
+ size_t bytes;
+
+ (void)a; /* UNUSED */
+ if (lastrm->avail_out > lastrm->avail_in)
+ bytes = lastrm->avail_in;
+ else
+ bytes = lastrm->avail_out;
+ if (bytes) {
+ memcpy(lastrm->next_out, lastrm->next_in, bytes);
+ lastrm->next_in += bytes;
+ lastrm->avail_in -= bytes;
+ lastrm->total_in += bytes;
+ lastrm->next_out += bytes;
+ lastrm->avail_out -= bytes;
+ lastrm->total_out += bytes;
+ }
+ if (action == ARCHIVE_Z_FINISH && lastrm->avail_in == 0)
+ return (ARCHIVE_EOF);
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_end_copy(struct archive *a, struct la_zstream *lastrm)
+{
+ (void)a; /* UNUSED */
+ lastrm->valid = 0;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * _7_DEFLATE compressor.
+ */
+#ifdef HAVE_ZLIB_H
+static int
+compression_init_encoder_deflate(struct archive *a,
+ struct la_zstream *lastrm, int level, int withheader)
+{
+ z_stream *strm;
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ strm = calloc(1, sizeof(*strm));
+ if (strm == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for gzip stream");
+ return (ARCHIVE_FATAL);
+ }
+ /* zlib.h is not const-correct, so we need this one bit
+ * of ugly hackery to convert a const * pointer to
+ * a non-const pointer. */
+ strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;
+ strm->avail_in = (uInt)lastrm->avail_in;
+ strm->total_in = (uLong)lastrm->total_in;
+ strm->next_out = lastrm->next_out;
+ strm->avail_out = (uInt)lastrm->avail_out;
+ strm->total_out = (uLong)lastrm->total_out;
+ if (deflateInit2(strm, level, Z_DEFLATED,
+ (withheader)?15:-15,
+ 8, Z_DEFAULT_STRATEGY) != Z_OK) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library");
+ return (ARCHIVE_FATAL);
+ }
+ lastrm->real_stream = strm;
+ lastrm->valid = 1;
+ lastrm->code = compression_code_deflate;
+ lastrm->end = compression_end_deflate;
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_code_deflate(struct archive *a,
+ struct la_zstream *lastrm, enum la_zaction action)
+{
+ z_stream *strm;
+ int r;
+
+ strm = (z_stream *)lastrm->real_stream;
+ /* zlib.h is not const-correct, so we need this one bit
+ * of ugly hackery to convert a const * pointer to
+ * a non-const pointer. */
+ strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;
+ strm->avail_in = (uInt)lastrm->avail_in;
+ strm->total_in = (uLong)lastrm->total_in;
+ strm->next_out = lastrm->next_out;
+ strm->avail_out = (uInt)lastrm->avail_out;
+ strm->total_out = (uLong)lastrm->total_out;
+ r = deflate(strm,
+ (action == ARCHIVE_Z_FINISH)? Z_FINISH: Z_NO_FLUSH);
+ lastrm->next_in = strm->next_in;
+ lastrm->avail_in = strm->avail_in;
+ lastrm->total_in = strm->total_in;
+ lastrm->next_out = strm->next_out;
+ lastrm->avail_out = strm->avail_out;
+ lastrm->total_out = strm->total_out;
+ switch (r) {
+ case Z_OK:
+ return (ARCHIVE_OK);
+ case Z_STREAM_END:
+ return (ARCHIVE_EOF);
+ default:
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "GZip compression failed:"
+ " deflate() call returned status %d", r);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+static int
+compression_end_deflate(struct archive *a, struct la_zstream *lastrm)
+{
+ z_stream *strm;
+ int r;
+
+ strm = (z_stream *)lastrm->real_stream;
+ r = deflateEnd(strm);
+ free(strm);
+ lastrm->real_stream = NULL;
+ lastrm->valid = 0;
+ if (r != Z_OK) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up compressor");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+#else
+static int
+compression_init_encoder_deflate(struct archive *a,
+ struct la_zstream *lastrm, int level, int withheader)
+{
+
+ (void) level; /* UNUSED */
+ (void) withheader; /* UNUSED */
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ return (compression_unsupported_encoder(a, lastrm, "deflate"));
+}
+#endif
+
+/*
+ * _7_BZIP2 compressor.
+ */
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+static int
+compression_init_encoder_bzip2(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+ bz_stream *strm;
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ strm = calloc(1, sizeof(*strm));
+ if (strm == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for bzip2 stream");
+ return (ARCHIVE_FATAL);
+ }
+ /* bzlib.h is not const-correct, so we need this one bit
+ * of ugly hackery to convert a const * pointer to
+ * a non-const pointer. */
+ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
+ strm->avail_in = (uint32_t)lastrm->avail_in;
+ strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
+ strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
+ strm->next_out = (char *)lastrm->next_out;
+ strm->avail_out = (uint32_t)lastrm->avail_out;
+ strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
+ strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
+ if (BZ2_bzCompressInit(strm, level, 0, 30) != BZ_OK) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library");
+ return (ARCHIVE_FATAL);
+ }
+ lastrm->real_stream = strm;
+ lastrm->valid = 1;
+ lastrm->code = compression_code_bzip2;
+ lastrm->end = compression_end_bzip2;
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_code_bzip2(struct archive *a,
+ struct la_zstream *lastrm, enum la_zaction action)
+{
+ bz_stream *strm;
+ int r;
+
+ strm = (bz_stream *)lastrm->real_stream;
+ /* bzlib.h is not const-correct, so we need this one bit
+ * of ugly hackery to convert a const * pointer to
+ * a non-const pointer. */
+ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
+ strm->avail_in = (uint32_t)lastrm->avail_in;
+ strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
+ strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
+ strm->next_out = (char *)lastrm->next_out;
+ strm->avail_out = (uint32_t)lastrm->avail_out;
+ strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
+ strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
+ r = BZ2_bzCompress(strm,
+ (action == ARCHIVE_Z_FINISH)? BZ_FINISH: BZ_RUN);
+ lastrm->next_in = (const unsigned char *)strm->next_in;
+ lastrm->avail_in = strm->avail_in;
+ lastrm->total_in =
+ (((uint64_t)(uint32_t)strm->total_in_hi32) << 32)
+ + (uint64_t)(uint32_t)strm->total_in_lo32;
+ lastrm->next_out = (unsigned char *)strm->next_out;
+ lastrm->avail_out = strm->avail_out;
+ lastrm->total_out =
+ (((uint64_t)(uint32_t)strm->total_out_hi32) << 32)
+ + (uint64_t)(uint32_t)strm->total_out_lo32;
+ switch (r) {
+ case BZ_RUN_OK: /* Non-finishing */
+ case BZ_FINISH_OK: /* Finishing: There's more work to do */
+ return (ARCHIVE_OK);
+ case BZ_STREAM_END: /* Finishing: all done */
+ /* Only occurs in finishing case */
+ return (ARCHIVE_EOF);
+ default:
+ /* Any other return value indicates an error */
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Bzip2 compression failed:"
+ " BZ2_bzCompress() call returned status %d", r);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+static int
+compression_end_bzip2(struct archive *a, struct la_zstream *lastrm)
+{
+ bz_stream *strm;
+ int r;
+
+ strm = (bz_stream *)lastrm->real_stream;
+ r = BZ2_bzCompressEnd(strm);
+ free(strm);
+ lastrm->real_stream = NULL;
+ lastrm->valid = 0;
+ if (r != BZ_OK) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up compressor");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+#else
+static int
+compression_init_encoder_bzip2(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+
+ (void) level; /* UNUSED */
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ return (compression_unsupported_encoder(a, lastrm, "bzip2"));
+}
+#endif
+
+/*
+ * _7_LZMA1, _7_LZMA2 compressor.
+ */
+#if defined(HAVE_LZMA_H)
+static int
+compression_init_encoder_lzma(struct archive *a,
+ struct la_zstream *lastrm, int level, uint64_t filter_id)
+{
+ static const lzma_stream lzma_init_data = LZMA_STREAM_INIT;
+ lzma_stream *strm;
+ lzma_filter *lzmafilters;
+ lzma_options_lzma lzma_opt;
+ int r;
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ strm = calloc(1, sizeof(*strm) + sizeof(*lzmafilters) * 2);
+ if (strm == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for lzma stream");
+ return (ARCHIVE_FATAL);
+ }
+ lzmafilters = (lzma_filter *)(strm+1);
+ if (level > 9)
+ level = 9;
+ if (lzma_lzma_preset(&lzma_opt, level)) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ENOMEM,
+ "Internal error initializing compression library");
+ return (ARCHIVE_FATAL);
+ }
+ lzmafilters[0].id = filter_id;
+ lzmafilters[0].options = &lzma_opt;
+ lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
+
+ r = lzma_properties_size(&(lastrm->prop_size), lzmafilters);
+ if (r != LZMA_OK) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "lzma_properties_size failed");
+ return (ARCHIVE_FATAL);
+ }
+ if (lastrm->prop_size) {
+ lastrm->props = malloc(lastrm->prop_size);
+ if (lastrm->props == NULL) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ENOMEM,
+ "Cannot allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ r = lzma_properties_encode(lzmafilters, lastrm->props);
+ if (r != LZMA_OK) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "lzma_properties_encode failed");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ *strm = lzma_init_data;
+ r = lzma_raw_encoder(strm, lzmafilters);
+ switch (r) {
+ case LZMA_OK:
+ lastrm->real_stream = strm;
+ lastrm->valid = 1;
+ lastrm->code = compression_code_lzma;
+ lastrm->end = compression_end_lzma;
+ r = ARCHIVE_OK;
+ break;
+ case LZMA_MEM_ERROR:
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ENOMEM,
+ "Internal error initializing compression library: "
+ "Cannot allocate memory");
+ r = ARCHIVE_FATAL;
+ break;
+ default:
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ "It's a bug in liblzma");
+ r = ARCHIVE_FATAL;
+ break;
+ }
+ return (r);
+}
+
+static int
+compression_init_encoder_lzma1(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+ return compression_init_encoder_lzma(a, lastrm, level,
+ LZMA_FILTER_LZMA1);
+}
+
+static int
+compression_init_encoder_lzma2(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+ return compression_init_encoder_lzma(a, lastrm, level,
+ LZMA_FILTER_LZMA2);
+}
+
+static int
+compression_code_lzma(struct archive *a,
+ struct la_zstream *lastrm, enum la_zaction action)
+{
+ lzma_stream *strm;
+ int r;
+
+ strm = (lzma_stream *)lastrm->real_stream;
+ strm->next_in = lastrm->next_in;
+ strm->avail_in = lastrm->avail_in;
+ strm->total_in = lastrm->total_in;
+ strm->next_out = lastrm->next_out;
+ strm->avail_out = lastrm->avail_out;
+ strm->total_out = lastrm->total_out;
+ r = lzma_code(strm,
+ (action == ARCHIVE_Z_FINISH)? LZMA_FINISH: LZMA_RUN);
+ lastrm->next_in = strm->next_in;
+ lastrm->avail_in = strm->avail_in;
+ lastrm->total_in = strm->total_in;
+ lastrm->next_out = strm->next_out;
+ lastrm->avail_out = strm->avail_out;
+ lastrm->total_out = strm->total_out;
+ switch (r) {
+ case LZMA_OK:
+ /* Non-finishing case */
+ return (ARCHIVE_OK);
+ case LZMA_STREAM_END:
+ /* This return can only occur in finishing case. */
+ return (ARCHIVE_EOF);
+ case LZMA_MEMLIMIT_ERROR:
+ archive_set_error(a, ENOMEM,
+ "lzma compression error:"
+ " %ju MiB would have been needed",
+ (uintmax_t)((lzma_memusage(strm) + 1024 * 1024 -1)
+ / (1024 * 1024)));
+ return (ARCHIVE_FATAL);
+ default:
+ /* Any other return value indicates an error */
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "lzma compression failed:"
+ " lzma_code() call returned status %d", r);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+static int
+compression_end_lzma(struct archive *a, struct la_zstream *lastrm)
+{
+ lzma_stream *strm;
+
+ (void)a; /* UNUSED */
+ strm = (lzma_stream *)lastrm->real_stream;
+ lzma_end(strm);
+ free(strm);
+ lastrm->valid = 0;
+ lastrm->real_stream = NULL;
+ return (ARCHIVE_OK);
+}
+#else
+static int
+compression_init_encoder_lzma1(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+
+ (void) level; /* UNUSED */
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ return (compression_unsupported_encoder(a, lastrm, "lzma"));
+}
+static int
+compression_init_encoder_lzma2(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+
+ (void) level; /* UNUSED */
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ return (compression_unsupported_encoder(a, lastrm, "lzma"));
+}
+#endif
+
+/*
+ * _7_PPMD compressor.
+ */
+static void
+ppmd_write(void *p, Byte b)
+{
+ struct archive_write *a = ((IByteOut *)p)->a;
+ struct _7zip *zip = (struct _7zip *)(a->format_data);
+ struct la_zstream *lastrm = &(zip->stream);
+ struct ppmd_stream *strm;
+
+ if (lastrm->avail_out) {
+ *lastrm->next_out++ = b;
+ lastrm->avail_out--;
+ lastrm->total_out++;
+ return;
+ }
+ strm = (struct ppmd_stream *)lastrm->real_stream;
+ if (strm->buff_ptr < strm->buff_end) {
+ *strm->buff_ptr++ = b;
+ strm->buff_bytes++;
+ }
+}
+
+static int
+compression_init_encoder_ppmd(struct archive *a,
+ struct la_zstream *lastrm, unsigned maxOrder, uint32_t msize)
+{
+ struct ppmd_stream *strm;
+ uint8_t *props;
+ int r;
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ strm = calloc(1, sizeof(*strm));
+ if (strm == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for PPMd");
+ return (ARCHIVE_FATAL);
+ }
+ strm->buff = malloc(32);
+ if (strm->buff == NULL) {
+ free(strm);
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for PPMd");
+ return (ARCHIVE_FATAL);
+ }
+ strm->buff_ptr = strm->buff;
+ strm->buff_end = strm->buff + 32;
+
+ props = malloc(1+4);
+ if (props == NULL) {
+ free(strm->buff);
+ free(strm);
+ archive_set_error(a, ENOMEM,
+ "Coludn't allocate memory for PPMd");
+ return (ARCHIVE_FATAL);
+ }
+ props[0] = maxOrder;
+ archive_le32enc(props+1, msize);
+ __archive_ppmd7_functions.Ppmd7_Construct(&strm->ppmd7_context);
+ r = __archive_ppmd7_functions.Ppmd7_Alloc(
+ &strm->ppmd7_context, msize);
+ if (r == 0) {
+ free(strm->buff);
+ free(strm);
+ free(props);
+ archive_set_error(a, ENOMEM,
+ "Coludn't allocate memory for PPMd");
+ return (ARCHIVE_FATAL);
+ }
+ __archive_ppmd7_functions.Ppmd7_Init(&(strm->ppmd7_context), maxOrder);
+ strm->byteout.a = (struct archive_write *)a;
+ strm->byteout.Write = ppmd_write;
+ strm->range_enc.Stream = &(strm->byteout);
+ __archive_ppmd7_functions.Ppmd7z_RangeEnc_Init(&(strm->range_enc));
+ strm->stat = 0;
+
+ lastrm->real_stream = strm;
+ lastrm->valid = 1;
+ lastrm->code = compression_code_ppmd;
+ lastrm->end = compression_end_ppmd;
+ lastrm->prop_size = 5;
+ lastrm->props = props;
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_code_ppmd(struct archive *a,
+ struct la_zstream *lastrm, enum la_zaction action)
+{
+ struct ppmd_stream *strm;
+
+ (void)a; /* UNUSED */
+
+ strm = (struct ppmd_stream *)lastrm->real_stream;
+
+ /* Copy encoded data if there are remaining bytes from previous call. */
+ if (strm->buff_bytes) {
+ uint8_t *p = strm->buff_ptr - strm->buff_bytes;
+ while (lastrm->avail_out && strm->buff_bytes) {
+ *lastrm->next_out++ = *p++;
+ lastrm->avail_out--;
+ lastrm->total_out++;
+ strm->buff_bytes--;
+ }
+ if (strm->buff_bytes)
+ return (ARCHIVE_OK);
+ if (strm->stat == 1)
+ return (ARCHIVE_EOF);
+ strm->buff_ptr = strm->buff;
+ }
+ while (lastrm->avail_in && lastrm->avail_out) {
+ __archive_ppmd7_functions.Ppmd7_EncodeSymbol(
+ &(strm->ppmd7_context), &(strm->range_enc),
+ *lastrm->next_in++);
+ lastrm->avail_in--;
+ lastrm->total_in++;
+ }
+ if (lastrm->avail_in == 0 && action == ARCHIVE_Z_FINISH) {
+ __archive_ppmd7_functions.Ppmd7z_RangeEnc_FlushData(
+ &(strm->range_enc));
+ strm->stat = 1;
+ /* Return EOF if there are no remaining bytes. */
+ if (strm->buff_bytes == 0)
+ return (ARCHIVE_EOF);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_end_ppmd(struct archive *a, struct la_zstream *lastrm)
+{
+ struct ppmd_stream *strm;
+
+ (void)a; /* UNUSED */
+
+ strm = (struct ppmd_stream *)lastrm->real_stream;
+ __archive_ppmd7_functions.Ppmd7_Free(&strm->ppmd7_context);
+ free(strm->buff);
+ free(strm);
+ lastrm->real_stream = NULL;
+ lastrm->valid = 0;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Universal compressor initializer.
+ */
+static int
+_7z_compression_init_encoder(struct archive_write *a, unsigned compression,
+ int compression_level)
+{
+ struct _7zip *zip;
+ int r;
+
+ zip = (struct _7zip *)a->format_data;
+ switch (compression) {
+ case _7Z_DEFLATE:
+ r = compression_init_encoder_deflate(
+ &(a->archive), &(zip->stream),
+ compression_level, 0);
+ break;
+ case _7Z_BZIP2:
+ r = compression_init_encoder_bzip2(
+ &(a->archive), &(zip->stream),
+ compression_level);
+ break;
+ case _7Z_LZMA1:
+ r = compression_init_encoder_lzma1(
+ &(a->archive), &(zip->stream),
+ compression_level);
+ break;
+ case _7Z_LZMA2:
+ r = compression_init_encoder_lzma2(
+ &(a->archive), &(zip->stream),
+ compression_level);
+ break;
+ case _7Z_PPMD:
+ r = compression_init_encoder_ppmd(
+ &(a->archive), &(zip->stream),
+ PPMD7_DEFAULT_ORDER, PPMD7_DEFAULT_MEM_SIZE);
+ break;
+ case _7Z_COPY:
+ default:
+ r = compression_init_encoder_copy(
+ &(a->archive), &(zip->stream));
+ break;
+ }
+ if (r == ARCHIVE_OK) {
+ zip->stream.total_in = 0;
+ zip->stream.next_out = zip->wbuff;
+ zip->stream.avail_out = sizeof(zip->wbuff);
+ zip->stream.total_out = 0;
+ }
+
+ return (r);
+}
+
+static int
+compression_code(struct archive *a, struct la_zstream *lastrm,
+ enum la_zaction action)
+{
+ if (lastrm->valid)
+ return (lastrm->code(a, lastrm, action));
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_end(struct archive *a, struct la_zstream *lastrm)
+{
+ if (lastrm->valid) {
+ lastrm->prop_size = 0;
+ free(lastrm->props);
+ lastrm->props = NULL;
+ return (lastrm->end(a, lastrm));
+ }
+ return (ARCHIVE_OK);
+}
+
+
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_ar.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_ar.c
new file mode 100644
index 00000000000..fc0de1e9f6f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_ar.c
@@ -0,0 +1,570 @@
+/*-
+ * Copyright (c) 2007 Kai Wang
+ * Copyright (c) 2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_ar.c 201108 2009-12-28 03:28:21Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct ar_w {
+ uint64_t entry_bytes_remaining;
+ uint64_t entry_padding;
+ int is_strtab;
+ int has_strtab;
+ char wrote_global_header;
+ char *strtab;
+};
+
+/*
+ * Define structure of the "ar" header.
+ */
+#define AR_name_offset 0
+#define AR_name_size 16
+#define AR_date_offset 16
+#define AR_date_size 12
+#define AR_uid_offset 28
+#define AR_uid_size 6
+#define AR_gid_offset 34
+#define AR_gid_size 6
+#define AR_mode_offset 40
+#define AR_mode_size 8
+#define AR_size_offset 48
+#define AR_size_size 10
+#define AR_fmag_offset 58
+#define AR_fmag_size 2
+
+static int archive_write_set_format_ar(struct archive_write *);
+static int archive_write_ar_header(struct archive_write *,
+ struct archive_entry *);
+static ssize_t archive_write_ar_data(struct archive_write *,
+ const void *buff, size_t s);
+static int archive_write_ar_free(struct archive_write *);
+static int archive_write_ar_close(struct archive_write *);
+static int archive_write_ar_finish_entry(struct archive_write *);
+static const char *ar_basename(const char *path);
+static int format_octal(int64_t v, char *p, int s);
+static int format_decimal(int64_t v, char *p, int s);
+
+int
+archive_write_set_format_ar_bsd(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_ar_bsd");
+ r = archive_write_set_format_ar(a);
+ if (r == ARCHIVE_OK) {
+ a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
+ a->archive.archive_format_name = "ar (BSD)";
+ }
+ return (r);
+}
+
+int
+archive_write_set_format_ar_svr4(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_ar_svr4");
+ r = archive_write_set_format_ar(a);
+ if (r == ARCHIVE_OK) {
+ a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU;
+ a->archive.archive_format_name = "ar (GNU/SVR4)";
+ }
+ return (r);
+}
+
+/*
+ * Generic initialization.
+ */
+static int
+archive_write_set_format_ar(struct archive_write *a)
+{
+ struct ar_w *ar;
+
+ /* If someone else was already registered, unregister them. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ ar = (struct ar_w *)calloc(1, sizeof(*ar));
+ if (ar == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate ar data");
+ return (ARCHIVE_FATAL);
+ }
+ a->format_data = ar;
+
+ a->format_name = "ar";
+ a->format_write_header = archive_write_ar_header;
+ a->format_write_data = archive_write_ar_data;
+ a->format_close = archive_write_ar_close;
+ a->format_free = archive_write_ar_free;
+ a->format_finish_entry = archive_write_ar_finish_entry;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
+{
+ int ret, append_fn;
+ char buff[60];
+ char *ss, *se;
+ struct ar_w *ar;
+ const char *pathname;
+ const char *filename;
+ int64_t size;
+
+ append_fn = 0;
+ ar = (struct ar_w *)a->format_data;
+ ar->is_strtab = 0;
+ filename = NULL;
+ size = archive_entry_size(entry);
+
+
+ /*
+ * Reject files with empty name.
+ */
+ pathname = archive_entry_pathname(entry);
+ if (pathname == NULL || *pathname == '\0') {
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid filename");
+ return (ARCHIVE_WARN);
+ }
+
+ /*
+ * If we are now at the beginning of the archive,
+ * we need first write the ar global header.
+ */
+ if (!ar->wrote_global_header) {
+ __archive_write_output(a, "!<arch>\n", 8);
+ ar->wrote_global_header = 1;
+ }
+
+ memset(buff, ' ', 60);
+ memcpy(&buff[AR_fmag_offset], "`\n", 2);
+
+ if (strcmp(pathname, "/") == 0 ) {
+ /* Entry is archive symbol table in GNU format */
+ buff[AR_name_offset] = '/';
+ goto stat;
+ }
+ if (strcmp(pathname, "/SYM64/") == 0) {
+ /* Entry is archive symbol table in GNU 64-bit format */
+ memcpy(buff + AR_name_offset, "/SYM64/", 7);
+ goto stat;
+ }
+ if (strcmp(pathname, "__.SYMDEF") == 0) {
+ /* Entry is archive symbol table in BSD format */
+ memcpy(buff + AR_name_offset, "__.SYMDEF", 9);
+ goto stat;
+ }
+ if (strcmp(pathname, "//") == 0) {
+ /*
+ * Entry is archive filename table, inform that we should
+ * collect strtab in next _data call.
+ */
+ ar->is_strtab = 1;
+ buff[AR_name_offset] = buff[AR_name_offset + 1] = '/';
+ /*
+ * For archive string table, only ar_size field should
+ * be set.
+ */
+ goto size;
+ }
+
+ /*
+ * Otherwise, entry is a normal archive member.
+ * Strip leading paths from filenames, if any.
+ */
+ if ((filename = ar_basename(pathname)) == NULL) {
+ /* Reject filenames with trailing "/" */
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid filename");
+ return (ARCHIVE_WARN);
+ }
+
+ if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) {
+ /*
+ * SVR4/GNU variant use a "/" to mark then end of the filename,
+ * make it possible to have embedded spaces in the filename.
+ * So, the longest filename here (without extension) is
+ * actually 15 bytes.
+ */
+ if (strlen(filename) <= 15) {
+ memcpy(&buff[AR_name_offset],
+ filename, strlen(filename));
+ buff[AR_name_offset + strlen(filename)] = '/';
+ } else {
+ /*
+ * For filename longer than 15 bytes, GNU variant
+ * makes use of a string table and instead stores the
+ * offset of the real filename to in the ar_name field.
+ * The string table should have been written before.
+ */
+ if (ar->has_strtab <= 0) {
+ archive_set_error(&a->archive, EINVAL,
+ "Can't find string table");
+ return (ARCHIVE_WARN);
+ }
+
+ se = (char *)malloc(strlen(filename) + 3);
+ if (se == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate filename buffer");
+ return (ARCHIVE_FATAL);
+ }
+
+ memcpy(se, filename, strlen(filename));
+ strcpy(se + strlen(filename), "/\n");
+
+ ss = strstr(ar->strtab, se);
+ free(se);
+
+ if (ss == NULL) {
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid string table");
+ return (ARCHIVE_WARN);
+ }
+
+ /*
+ * GNU variant puts "/" followed by digits into
+ * ar_name field. These digits indicates the real
+ * filename string's offset to the string table.
+ */
+ buff[AR_name_offset] = '/';
+ if (format_decimal(ss - ar->strtab,
+ buff + AR_name_offset + 1,
+ AR_name_size - 1)) {
+ archive_set_error(&a->archive, ERANGE,
+ "string table offset too large");
+ return (ARCHIVE_WARN);
+ }
+ }
+ } else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD) {
+ /*
+ * BSD variant: for any file name which is more than
+ * 16 chars or contains one or more embedded space(s), the
+ * string "#1/" followed by the ASCII length of the name is
+ * put into the ar_name field. The file size (stored in the
+ * ar_size field) is incremented by the length of the name.
+ * The name is then written immediately following the
+ * archive header.
+ */
+ if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) {
+ memcpy(&buff[AR_name_offset], filename, strlen(filename));
+ buff[AR_name_offset + strlen(filename)] = ' ';
+ }
+ else {
+ memcpy(buff + AR_name_offset, "#1/", 3);
+ if (format_decimal(strlen(filename),
+ buff + AR_name_offset + 3,
+ AR_name_size - 3)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File name too long");
+ return (ARCHIVE_WARN);
+ }
+ append_fn = 1;
+ size += strlen(filename);
+ }
+ }
+
+stat:
+ if (format_decimal(archive_entry_mtime(entry), buff + AR_date_offset, AR_date_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File modification time too large");
+ return (ARCHIVE_WARN);
+ }
+ if (format_decimal(archive_entry_uid(entry), buff + AR_uid_offset, AR_uid_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric user ID too large");
+ return (ARCHIVE_WARN);
+ }
+ if (format_decimal(archive_entry_gid(entry), buff + AR_gid_offset, AR_gid_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric group ID too large");
+ return (ARCHIVE_WARN);
+ }
+ if (format_octal(archive_entry_mode(entry), buff + AR_mode_offset, AR_mode_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric mode too large");
+ return (ARCHIVE_WARN);
+ }
+ /*
+ * Sanity Check: A non-pseudo archive member should always be
+ * a regular file.
+ */
+ if (filename != NULL && archive_entry_filetype(entry) != AE_IFREG) {
+ archive_set_error(&a->archive, EINVAL,
+ "Regular file required for non-pseudo member");
+ return (ARCHIVE_WARN);
+ }
+
+size:
+ if (format_decimal(size, buff + AR_size_offset, AR_size_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File size out of range");
+ return (ARCHIVE_WARN);
+ }
+
+ ret = __archive_write_output(a, buff, 60);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+
+ ar->entry_bytes_remaining = size;
+ ar->entry_padding = ar->entry_bytes_remaining % 2;
+
+ if (append_fn > 0) {
+ ret = __archive_write_output(a, filename, strlen(filename));
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ ar->entry_bytes_remaining -= strlen(filename);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+archive_write_ar_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct ar_w *ar;
+ int ret;
+
+ ar = (struct ar_w *)a->format_data;
+ if (s > ar->entry_bytes_remaining)
+ s = (size_t)ar->entry_bytes_remaining;
+
+ if (ar->is_strtab > 0) {
+ if (ar->has_strtab > 0) {
+ archive_set_error(&a->archive, EINVAL,
+ "More than one string tables exist");
+ return (ARCHIVE_WARN);
+ }
+
+ ar->strtab = (char *)malloc(s + 1);
+ if (ar->strtab == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate strtab buffer");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(ar->strtab, buff, s);
+ ar->strtab[s] = '\0';
+ ar->has_strtab = 1;
+ }
+
+ ret = __archive_write_output(a, buff, s);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+
+ ar->entry_bytes_remaining -= s;
+ return (s);
+}
+
+static int
+archive_write_ar_free(struct archive_write *a)
+{
+ struct ar_w *ar;
+
+ ar = (struct ar_w *)a->format_data;
+
+ if (ar == NULL)
+ return (ARCHIVE_OK);
+
+ if (ar->has_strtab > 0) {
+ free(ar->strtab);
+ ar->strtab = NULL;
+ }
+
+ free(ar);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_ar_close(struct archive_write *a)
+{
+ struct ar_w *ar;
+ int ret;
+
+ /*
+ * If we haven't written anything yet, we need to write
+ * the ar global header now to make it a valid ar archive.
+ */
+ ar = (struct ar_w *)a->format_data;
+ if (!ar->wrote_global_header) {
+ ar->wrote_global_header = 1;
+ ret = __archive_write_output(a, "!<arch>\n", 8);
+ return (ret);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_ar_finish_entry(struct archive_write *a)
+{
+ struct ar_w *ar;
+ int ret;
+
+ ar = (struct ar_w *)a->format_data;
+
+ if (ar->entry_bytes_remaining != 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Entry remaining bytes larger than 0");
+ return (ARCHIVE_WARN);
+ }
+
+ if (ar->entry_padding == 0) {
+ return (ARCHIVE_OK);
+ }
+
+ if (ar->entry_padding != 1) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Padding wrong size: %ju should be 1 or 0",
+ (uintmax_t)ar->entry_padding);
+ return (ARCHIVE_WARN);
+ }
+
+ ret = __archive_write_output(a, "\n", 1);
+ return (ret);
+}
+
+/*
+ * Format a number into the specified field using base-8.
+ * NB: This version is slightly different from the one in
+ * _ustar.c
+ */
+static int
+format_octal(int64_t v, char *p, int s)
+{
+ int len;
+ char *h;
+
+ len = s;
+ h = p;
+
+ /* Octal values can't be negative, so use 0. */
+ if (v < 0) {
+ while (len-- > 0)
+ *p++ = '0';
+ return (-1);
+ }
+
+ p += s; /* Start at the end and work backwards. */
+ do {
+ *--p = (char)('0' + (v & 7));
+ v >>= 3;
+ } while (--s > 0 && v > 0);
+
+ if (v == 0) {
+ memmove(h, p, len - s);
+ p = h + len - s;
+ while (s-- > 0)
+ *p++ = ' ';
+ return (0);
+ }
+ /* If it overflowed, fill field with max value. */
+ while (len-- > 0)
+ *p++ = '7';
+
+ return (-1);
+}
+
+/*
+ * Format a number into the specified field using base-10.
+ */
+static int
+format_decimal(int64_t v, char *p, int s)
+{
+ int len;
+ char *h;
+
+ len = s;
+ h = p;
+
+ /* Negative values in ar header are meaningless, so use 0. */
+ if (v < 0) {
+ while (len-- > 0)
+ *p++ = '0';
+ return (-1);
+ }
+
+ p += s;
+ do {
+ *--p = (char)('0' + (v % 10));
+ v /= 10;
+ } while (--s > 0 && v > 0);
+
+ if (v == 0) {
+ memmove(h, p, len - s);
+ p = h + len - s;
+ while (s-- > 0)
+ *p++ = ' ';
+ return (0);
+ }
+ /* If it overflowed, fill field with max value. */
+ while (len-- > 0)
+ *p++ = '9';
+
+ return (-1);
+}
+
+static const char *
+ar_basename(const char *path)
+{
+ const char *endp, *startp;
+
+ endp = path + strlen(path) - 1;
+ /*
+ * For filename with trailing slash(es), we return
+ * NULL indicating an error.
+ */
+ if (*endp == '/')
+ return (NULL);
+
+ /* Find the start of the base */
+ startp = endp;
+ while (startp > path && *(startp - 1) != '/')
+ startp--;
+
+ return (startp);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_by_name.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_by_name.c
new file mode 100644
index 00000000000..bfb4b3545f2
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_by_name.c
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_by_name.c 201168 2009-12-29 06:15:32Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+/* A table that maps names to functions. */
+static const
+struct { const char *name; int (*setter)(struct archive *); } names[] =
+{
+ { "7zip", archive_write_set_format_7zip },
+ { "ar", archive_write_set_format_ar_bsd },
+ { "arbsd", archive_write_set_format_ar_bsd },
+ { "argnu", archive_write_set_format_ar_svr4 },
+ { "arsvr4", archive_write_set_format_ar_svr4 },
+ { "bin", archive_write_set_format_cpio_bin },
+ { "bsdtar", archive_write_set_format_pax_restricted },
+ { "cd9660", archive_write_set_format_iso9660 },
+ { "cpio", archive_write_set_format_cpio },
+ { "gnutar", archive_write_set_format_gnutar },
+ { "iso", archive_write_set_format_iso9660 },
+ { "iso9660", archive_write_set_format_iso9660 },
+ { "mtree", archive_write_set_format_mtree },
+ { "mtree-classic", archive_write_set_format_mtree_classic },
+ { "newc", archive_write_set_format_cpio_newc },
+ { "odc", archive_write_set_format_cpio_odc },
+ { "oldtar", archive_write_set_format_v7tar },
+ { "pax", archive_write_set_format_pax },
+ { "paxr", archive_write_set_format_pax_restricted },
+ { "posix", archive_write_set_format_pax },
+ { "pwb", archive_write_set_format_cpio_pwb },
+ { "raw", archive_write_set_format_raw },
+ { "rpax", archive_write_set_format_pax_restricted },
+ { "shar", archive_write_set_format_shar },
+ { "shardump", archive_write_set_format_shar_dump },
+ { "ustar", archive_write_set_format_ustar },
+ { "v7tar", archive_write_set_format_v7tar },
+ { "v7", archive_write_set_format_v7tar },
+ { "warc", archive_write_set_format_warc },
+ { "xar", archive_write_set_format_xar },
+ { "zip", archive_write_set_format_zip },
+ { NULL, NULL }
+};
+
+int
+archive_write_set_format_by_name(struct archive *a, const char *name)
+{
+ int i;
+
+ for (i = 0; names[i].name != NULL; i++) {
+ if (strcmp(name, names[i].name) == 0)
+ return ((names[i].setter)(a));
+ }
+
+ archive_set_error(a, EINVAL, "No such format '%s'", name);
+ a->state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio.c
new file mode 100644
index 00000000000..47152cc6a98
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio.c
@@ -0,0 +1,11 @@
+#include "archive_platform.h"
+#include "archive.h"
+
+/*
+ * Set output format to the default 'cpio' format.
+ */
+int
+archive_write_set_format_cpio(struct archive *_a)
+{
+ return archive_write_set_format_cpio_odc(_a);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_binary.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_binary.c
new file mode 100644
index 00000000000..d6ce35a7bc1
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_binary.c
@@ -0,0 +1,610 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_cpio.c 201170 2009-12-29 06:34:23Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+static ssize_t archive_write_binary_data(struct archive_write *,
+ const void *buff, size_t s);
+static int archive_write_binary_close(struct archive_write *);
+static int archive_write_binary_free(struct archive_write *);
+static int archive_write_binary_finish_entry(struct archive_write *);
+static int archive_write_binary_header(struct archive_write *,
+ struct archive_entry *);
+static int archive_write_binary_options(struct archive_write *,
+ const char *, const char *);
+static int write_header(struct archive_write *, struct archive_entry *);
+
+struct cpio {
+ uint64_t entry_bytes_remaining;
+
+ int64_t ino_next;
+
+ struct { int64_t old; int new;} *ino_list;
+ size_t ino_list_size;
+ size_t ino_list_next;
+
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_default;
+ int init_default_conversion;
+};
+
+/* This struct needs to be packed to get the header right */
+
+#if defined(__GNUC__)
+#define PACKED(x) x __attribute__((packed))
+#elif defined(_MSC_VER)
+#define PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop))
+#else
+#define PACKED(x) x
+#endif
+
+#define HSIZE 26
+
+PACKED(struct cpio_binary_header {
+ uint16_t h_magic;
+ uint16_t h_dev;
+ uint16_t h_ino;
+ uint16_t h_mode;
+ uint16_t h_uid;
+ uint16_t h_gid;
+ uint16_t h_nlink;
+ uint16_t h_majmin;
+ uint32_t h_mtime;
+ uint16_t h_namesize;
+ uint32_t h_filesize;
+});
+
+/* Back in the day, the 7th Edition cpio.c had this, to
+ * adapt to, as the comment said, "VAX, Interdata, ...":
+ *
+ * union { long l; short s[2]; char c[4]; } U;
+ * #define MKSHORT(v,lv) {U.l=1L;if(U.c[0]) U.l=lv,v[0]=U.s[1],v[1]=U.s[0]; else U.l=lv,v[0]=U.s[0],v[1]=U.s[1];}
+ * long mklong(v)
+ * short v[];
+ * {
+ * U.l = 1;
+ * if(U.c[0])
+ * U.s[0] = v[1], U.s[1] = v[0];
+ * else
+ * U.s[0] = v[0], U.s[1] = v[1];
+ * return U.l;
+ * }
+ *
+ * Of course, that assumes that all machines have little-endian shorts,
+ * and just adapts the others to the special endianness of the PDP-11.
+ *
+ * Now, we could do this:
+ *
+ * union { uint32_t l; uint16_t s[2]; uint8_t c[4]; } U;
+ * #define PUTI16(v,sv) {U.s[0]=1;if(U.c[0]) v=sv; else U.s[0]=sv,U.c[2]=U.c[1],U.c[3]=U.c[0],v=U.s[1];}
+ * #define PUTI32(v,lv) {char_t Ut;U.l=1;if(U.c[0]) U.l=lv,v[0]=U.s[1],v[1]=U.s[0]; else U.l=lv,Ut=U.c[0],U.c[0]=U.c[1],U.c[1]=Ut,Ut=U.c[2],U.c[2]=U.c[3],U.c[3]=Ut,v[0]=U.s[0],v[1]=U.s[1];}
+ *
+ * ...but it feels a little better to do it like this:
+ */
+
+static uint16_t la_swap16(uint16_t in) {
+ union {
+ uint16_t s[2];
+ uint8_t c[4];
+ } U;
+ U.s[0] = 1;
+ if (U.c[0])
+ return in;
+ else {
+ U.s[0] = in;
+ U.c[2] = U.c[1];
+ U.c[3] = U.c[0];
+ return U.s[1];
+ }
+ /* NOTREACHED */
+}
+
+static uint32_t la_swap32(uint32_t in) {
+ union {
+ uint32_t l;
+ uint16_t s[2];
+ uint8_t c[4];
+ } U;
+ U.l = 1;
+ if (U.c[0]) { /* Little-endian */
+ uint16_t t;
+ U.l = in;
+ t = U.s[0];
+ U.s[0] = U.s[1];
+ U.s[1] = t;
+ } else if (U.c[3]) { /* Big-endian */
+ U.l = in;
+ U.s[0] = la_swap16(U.s[0]);
+ U.s[1] = la_swap16(U.s[1]);
+ } else { /* PDP-endian */
+ U.l = in;
+ }
+ return U.l;
+}
+
+/*
+ * Set output format to the selected binary variant
+ */
+static int
+archive_write_set_format_cpio_binary(struct archive *_a, int format)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct cpio *cpio;
+
+ if (sizeof(struct cpio_binary_header) != HSIZE) {
+ archive_set_error(&a->archive, EINVAL,
+ "Binary cpio format not supported on this platform");
+ return (ARCHIVE_FATAL);
+ }
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_binary");
+
+ /* If someone else was already registered, unregister them. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ cpio = (struct cpio *)calloc(1, sizeof(*cpio));
+ if (cpio == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
+ return (ARCHIVE_FATAL);
+ }
+ a->format_data = cpio;
+ a->format_name = "cpio";
+ a->format_options = archive_write_binary_options;
+ a->format_write_header = archive_write_binary_header;
+ a->format_write_data = archive_write_binary_data;
+ a->format_finish_entry = archive_write_binary_finish_entry;
+ a->format_close = archive_write_binary_close;
+ a->format_free = archive_write_binary_free;
+ a->archive.archive_format = format;
+ switch (format) {
+ case ARCHIVE_FORMAT_CPIO_PWB:
+ a->archive.archive_format_name = "PWB cpio";
+ break;
+ case ARCHIVE_FORMAT_CPIO_BIN_LE:
+ a->archive.archive_format_name = "7th Edition cpio";
+ break;
+ default:
+ archive_set_error(&a->archive, EINVAL, "binary format must be 'pwb' or 'bin'");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Set output format to PWB (6th Edition) binary format
+ */
+int
+archive_write_set_format_cpio_pwb(struct archive *_a)
+{
+ return archive_write_set_format_cpio_binary(_a, ARCHIVE_FORMAT_CPIO_PWB);
+}
+
+/*
+ * Set output format to 7th Edition binary format
+ */
+int
+archive_write_set_format_cpio_bin(struct archive *_a)
+{
+ return archive_write_set_format_cpio_binary(_a, ARCHIVE_FORMAT_CPIO_BIN_LE);
+}
+
+static int
+archive_write_binary_options(struct archive_write *a, const char *key,
+ const char *val)
+{
+ struct cpio *cpio = (struct cpio *)a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: hdrcharset option needs a character-set name",
+ a->format_name);
+ else {
+ cpio->opt_sconv = archive_string_conversion_to_charset(
+ &a->archive, val, 0);
+ if (cpio->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Ino values are as long as 64 bits on some systems; cpio format
+ * only allows 16 bits and relies on the ino values to identify hardlinked
+ * files. So, we can't merely "hash" the ino numbers since collisions
+ * would corrupt the archive. Instead, we generate synthetic ino values
+ * to store in the archive and maintain a map of original ino values to
+ * synthetic ones so we can preserve hardlink information.
+ *
+ * TODO: Make this more efficient. It's not as bad as it looks (most
+ * files don't have any hardlinks and we don't do any work here for those),
+ * but it wouldn't be hard to do better.
+ *
+ * TODO: Work with dev/ino pairs here instead of just ino values.
+ */
+static int
+synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry)
+{
+ int64_t ino = archive_entry_ino64(entry);
+ int ino_new;
+ size_t i;
+
+ /*
+ * If no index number was given, don't assign one. In
+ * particular, this handles the end-of-archive marker
+ * correctly by giving it a zero index value. (This is also
+ * why we start our synthetic index numbers with one below.)
+ */
+ if (ino == 0)
+ return (0);
+
+ /* Don't store a mapping if we don't need to. */
+ if (archive_entry_nlink(entry) < 2) {
+ return (int)(++cpio->ino_next);
+ }
+
+ /* Look up old ino; if we have it, this is a hardlink
+ * and we reuse the same value. */
+ for (i = 0; i < cpio->ino_list_next; ++i) {
+ if (cpio->ino_list[i].old == ino)
+ return (cpio->ino_list[i].new);
+ }
+
+ /* Assign a new index number. */
+ ino_new = (int)(++cpio->ino_next);
+
+ /* Ensure space for the new mapping. */
+ if (cpio->ino_list_size <= cpio->ino_list_next) {
+ size_t newsize = cpio->ino_list_size < 512
+ ? 512 : cpio->ino_list_size * 2;
+ void *newlist = realloc(cpio->ino_list,
+ sizeof(cpio->ino_list[0]) * newsize);
+ if (newlist == NULL)
+ return (-1);
+
+ cpio->ino_list_size = newsize;
+ cpio->ino_list = newlist;
+ }
+
+ /* Record and return the new value. */
+ cpio->ino_list[cpio->ino_list_next].old = ino;
+ cpio->ino_list[cpio->ino_list_next].new = ino_new;
+ ++cpio->ino_list_next;
+ return (ino_new);
+}
+
+
+static struct archive_string_conv *
+get_sconv(struct archive_write *a)
+{
+ struct cpio *cpio;
+ struct archive_string_conv *sconv;
+
+ cpio = (struct cpio *)a->format_data;
+ sconv = cpio->opt_sconv;
+ if (sconv == NULL) {
+ if (!cpio->init_default_conversion) {
+ cpio->sconv_default =
+ archive_string_default_conversion_for_write(
+ &(a->archive));
+ cpio->init_default_conversion = 1;
+ }
+ sconv = cpio->sconv_default;
+ }
+ return (sconv);
+}
+
+static int
+archive_write_binary_header(struct archive_write *a, struct archive_entry *entry)
+{
+ const char *path;
+ size_t len;
+
+ if (archive_entry_filetype(entry) == 0 && archive_entry_hardlink(entry) == NULL) {
+ archive_set_error(&a->archive, -1, "Filetype required");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0
+ && errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ if (len == 0 || path == NULL || path[0] == '\0') {
+ archive_set_error(&a->archive, -1, "Pathname required");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0) {
+ archive_set_error(&a->archive, -1, "Size required");
+ return (ARCHIVE_FAILED);
+ }
+ return write_header(a, entry);
+}
+
+static int
+write_header(struct archive_write *a, struct archive_entry *entry)
+{
+ struct cpio *cpio;
+ const char *p, *path;
+ int pathlength, ret, ret_final;
+ int64_t ino;
+ struct cpio_binary_header h;
+ struct archive_string_conv *sconv;
+ struct archive_entry *entry_main;
+ size_t len;
+
+ cpio = (struct cpio *)a->format_data;
+ ret_final = ARCHIVE_OK;
+ sconv = get_sconv(a);
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Make sure the path separators in pathname, hardlink and symlink
+ * are all slash '/', not the Windows path separator '\'. */
+ entry_main = __la_win_entry_in_posix_pathseparator(entry);
+ if (entry_main == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ return(ARCHIVE_FATAL);
+ }
+ if (entry != entry_main)
+ entry = entry_main;
+ else
+ entry_main = NULL;
+#else
+ entry_main = NULL;
+#endif
+
+ ret = archive_entry_pathname_l(entry, &path, &len, sconv);
+ if (ret != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s",
+ archive_entry_pathname(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret_final = ARCHIVE_WARN;
+ }
+ /* Include trailing null */
+ pathlength = (int)len + 1;
+
+ h.h_magic = la_swap16(070707);
+ h.h_dev = la_swap16(archive_entry_dev(entry));
+
+ ino = synthesize_ino_value(cpio, entry);
+ if (ino < 0) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for ino translation table");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ } else if (ino > 077777) {
+ archive_set_error(&a->archive, ERANGE,
+ "Too many files for this cpio format");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ h.h_ino = la_swap16((uint16_t)ino);
+
+ h.h_mode = archive_entry_mode(entry);
+ if (((h.h_mode & AE_IFMT) == AE_IFSOCK) || ((h.h_mode & AE_IFMT) == AE_IFIFO)) {
+ archive_set_error(&a->archive, EINVAL,
+ "sockets and fifos cannot be represented in the binary cpio formats");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_PWB) {
+ if ((h.h_mode & AE_IFMT) == AE_IFLNK) {
+ archive_set_error(&a->archive, EINVAL,
+ "symbolic links cannot be represented in the PWB cpio format");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ /* we could turn off AE_IFREG here, but it does no harm, */
+ /* and allows v7 cpio to read the entry without confusion */
+ }
+ h.h_mode = la_swap16(h.h_mode);
+
+ h.h_uid = la_swap16((uint16_t)archive_entry_uid(entry));
+ h.h_gid = la_swap16((uint16_t)archive_entry_gid(entry));
+ h.h_nlink = la_swap16((uint16_t)archive_entry_nlink(entry));
+
+ if (archive_entry_filetype(entry) == AE_IFBLK
+ || archive_entry_filetype(entry) == AE_IFCHR)
+ h.h_majmin = la_swap16(archive_entry_rdev(entry));
+ else
+ h.h_majmin = 0;
+
+ h.h_mtime = la_swap32((uint32_t)archive_entry_mtime(entry));
+ h.h_namesize = la_swap16(pathlength);
+
+ /* Non-regular files don't store bodies. */
+ if (archive_entry_filetype(entry) != AE_IFREG)
+ archive_entry_set_size(entry, 0);
+
+ /* Symlinks get the link written as the body of the entry. */
+ ret = archive_entry_symlink_l(entry, &p, &len, sconv);
+ if (ret != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ archive_entry_symlink(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret_final = ARCHIVE_WARN;
+ }
+
+ if (len > 0 && p != NULL && *p != '\0') {
+ if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_PWB) {
+ archive_set_error(&a->archive, EINVAL,
+ "symlinks are not supported by UNIX V6 or by PWB cpio");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ h.h_filesize = la_swap32((uint32_t)strlen(p)); /* symlink */
+ } else {
+ if ((a->archive.archive_format == ARCHIVE_FORMAT_CPIO_PWB) &&
+ (archive_entry_size(entry) > 256*256*256-1)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File is too large for PWB binary cpio format.");
+ ret_final = ARCHIVE_FAILED;
+ goto exit_write_header;
+ } else if (archive_entry_size(entry) > INT32_MAX) {
+ archive_set_error(&a->archive, ERANGE,
+ "File is too large for binary cpio format.");
+ ret_final = ARCHIVE_FAILED;
+ goto exit_write_header;
+ }
+ h.h_filesize = la_swap32((uint32_t)archive_entry_size(entry)); /* file */
+ }
+
+ ret = __archive_write_output(a, &h, HSIZE);
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+
+ ret = __archive_write_output(a, path, pathlength);
+ if ((ret == ARCHIVE_OK) && ((pathlength % 2) != 0))
+ ret = __archive_write_nulls(a, 1);
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+
+ cpio->entry_bytes_remaining = archive_entry_size(entry);
+ if ((cpio->entry_bytes_remaining % 2) != 0)
+ cpio->entry_bytes_remaining++;
+
+ /* Write the symlink now. */
+ if (p != NULL && *p != '\0') {
+ ret = __archive_write_output(a, p, strlen(p));
+ if ((ret == ARCHIVE_OK) && ((strlen(p) % 2) != 0))
+ ret = __archive_write_nulls(a, 1);
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ }
+
+exit_write_header:
+ archive_entry_free(entry_main);
+ return (ret_final);
+}
+
+static ssize_t
+archive_write_binary_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct cpio *cpio;
+ int ret;
+
+ cpio = (struct cpio *)a->format_data;
+ if (s > cpio->entry_bytes_remaining)
+ s = (size_t)cpio->entry_bytes_remaining;
+
+ ret = __archive_write_output(a, buff, s);
+ cpio->entry_bytes_remaining -= s;
+ if (ret >= 0)
+ return (s);
+ else
+ return (ret);
+}
+
+static int
+archive_write_binary_close(struct archive_write *a)
+{
+ int er;
+ struct archive_entry *trailer;
+
+ trailer = archive_entry_new2(NULL);
+ /* nlink = 1 here for GNU cpio compat. */
+ archive_entry_set_nlink(trailer, 1);
+ archive_entry_set_size(trailer, 0);
+ archive_entry_set_pathname(trailer, "TRAILER!!!");
+ er = write_header(a, trailer);
+ archive_entry_free(trailer);
+ return (er);
+}
+
+static int
+archive_write_binary_free(struct archive_write *a)
+{
+ struct cpio *cpio;
+
+ cpio = (struct cpio *)a->format_data;
+ free(cpio->ino_list);
+ free(cpio);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_binary_finish_entry(struct archive_write *a)
+{
+ struct cpio *cpio;
+
+ cpio = (struct cpio *)a->format_data;
+ return (__archive_write_nulls(a,
+ (size_t)cpio->entry_bytes_remaining));
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_newc.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_newc.c
new file mode 100644
index 00000000000..f0f39809dad
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_newc.c
@@ -0,0 +1,457 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2006 Rudolf Marek SYSGO s.r.o.
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_cpio_newc.c 201160 2009-12-29 05:41:57Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+static ssize_t archive_write_newc_data(struct archive_write *,
+ const void *buff, size_t s);
+static int archive_write_newc_close(struct archive_write *);
+static int archive_write_newc_free(struct archive_write *);
+static int archive_write_newc_finish_entry(struct archive_write *);
+static int archive_write_newc_header(struct archive_write *,
+ struct archive_entry *);
+static int archive_write_newc_options(struct archive_write *,
+ const char *, const char *);
+static int format_hex(int64_t, void *, int);
+static int64_t format_hex_recursive(int64_t, char *, int);
+static int write_header(struct archive_write *, struct archive_entry *);
+
+struct cpio {
+ uint64_t entry_bytes_remaining;
+ int padding;
+
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_default;
+ int init_default_conversion;
+};
+
+#define c_magic_offset 0
+#define c_magic_size 6
+#define c_ino_offset 6
+#define c_ino_size 8
+#define c_mode_offset 14
+#define c_mode_size 8
+#define c_uid_offset 22
+#define c_uid_size 8
+#define c_gid_offset 30
+#define c_gid_size 8
+#define c_nlink_offset 38
+#define c_nlink_size 8
+#define c_mtime_offset 46
+#define c_mtime_size 8
+#define c_filesize_offset 54
+#define c_filesize_size 8
+#define c_devmajor_offset 62
+#define c_devmajor_size 8
+#define c_devminor_offset 70
+#define c_devminor_size 8
+#define c_rdevmajor_offset 78
+#define c_rdevmajor_size 8
+#define c_rdevminor_offset 86
+#define c_rdevminor_size 8
+#define c_namesize_offset 94
+#define c_namesize_size 8
+#define c_checksum_offset 102
+#define c_checksum_size 8
+#define c_header_size 110
+
+/* Logic trick: difference between 'n' and next multiple of 4 */
+#define PAD4(n) (3 & (1 + ~(n)))
+
+/*
+ * Set output format to 'cpio' format.
+ */
+int
+archive_write_set_format_cpio_newc(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct cpio *cpio;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_newc");
+
+ /* If someone else was already registered, unregister them. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ cpio = (struct cpio *)calloc(1, sizeof(*cpio));
+ if (cpio == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
+ return (ARCHIVE_FATAL);
+ }
+ a->format_data = cpio;
+ a->format_name = "cpio";
+ a->format_options = archive_write_newc_options;
+ a->format_write_header = archive_write_newc_header;
+ a->format_write_data = archive_write_newc_data;
+ a->format_finish_entry = archive_write_newc_finish_entry;
+ a->format_close = archive_write_newc_close;
+ a->format_free = archive_write_newc_free;
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;
+ a->archive.archive_format_name = "SVR4 cpio nocrc";
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_newc_options(struct archive_write *a, const char *key,
+ const char *val)
+{
+ struct cpio *cpio = (struct cpio *)a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: hdrcharset option needs a character-set name",
+ a->format_name);
+ else {
+ cpio->opt_sconv = archive_string_conversion_to_charset(
+ &a->archive, val, 0);
+ if (cpio->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static struct archive_string_conv *
+get_sconv(struct archive_write *a)
+{
+ struct cpio *cpio;
+ struct archive_string_conv *sconv;
+
+ cpio = (struct cpio *)a->format_data;
+ sconv = cpio->opt_sconv;
+ if (sconv == NULL) {
+ if (!cpio->init_default_conversion) {
+ cpio->sconv_default =
+ archive_string_default_conversion_for_write(
+ &(a->archive));
+ cpio->init_default_conversion = 1;
+ }
+ sconv = cpio->sconv_default;
+ }
+ return (sconv);
+}
+
+static int
+archive_write_newc_header(struct archive_write *a, struct archive_entry *entry)
+{
+ const char *path;
+ size_t len;
+
+ if (archive_entry_filetype(entry) == 0 && archive_entry_hardlink(entry) == NULL) {
+ archive_set_error(&a->archive, -1, "Filetype required");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0
+ && errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ if (len == 0 || path == NULL || path[0] == '\0') {
+ archive_set_error(&a->archive, -1, "Pathname required");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (archive_entry_hardlink(entry) == NULL
+ && (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0)) {
+ archive_set_error(&a->archive, -1, "Size required");
+ return (ARCHIVE_FAILED);
+ }
+ return write_header(a, entry);
+}
+
+static int
+write_header(struct archive_write *a, struct archive_entry *entry)
+{
+ int64_t ino;
+ struct cpio *cpio;
+ const char *p, *path;
+ int pathlength, ret, ret_final;
+ char h[c_header_size];
+ struct archive_string_conv *sconv;
+ struct archive_entry *entry_main;
+ size_t len;
+ int pad;
+
+ cpio = (struct cpio *)a->format_data;
+ ret_final = ARCHIVE_OK;
+ sconv = get_sconv(a);
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Make sure the path separators in pathname, hardlink and symlink
+ * are all slash '/', not the Windows path separator '\'. */
+ entry_main = __la_win_entry_in_posix_pathseparator(entry);
+ if (entry_main == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ return(ARCHIVE_FATAL);
+ }
+ if (entry != entry_main)
+ entry = entry_main;
+ else
+ entry_main = NULL;
+#else
+ entry_main = NULL;
+#endif
+
+ ret = archive_entry_pathname_l(entry, &path, &len, sconv);
+ if (ret != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s",
+ archive_entry_pathname(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret_final = ARCHIVE_WARN;
+ }
+ pathlength = (int)len + 1; /* Include trailing null. */
+
+ memset(h, 0, c_header_size);
+ format_hex(0x070701, h + c_magic_offset, c_magic_size);
+ format_hex(archive_entry_devmajor(entry), h + c_devmajor_offset,
+ c_devmajor_size);
+ format_hex(archive_entry_devminor(entry), h + c_devminor_offset,
+ c_devminor_size);
+
+ ino = archive_entry_ino64(entry);
+ if (ino > 0xffffffff) {
+ archive_set_error(&a->archive, ERANGE,
+ "large inode number truncated");
+ ret_final = ARCHIVE_WARN;
+ }
+
+ /* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */
+ format_hex(ino & 0xffffffff, h + c_ino_offset, c_ino_size);
+ format_hex(archive_entry_mode(entry), h + c_mode_offset, c_mode_size);
+ format_hex(archive_entry_uid(entry), h + c_uid_offset, c_uid_size);
+ format_hex(archive_entry_gid(entry), h + c_gid_offset, c_gid_size);
+ format_hex(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size);
+ if (archive_entry_filetype(entry) == AE_IFBLK
+ || archive_entry_filetype(entry) == AE_IFCHR) {
+ format_hex(archive_entry_rdevmajor(entry), h + c_rdevmajor_offset, c_rdevmajor_size);
+ format_hex(archive_entry_rdevminor(entry), h + c_rdevminor_offset, c_rdevminor_size);
+ } else {
+ format_hex(0, h + c_rdevmajor_offset, c_rdevmajor_size);
+ format_hex(0, h + c_rdevminor_offset, c_rdevminor_size);
+ }
+ format_hex(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size);
+ format_hex(pathlength, h + c_namesize_offset, c_namesize_size);
+ format_hex(0, h + c_checksum_offset, c_checksum_size);
+
+ /* Non-regular files don't store bodies. */
+ if (archive_entry_filetype(entry) != AE_IFREG)
+ archive_entry_set_size(entry, 0);
+
+ /* Symlinks get the link written as the body of the entry. */
+ ret = archive_entry_symlink_l(entry, &p, &len, sconv);
+ if (ret != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Likname");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ archive_entry_symlink(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret_final = ARCHIVE_WARN;
+ }
+ if (len > 0 && p != NULL && *p != '\0')
+ ret = format_hex(strlen(p), h + c_filesize_offset,
+ c_filesize_size);
+ else
+ ret = format_hex(archive_entry_size(entry),
+ h + c_filesize_offset, c_filesize_size);
+ if (ret) {
+ archive_set_error(&a->archive, ERANGE,
+ "File is too large for this format.");
+ ret_final = ARCHIVE_FAILED;
+ goto exit_write_header;
+ }
+
+ ret = __archive_write_output(a, h, c_header_size);
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+
+ /* Pad pathname to even length. */
+ ret = __archive_write_output(a, path, pathlength);
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ pad = PAD4(pathlength + c_header_size);
+ if (pad) {
+ ret = __archive_write_output(a, "\0\0\0", pad);
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ }
+
+ cpio->entry_bytes_remaining = archive_entry_size(entry);
+ cpio->padding = (int)PAD4(cpio->entry_bytes_remaining);
+
+ /* Write the symlink now. */
+ if (p != NULL && *p != '\0') {
+ ret = __archive_write_output(a, p, strlen(p));
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ pad = PAD4(strlen(p));
+ ret = __archive_write_output(a, "\0\0\0", pad);
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ }
+exit_write_header:
+ archive_entry_free(entry_main);
+ return (ret_final);
+}
+
+static ssize_t
+archive_write_newc_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct cpio *cpio;
+ int ret;
+
+ cpio = (struct cpio *)a->format_data;
+ if (s > cpio->entry_bytes_remaining)
+ s = (size_t)cpio->entry_bytes_remaining;
+
+ ret = __archive_write_output(a, buff, s);
+ cpio->entry_bytes_remaining -= s;
+ if (ret >= 0)
+ return (s);
+ else
+ return (ret);
+}
+
+/*
+ * Format a number into the specified field.
+ */
+static int
+format_hex(int64_t v, void *p, int digits)
+{
+ int64_t max;
+ int ret;
+
+ max = (((int64_t)1) << (digits * 4)) - 1;
+ if (v >= 0 && v <= max) {
+ format_hex_recursive(v, (char *)p, digits);
+ ret = 0;
+ } else {
+ format_hex_recursive(max, (char *)p, digits);
+ ret = -1;
+ }
+ return (ret);
+}
+
+static int64_t
+format_hex_recursive(int64_t v, char *p, int s)
+{
+ if (s == 0)
+ return (v);
+ v = format_hex_recursive(v, p+1, s-1);
+ *p = "0123456789abcdef"[v & 0xf];
+ return (v >> 4);
+}
+
+static int
+archive_write_newc_close(struct archive_write *a)
+{
+ int er;
+ struct archive_entry *trailer;
+
+ trailer = archive_entry_new();
+ archive_entry_set_nlink(trailer, 1);
+ archive_entry_set_size(trailer, 0);
+ archive_entry_set_pathname(trailer, "TRAILER!!!");
+ /* Bypass the required data checks. */
+ er = write_header(a, trailer);
+ archive_entry_free(trailer);
+ return (er);
+}
+
+static int
+archive_write_newc_free(struct archive_write *a)
+{
+ struct cpio *cpio;
+
+ cpio = (struct cpio *)a->format_data;
+ free(cpio);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_newc_finish_entry(struct archive_write *a)
+{
+ struct cpio *cpio;
+
+ cpio = (struct cpio *)a->format_data;
+ return (__archive_write_nulls(a,
+ (size_t)cpio->entry_bytes_remaining + cpio->padding));
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_odc.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_odc.c
new file mode 100644
index 00000000000..091925a2f9f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_odc.c
@@ -0,0 +1,500 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_cpio.c 201170 2009-12-29 06:34:23Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+static ssize_t archive_write_odc_data(struct archive_write *,
+ const void *buff, size_t s);
+static int archive_write_odc_close(struct archive_write *);
+static int archive_write_odc_free(struct archive_write *);
+static int archive_write_odc_finish_entry(struct archive_write *);
+static int archive_write_odc_header(struct archive_write *,
+ struct archive_entry *);
+static int archive_write_odc_options(struct archive_write *,
+ const char *, const char *);
+static int format_octal(int64_t, void *, int);
+static int64_t format_octal_recursive(int64_t, char *, int);
+static int write_header(struct archive_write *, struct archive_entry *);
+
+struct cpio {
+ uint64_t entry_bytes_remaining;
+
+ int64_t ino_next;
+
+ struct { int64_t old; int new;} *ino_list;
+ size_t ino_list_size;
+ size_t ino_list_next;
+
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_default;
+ int init_default_conversion;
+};
+
+#define c_magic_offset 0
+#define c_magic_size 6
+#define c_dev_offset 6
+#define c_dev_size 6
+#define c_ino_offset 12
+#define c_ino_size 6
+#define c_mode_offset 18
+#define c_mode_size 6
+#define c_uid_offset 24
+#define c_uid_size 6
+#define c_gid_offset 30
+#define c_gid_size 6
+#define c_nlink_offset 36
+#define c_nlink_size 6
+#define c_rdev_offset 42
+#define c_rdev_size 6
+#define c_mtime_offset 48
+#define c_mtime_size 11
+#define c_namesize_offset 59
+#define c_namesize_size 6
+#define c_filesize_offset 65
+#define c_filesize_size 11
+
+/*
+ * Set output format to 'cpio' format.
+ */
+int
+archive_write_set_format_cpio_odc(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct cpio *cpio;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_odc");
+
+ /* If someone else was already registered, unregister them. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ cpio = (struct cpio *)calloc(1, sizeof(*cpio));
+ if (cpio == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
+ return (ARCHIVE_FATAL);
+ }
+ a->format_data = cpio;
+ a->format_name = "cpio";
+ a->format_options = archive_write_odc_options;
+ a->format_write_header = archive_write_odc_header;
+ a->format_write_data = archive_write_odc_data;
+ a->format_finish_entry = archive_write_odc_finish_entry;
+ a->format_close = archive_write_odc_close;
+ a->format_free = archive_write_odc_free;
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
+ a->archive.archive_format_name = "POSIX cpio";
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_odc_options(struct archive_write *a, const char *key,
+ const char *val)
+{
+ struct cpio *cpio = (struct cpio *)a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: hdrcharset option needs a character-set name",
+ a->format_name);
+ else {
+ cpio->opt_sconv = archive_string_conversion_to_charset(
+ &a->archive, val, 0);
+ if (cpio->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Ino values are as long as 64 bits on some systems; cpio format
+ * only allows 18 bits and relies on the ino values to identify hardlinked
+ * files. So, we can't merely "hash" the ino numbers since collisions
+ * would corrupt the archive. Instead, we generate synthetic ino values
+ * to store in the archive and maintain a map of original ino values to
+ * synthetic ones so we can preserve hardlink information.
+ *
+ * TODO: Make this more efficient. It's not as bad as it looks (most
+ * files don't have any hardlinks and we don't do any work here for those),
+ * but it wouldn't be hard to do better.
+ *
+ * TODO: Work with dev/ino pairs here instead of just ino values.
+ */
+static int
+synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry)
+{
+ int64_t ino = archive_entry_ino64(entry);
+ int ino_new;
+ size_t i;
+
+ /*
+ * If no index number was given, don't assign one. In
+ * particular, this handles the end-of-archive marker
+ * correctly by giving it a zero index value. (This is also
+ * why we start our synthetic index numbers with one below.)
+ */
+ if (ino == 0)
+ return (0);
+
+ /* Don't store a mapping if we don't need to. */
+ if (archive_entry_nlink(entry) < 2) {
+ return (int)(++cpio->ino_next);
+ }
+
+ /* Look up old ino; if we have it, this is a hardlink
+ * and we reuse the same value. */
+ for (i = 0; i < cpio->ino_list_next; ++i) {
+ if (cpio->ino_list[i].old == ino)
+ return (cpio->ino_list[i].new);
+ }
+
+ /* Assign a new index number. */
+ ino_new = (int)(++cpio->ino_next);
+
+ /* Ensure space for the new mapping. */
+ if (cpio->ino_list_size <= cpio->ino_list_next) {
+ size_t newsize = cpio->ino_list_size < 512
+ ? 512 : cpio->ino_list_size * 2;
+ void *newlist = realloc(cpio->ino_list,
+ sizeof(cpio->ino_list[0]) * newsize);
+ if (newlist == NULL)
+ return (-1);
+
+ cpio->ino_list_size = newsize;
+ cpio->ino_list = newlist;
+ }
+
+ /* Record and return the new value. */
+ cpio->ino_list[cpio->ino_list_next].old = ino;
+ cpio->ino_list[cpio->ino_list_next].new = ino_new;
+ ++cpio->ino_list_next;
+ return (ino_new);
+}
+
+
+static struct archive_string_conv *
+get_sconv(struct archive_write *a)
+{
+ struct cpio *cpio;
+ struct archive_string_conv *sconv;
+
+ cpio = (struct cpio *)a->format_data;
+ sconv = cpio->opt_sconv;
+ if (sconv == NULL) {
+ if (!cpio->init_default_conversion) {
+ cpio->sconv_default =
+ archive_string_default_conversion_for_write(
+ &(a->archive));
+ cpio->init_default_conversion = 1;
+ }
+ sconv = cpio->sconv_default;
+ }
+ return (sconv);
+}
+
+static int
+archive_write_odc_header(struct archive_write *a, struct archive_entry *entry)
+{
+ const char *path;
+ size_t len;
+
+ if (archive_entry_filetype(entry) == 0 && archive_entry_hardlink(entry) == NULL) {
+ archive_set_error(&a->archive, -1, "Filetype required");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0
+ && errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ if (len == 0 || path == NULL || path[0] == '\0') {
+ archive_set_error(&a->archive, -1, "Pathname required");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0) {
+ archive_set_error(&a->archive, -1, "Size required");
+ return (ARCHIVE_FAILED);
+ }
+ return write_header(a, entry);
+}
+
+static int
+write_header(struct archive_write *a, struct archive_entry *entry)
+{
+ struct cpio *cpio;
+ const char *p, *path;
+ int pathlength, ret, ret_final;
+ int64_t ino;
+ char h[76];
+ struct archive_string_conv *sconv;
+ struct archive_entry *entry_main;
+ size_t len;
+
+ cpio = (struct cpio *)a->format_data;
+ ret_final = ARCHIVE_OK;
+ sconv = get_sconv(a);
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Make sure the path separators in pathname, hardlink and symlink
+ * are all slash '/', not the Windows path separator '\'. */
+ entry_main = __la_win_entry_in_posix_pathseparator(entry);
+ if (entry_main == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ return(ARCHIVE_FATAL);
+ }
+ if (entry != entry_main)
+ entry = entry_main;
+ else
+ entry_main = NULL;
+#else
+ entry_main = NULL;
+#endif
+
+ ret = archive_entry_pathname_l(entry, &path, &len, sconv);
+ if (ret != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s",
+ archive_entry_pathname(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret_final = ARCHIVE_WARN;
+ }
+ /* Include trailing null. */
+ pathlength = (int)len + 1;
+
+ memset(h, 0, sizeof(h));
+ format_octal(070707, h + c_magic_offset, c_magic_size);
+ format_octal(archive_entry_dev(entry), h + c_dev_offset, c_dev_size);
+
+ ino = synthesize_ino_value(cpio, entry);
+ if (ino < 0) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for ino translation table");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ } else if (ino > 0777777) {
+ archive_set_error(&a->archive, ERANGE,
+ "Too many files for this cpio format");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ format_octal(ino & 0777777, h + c_ino_offset, c_ino_size);
+
+ /* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */
+ format_octal(archive_entry_mode(entry), h + c_mode_offset, c_mode_size);
+ format_octal(archive_entry_uid(entry), h + c_uid_offset, c_uid_size);
+ format_octal(archive_entry_gid(entry), h + c_gid_offset, c_gid_size);
+ format_octal(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size);
+ if (archive_entry_filetype(entry) == AE_IFBLK
+ || archive_entry_filetype(entry) == AE_IFCHR)
+ format_octal(archive_entry_rdev(entry), h + c_rdev_offset, c_rdev_size);
+ else
+ format_octal(0, h + c_rdev_offset, c_rdev_size);
+ format_octal(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size);
+ format_octal(pathlength, h + c_namesize_offset, c_namesize_size);
+
+ /* Non-regular files don't store bodies. */
+ if (archive_entry_filetype(entry) != AE_IFREG)
+ archive_entry_set_size(entry, 0);
+
+ /* Symlinks get the link written as the body of the entry. */
+ ret = archive_entry_symlink_l(entry, &p, &len, sconv);
+ if (ret != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ archive_entry_symlink(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret_final = ARCHIVE_WARN;
+ }
+ if (len > 0 && p != NULL && *p != '\0')
+ ret = format_octal(strlen(p), h + c_filesize_offset,
+ c_filesize_size);
+ else
+ ret = format_octal(archive_entry_size(entry),
+ h + c_filesize_offset, c_filesize_size);
+ if (ret) {
+ archive_set_error(&a->archive, ERANGE,
+ "File is too large for cpio format.");
+ ret_final = ARCHIVE_FAILED;
+ goto exit_write_header;
+ }
+
+ ret = __archive_write_output(a, h, sizeof(h));
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+
+ ret = __archive_write_output(a, path, pathlength);
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+
+ cpio->entry_bytes_remaining = archive_entry_size(entry);
+
+ /* Write the symlink now. */
+ if (p != NULL && *p != '\0') {
+ ret = __archive_write_output(a, p, strlen(p));
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ }
+exit_write_header:
+ archive_entry_free(entry_main);
+ return (ret_final);
+}
+
+static ssize_t
+archive_write_odc_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct cpio *cpio;
+ int ret;
+
+ cpio = (struct cpio *)a->format_data;
+ if (s > cpio->entry_bytes_remaining)
+ s = (size_t)cpio->entry_bytes_remaining;
+
+ ret = __archive_write_output(a, buff, s);
+ cpio->entry_bytes_remaining -= s;
+ if (ret >= 0)
+ return (s);
+ else
+ return (ret);
+}
+
+/*
+ * Format a number into the specified field.
+ */
+static int
+format_octal(int64_t v, void *p, int digits)
+{
+ int64_t max;
+ int ret;
+
+ max = (((int64_t)1) << (digits * 3)) - 1;
+ if (v >= 0 && v <= max) {
+ format_octal_recursive(v, (char *)p, digits);
+ ret = 0;
+ } else {
+ format_octal_recursive(max, (char *)p, digits);
+ ret = -1;
+ }
+ return (ret);
+}
+
+static int64_t
+format_octal_recursive(int64_t v, char *p, int s)
+{
+ if (s == 0)
+ return (v);
+ v = format_octal_recursive(v, p+1, s-1);
+ *p = '0' + ((char)v & 7);
+ return (v >> 3);
+}
+
+static int
+archive_write_odc_close(struct archive_write *a)
+{
+ int er;
+ struct archive_entry *trailer;
+
+ trailer = archive_entry_new2(NULL);
+ /* nlink = 1 here for GNU cpio compat. */
+ archive_entry_set_nlink(trailer, 1);
+ archive_entry_set_size(trailer, 0);
+ archive_entry_set_pathname(trailer, "TRAILER!!!");
+ er = write_header(a, trailer);
+ archive_entry_free(trailer);
+ return (er);
+}
+
+static int
+archive_write_odc_free(struct archive_write *a)
+{
+ struct cpio *cpio;
+
+ cpio = (struct cpio *)a->format_data;
+ free(cpio->ino_list);
+ free(cpio);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_odc_finish_entry(struct archive_write *a)
+{
+ struct cpio *cpio;
+
+ cpio = (struct cpio *)a->format_data;
+ return (__archive_write_nulls(a,
+ (size_t)cpio->entry_bytes_remaining));
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_filter_by_ext.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_filter_by_ext.c
new file mode 100644
index 00000000000..9fe21e4542a
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_filter_by_ext.c
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2015 Okhotnikov Kirill
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_by_name.c 201168 2009-12-29 06:15:32Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+/* A table that maps names to functions. */
+static const
+struct { const char *name; int (*format)(struct archive *); int (*filter)(struct archive *); } names[] =
+{
+ { ".7z", archive_write_set_format_7zip, archive_write_add_filter_none},
+ { ".zip", archive_write_set_format_zip, archive_write_add_filter_none},
+ { ".jar", archive_write_set_format_zip, archive_write_add_filter_none},
+ { ".cpio", archive_write_set_format_cpio, archive_write_add_filter_none},
+ { ".iso", archive_write_set_format_iso9660, archive_write_add_filter_none},
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
+ { ".a", archive_write_set_format_ar_bsd, archive_write_add_filter_none},
+ { ".ar", archive_write_set_format_ar_bsd, archive_write_add_filter_none},
+#else
+ { ".a", archive_write_set_format_ar_svr4, archive_write_add_filter_none},
+ { ".ar", archive_write_set_format_ar_svr4, archive_write_add_filter_none},
+#endif
+ { ".tar", archive_write_set_format_pax_restricted, archive_write_add_filter_none},
+ { ".tgz", archive_write_set_format_pax_restricted, archive_write_add_filter_gzip},
+ { ".tar.gz", archive_write_set_format_pax_restricted, archive_write_add_filter_gzip},
+ { ".tar.bz2", archive_write_set_format_pax_restricted, archive_write_add_filter_bzip2},
+ { ".tar.xz", archive_write_set_format_pax_restricted, archive_write_add_filter_xz},
+ { NULL, NULL, NULL }
+};
+
+static
+int cmpsuff(const char *str, const char *suffix)
+{
+ size_t length_str, length_suffix;
+
+ if ((str == NULL) || (suffix == NULL))
+ return -1;
+
+ length_str = strlen(str);
+ length_suffix = strlen(suffix);
+
+ if (length_str >= length_suffix) {
+ return strcmp(str + (length_str - length_suffix), suffix);
+ } else {
+ return -1;
+ }
+}
+
+static int get_array_index(const char *name)
+{
+ int i;
+
+ for (i = 0; names[i].name != NULL; i++)
+ {
+ if (cmpsuff(name, names[i].name) == 0)
+ return i;
+ }
+ return -1;
+
+}
+
+int
+archive_write_set_format_filter_by_ext(struct archive *a, const char *filename)
+{
+ int names_index = get_array_index(filename);
+
+ if (names_index >= 0)
+ {
+ int format_state = (names[names_index].format)(a);
+ if (format_state == ARCHIVE_OK)
+ return ((names[names_index].filter)(a));
+ else
+ return format_state;
+ }
+
+ archive_set_error(a, EINVAL, "No such format '%s'", filename);
+ a->state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+}
+
+int
+archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext)
+{
+ int names_index = get_array_index(filename);
+
+ if (names_index < 0)
+ names_index = get_array_index(def_ext);
+
+ if (names_index >= 0)
+ {
+ int format_state = (names[names_index].format)(a);
+ if (format_state == ARCHIVE_OK)
+ return ((names[names_index].filter)(a));
+ else
+ return format_state;
+ }
+
+ archive_set_error(a, EINVAL, "No such format '%s'", filename);
+ a->state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+}
+
+
+
+
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_gnutar.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_gnutar.c
new file mode 100644
index 00000000000..ec29c5c418e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_gnutar.c
@@ -0,0 +1,755 @@
+/*-
+ * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * Author: Jonas Gastal <jgastal@profusion.mobi>
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_gnu_tar.c 191579 2009-04-27 18:35:03Z gastal $");
+
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct gnutar {
+ uint64_t entry_bytes_remaining;
+ uint64_t entry_padding;
+ const char * linkname;
+ size_t linkname_length;
+ const char * pathname;
+ size_t pathname_length;
+ const char * uname;
+ size_t uname_length;
+ const char * gname;
+ size_t gname_length;
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_default;
+ int init_default_conversion;
+};
+
+/*
+ * Define structure of GNU tar header.
+ */
+#define GNUTAR_name_offset 0
+#define GNUTAR_name_size 100
+#define GNUTAR_mode_offset 100
+#define GNUTAR_mode_size 7
+#define GNUTAR_mode_max_size 8
+#define GNUTAR_uid_offset 108
+#define GNUTAR_uid_size 7
+#define GNUTAR_uid_max_size 8
+#define GNUTAR_gid_offset 116
+#define GNUTAR_gid_size 7
+#define GNUTAR_gid_max_size 8
+#define GNUTAR_size_offset 124
+#define GNUTAR_size_size 11
+#define GNUTAR_size_max_size 12
+#define GNUTAR_mtime_offset 136
+#define GNUTAR_mtime_size 11
+#define GNUTAR_mtime_max_size 11
+#define GNUTAR_checksum_offset 148
+#define GNUTAR_checksum_size 8
+#define GNUTAR_typeflag_offset 156
+#define GNUTAR_typeflag_size 1
+#define GNUTAR_linkname_offset 157
+#define GNUTAR_linkname_size 100
+#define GNUTAR_magic_offset 257
+#define GNUTAR_magic_size 6
+#define GNUTAR_version_offset 263
+#define GNUTAR_version_size 2
+#define GNUTAR_uname_offset 265
+#define GNUTAR_uname_size 32
+#define GNUTAR_gname_offset 297
+#define GNUTAR_gname_size 32
+#define GNUTAR_rdevmajor_offset 329
+#define GNUTAR_rdevmajor_size 6
+#define GNUTAR_rdevmajor_max_size 8
+#define GNUTAR_rdevminor_offset 337
+#define GNUTAR_rdevminor_size 6
+#define GNUTAR_rdevminor_max_size 8
+
+/*
+ * A filled-in copy of the header for initialization.
+ */
+static const char template_header[] = {
+ /* name: 100 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,
+ /* Mode, null termination: 8 bytes */
+ '0','0','0','0','0','0', '0','\0',
+ /* uid, null termination: 8 bytes */
+ '0','0','0','0','0','0', '0','\0',
+ /* gid, null termination: 8 bytes */
+ '0','0','0','0','0','0', '0','\0',
+ /* size, space termination: 12 bytes */
+ '0','0','0','0','0','0','0','0','0','0','0', '\0',
+ /* mtime, space termination: 12 bytes */
+ '0','0','0','0','0','0','0','0','0','0','0', '\0',
+ /* Initial checksum value: 8 spaces */
+ ' ',' ',' ',' ',' ',' ',' ',' ',
+ /* Typeflag: 1 byte */
+ '0', /* '0' = regular file */
+ /* Linkname: 100 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,
+ /* Magic: 8 bytes */
+ 'u','s','t','a','r',' ', ' ','\0',
+ /* Uname: 32 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ /* Gname: 32 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ /* rdevmajor + null padding: 8 bytes */
+ '\0','\0','\0','\0','\0','\0', '\0','\0',
+ /* rdevminor + null padding: 8 bytes */
+ '\0','\0','\0','\0','\0','\0', '\0','\0',
+ /* Padding: 167 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0
+};
+
+static int archive_write_gnutar_options(struct archive_write *,
+ const char *, const char *);
+static int archive_format_gnutar_header(struct archive_write *, char h[512],
+ struct archive_entry *, int tartype);
+static int archive_write_gnutar_header(struct archive_write *,
+ struct archive_entry *entry);
+static ssize_t archive_write_gnutar_data(struct archive_write *a, const void *buff,
+ size_t s);
+static int archive_write_gnutar_free(struct archive_write *);
+static int archive_write_gnutar_close(struct archive_write *);
+static int archive_write_gnutar_finish_entry(struct archive_write *);
+static int format_256(int64_t, char *, int);
+static int format_number(int64_t, char *, int size, int maxsize);
+static int format_octal(int64_t, char *, int);
+
+/*
+ * Set output format to 'GNU tar' format.
+ */
+int
+archive_write_set_format_gnutar(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct gnutar *gnutar;
+
+ gnutar = (struct gnutar *)calloc(1, sizeof(*gnutar));
+ if (gnutar == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate gnutar data");
+ return (ARCHIVE_FATAL);
+ }
+ a->format_data = gnutar;
+ a->format_name = "gnutar";
+ a->format_options = archive_write_gnutar_options;
+ a->format_write_header = archive_write_gnutar_header;
+ a->format_write_data = archive_write_gnutar_data;
+ a->format_close = archive_write_gnutar_close;
+ a->format_free = archive_write_gnutar_free;
+ a->format_finish_entry = archive_write_gnutar_finish_entry;
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR;
+ a->archive.archive_format_name = "GNU tar";
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_gnutar_options(struct archive_write *a, const char *key,
+ const char *val)
+{
+ struct gnutar *gnutar = (struct gnutar *)a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: hdrcharset option needs a character-set name",
+ a->format_name);
+ else {
+ gnutar->opt_sconv = archive_string_conversion_to_charset(
+ &a->archive, val, 0);
+ if (gnutar->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_gnutar_close(struct archive_write *a)
+{
+ return (__archive_write_nulls(a, 512*2));
+}
+
+static int
+archive_write_gnutar_free(struct archive_write *a)
+{
+ struct gnutar *gnutar;
+
+ gnutar = (struct gnutar *)a->format_data;
+ free(gnutar);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_gnutar_finish_entry(struct archive_write *a)
+{
+ struct gnutar *gnutar;
+ int ret;
+
+ gnutar = (struct gnutar *)a->format_data;
+ ret = __archive_write_nulls(a, (size_t)
+ (gnutar->entry_bytes_remaining + gnutar->entry_padding));
+ gnutar->entry_bytes_remaining = gnutar->entry_padding = 0;
+ return (ret);
+}
+
+static ssize_t
+archive_write_gnutar_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct gnutar *gnutar;
+ int ret;
+
+ gnutar = (struct gnutar *)a->format_data;
+ if (s > gnutar->entry_bytes_remaining)
+ s = (size_t)gnutar->entry_bytes_remaining;
+ ret = __archive_write_output(a, buff, s);
+ gnutar->entry_bytes_remaining -= s;
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ return (s);
+}
+
+static int
+archive_write_gnutar_header(struct archive_write *a,
+ struct archive_entry *entry)
+{
+ char buff[512];
+ int r, ret, ret2 = ARCHIVE_OK;
+ int tartype;
+ struct gnutar *gnutar;
+ struct archive_string_conv *sconv;
+ struct archive_entry *entry_main;
+
+ gnutar = (struct gnutar *)a->format_data;
+
+ /* Setup default string conversion. */
+ if (gnutar->opt_sconv == NULL) {
+ if (!gnutar->init_default_conversion) {
+ gnutar->sconv_default =
+ archive_string_default_conversion_for_write(
+ &(a->archive));
+ gnutar->init_default_conversion = 1;
+ }
+ sconv = gnutar->sconv_default;
+ } else
+ sconv = gnutar->opt_sconv;
+
+ /* Only regular files (not hardlinks) have data. */
+ if (archive_entry_hardlink(entry) != NULL ||
+ archive_entry_symlink(entry) != NULL ||
+ !(archive_entry_filetype(entry) == AE_IFREG))
+ archive_entry_set_size(entry, 0);
+
+ if (AE_IFDIR == archive_entry_filetype(entry)) {
+ const char *p;
+ size_t path_length;
+ /*
+ * Ensure a trailing '/'. Modify the entry so
+ * the client sees the change.
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ const wchar_t *wp;
+
+ wp = archive_entry_pathname_w(entry);
+ if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
+ struct archive_wstring ws;
+
+ archive_string_init(&ws);
+ path_length = wcslen(wp);
+ if (archive_wstring_ensure(&ws,
+ path_length + 2) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ archive_wstring_free(&ws);
+ return(ARCHIVE_FATAL);
+ }
+ /* Should we keep '\' ? */
+ if (wp[path_length -1] == L'\\')
+ path_length--;
+ archive_wstrncpy(&ws, wp, path_length);
+ archive_wstrappend_wchar(&ws, L'/');
+ archive_entry_copy_pathname_w(entry, ws.s);
+ archive_wstring_free(&ws);
+ p = NULL;
+ } else
+#endif
+ p = archive_entry_pathname(entry);
+ /*
+ * On Windows, this is a backup operation just in
+ * case getting WCS failed. On POSIX, this is a
+ * normal operation.
+ */
+ if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {
+ struct archive_string as;
+
+ archive_string_init(&as);
+ path_length = strlen(p);
+ if (archive_string_ensure(&as,
+ path_length + 2) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ archive_string_free(&as);
+ return(ARCHIVE_FATAL);
+ }
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* NOTE: This might break the pathname
+ * if the current code page is CP932 and
+ * the pathname includes a character '\'
+ * as a part of its multibyte pathname. */
+ if (p[strlen(p) -1] == '\\')
+ path_length--;
+ else
+#endif
+ archive_strncpy(&as, p, path_length);
+ archive_strappend_char(&as, '/');
+ archive_entry_copy_pathname(entry, as.s);
+ archive_string_free(&as);
+ }
+ }
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Make sure the path separators in pathname, hardlink and symlink
+ * are all slash '/', not the Windows path separator '\'. */
+ entry_main = __la_win_entry_in_posix_pathseparator(entry);
+ if (entry_main == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ return(ARCHIVE_FATAL);
+ }
+ if (entry != entry_main)
+ entry = entry_main;
+ else
+ entry_main = NULL;
+#else
+ entry_main = NULL;
+#endif
+ r = archive_entry_pathname_l(entry, &(gnutar->pathname),
+ &(gnutar->pathname_length), sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathame");
+ ret = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s",
+ archive_entry_pathname(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret2 = ARCHIVE_WARN;
+ }
+ r = archive_entry_uname_l(entry, &(gnutar->uname),
+ &(gnutar->uname_length), sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Uname");
+ ret = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate uname '%s' to %s",
+ archive_entry_uname(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret2 = ARCHIVE_WARN;
+ }
+ r = archive_entry_gname_l(entry, &(gnutar->gname),
+ &(gnutar->gname_length), sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Gname");
+ ret = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate gname '%s' to %s",
+ archive_entry_gname(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret2 = ARCHIVE_WARN;
+ }
+
+ /* If linkname is longer than 100 chars we need to add a 'K' header. */
+ r = archive_entry_hardlink_l(entry, &(gnutar->linkname),
+ &(gnutar->linkname_length), sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ ret = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ archive_entry_hardlink(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret2 = ARCHIVE_WARN;
+ }
+ if (gnutar->linkname_length == 0) {
+ r = archive_entry_symlink_l(entry, &(gnutar->linkname),
+ &(gnutar->linkname_length), sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ ret = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ archive_entry_hardlink(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret2 = ARCHIVE_WARN;
+ }
+ }
+ if (gnutar->linkname_length > GNUTAR_linkname_size) {
+ size_t length = gnutar->linkname_length + 1;
+ struct archive_entry *temp = archive_entry_new2(&a->archive);
+
+ /* Uname/gname here don't really matter since no one reads them;
+ * these are the values that GNU tar happens to use on FreeBSD. */
+ archive_entry_set_uname(temp, "root");
+ archive_entry_set_gname(temp, "wheel");
+
+ archive_entry_set_pathname(temp, "././@LongLink");
+ archive_entry_set_size(temp, length);
+ ret = archive_format_gnutar_header(a, buff, temp, 'K');
+ archive_entry_free(temp);
+ if (ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ ret = __archive_write_output(a, buff, 512);
+ if (ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ /* Write name and trailing null byte. */
+ ret = __archive_write_output(a, gnutar->linkname, length);
+ if (ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ /* Pad to 512 bytes */
+ ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length));
+ if (ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ }
+
+ /* If pathname is longer than 100 chars we need to add an 'L' header. */
+ if (gnutar->pathname_length > GNUTAR_name_size) {
+ const char *pathname = gnutar->pathname;
+ size_t length = gnutar->pathname_length + 1;
+ struct archive_entry *temp = archive_entry_new2(&a->archive);
+
+ /* Uname/gname here don't really matter since no one reads them;
+ * these are the values that GNU tar happens to use on FreeBSD. */
+ archive_entry_set_uname(temp, "root");
+ archive_entry_set_gname(temp, "wheel");
+
+ archive_entry_set_pathname(temp, "././@LongLink");
+ archive_entry_set_size(temp, length);
+ ret = archive_format_gnutar_header(a, buff, temp, 'L');
+ archive_entry_free(temp);
+ if (ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ ret = __archive_write_output(a, buff, 512);
+ if(ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ /* Write pathname + trailing null byte. */
+ ret = __archive_write_output(a, pathname, length);
+ if(ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ /* Pad to multiple of 512 bytes. */
+ ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length));
+ if (ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ }
+
+ if (archive_entry_hardlink(entry) != NULL) {
+ tartype = '1';
+ } else
+ switch (archive_entry_filetype(entry)) {
+ case AE_IFREG: tartype = '0' ; break;
+ case AE_IFLNK: tartype = '2' ; break;
+ case AE_IFCHR: tartype = '3' ; break;
+ case AE_IFBLK: tartype = '4' ; break;
+ case AE_IFDIR: tartype = '5' ; break;
+ case AE_IFIFO: tartype = '6' ; break;
+ default: /* AE_IFSOCK and unknown */
+ __archive_write_entry_filetype_unsupported(
+ &a->archive, entry, "gnutar");
+ ret = ARCHIVE_FAILED;
+ goto exit_write_header;
+ }
+
+ ret = archive_format_gnutar_header(a, buff, entry, tartype);
+ if (ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ if (ret2 < ret)
+ ret = ret2;
+ ret2 = __archive_write_output(a, buff, 512);
+ if (ret2 < ARCHIVE_WARN) {
+ ret = ret2;
+ goto exit_write_header;
+ }
+ if (ret2 < ret)
+ ret = ret2;
+
+ gnutar->entry_bytes_remaining = archive_entry_size(entry);
+ gnutar->entry_padding = 0x1ff & (-(int64_t)gnutar->entry_bytes_remaining);
+exit_write_header:
+ archive_entry_free(entry_main);
+ return (ret);
+}
+
+static int
+archive_format_gnutar_header(struct archive_write *a, char h[512],
+ struct archive_entry *entry, int tartype)
+{
+ unsigned int checksum;
+ int i, ret;
+ size_t copy_length;
+ const char *p;
+ struct gnutar *gnutar;
+
+ gnutar = (struct gnutar *)a->format_data;
+
+ ret = 0;
+
+ /*
+ * The "template header" already includes the signature,
+ * various end-of-field markers, and other required elements.
+ */
+ memcpy(h, &template_header, 512);
+
+ /*
+ * Because the block is already null-filled, and strings
+ * are allowed to exactly fill their destination (without null),
+ * I use memcpy(dest, src, strlen()) here a lot to copy strings.
+ */
+
+ if (tartype == 'K' || tartype == 'L') {
+ p = archive_entry_pathname(entry);
+ copy_length = strlen(p);
+ } else {
+ p = gnutar->pathname;
+ copy_length = gnutar->pathname_length;
+ }
+ if (copy_length > GNUTAR_name_size)
+ copy_length = GNUTAR_name_size;
+ memcpy(h + GNUTAR_name_offset, p, copy_length);
+
+ if ((copy_length = gnutar->linkname_length) > 0) {
+ if (copy_length > GNUTAR_linkname_size)
+ copy_length = GNUTAR_linkname_size;
+ memcpy(h + GNUTAR_linkname_offset, gnutar->linkname,
+ copy_length);
+ }
+
+ /* TODO: How does GNU tar handle unames longer than GNUTAR_uname_size? */
+ if (tartype == 'K' || tartype == 'L') {
+ p = archive_entry_uname(entry);
+ copy_length = strlen(p);
+ } else {
+ p = gnutar->uname;
+ copy_length = gnutar->uname_length;
+ }
+ if (copy_length > 0) {
+ if (copy_length > GNUTAR_uname_size)
+ copy_length = GNUTAR_uname_size;
+ memcpy(h + GNUTAR_uname_offset, p, copy_length);
+ }
+
+ /* TODO: How does GNU tar handle gnames longer than GNUTAR_gname_size? */
+ if (tartype == 'K' || tartype == 'L') {
+ p = archive_entry_gname(entry);
+ copy_length = strlen(p);
+ } else {
+ p = gnutar->gname;
+ copy_length = gnutar->gname_length;
+ }
+ if (copy_length > 0) {
+ if (strlen(p) > GNUTAR_gname_size)
+ copy_length = GNUTAR_gname_size;
+ memcpy(h + GNUTAR_gname_offset, p, copy_length);
+ }
+
+ /* By truncating the mode here, we ensure it always fits. */
+ format_octal(archive_entry_mode(entry) & 07777,
+ h + GNUTAR_mode_offset, GNUTAR_mode_size);
+
+ /* GNU tar supports base-256 here, so should never overflow. */
+ if (format_number(archive_entry_uid(entry), h + GNUTAR_uid_offset,
+ GNUTAR_uid_size, GNUTAR_uid_max_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric user ID %jd too large",
+ (intmax_t)archive_entry_uid(entry));
+ ret = ARCHIVE_FAILED;
+ }
+
+ /* GNU tar supports base-256 here, so should never overflow. */
+ if (format_number(archive_entry_gid(entry), h + GNUTAR_gid_offset,
+ GNUTAR_gid_size, GNUTAR_gid_max_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric group ID %jd too large",
+ (intmax_t)archive_entry_gid(entry));
+ ret = ARCHIVE_FAILED;
+ }
+
+ /* GNU tar supports base-256 here, so should never overflow. */
+ if (format_number(archive_entry_size(entry), h + GNUTAR_size_offset,
+ GNUTAR_size_size, GNUTAR_size_max_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File size out of range");
+ ret = ARCHIVE_FAILED;
+ }
+
+ /* Shouldn't overflow before 2106, since mtime field is 33 bits. */
+ format_octal(archive_entry_mtime(entry),
+ h + GNUTAR_mtime_offset, GNUTAR_mtime_size);
+
+ if (archive_entry_filetype(entry) == AE_IFBLK
+ || archive_entry_filetype(entry) == AE_IFCHR) {
+ if (format_octal(archive_entry_rdevmajor(entry),
+ h + GNUTAR_rdevmajor_offset,
+ GNUTAR_rdevmajor_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Major device number too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_octal(archive_entry_rdevminor(entry),
+ h + GNUTAR_rdevminor_offset,
+ GNUTAR_rdevminor_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Minor device number too large");
+ ret = ARCHIVE_FAILED;
+ }
+ }
+
+ h[GNUTAR_typeflag_offset] = tartype;
+
+ checksum = 0;
+ for (i = 0; i < 512; i++)
+ checksum += 255 & (unsigned int)h[i];
+ h[GNUTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */
+ /* h[GNUTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */
+ format_octal(checksum, h + GNUTAR_checksum_offset, 6);
+ return (ret);
+}
+
+/*
+ * Format a number into a field, falling back to base-256 if necessary.
+ */
+static int
+format_number(int64_t v, char *p, int s, int maxsize)
+{
+ int64_t limit = ((int64_t)1 << (s*3));
+
+ if (v < limit)
+ return (format_octal(v, p, s));
+ return (format_256(v, p, maxsize));
+}
+
+/*
+ * Format a number into the specified field using base-256.
+ */
+static int
+format_256(int64_t v, char *p, int s)
+{
+ p += s;
+ while (s-- > 0) {
+ *--p = (char)(v & 0xff);
+ v >>= 8;
+ }
+ *p |= 0x80; /* Set the base-256 marker bit. */
+ return (0);
+}
+
+/*
+ * Format a number into the specified field using octal.
+ */
+static int
+format_octal(int64_t v, char *p, int s)
+{
+ int len = s;
+
+ /* Octal values can't be negative, so use 0. */
+ if (v < 0)
+ v = 0;
+
+ p += s; /* Start at the end and work backwards. */
+ while (s-- > 0) {
+ *--p = (char)('0' + (v & 7));
+ v >>= 3;
+ }
+
+ if (v == 0)
+ return (0);
+
+ /* If it overflowed, fill field with max value. */
+ while (len-- > 0)
+ *p++ = '7';
+
+ return (-1);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_iso9660.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_iso9660.c
new file mode 100644
index 00000000000..2a3ae07fa2b
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_iso9660.c
@@ -0,0 +1,8161 @@
+/*-
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <time.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_write_private.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#define getuid() 0
+#define getgid() 0
+#endif
+
+/*#define DEBUG 1*/
+#ifdef DEBUG
+/* To compare to the ISO image file made by mkisofs. */
+#define COMPAT_MKISOFS 1
+#endif
+
+#define LOGICAL_BLOCK_BITS 11
+#define LOGICAL_BLOCK_SIZE 2048
+#define PATH_TABLE_BLOCK_SIZE 4096
+
+#define SYSTEM_AREA_BLOCK 16
+#define PRIMARY_VOLUME_DESCRIPTOR_BLOCK 1
+#define SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK 1
+#define BOOT_RECORD_DESCRIPTOR_BLOCK 1
+#define VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK 1
+#define NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK 1
+#define RRIP_ER_BLOCK 1
+#define PADDING_BLOCK 150
+
+#define FD_1_2M_SIZE (1024 * 1200)
+#define FD_1_44M_SIZE (1024 * 1440)
+#define FD_2_88M_SIZE (1024 * 2880)
+#define MULTI_EXTENT_SIZE (ARCHIVE_LITERAL_LL(1) << 32) /* 4Gi bytes. */
+#define MAX_DEPTH 8
+#define RR_CE_SIZE 28 /* SUSP "CE" extension size */
+
+#define FILE_FLAG_EXISTENCE 0x01
+#define FILE_FLAG_DIRECTORY 0x02
+#define FILE_FLAG_ASSOCIATED 0x04
+#define FILE_FLAG_RECORD 0x08
+#define FILE_FLAG_PROTECTION 0x10
+#define FILE_FLAG_MULTI_EXTENT 0x80
+
+static const char rrip_identifier[] =
+ "RRIP_1991A";
+static const char rrip_descriptor[] =
+ "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR "
+ "POSIX FILE SYSTEM SEMANTICS";
+static const char rrip_source[] =
+ "PLEASE CONTACT DISC PUBLISHER FOR SPECIFICATION SOURCE. "
+ "SEE PUBLISHER IDENTIFIER IN PRIMARY VOLUME DESCRIPTOR FOR "
+ "CONTACT INFORMATION.";
+#define RRIP_ER_ID_SIZE (sizeof(rrip_identifier)-1)
+#define RRIP_ER_DSC_SIZE (sizeof(rrip_descriptor)-1)
+#define RRIP_ER_SRC_SIZE (sizeof(rrip_source)-1)
+#define RRIP_ER_SIZE (8 + RRIP_ER_ID_SIZE + \
+ RRIP_ER_DSC_SIZE + RRIP_ER_SRC_SIZE)
+
+static const unsigned char zisofs_magic[8] = {
+ 0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07
+};
+
+#define ZF_HEADER_SIZE 16 /* zisofs header size. */
+#define ZF_LOG2_BS 15 /* log2 block size; 32K bytes. */
+#define ZF_BLOCK_SIZE (1UL << ZF_LOG2_BS)
+
+/*
+ * Manage extra records.
+ */
+struct extr_rec {
+ int location;
+ int offset;
+ unsigned char buf[LOGICAL_BLOCK_SIZE];
+ struct extr_rec *next;
+};
+
+struct ctl_extr_rec {
+ int use_extr;
+ unsigned char *bp;
+ struct isoent *isoent;
+ unsigned char *ce_ptr;
+ int cur_len;
+ int dr_len;
+ int limit;
+ int extr_off;
+ int extr_loc;
+};
+#define DR_SAFETY RR_CE_SIZE
+#define DR_LIMIT (254 - DR_SAFETY)
+
+/*
+ * The relation of struct isofile and isoent and archive_entry.
+ *
+ * Primary volume tree --> struct isoent
+ * |
+ * v
+ * struct isofile --> archive_entry
+ * ^
+ * |
+ * Joliet volume tree --> struct isoent
+ *
+ * struct isoent has specific information for volume.
+ */
+
+struct isofile {
+ /* Used for managing struct isofile list. */
+ struct isofile *allnext;
+ struct isofile *datanext;
+ /* Used for managing a hardlinked struct isofile list. */
+ struct isofile *hlnext;
+ struct isofile *hardlink_target;
+
+ struct archive_entry *entry;
+
+ /*
+ * Used for making a directory tree.
+ */
+ struct archive_string parentdir;
+ struct archive_string basename;
+ struct archive_string basename_utf16;
+ struct archive_string symlink;
+ int dircnt; /* The number of elements of
+ * its parent directory */
+
+ /*
+ * Used for a Directory Record.
+ */
+ struct content {
+ int64_t offset_of_temp;
+ int64_t size;
+ int blocks;
+ uint32_t location;
+ /*
+ * One extent equals one content.
+ * If this entry has multi extent, `next' variable points
+ * next content data.
+ */
+ struct content *next; /* next content */
+ } content, *cur_content;
+ int write_content;
+
+ enum {
+ NO = 0,
+ BOOT_CATALOG,
+ BOOT_IMAGE
+ } boot;
+
+ /*
+ * Used for a zisofs.
+ */
+ struct {
+ unsigned char header_size;
+ unsigned char log2_bs;
+ uint32_t uncompressed_size;
+ } zisofs;
+};
+
+struct isoent {
+ /* Keep `rbnode' at the first member of struct isoent. */
+ struct archive_rb_node rbnode;
+
+ struct isofile *file;
+
+ struct isoent *parent;
+ /* A list of children.(use chnext) */
+ struct {
+ struct isoent *first;
+ struct isoent **last;
+ int cnt;
+ } children;
+ struct archive_rb_tree rbtree;
+
+ /* A list of sub directories.(use drnext) */
+ struct {
+ struct isoent *first;
+ struct isoent **last;
+ int cnt;
+ } subdirs;
+ /* A sorted list of sub directories. */
+ struct isoent **children_sorted;
+ /* Used for managing struct isoent list. */
+ struct isoent *chnext;
+ struct isoent *drnext;
+ struct isoent *ptnext;
+
+ /*
+ * Used for making a Directory Record.
+ */
+ int dir_number;
+ struct {
+ int vd;
+ int self;
+ int parent;
+ int normal;
+ } dr_len;
+ uint32_t dir_location;
+ int dir_block;
+
+ /*
+ * Identifier:
+ * on primary, ISO9660 file/directory name.
+ * on joliet, UCS2 file/directory name.
+ * ext_off : offset of identifier extension.
+ * ext_len : length of identifier extension.
+ * id_len : byte size of identifier.
+ * on primary, this is ext_off + ext_len + version length.
+ * on joliet, this is ext_off + ext_len.
+ * mb_len : length of multibyte-character of identifier.
+ * on primary, mb_len and id_len are always the same.
+ * on joliet, mb_len and id_len are different.
+ */
+ char *identifier;
+ int ext_off;
+ int ext_len;
+ int id_len;
+ int mb_len;
+
+ /*
+ * Used for making a Rockridge extension.
+ * This is a part of Directory Records.
+ */
+ struct isoent *rr_parent;
+ struct isoent *rr_child;
+
+ /* Extra Record.(which we call in this source file)
+ * A maximum size of the Directory Record is 254.
+ * so, if generated RRIP data of a file cannot into a Directory
+ * Record because of its size, that surplus data relocate this
+ * Extra Record.
+ */
+ struct {
+ struct extr_rec *first;
+ struct extr_rec **last;
+ struct extr_rec *current;
+ } extr_rec_list;
+
+ unsigned int virtual:1;
+ /* If set to one, this file type is a directory.
+ * A convenience flag to be used as
+ * "archive_entry_filetype(isoent->file->entry) == AE_IFDIR".
+ */
+ unsigned int dir:1;
+};
+
+struct hardlink {
+ struct archive_rb_node rbnode;
+ int nlink;
+ struct {
+ struct isofile *first;
+ struct isofile **last;
+ } file_list;
+};
+
+/*
+ * ISO writer options
+ */
+struct iso_option {
+ /*
+ * Usage : abstract-file=<value>
+ * Type : string, max 37 bytes
+ * Default: Not specified
+ * COMPAT : mkisofs -abstract <value>
+ *
+ * Specifies Abstract Filename.
+ * This file shall be described in the Root Directory
+ * and containing a abstract statement.
+ */
+ unsigned int abstract_file:1;
+#define OPT_ABSTRACT_FILE_DEFAULT 0 /* Not specified */
+#define ABSTRACT_FILE_SIZE 37
+
+ /*
+ * Usage : application-id=<value>
+ * Type : string, max 128 bytes
+ * Default: Not specified
+ * COMPAT : mkisofs -A/-appid <value>.
+ *
+ * Specifies Application Identifier.
+ * If the first byte is set to '_'(5F), the remaining
+ * bytes of this option shall specify an identifier
+ * for a file containing the identification of the
+ * application.
+ * This file shall be described in the Root Directory.
+ */
+ unsigned int application_id:1;
+#define OPT_APPLICATION_ID_DEFAULT 0 /* Use default identifier */
+#define APPLICATION_IDENTIFIER_SIZE 128
+
+ /*
+ * Usage : !allow-vernum
+ * Type : boolean
+ * Default: Enabled
+ * : Violates the ISO9660 standard if disable.
+ * COMPAT: mkisofs -N
+ *
+ * Allow filenames to use version numbers.
+ */
+ unsigned int allow_vernum:1;
+#define OPT_ALLOW_VERNUM_DEFAULT 1 /* Enabled */
+
+ /*
+ * Usage : biblio-file=<value>
+ * Type : string, max 37 bytes
+ * Default: Not specified
+ * COMPAT : mkisofs -biblio <value>
+ *
+ * Specifies Bibliographic Filename.
+ * This file shall be described in the Root Directory
+ * and containing bibliographic records.
+ */
+ unsigned int biblio_file:1;
+#define OPT_BIBLIO_FILE_DEFAULT 0 /* Not specified */
+#define BIBLIO_FILE_SIZE 37
+
+ /*
+ * Usage : boot=<value>
+ * Type : string
+ * Default: Not specified
+ * COMPAT : mkisofs -b/-eltorito-boot <value>
+ *
+ * Specifies "El Torito" boot image file to make
+ * a bootable CD.
+ */
+ unsigned int boot:1;
+#define OPT_BOOT_DEFAULT 0 /* Not specified */
+
+ /*
+ * Usage : boot-catalog=<value>
+ * Type : string
+ * Default: "boot.catalog"
+ * COMPAT : mkisofs -c/-eltorito-catalog <value>
+ *
+ * Specifies a fullpath of El Torito boot catalog.
+ */
+ unsigned int boot_catalog:1;
+#define OPT_BOOT_CATALOG_DEFAULT 0 /* Not specified */
+
+ /*
+ * Usage : boot-info-table
+ * Type : boolean
+ * Default: Disabled
+ * COMPAT : mkisofs -boot-info-table
+ *
+ * Modify the boot image file specified by `boot'
+ * option; ISO writer stores boot file information
+ * into the boot file in ISO image at offset 8
+ * through offset 64.
+ */
+ unsigned int boot_info_table:1;
+#define OPT_BOOT_INFO_TABLE_DEFAULT 0 /* Disabled */
+
+ /*
+ * Usage : boot-load-seg=<value>
+ * Type : hexadecimal
+ * Default: Not specified
+ * COMPAT : mkisofs -boot-load-seg <value>
+ *
+ * Specifies a load segment for boot image.
+ * This is used with no-emulation mode.
+ */
+ unsigned int boot_load_seg:1;
+#define OPT_BOOT_LOAD_SEG_DEFAULT 0 /* Not specified */
+
+ /*
+ * Usage : boot-load-size=<value>
+ * Type : decimal
+ * Default: Not specified
+ * COMPAT : mkisofs -boot-load-size <value>
+ *
+ * Specifies a sector count for boot image.
+ * This is used with no-emulation mode.
+ */
+ unsigned int boot_load_size:1;
+#define OPT_BOOT_LOAD_SIZE_DEFAULT 0 /* Not specified */
+
+ /*
+ * Usage : boot-type=<boot-media-type>
+ * : 'no-emulation' : 'no emulation' image
+ * : 'fd' : floppy disk image
+ * : 'hard-disk' : hard disk image
+ * Type : string
+ * Default: Auto detect
+ * : We check a size of boot image;
+ * : If the size is just 1.22M/1.44M/2.88M,
+ * : we assume boot_type is 'fd';
+ * : otherwise boot_type is 'no-emulation'.
+ * COMPAT :
+ * boot=no-emulation
+ * mkisofs -no-emul-boot
+ * boot=fd
+ * This is a default on the mkisofs.
+ * boot=hard-disk
+ * mkisofs -hard-disk-boot
+ *
+ * Specifies a type of "El Torito" boot image.
+ */
+ unsigned int boot_type:2;
+#define OPT_BOOT_TYPE_AUTO 0 /* auto detect */
+#define OPT_BOOT_TYPE_NO_EMU 1 /* ``no emulation'' image */
+#define OPT_BOOT_TYPE_FD 2 /* floppy disk image */
+#define OPT_BOOT_TYPE_HARD_DISK 3 /* hard disk image */
+#define OPT_BOOT_TYPE_DEFAULT OPT_BOOT_TYPE_AUTO
+
+ /*
+ * Usage : compression-level=<value>
+ * Type : decimal
+ * Default: Not specified
+ * COMPAT : NONE
+ *
+ * Specifies compression level for option zisofs=direct.
+ */
+ unsigned int compression_level:1;
+#define OPT_COMPRESSION_LEVEL_DEFAULT 0 /* Not specified */
+
+ /*
+ * Usage : copyright-file=<value>
+ * Type : string, max 37 bytes
+ * Default: Not specified
+ * COMPAT : mkisofs -copyright <value>
+ *
+ * Specifies Copyright Filename.
+ * This file shall be described in the Root Directory
+ * and containing a copyright statement.
+ */
+ unsigned int copyright_file:1;
+#define OPT_COPYRIGHT_FILE_DEFAULT 0 /* Not specified */
+#define COPYRIGHT_FILE_SIZE 37
+
+ /*
+ * Usage : gid=<value>
+ * Type : decimal
+ * Default: Not specified
+ * COMPAT : mkisofs -gid <value>
+ *
+ * Specifies a group id to rewrite the group id of all files.
+ */
+ unsigned int gid:1;
+#define OPT_GID_DEFAULT 0 /* Not specified */
+
+ /*
+ * Usage : iso-level=[1234]
+ * Type : decimal
+ * Default: 1
+ * COMPAT : mkisofs -iso-level <value>
+ *
+ * Specifies ISO9600 Level.
+ * Level 1: [DEFAULT]
+ * - limits each file size less than 4Gi bytes;
+ * - a File Name shall not contain more than eight
+ * d-characters or eight d1-characters;
+ * - a File Name Extension shall not contain more than
+ * three d-characters or three d1-characters;
+ * - a Directory Identifier shall not contain more
+ * than eight d-characters or eight d1-characters.
+ * Level 2:
+ * - limits each file size less than 4Giga bytes;
+ * - a File Name shall not contain more than thirty
+ * d-characters or thirty d1-characters;
+ * - a File Name Extension shall not contain more than
+ * thirty d-characters or thirty d1-characters;
+ * - a Directory Identifier shall not contain more
+ * than thirty-one d-characters or thirty-one
+ * d1-characters.
+ * Level 3:
+ * - no limit of file size; use multi extent.
+ * Level 4:
+ * - this level 4 simulates mkisofs option
+ * '-iso-level 4';
+ * - crate a enhanced volume as mkisofs doing;
+ * - allow a File Name to have leading dot;
+ * - allow a File Name to have all ASCII letters;
+ * - allow a File Name to have multiple dots;
+ * - allow more then 8 depths of directory trees;
+ * - disable a version number to a File Name;
+ * - disable a forced period to the tail of a File Name;
+ * - the maximum length of files and directories is raised to 193.
+ * if rockridge option is disabled, raised to 207.
+ */
+ unsigned int iso_level:3;
+#define OPT_ISO_LEVEL_DEFAULT 1 /* ISO Level 1 */
+
+ /*
+ * Usage : joliet[=long]
+ * : !joliet
+ * : Do not generate Joliet Volume and Records.
+ * : joliet [DEFAULT]
+ * : Generates Joliet Volume and Directory Records.
+ * : [COMPAT: mkisofs -J/-joliet]
+ * : joliet=long
+ * : The joliet filenames are up to 103 Unicode
+ * : characters.
+ * : This option breaks the Joliet specification.
+ * : [COMPAT: mkisofs -J -joliet-long]
+ * Type : boolean/string
+ * Default: Enabled
+ * COMPAT : mkisofs -J / -joliet-long
+ *
+ * Generates Joliet Volume and Directory Records.
+ */
+ unsigned int joliet:2;
+#define OPT_JOLIET_DISABLE 0 /* Not generate Joliet Records. */
+#define OPT_JOLIET_ENABLE 1 /* Generate Joliet Records. */
+#define OPT_JOLIET_LONGNAME 2 /* Use long joliet filenames.*/
+#define OPT_JOLIET_DEFAULT OPT_JOLIET_ENABLE
+
+ /*
+ * Usage : !limit-depth
+ * Type : boolean
+ * Default: Enabled
+ * : Violates the ISO9660 standard if disable.
+ * COMPAT : mkisofs -D/-disable-deep-relocation
+ *
+ * The number of levels in hierarchy cannot exceed eight.
+ */
+ unsigned int limit_depth:1;
+#define OPT_LIMIT_DEPTH_DEFAULT 1 /* Enabled */
+
+ /*
+ * Usage : !limit-dirs
+ * Type : boolean
+ * Default: Enabled
+ * : Violates the ISO9660 standard if disable.
+ * COMPAT : mkisofs -no-limit-pathtables
+ *
+ * Limits the number of directories less than 65536 due
+ * to the size of the Parent Directory Number of Path
+ * Table.
+ */
+ unsigned int limit_dirs:1;
+#define OPT_LIMIT_DIRS_DEFAULT 1 /* Enabled */
+
+ /*
+ * Usage : !pad
+ * Type : boolean
+ * Default: Enabled
+ * COMPAT : -pad/-no-pad
+ *
+ * Pads the end of the ISO image by null of 300Ki bytes.
+ */
+ unsigned int pad:1;
+#define OPT_PAD_DEFAULT 1 /* Enabled */
+
+ /*
+ * Usage : publisher=<value>
+ * Type : string, max 128 bytes
+ * Default: Not specified
+ * COMPAT : mkisofs -publisher <value>
+ *
+ * Specifies Publisher Identifier.
+ * If the first byte is set to '_'(5F), the remaining
+ * bytes of this option shall specify an identifier
+ * for a file containing the identification of the user.
+ * This file shall be described in the Root Directory.
+ */
+ unsigned int publisher:1;
+#define OPT_PUBLISHER_DEFAULT 0 /* Not specified */
+#define PUBLISHER_IDENTIFIER_SIZE 128
+
+ /*
+ * Usage : rockridge
+ * : !rockridge
+ * : disable to generate SUSP and RR records.
+ * : rockridge
+ * : the same as 'rockridge=useful'.
+ * : rockridge=strict
+ * : generate SUSP and RR records.
+ * : [COMPAT: mkisofs -R]
+ * : rockridge=useful [DEFAULT]
+ * : generate SUSP and RR records.
+ * : [COMPAT: mkisofs -r]
+ * : NOTE Our rockridge=useful option does not set a zero
+ * : to uid and gid, you should use application
+ * : option such as --gid,--gname,--uid and --uname
+ * : bsdtar options instead.
+ * Type : boolean/string
+ * Default: Enabled as rockridge=useful
+ * COMPAT : mkisofs -r / -R
+ *
+ * Generates SUSP and RR records.
+ */
+ unsigned int rr:2;
+#define OPT_RR_DISABLED 0
+#define OPT_RR_STRICT 1
+#define OPT_RR_USEFUL 2
+#define OPT_RR_DEFAULT OPT_RR_USEFUL
+
+ /*
+ * Usage : volume-id=<value>
+ * Type : string, max 32 bytes
+ * Default: Not specified
+ * COMPAT : mkisofs -V <value>
+ *
+ * Specifies Volume Identifier.
+ */
+ unsigned int volume_id:1;
+#define OPT_VOLUME_ID_DEFAULT 0 /* Use default identifier */
+#define VOLUME_IDENTIFIER_SIZE 32
+
+ /*
+ * Usage : !zisofs [DEFAULT]
+ * : Disable to generate RRIP 'ZF' extension.
+ * : zisofs
+ * : Make files zisofs file and generate RRIP 'ZF'
+ * : extension. So you do not need mkzftree utility
+ * : for making zisofs.
+ * : When the file size is less than one Logical Block
+ * : size, that file will not zisofs'ed since it does
+ * : reduce an ISO-image size.
+ * :
+ * : When you specify option 'boot=<boot-image>', that
+ * : 'boot-image' file won't be converted to zisofs file.
+ * Type : boolean
+ * Default: Disabled
+ *
+ * Generates RRIP 'ZF' System Use Entry.
+ */
+ unsigned int zisofs:1;
+#define OPT_ZISOFS_DISABLED 0
+#define OPT_ZISOFS_DIRECT 1
+#define OPT_ZISOFS_DEFAULT OPT_ZISOFS_DISABLED
+
+};
+
+struct iso9660 {
+ /* The creation time of ISO image. */
+ time_t birth_time;
+ /* A file stream of a temporary file, which file contents
+ * save to until ISO image can be created. */
+ int temp_fd;
+
+ struct isofile *cur_file;
+ struct isoent *cur_dirent;
+ struct archive_string cur_dirstr;
+ uint64_t bytes_remaining;
+ int need_multi_extent;
+
+ /* Temporary string buffer for Joliet extension. */
+ struct archive_string utf16be;
+ struct archive_string mbs;
+
+ struct archive_string_conv *sconv_to_utf16be;
+ struct archive_string_conv *sconv_from_utf16be;
+
+ /* A list of all of struct isofile entries. */
+ struct {
+ struct isofile *first;
+ struct isofile **last;
+ } all_file_list;
+
+ /* A list of struct isofile entries which have its
+ * contents and are not a directory, a hardlinked file
+ * and a symlink file. */
+ struct {
+ struct isofile *first;
+ struct isofile **last;
+ } data_file_list;
+
+ /* Used for managing to find hardlinking files. */
+ struct archive_rb_tree hardlink_rbtree;
+
+ /* Used for making the Path Table Record. */
+ struct vdd {
+ /* the root of entry tree. */
+ struct isoent *rootent;
+ enum vdd_type {
+ VDD_PRIMARY,
+ VDD_JOLIET,
+ VDD_ENHANCED
+ } vdd_type;
+
+ struct path_table {
+ struct isoent *first;
+ struct isoent **last;
+ struct isoent **sorted;
+ int cnt;
+ } *pathtbl;
+ int max_depth;
+
+ int path_table_block;
+ int path_table_size;
+ int location_type_L_path_table;
+ int location_type_M_path_table;
+ int total_dir_block;
+ } primary, joliet;
+
+ /* Used for making a Volume Descriptor. */
+ int volume_space_size;
+ int volume_sequence_number;
+ int total_file_block;
+ struct archive_string volume_identifier;
+ struct archive_string publisher_identifier;
+ struct archive_string data_preparer_identifier;
+ struct archive_string application_identifier;
+ struct archive_string copyright_file_identifier;
+ struct archive_string abstract_file_identifier;
+ struct archive_string bibliographic_file_identifier;
+
+ /* Used for making rockridge extensions. */
+ int location_rrip_er;
+
+ /* Used for making zisofs. */
+ struct {
+ unsigned int detect_magic:1;
+ unsigned int making:1;
+ unsigned int allzero:1;
+ unsigned char magic_buffer[64];
+ int magic_cnt;
+
+#ifdef HAVE_ZLIB_H
+ /*
+ * Copy a compressed file to iso9660.zisofs.temp_fd
+ * and also copy a uncompressed file(original file) to
+ * iso9660.temp_fd . If the number of logical block
+ * of the compressed file is less than the number of
+ * logical block of the uncompressed file, use it and
+ * remove the copy of the uncompressed file.
+ * but if not, we use uncompressed file and remove
+ * the copy of the compressed file.
+ */
+ uint32_t *block_pointers;
+ size_t block_pointers_allocated;
+ int block_pointers_cnt;
+ int block_pointers_idx;
+ int64_t total_size;
+ int64_t block_offset;
+
+ z_stream stream;
+ int stream_valid;
+ int64_t remaining;
+ int compression_level;
+#endif
+ } zisofs;
+
+ struct isoent *directories_too_deep;
+ int dircnt_max;
+
+ /* Write buffer. */
+#define wb_buffmax() (LOGICAL_BLOCK_SIZE * 32)
+#define wb_remaining(a) (((struct iso9660 *)(a)->format_data)->wbuff_remaining)
+#define wb_offset(a) (((struct iso9660 *)(a)->format_data)->wbuff_offset \
+ + wb_buffmax() - wb_remaining(a))
+ unsigned char wbuff[LOGICAL_BLOCK_SIZE * 32];
+ size_t wbuff_remaining;
+ enum {
+ WB_TO_STREAM,
+ WB_TO_TEMP
+ } wbuff_type;
+ int64_t wbuff_offset;
+ int64_t wbuff_written;
+ int64_t wbuff_tail;
+
+ /* 'El Torito' boot data. */
+ struct {
+ /* boot catalog file */
+ struct archive_string catalog_filename;
+ struct isoent *catalog;
+ /* boot image file */
+ struct archive_string boot_filename;
+ struct isoent *boot;
+
+ unsigned char platform_id;
+#define BOOT_PLATFORM_X86 0
+#define BOOT_PLATFORM_PPC 1
+#define BOOT_PLATFORM_MAC 2
+ struct archive_string id;
+ unsigned char media_type;
+#define BOOT_MEDIA_NO_EMULATION 0
+#define BOOT_MEDIA_1_2M_DISKETTE 1
+#define BOOT_MEDIA_1_44M_DISKETTE 2
+#define BOOT_MEDIA_2_88M_DISKETTE 3
+#define BOOT_MEDIA_HARD_DISK 4
+ unsigned char system_type;
+ uint16_t boot_load_seg;
+ uint16_t boot_load_size;
+#define BOOT_LOAD_SIZE 4
+ } el_torito;
+
+ struct iso_option opt;
+};
+
+/*
+ * Types of Volume Descriptor
+ */
+enum VD_type {
+ VDT_BOOT_RECORD=0, /* Boot Record Volume Descriptor */
+ VDT_PRIMARY=1, /* Primary Volume Descriptor */
+ VDT_SUPPLEMENTARY=2, /* Supplementary Volume Descriptor */
+ VDT_TERMINATOR=255 /* Volume Descriptor Set Terminator */
+};
+
+/*
+ * Types of Directory Record
+ */
+enum dir_rec_type {
+ DIR_REC_VD, /* Stored in Volume Descriptor. */
+ DIR_REC_SELF, /* Stored as Current Directory. */
+ DIR_REC_PARENT, /* Stored as Parent Directory. */
+ DIR_REC_NORMAL /* Stored as Child. */
+};
+
+/*
+ * Kinds of Volume Descriptor Character
+ */
+enum vdc {
+ VDC_STD,
+ VDC_LOWERCASE,
+ VDC_UCS2,
+ VDC_UCS2_DIRECT
+};
+
+/*
+ * IDentifier Resolver.
+ * Used for resolving duplicated filenames.
+ */
+struct idr {
+ struct idrent {
+ struct archive_rb_node rbnode;
+ /* Used in wait_list. */
+ struct idrent *wnext;
+ struct idrent *avail;
+
+ struct isoent *isoent;
+ int weight;
+ int noff;
+ int rename_num;
+ } *idrent_pool;
+
+ struct archive_rb_tree rbtree;
+
+ struct {
+ struct idrent *first;
+ struct idrent **last;
+ } wait_list;
+
+ int pool_size;
+ int pool_idx;
+ int num_size;
+ int null_size;
+
+ char char_map[0x80];
+};
+
+enum char_type {
+ A_CHAR,
+ D_CHAR
+};
+
+
+static int iso9660_options(struct archive_write *,
+ const char *, const char *);
+static int iso9660_write_header(struct archive_write *,
+ struct archive_entry *);
+static ssize_t iso9660_write_data(struct archive_write *,
+ const void *, size_t);
+static int iso9660_finish_entry(struct archive_write *);
+static int iso9660_close(struct archive_write *);
+static int iso9660_free(struct archive_write *);
+
+static void get_system_identitier(char *, size_t);
+static void set_str(unsigned char *, const char *, size_t, char,
+ const char *);
+static inline int joliet_allowed_char(unsigned char, unsigned char);
+static int set_str_utf16be(struct archive_write *, unsigned char *,
+ const char *, size_t, uint16_t, enum vdc);
+static int set_str_a_characters_bp(struct archive_write *,
+ unsigned char *, int, int, const char *, enum vdc);
+static int set_str_d_characters_bp(struct archive_write *,
+ unsigned char *, int, int, const char *, enum vdc);
+static void set_VD_bp(unsigned char *, enum VD_type, unsigned char);
+static inline void set_unused_field_bp(unsigned char *, int, int);
+
+static unsigned char *extra_open_record(unsigned char *, int,
+ struct isoent *, struct ctl_extr_rec *);
+static void extra_close_record(struct ctl_extr_rec *, int);
+static unsigned char * extra_next_record(struct ctl_extr_rec *, int);
+static unsigned char *extra_get_record(struct isoent *, int *, int *, int *);
+static void extra_tell_used_size(struct ctl_extr_rec *, int);
+static int extra_setup_location(struct isoent *, int);
+static int set_directory_record_rr(unsigned char *, int,
+ struct isoent *, struct iso9660 *, enum dir_rec_type);
+static int set_directory_record(unsigned char *, size_t,
+ struct isoent *, struct iso9660 *, enum dir_rec_type,
+ enum vdd_type);
+static inline int get_dir_rec_size(struct iso9660 *, struct isoent *,
+ enum dir_rec_type, enum vdd_type);
+static inline unsigned char *wb_buffptr(struct archive_write *);
+static int wb_write_out(struct archive_write *);
+static int wb_consume(struct archive_write *, size_t);
+#ifdef HAVE_ZLIB_H
+static int wb_set_offset(struct archive_write *, int64_t);
+#endif
+static int write_null(struct archive_write *, size_t);
+static int write_VD_terminator(struct archive_write *);
+static int set_file_identifier(unsigned char *, int, int, enum vdc,
+ struct archive_write *, struct vdd *,
+ struct archive_string *, const char *, int,
+ enum char_type);
+static int write_VD(struct archive_write *, struct vdd *);
+static int write_VD_boot_record(struct archive_write *);
+static int write_information_block(struct archive_write *);
+static int write_path_table(struct archive_write *, int,
+ struct vdd *);
+static int write_directory_descriptors(struct archive_write *,
+ struct vdd *);
+static int write_file_descriptors(struct archive_write *);
+static int write_rr_ER(struct archive_write *);
+static void calculate_path_table_size(struct vdd *);
+
+static void isofile_init_entry_list(struct iso9660 *);
+static void isofile_add_entry(struct iso9660 *, struct isofile *);
+static void isofile_free_all_entries(struct iso9660 *);
+static void isofile_init_entry_data_file_list(struct iso9660 *);
+static void isofile_add_data_file(struct iso9660 *, struct isofile *);
+static struct isofile * isofile_new(struct archive_write *,
+ struct archive_entry *);
+static void isofile_free(struct isofile *);
+static int isofile_gen_utility_names(struct archive_write *,
+ struct isofile *);
+static int isofile_register_hardlink(struct archive_write *,
+ struct isofile *);
+static void isofile_connect_hardlink_files(struct iso9660 *);
+static void isofile_init_hardlinks(struct iso9660 *);
+static void isofile_free_hardlinks(struct iso9660 *);
+
+static struct isoent *isoent_new(struct isofile *);
+static int isoent_clone_tree(struct archive_write *,
+ struct isoent **, struct isoent *);
+static void _isoent_free(struct isoent *isoent);
+static void isoent_free_all(struct isoent *);
+static struct isoent * isoent_create_virtual_dir(struct archive_write *,
+ struct iso9660 *, const char *);
+static int isoent_cmp_node(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+static int isoent_cmp_key(const struct archive_rb_node *,
+ const void *);
+static int isoent_add_child_head(struct isoent *, struct isoent *);
+static int isoent_add_child_tail(struct isoent *, struct isoent *);
+static void isoent_remove_child(struct isoent *, struct isoent *);
+static void isoent_setup_directory_location(struct iso9660 *,
+ int, struct vdd *);
+static void isoent_setup_file_location(struct iso9660 *, int);
+static int get_path_component(char *, size_t, const char *);
+static int isoent_tree(struct archive_write *, struct isoent **);
+static struct isoent *isoent_find_child(struct isoent *, const char *);
+static struct isoent *isoent_find_entry(struct isoent *, const char *);
+static void idr_relaxed_filenames(char *);
+static void idr_init(struct iso9660 *, struct vdd *, struct idr *);
+static void idr_cleanup(struct idr *);
+static int idr_ensure_poolsize(struct archive_write *, struct idr *,
+ int);
+static int idr_start(struct archive_write *, struct idr *,
+ int, int, int, int, const struct archive_rb_tree_ops *);
+static void idr_register(struct idr *, struct isoent *, int,
+ int);
+static void idr_extend_identifier(struct idrent *, int, int);
+static void idr_resolve(struct idr *, void (*)(unsigned char *, int));
+static void idr_set_num(unsigned char *, int);
+static void idr_set_num_beutf16(unsigned char *, int);
+static int isoent_gen_iso9660_identifier(struct archive_write *,
+ struct isoent *, struct idr *);
+static int isoent_gen_joliet_identifier(struct archive_write *,
+ struct isoent *, struct idr *);
+static int isoent_cmp_iso9660_identifier(const struct isoent *,
+ const struct isoent *);
+static int isoent_cmp_node_iso9660(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+static int isoent_cmp_key_iso9660(const struct archive_rb_node *,
+ const void *);
+static int isoent_cmp_joliet_identifier(const struct isoent *,
+ const struct isoent *);
+static int isoent_cmp_node_joliet(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+static int isoent_cmp_key_joliet(const struct archive_rb_node *,
+ const void *);
+static inline void path_table_add_entry(struct path_table *, struct isoent *);
+static inline struct isoent * path_table_last_entry(struct path_table *);
+static int isoent_make_path_table(struct archive_write *);
+static int isoent_find_out_boot_file(struct archive_write *,
+ struct isoent *);
+static int isoent_create_boot_catalog(struct archive_write *,
+ struct isoent *);
+static size_t fd_boot_image_size(int);
+static int make_boot_catalog(struct archive_write *);
+static int setup_boot_information(struct archive_write *);
+
+static int zisofs_init(struct archive_write *, struct isofile *);
+static void zisofs_detect_magic(struct archive_write *,
+ const void *, size_t);
+static int zisofs_write_to_temp(struct archive_write *,
+ const void *, size_t);
+static int zisofs_finish_entry(struct archive_write *);
+static int zisofs_rewind_boot_file(struct archive_write *);
+static int zisofs_free(struct archive_write *);
+
+int
+archive_write_set_format_iso9660(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct iso9660 *iso9660;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_iso9660");
+
+ /* If another format was already registered, unregister it. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ iso9660 = calloc(1, sizeof(*iso9660));
+ if (iso9660 == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate iso9660 data");
+ return (ARCHIVE_FATAL);
+ }
+ iso9660->birth_time = 0;
+ iso9660->temp_fd = -1;
+ iso9660->cur_file = NULL;
+ iso9660->primary.max_depth = 0;
+ iso9660->primary.vdd_type = VDD_PRIMARY;
+ iso9660->primary.pathtbl = NULL;
+ iso9660->joliet.rootent = NULL;
+ iso9660->joliet.max_depth = 0;
+ iso9660->joliet.vdd_type = VDD_JOLIET;
+ iso9660->joliet.pathtbl = NULL;
+ isofile_init_entry_list(iso9660);
+ isofile_init_entry_data_file_list(iso9660);
+ isofile_init_hardlinks(iso9660);
+ iso9660->directories_too_deep = NULL;
+ iso9660->dircnt_max = 1;
+ iso9660->wbuff_remaining = wb_buffmax();
+ iso9660->wbuff_type = WB_TO_TEMP;
+ iso9660->wbuff_offset = 0;
+ iso9660->wbuff_written = 0;
+ iso9660->wbuff_tail = 0;
+ archive_string_init(&(iso9660->utf16be));
+ archive_string_init(&(iso9660->mbs));
+
+ /*
+ * Init Identifiers used for PVD and SVD.
+ */
+ archive_string_init(&(iso9660->volume_identifier));
+ archive_strcpy(&(iso9660->volume_identifier), "CDROM");
+ archive_string_init(&(iso9660->publisher_identifier));
+ archive_string_init(&(iso9660->data_preparer_identifier));
+ archive_string_init(&(iso9660->application_identifier));
+ archive_strcpy(&(iso9660->application_identifier),
+ archive_version_string());
+ archive_string_init(&(iso9660->copyright_file_identifier));
+ archive_string_init(&(iso9660->abstract_file_identifier));
+ archive_string_init(&(iso9660->bibliographic_file_identifier));
+
+ /*
+ * Init El Torito bootable CD variables.
+ */
+ archive_string_init(&(iso9660->el_torito.catalog_filename));
+ iso9660->el_torito.catalog = NULL;
+ /* Set default file name of boot catalog */
+ archive_strcpy(&(iso9660->el_torito.catalog_filename),
+ "boot.catalog");
+ archive_string_init(&(iso9660->el_torito.boot_filename));
+ iso9660->el_torito.boot = NULL;
+ iso9660->el_torito.platform_id = BOOT_PLATFORM_X86;
+ archive_string_init(&(iso9660->el_torito.id));
+ iso9660->el_torito.boot_load_seg = 0;
+ iso9660->el_torito.boot_load_size = BOOT_LOAD_SIZE;
+
+ /*
+ * Init zisofs variables.
+ */
+#ifdef HAVE_ZLIB_H
+ iso9660->zisofs.block_pointers = NULL;
+ iso9660->zisofs.block_pointers_allocated = 0;
+ iso9660->zisofs.stream_valid = 0;
+ iso9660->zisofs.compression_level = 9;
+ memset(&(iso9660->zisofs.stream), 0,
+ sizeof(iso9660->zisofs.stream));
+#endif
+
+ /*
+ * Set default value of iso9660 options.
+ */
+ iso9660->opt.abstract_file = OPT_ABSTRACT_FILE_DEFAULT;
+ iso9660->opt.application_id = OPT_APPLICATION_ID_DEFAULT;
+ iso9660->opt.allow_vernum = OPT_ALLOW_VERNUM_DEFAULT;
+ iso9660->opt.biblio_file = OPT_BIBLIO_FILE_DEFAULT;
+ iso9660->opt.boot = OPT_BOOT_DEFAULT;
+ iso9660->opt.boot_catalog = OPT_BOOT_CATALOG_DEFAULT;
+ iso9660->opt.boot_info_table = OPT_BOOT_INFO_TABLE_DEFAULT;
+ iso9660->opt.boot_load_seg = OPT_BOOT_LOAD_SEG_DEFAULT;
+ iso9660->opt.boot_load_size = OPT_BOOT_LOAD_SIZE_DEFAULT;
+ iso9660->opt.boot_type = OPT_BOOT_TYPE_DEFAULT;
+ iso9660->opt.compression_level = OPT_COMPRESSION_LEVEL_DEFAULT;
+ iso9660->opt.copyright_file = OPT_COPYRIGHT_FILE_DEFAULT;
+ iso9660->opt.iso_level = OPT_ISO_LEVEL_DEFAULT;
+ iso9660->opt.joliet = OPT_JOLIET_DEFAULT;
+ iso9660->opt.limit_depth = OPT_LIMIT_DEPTH_DEFAULT;
+ iso9660->opt.limit_dirs = OPT_LIMIT_DIRS_DEFAULT;
+ iso9660->opt.pad = OPT_PAD_DEFAULT;
+ iso9660->opt.publisher = OPT_PUBLISHER_DEFAULT;
+ iso9660->opt.rr = OPT_RR_DEFAULT;
+ iso9660->opt.volume_id = OPT_VOLUME_ID_DEFAULT;
+ iso9660->opt.zisofs = OPT_ZISOFS_DEFAULT;
+
+ /* Create the root directory. */
+ iso9660->primary.rootent =
+ isoent_create_virtual_dir(a, iso9660, "");
+ if (iso9660->primary.rootent == NULL) {
+ free(iso9660);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ iso9660->primary.rootent->parent = iso9660->primary.rootent;
+ iso9660->cur_dirent = iso9660->primary.rootent;
+ archive_string_init(&(iso9660->cur_dirstr));
+ archive_string_ensure(&(iso9660->cur_dirstr), 1);
+ iso9660->cur_dirstr.s[0] = 0;
+ iso9660->sconv_to_utf16be = NULL;
+ iso9660->sconv_from_utf16be = NULL;
+
+ a->format_data = iso9660;
+ a->format_name = "iso9660";
+ a->format_options = iso9660_options;
+ a->format_write_header = iso9660_write_header;
+ a->format_write_data = iso9660_write_data;
+ a->format_finish_entry = iso9660_finish_entry;
+ a->format_close = iso9660_close;
+ a->format_free = iso9660_free;
+ a->archive.archive_format = ARCHIVE_FORMAT_ISO9660;
+ a->archive.archive_format_name = "ISO9660";
+
+ return (ARCHIVE_OK);
+}
+
+static int
+get_str_opt(struct archive_write *a, struct archive_string *s,
+ size_t maxsize, const char *key, const char *value)
+{
+
+ if (strlen(value) > maxsize) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Value is longer than %zu characters "
+ "for option ``%s''", maxsize, key);
+ return (ARCHIVE_FATAL);
+ }
+ archive_strcpy(s, value);
+ return (ARCHIVE_OK);
+}
+
+static int
+get_num_opt(struct archive_write *a, int *num, int high, int low,
+ const char *key, const char *value)
+{
+ const char *p = value;
+ int data = 0;
+ int neg = 0;
+
+ if (p == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid value(empty) for option ``%s''", key);
+ return (ARCHIVE_FATAL);
+ }
+ if (*p == '-') {
+ neg = 1;
+ p++;
+ }
+ while (*p) {
+ if (*p >= '0' && *p <= '9')
+ data = data * 10 + *p - '0';
+ else {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid value for option ``%s''", key);
+ return (ARCHIVE_FATAL);
+ }
+ if (data > high) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid value(over %d) for "
+ "option ``%s''", high, key);
+ return (ARCHIVE_FATAL);
+ }
+ if (data < low) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid value(under %d) for "
+ "option ``%s''", low, key);
+ return (ARCHIVE_FATAL);
+ }
+ p++;
+ }
+ if (neg)
+ data *= -1;
+ *num = data;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+iso9660_options(struct archive_write *a, const char *key, const char *value)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ const char *p;
+ int r;
+
+ switch (key[0]) {
+ case 'a':
+ if (strcmp(key, "abstract-file") == 0) {
+ r = get_str_opt(a,
+ &(iso9660->abstract_file_identifier),
+ ABSTRACT_FILE_SIZE, key, value);
+ iso9660->opt.abstract_file = r == ARCHIVE_OK;
+ return (r);
+ }
+ if (strcmp(key, "application-id") == 0) {
+ r = get_str_opt(a,
+ &(iso9660->application_identifier),
+ APPLICATION_IDENTIFIER_SIZE, key, value);
+ iso9660->opt.application_id = r == ARCHIVE_OK;
+ return (r);
+ }
+ if (strcmp(key, "allow-vernum") == 0) {
+ iso9660->opt.allow_vernum = value != NULL;
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'b':
+ if (strcmp(key, "biblio-file") == 0) {
+ r = get_str_opt(a,
+ &(iso9660->bibliographic_file_identifier),
+ BIBLIO_FILE_SIZE, key, value);
+ iso9660->opt.biblio_file = r == ARCHIVE_OK;
+ return (r);
+ }
+ if (strcmp(key, "boot") == 0) {
+ if (value == NULL)
+ iso9660->opt.boot = 0;
+ else {
+ iso9660->opt.boot = 1;
+ archive_strcpy(
+ &(iso9660->el_torito.boot_filename),
+ value);
+ }
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "boot-catalog") == 0) {
+ r = get_str_opt(a,
+ &(iso9660->el_torito.catalog_filename),
+ 1024, key, value);
+ iso9660->opt.boot_catalog = r == ARCHIVE_OK;
+ return (r);
+ }
+ if (strcmp(key, "boot-info-table") == 0) {
+ iso9660->opt.boot_info_table = value != NULL;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "boot-load-seg") == 0) {
+ uint32_t seg;
+
+ iso9660->opt.boot_load_seg = 0;
+ if (value == NULL)
+ goto invalid_value;
+ seg = 0;
+ p = value;
+ if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
+ p += 2;
+ while (*p) {
+ if (seg)
+ seg <<= 4;
+ if (*p >= 'A' && *p <= 'F')
+ seg += *p - 'A' + 0x0a;
+ else if (*p >= 'a' && *p <= 'f')
+ seg += *p - 'a' + 0x0a;
+ else if (*p >= '0' && *p <= '9')
+ seg += *p - '0';
+ else
+ goto invalid_value;
+ if (seg > 0xffff) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid value(over 0xffff) for "
+ "option ``%s''", key);
+ return (ARCHIVE_FATAL);
+ }
+ p++;
+ }
+ iso9660->el_torito.boot_load_seg = (uint16_t)seg;
+ iso9660->opt.boot_load_seg = 1;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "boot-load-size") == 0) {
+ int num = 0;
+ r = get_num_opt(a, &num, 0xffff, 1, key, value);
+ iso9660->opt.boot_load_size = r == ARCHIVE_OK;
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ iso9660->el_torito.boot_load_size = (uint16_t)num;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "boot-type") == 0) {
+ if (value == NULL)
+ goto invalid_value;
+ if (strcmp(value, "no-emulation") == 0)
+ iso9660->opt.boot_type = OPT_BOOT_TYPE_NO_EMU;
+ else if (strcmp(value, "fd") == 0)
+ iso9660->opt.boot_type = OPT_BOOT_TYPE_FD;
+ else if (strcmp(value, "hard-disk") == 0)
+ iso9660->opt.boot_type = OPT_BOOT_TYPE_HARD_DISK;
+ else
+ goto invalid_value;
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'c':
+ if (strcmp(key, "compression-level") == 0) {
+#ifdef HAVE_ZLIB_H
+ if (value == NULL ||
+ !(value[0] >= '0' && value[0] <= '9') ||
+ value[1] != '\0')
+ goto invalid_value;
+ iso9660->zisofs.compression_level = value[0] - '0';
+ iso9660->opt.compression_level = 1;
+ return (ARCHIVE_OK);
+#else
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Option ``%s'' "
+ "is not supported on this platform.", key);
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ if (strcmp(key, "copyright-file") == 0) {
+ r = get_str_opt(a,
+ &(iso9660->copyright_file_identifier),
+ COPYRIGHT_FILE_SIZE, key, value);
+ iso9660->opt.copyright_file = r == ARCHIVE_OK;
+ return (r);
+ }
+#ifdef DEBUG
+ /* Specifies Volume creation date and time;
+ * year(4),month(2),day(2),hour(2),minute(2),second(2).
+ * e.g. "20090929033757"
+ */
+ if (strcmp(key, "creation") == 0) {
+ struct tm tm;
+ char buf[5];
+
+ p = value;
+ if (p == NULL || strlen(p) < 14)
+ goto invalid_value;
+ memset(&tm, 0, sizeof(tm));
+ memcpy(buf, p, 4); buf[4] = '\0'; p += 4;
+ tm.tm_year = strtol(buf, NULL, 10) - 1900;
+ memcpy(buf, p, 2); buf[2] = '\0'; p += 2;
+ tm.tm_mon = strtol(buf, NULL, 10) - 1;
+ memcpy(buf, p, 2); buf[2] = '\0'; p += 2;
+ tm.tm_mday = strtol(buf, NULL, 10);
+ memcpy(buf, p, 2); buf[2] = '\0'; p += 2;
+ tm.tm_hour = strtol(buf, NULL, 10);
+ memcpy(buf, p, 2); buf[2] = '\0'; p += 2;
+ tm.tm_min = strtol(buf, NULL, 10);
+ memcpy(buf, p, 2); buf[2] = '\0';
+ tm.tm_sec = strtol(buf, NULL, 10);
+ iso9660->birth_time = mktime(&tm);
+ return (ARCHIVE_OK);
+ }
+#endif
+ break;
+ case 'i':
+ if (strcmp(key, "iso-level") == 0) {
+ if (value != NULL && value[1] == '\0' &&
+ (value[0] >= '1' && value[0] <= '4')) {
+ iso9660->opt.iso_level = value[0]-'0';
+ return (ARCHIVE_OK);
+ }
+ goto invalid_value;
+ }
+ break;
+ case 'j':
+ if (strcmp(key, "joliet") == 0) {
+ if (value == NULL)
+ iso9660->opt.joliet = OPT_JOLIET_DISABLE;
+ else if (strcmp(value, "1") == 0)
+ iso9660->opt.joliet = OPT_JOLIET_ENABLE;
+ else if (strcmp(value, "long") == 0)
+ iso9660->opt.joliet = OPT_JOLIET_LONGNAME;
+ else
+ goto invalid_value;
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'l':
+ if (strcmp(key, "limit-depth") == 0) {
+ iso9660->opt.limit_depth = value != NULL;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "limit-dirs") == 0) {
+ iso9660->opt.limit_dirs = value != NULL;
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'p':
+ if (strcmp(key, "pad") == 0) {
+ iso9660->opt.pad = value != NULL;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "publisher") == 0) {
+ r = get_str_opt(a,
+ &(iso9660->publisher_identifier),
+ PUBLISHER_IDENTIFIER_SIZE, key, value);
+ iso9660->opt.publisher = r == ARCHIVE_OK;
+ return (r);
+ }
+ break;
+ case 'r':
+ if (strcmp(key, "rockridge") == 0 ||
+ strcmp(key, "Rockridge") == 0) {
+ if (value == NULL)
+ iso9660->opt.rr = OPT_RR_DISABLED;
+ else if (strcmp(value, "1") == 0)
+ iso9660->opt.rr = OPT_RR_USEFUL;
+ else if (strcmp(value, "strict") == 0)
+ iso9660->opt.rr = OPT_RR_STRICT;
+ else if (strcmp(value, "useful") == 0)
+ iso9660->opt.rr = OPT_RR_USEFUL;
+ else
+ goto invalid_value;
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'v':
+ if (strcmp(key, "volume-id") == 0) {
+ r = get_str_opt(a, &(iso9660->volume_identifier),
+ VOLUME_IDENTIFIER_SIZE, key, value);
+ iso9660->opt.volume_id = r == ARCHIVE_OK;
+ return (r);
+ }
+ break;
+ case 'z':
+ if (strcmp(key, "zisofs") == 0) {
+ if (value == NULL)
+ iso9660->opt.zisofs = OPT_ZISOFS_DISABLED;
+ else {
+#ifdef HAVE_ZLIB_H
+ iso9660->opt.zisofs = OPT_ZISOFS_DIRECT;
+#else
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "``zisofs'' "
+ "is not supported on this platform.");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ return (ARCHIVE_OK);
+ }
+ break;
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+
+invalid_value:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid value for option ``%s''", key);
+ return (ARCHIVE_FAILED);
+}
+
+static int
+iso9660_write_header(struct archive_write *a, struct archive_entry *entry)
+{
+ struct iso9660 *iso9660;
+ struct isofile *file;
+ struct isoent *isoent;
+ int r, ret = ARCHIVE_OK;
+
+ iso9660 = a->format_data;
+
+ iso9660->cur_file = NULL;
+ iso9660->bytes_remaining = 0;
+ iso9660->need_multi_extent = 0;
+ if (archive_entry_filetype(entry) == AE_IFLNK
+ && iso9660->opt.rr == OPT_RR_DISABLED) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Ignore symlink file.");
+ iso9660->cur_file = NULL;
+ return (ARCHIVE_WARN);
+ }
+ if (archive_entry_filetype(entry) == AE_IFREG &&
+ archive_entry_size(entry) >= MULTI_EXTENT_SIZE) {
+ if (iso9660->opt.iso_level < 3) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Ignore over %lld bytes file. "
+ "This file too large.",
+ MULTI_EXTENT_SIZE);
+ iso9660->cur_file = NULL;
+ return (ARCHIVE_WARN);
+ }
+ iso9660->need_multi_extent = 1;
+ }
+
+ file = isofile_new(a, entry);
+ if (file == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate data");
+ return (ARCHIVE_FATAL);
+ }
+ r = isofile_gen_utility_names(a, file);
+ if (r < ARCHIVE_WARN) {
+ isofile_free(file);
+ return (r);
+ }
+ else if (r < ret)
+ ret = r;
+
+ /*
+ * Ignore a path which looks like the top of directory name
+ * since we have already made the root directory of an ISO image.
+ */
+ if (archive_strlen(&(file->parentdir)) == 0 &&
+ archive_strlen(&(file->basename)) == 0) {
+ isofile_free(file);
+ return (r);
+ }
+
+ isofile_add_entry(iso9660, file);
+ isoent = isoent_new(file);
+ if (isoent == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate data");
+ return (ARCHIVE_FATAL);
+ }
+ if (isoent->file->dircnt > iso9660->dircnt_max)
+ iso9660->dircnt_max = isoent->file->dircnt;
+
+ /* Add the current file into tree */
+ r = isoent_tree(a, &isoent);
+ if (r != ARCHIVE_OK)
+ return (r);
+
+ /* If there is the same file in tree and
+ * the current file is older than the file in tree.
+ * So we don't need the current file data anymore. */
+ if (isoent->file != file)
+ return (ARCHIVE_OK);
+
+ /* Non regular files contents are unneeded to be saved to
+ * temporary files. */
+ if (archive_entry_filetype(file->entry) != AE_IFREG)
+ return (ret);
+
+ /*
+ * Set the current file to cur_file to read its contents.
+ */
+ iso9660->cur_file = file;
+
+ if (archive_entry_nlink(file->entry) > 1) {
+ r = isofile_register_hardlink(a, file);
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Prepare to save the contents of the file.
+ */
+ if (iso9660->temp_fd < 0) {
+ iso9660->temp_fd = __archive_mktemp(NULL);
+ if (iso9660->temp_fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't create temporary file");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ /* Save an offset of current file in temporary file. */
+ file->content.offset_of_temp = wb_offset(a);
+ file->cur_content = &(file->content);
+ r = zisofs_init(a, file);
+ if (r < ret)
+ ret = r;
+ iso9660->bytes_remaining = archive_entry_size(file->entry);
+
+ return (ret);
+}
+
+static int
+write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ ssize_t written;
+ const unsigned char *b;
+
+ b = (const unsigned char *)buff;
+ while (s) {
+ written = write(iso9660->temp_fd, b, s);
+ if (written < 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't write to temporary file");
+ return (ARCHIVE_FATAL);
+ }
+ s -= written;
+ b += written;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+wb_write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+ const char *xp = buff;
+ size_t xs = s;
+
+ /*
+ * If a written data size is big enough to use system-call
+ * and there is no waiting data, this calls write_to_temp() in
+ * order to reduce a extra memory copy.
+ */
+ if (wb_remaining(a) == wb_buffmax() && s > (1024 * 16)) {
+ struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+ xs = s % LOGICAL_BLOCK_SIZE;
+ iso9660->wbuff_offset += s - xs;
+ if (write_to_temp(a, buff, s - xs) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ if (xs == 0)
+ return (ARCHIVE_OK);
+ xp += s - xs;
+ }
+
+ while (xs) {
+ size_t size = xs;
+ if (size > wb_remaining(a))
+ size = wb_remaining(a);
+ memcpy(wb_buffptr(a), xp, size);
+ if (wb_consume(a, size) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ xs -= size;
+ xp += size;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+wb_write_padding_to_temp(struct archive_write *a, int64_t csize)
+{
+ size_t ns;
+ int ret;
+
+ ns = (size_t)(csize % LOGICAL_BLOCK_SIZE);
+ if (ns != 0)
+ ret = write_null(a, LOGICAL_BLOCK_SIZE - ns);
+ else
+ ret = ARCHIVE_OK;
+ return (ret);
+}
+
+static ssize_t
+write_iso9660_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ size_t ws;
+
+ if (iso9660->temp_fd < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Couldn't create temporary file");
+ return (ARCHIVE_FATAL);
+ }
+
+ ws = s;
+ if (iso9660->need_multi_extent &&
+ (iso9660->cur_file->cur_content->size + ws) >=
+ (MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE)) {
+ struct content *con;
+ size_t ts;
+
+ ts = (size_t)(MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE -
+ iso9660->cur_file->cur_content->size);
+
+ if (iso9660->zisofs.detect_magic)
+ zisofs_detect_magic(a, buff, ts);
+
+ if (iso9660->zisofs.making) {
+ if (zisofs_write_to_temp(a, buff, ts) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } else {
+ if (wb_write_to_temp(a, buff, ts) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ iso9660->cur_file->cur_content->size += ts;
+ }
+
+ /* Write padding. */
+ if (wb_write_padding_to_temp(a,
+ iso9660->cur_file->cur_content->size) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Compute the logical block number. */
+ iso9660->cur_file->cur_content->blocks = (int)
+ ((iso9660->cur_file->cur_content->size
+ + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS);
+
+ /*
+ * Make next extent.
+ */
+ ws -= ts;
+ buff = (const void *)(((const unsigned char *)buff) + ts);
+ /* Make a content for next extent. */
+ con = calloc(1, sizeof(*con));
+ if (con == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate content data");
+ return (ARCHIVE_FATAL);
+ }
+ con->offset_of_temp = wb_offset(a);
+ iso9660->cur_file->cur_content->next = con;
+ iso9660->cur_file->cur_content = con;
+#ifdef HAVE_ZLIB_H
+ iso9660->zisofs.block_offset = 0;
+#endif
+ }
+
+ if (iso9660->zisofs.detect_magic)
+ zisofs_detect_magic(a, buff, ws);
+
+ if (iso9660->zisofs.making) {
+ if (zisofs_write_to_temp(a, buff, ws) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } else {
+ if (wb_write_to_temp(a, buff, ws) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ iso9660->cur_file->cur_content->size += ws;
+ }
+
+ return (s);
+}
+
+static ssize_t
+iso9660_write_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ ssize_t r;
+
+ if (iso9660->cur_file == NULL)
+ return (0);
+ if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG)
+ return (0);
+ if (s > iso9660->bytes_remaining)
+ s = (size_t)iso9660->bytes_remaining;
+ if (s == 0)
+ return (0);
+
+ r = write_iso9660_data(a, buff, s);
+ if (r > 0)
+ iso9660->bytes_remaining -= r;
+ return (r);
+}
+
+static int
+iso9660_finish_entry(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+
+ if (iso9660->cur_file == NULL)
+ return (ARCHIVE_OK);
+ if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG)
+ return (ARCHIVE_OK);
+ if (iso9660->cur_file->content.size == 0)
+ return (ARCHIVE_OK);
+
+ /* If there are unwritten data, write null data instead. */
+ while (iso9660->bytes_remaining > 0) {
+ size_t s;
+
+ s = (iso9660->bytes_remaining > a->null_length)?
+ a->null_length: (size_t)iso9660->bytes_remaining;
+ if (write_iso9660_data(a, a->nulls, s) < 0)
+ return (ARCHIVE_FATAL);
+ iso9660->bytes_remaining -= s;
+ }
+
+ if (iso9660->zisofs.making && zisofs_finish_entry(a) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Write padding. */
+ if (wb_write_padding_to_temp(a, iso9660->cur_file->cur_content->size)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Compute the logical block number. */
+ iso9660->cur_file->cur_content->blocks = (int)
+ ((iso9660->cur_file->cur_content->size
+ + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS);
+
+ /* Add the current file to data file list. */
+ isofile_add_data_file(iso9660, iso9660->cur_file);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+iso9660_close(struct archive_write *a)
+{
+ struct iso9660 *iso9660;
+ int ret, blocks;
+
+ iso9660 = a->format_data;
+
+ /*
+ * Write remaining data out to the temporary file.
+ */
+ if (wb_remaining(a) > 0) {
+ ret = wb_write_out(a);
+ if (ret < 0)
+ return (ret);
+ }
+
+ /*
+ * Preparations...
+ */
+#ifdef DEBUG
+ if (iso9660->birth_time == 0)
+#endif
+ time(&(iso9660->birth_time));
+
+ /*
+ * Prepare a bootable ISO image.
+ */
+ if (iso9660->opt.boot) {
+ /* Find out the boot file entry. */
+ ret = isoent_find_out_boot_file(a, iso9660->primary.rootent);
+ if (ret < 0)
+ return (ret);
+ /* Reconvert the boot file from zisofs'ed form to
+ * plain form. */
+ ret = zisofs_rewind_boot_file(a);
+ if (ret < 0)
+ return (ret);
+ /* Write remaining data out to the temporary file. */
+ if (wb_remaining(a) > 0) {
+ ret = wb_write_out(a);
+ if (ret < 0)
+ return (ret);
+ }
+ /* Create the boot catalog. */
+ ret = isoent_create_boot_catalog(a, iso9660->primary.rootent);
+ if (ret < 0)
+ return (ret);
+ }
+
+ /*
+ * Prepare joliet extensions.
+ */
+ if (iso9660->opt.joliet) {
+ /* Make a new tree for joliet. */
+ ret = isoent_clone_tree(a, &(iso9660->joliet.rootent),
+ iso9660->primary.rootent);
+ if (ret < 0)
+ return (ret);
+ /* Make sure we have UTF-16BE converters.
+ * if there is no file entry, converters are still
+ * uninitialized. */
+ if (iso9660->sconv_to_utf16be == NULL) {
+ iso9660->sconv_to_utf16be =
+ archive_string_conversion_to_charset(
+ &(a->archive), "UTF-16BE", 1);
+ if (iso9660->sconv_to_utf16be == NULL)
+ /* Couldn't allocate memory */
+ return (ARCHIVE_FATAL);
+ iso9660->sconv_from_utf16be =
+ archive_string_conversion_from_charset(
+ &(a->archive), "UTF-16BE", 1);
+ if (iso9660->sconv_from_utf16be == NULL)
+ /* Couldn't allocate memory */
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ /*
+ * Make Path Tables.
+ */
+ ret = isoent_make_path_table(a);
+ if (ret < 0)
+ return (ret);
+
+ /*
+ * Calculate a total volume size and setup all locations of
+ * contents of an iso9660 image.
+ */
+ blocks = SYSTEM_AREA_BLOCK
+ + PRIMARY_VOLUME_DESCRIPTOR_BLOCK
+ + VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK
+ + NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK;
+ if (iso9660->opt.boot)
+ blocks += BOOT_RECORD_DESCRIPTOR_BLOCK;
+ if (iso9660->opt.joliet)
+ blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK;
+ if (iso9660->opt.iso_level == 4)
+ blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK;
+
+ /* Setup the locations of Path Table. */
+ iso9660->primary.location_type_L_path_table = blocks;
+ blocks += iso9660->primary.path_table_block;
+ iso9660->primary.location_type_M_path_table = blocks;
+ blocks += iso9660->primary.path_table_block;
+ if (iso9660->opt.joliet) {
+ iso9660->joliet.location_type_L_path_table = blocks;
+ blocks += iso9660->joliet.path_table_block;
+ iso9660->joliet.location_type_M_path_table = blocks;
+ blocks += iso9660->joliet.path_table_block;
+ }
+
+ /* Setup the locations of directories. */
+ isoent_setup_directory_location(iso9660, blocks,
+ &(iso9660->primary));
+ blocks += iso9660->primary.total_dir_block;
+ if (iso9660->opt.joliet) {
+ isoent_setup_directory_location(iso9660, blocks,
+ &(iso9660->joliet));
+ blocks += iso9660->joliet.total_dir_block;
+ }
+
+ if (iso9660->opt.rr) {
+ iso9660->location_rrip_er = blocks;
+ blocks += RRIP_ER_BLOCK;
+ }
+
+ /* Setup the locations of all file contents. */
+ isoent_setup_file_location(iso9660, blocks);
+ blocks += iso9660->total_file_block;
+ if (iso9660->opt.boot && iso9660->opt.boot_info_table) {
+ ret = setup_boot_information(a);
+ if (ret < 0)
+ return (ret);
+ }
+
+ /* Now we have a total volume size. */
+ iso9660->volume_space_size = blocks;
+ if (iso9660->opt.pad)
+ iso9660->volume_space_size += PADDING_BLOCK;
+ iso9660->volume_sequence_number = 1;
+
+
+ /*
+ * Write an ISO 9660 image.
+ */
+
+ /* Switch to start using wbuff as file buffer. */
+ iso9660->wbuff_remaining = wb_buffmax();
+ iso9660->wbuff_type = WB_TO_STREAM;
+ iso9660->wbuff_offset = 0;
+ iso9660->wbuff_written = 0;
+ iso9660->wbuff_tail = 0;
+
+ /* Write The System Area */
+ ret = write_null(a, SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Write Primary Volume Descriptor */
+ ret = write_VD(a, &(iso9660->primary));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ if (iso9660->opt.boot) {
+ /* Write Boot Record Volume Descriptor */
+ ret = write_VD_boot_record(a);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ if (iso9660->opt.iso_level == 4) {
+ /* Write Enhanced Volume Descriptor */
+ iso9660->primary.vdd_type = VDD_ENHANCED;
+ ret = write_VD(a, &(iso9660->primary));
+ iso9660->primary.vdd_type = VDD_PRIMARY;
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ if (iso9660->opt.joliet) {
+ ret = write_VD(a, &(iso9660->joliet));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Write Volume Descriptor Set Terminator */
+ ret = write_VD_terminator(a);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Write Non-ISO File System Information */
+ ret = write_information_block(a);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Write Type L Path Table */
+ ret = write_path_table(a, 0, &(iso9660->primary));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Write Type M Path Table */
+ ret = write_path_table(a, 1, &(iso9660->primary));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ if (iso9660->opt.joliet) {
+ /* Write Type L Path Table */
+ ret = write_path_table(a, 0, &(iso9660->joliet));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Write Type M Path Table */
+ ret = write_path_table(a, 1, &(iso9660->joliet));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Write Directory Descriptors */
+ ret = write_directory_descriptors(a, &(iso9660->primary));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ if (iso9660->opt.joliet) {
+ ret = write_directory_descriptors(a, &(iso9660->joliet));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ if (iso9660->opt.rr) {
+ /* Write Rockridge ER(Extensions Reference) */
+ ret = write_rr_ER(a);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Write File Descriptors */
+ ret = write_file_descriptors(a);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Write Padding */
+ if (iso9660->opt.pad) {
+ ret = write_null(a, PADDING_BLOCK * LOGICAL_BLOCK_SIZE);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ if (iso9660->directories_too_deep != NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: Directories too deep.",
+ archive_entry_pathname(
+ iso9660->directories_too_deep->file->entry));
+ return (ARCHIVE_WARN);
+ }
+
+ /* Write remaining data out. */
+ ret = wb_write_out(a);
+
+ return (ret);
+}
+
+static int
+iso9660_free(struct archive_write *a)
+{
+ struct iso9660 *iso9660;
+ int i, ret;
+
+ iso9660 = a->format_data;
+
+ /* Close the temporary file. */
+ if (iso9660->temp_fd >= 0)
+ close(iso9660->temp_fd);
+
+ /* Free some stuff for zisofs operations. */
+ ret = zisofs_free(a);
+
+ /* Remove directory entries in tree which includes file entries. */
+ isoent_free_all(iso9660->primary.rootent);
+ for (i = 0; i < iso9660->primary.max_depth; i++)
+ free(iso9660->primary.pathtbl[i].sorted);
+ free(iso9660->primary.pathtbl);
+
+ if (iso9660->opt.joliet) {
+ isoent_free_all(iso9660->joliet.rootent);
+ for (i = 0; i < iso9660->joliet.max_depth; i++)
+ free(iso9660->joliet.pathtbl[i].sorted);
+ free(iso9660->joliet.pathtbl);
+ }
+
+ /* Remove isofile entries. */
+ isofile_free_all_entries(iso9660);
+ isofile_free_hardlinks(iso9660);
+
+ archive_string_free(&(iso9660->cur_dirstr));
+ archive_string_free(&(iso9660->volume_identifier));
+ archive_string_free(&(iso9660->publisher_identifier));
+ archive_string_free(&(iso9660->data_preparer_identifier));
+ archive_string_free(&(iso9660->application_identifier));
+ archive_string_free(&(iso9660->copyright_file_identifier));
+ archive_string_free(&(iso9660->abstract_file_identifier));
+ archive_string_free(&(iso9660->bibliographic_file_identifier));
+ archive_string_free(&(iso9660->el_torito.catalog_filename));
+ archive_string_free(&(iso9660->el_torito.boot_filename));
+ archive_string_free(&(iso9660->el_torito.id));
+ archive_string_free(&(iso9660->utf16be));
+ archive_string_free(&(iso9660->mbs));
+
+ free(iso9660);
+ a->format_data = NULL;
+
+ return (ret);
+}
+
+/*
+ * Get the System Identifier
+ */
+static void
+get_system_identitier(char *system_id, size_t size)
+{
+#if defined(HAVE_SYS_UTSNAME_H)
+ struct utsname u;
+
+ uname(&u);
+ strncpy(system_id, u.sysname, size-1);
+ system_id[size-1] = '\0';
+#elif defined(_WIN32) && !defined(__CYGWIN__)
+ strncpy(system_id, "Windows", size-1);
+ system_id[size-1] = '\0';
+#else
+ strncpy(system_id, "Unknown", size-1);
+ system_id[size-1] = '\0';
+#endif
+}
+
+static void
+set_str(unsigned char *p, const char *s, size_t l, char f, const char *map)
+{
+ unsigned char c;
+
+ if (s == NULL)
+ s = "";
+ while ((c = *s++) != 0 && l > 0) {
+ if (c >= 0x80 || map[c] == 0)
+ {
+ /* illegal character */
+ if (c >= 'a' && c <= 'z') {
+ /* convert c from a-z to A-Z */
+ c -= 0x20;
+ } else
+ c = 0x5f;
+ }
+ *p++ = c;
+ l--;
+ }
+ /* If l isn't zero, fill p buffer by the character
+ * which indicated by f. */
+ if (l > 0)
+ memset(p , f, l);
+}
+
+static inline int
+joliet_allowed_char(unsigned char high, unsigned char low)
+{
+ int utf16 = (high << 8) | low;
+
+ if (utf16 <= 0x001F)
+ return (0);
+
+ switch (utf16) {
+ case 0x002A: /* '*' */
+ case 0x002F: /* '/' */
+ case 0x003A: /* ':' */
+ case 0x003B: /* ';' */
+ case 0x003F: /* '?' */
+ case 0x005C: /* '\' */
+ return (0);/* Not allowed. */
+ }
+ return (1);
+}
+
+static int
+set_str_utf16be(struct archive_write *a, unsigned char *p, const char *s,
+ size_t l, uint16_t uf, enum vdc vdc)
+{
+ size_t size, i;
+ int onepad;
+
+ if (s == NULL)
+ s = "";
+ if (l & 0x01) {
+ onepad = 1;
+ l &= ~1;
+ } else
+ onepad = 0;
+ if (vdc == VDC_UCS2) {
+ struct iso9660 *iso9660 = a->format_data;
+ if (archive_strncpy_l(&iso9660->utf16be, s, strlen(s),
+ iso9660->sconv_to_utf16be) != 0 && errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for UTF-16BE");
+ return (ARCHIVE_FATAL);
+ }
+ size = iso9660->utf16be.length;
+ if (size > l)
+ size = l;
+ memcpy(p, iso9660->utf16be.s, size);
+ } else {
+ const uint16_t *u16 = (const uint16_t *)s;
+
+ size = 0;
+ while (*u16++)
+ size += 2;
+ if (size > l)
+ size = l;
+ memcpy(p, s, size);
+ }
+ for (i = 0; i < size; i += 2, p += 2) {
+ if (!joliet_allowed_char(p[0], p[1]))
+ archive_be16enc(p, 0x005F);/* '_' */
+ }
+ l -= size;
+ while (l > 0) {
+ archive_be16enc(p, uf);
+ p += 2;
+ l -= 2;
+ }
+ if (onepad)
+ *p = 0;
+ return (ARCHIVE_OK);
+}
+
+static const char a_characters_map[0x80] = {
+/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */
+ 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */
+};
+
+static const char a1_characters_map[0x80] = {
+/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */
+ 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */
+};
+
+static const char d_characters_map[0x80] = {
+/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */
+};
+
+static const char d1_characters_map[0x80] = {
+/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */
+};
+
+static int
+set_str_a_characters_bp(struct archive_write *a, unsigned char *bp,
+ int from, int to, const char *s, enum vdc vdc)
+{
+ int r;
+
+ switch (vdc) {
+ case VDC_STD:
+ set_str(bp+from, s, to - from + 1, 0x20,
+ a_characters_map);
+ r = ARCHIVE_OK;
+ break;
+ case VDC_LOWERCASE:
+ set_str(bp+from, s, to - from + 1, 0x20,
+ a1_characters_map);
+ r = ARCHIVE_OK;
+ break;
+ case VDC_UCS2:
+ case VDC_UCS2_DIRECT:
+ r = set_str_utf16be(a, bp+from, s, to - from + 1,
+ 0x0020, vdc);
+ break;
+ default:
+ r = ARCHIVE_FATAL;
+ }
+ return (r);
+}
+
+static int
+set_str_d_characters_bp(struct archive_write *a, unsigned char *bp,
+ int from, int to, const char *s, enum vdc vdc)
+{
+ int r;
+
+ switch (vdc) {
+ case VDC_STD:
+ set_str(bp+from, s, to - from + 1, 0x20,
+ d_characters_map);
+ r = ARCHIVE_OK;
+ break;
+ case VDC_LOWERCASE:
+ set_str(bp+from, s, to - from + 1, 0x20,
+ d1_characters_map);
+ r = ARCHIVE_OK;
+ break;
+ case VDC_UCS2:
+ case VDC_UCS2_DIRECT:
+ r = set_str_utf16be(a, bp+from, s, to - from + 1,
+ 0x0020, vdc);
+ break;
+ default:
+ r = ARCHIVE_FATAL;
+ }
+ return (r);
+}
+
+static void
+set_VD_bp(unsigned char *bp, enum VD_type type, unsigned char ver)
+{
+
+ /* Volume Descriptor Type */
+ bp[1] = (unsigned char)type;
+ /* Standard Identifier */
+ memcpy(bp + 2, "CD001", 5);
+ /* Volume Descriptor Version */
+ bp[7] = ver;
+}
+
+static inline void
+set_unused_field_bp(unsigned char *bp, int from, int to)
+{
+ memset(bp + from, 0, to - from + 1);
+}
+
+/*
+ * 8-bit unsigned numerical values.
+ * ISO9660 Standard 7.1.1
+ */
+static inline void
+set_num_711(unsigned char *p, unsigned char value)
+{
+ *p = value;
+}
+
+/*
+ * 8-bit signed numerical values.
+ * ISO9660 Standard 7.1.2
+ */
+static inline void
+set_num_712(unsigned char *p, char value)
+{
+ *((char *)p) = value;
+}
+
+/*
+ * Least significant byte first.
+ * ISO9660 Standard 7.2.1
+ */
+static inline void
+set_num_721(unsigned char *p, uint16_t value)
+{
+ archive_le16enc(p, value);
+}
+
+/*
+ * Most significant byte first.
+ * ISO9660 Standard 7.2.2
+ */
+static inline void
+set_num_722(unsigned char *p, uint16_t value)
+{
+ archive_be16enc(p, value);
+}
+
+/*
+ * Both-byte orders.
+ * ISO9660 Standard 7.2.3
+ */
+static void
+set_num_723(unsigned char *p, uint16_t value)
+{
+ archive_le16enc(p, value);
+ archive_be16enc(p+2, value);
+}
+
+/*
+ * Least significant byte first.
+ * ISO9660 Standard 7.3.1
+ */
+static inline void
+set_num_731(unsigned char *p, uint32_t value)
+{
+ archive_le32enc(p, value);
+}
+
+/*
+ * Most significant byte first.
+ * ISO9660 Standard 7.3.2
+ */
+static inline void
+set_num_732(unsigned char *p, uint32_t value)
+{
+ archive_be32enc(p, value);
+}
+
+/*
+ * Both-byte orders.
+ * ISO9660 Standard 7.3.3
+ */
+static inline void
+set_num_733(unsigned char *p, uint32_t value)
+{
+ archive_le32enc(p, value);
+ archive_be32enc(p+4, value);
+}
+
+static void
+set_digit(unsigned char *p, size_t s, int value)
+{
+
+ while (s--) {
+ p[s] = '0' + (value % 10);
+ value /= 10;
+ }
+}
+
+#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
+#define get_gmoffset(tm) ((tm)->tm_gmtoff)
+#elif defined(HAVE_STRUCT_TM___TM_GMTOFF)
+#define get_gmoffset(tm) ((tm)->__tm_gmtoff)
+#else
+static long
+get_gmoffset(struct tm *tm)
+{
+ long offset;
+
+#if defined(HAVE__GET_TIMEZONE)
+ _get_timezone(&offset);
+#elif defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)
+ offset = _timezone;
+#else
+ offset = timezone;
+#endif
+ offset *= -1;
+ if (tm->tm_isdst)
+ offset += 3600;
+ return (offset);
+}
+#endif
+
+static void
+get_tmfromtime(struct tm *tm, time_t *t)
+{
+#if HAVE_LOCALTIME_S
+ localtime_s(tm, t);
+#elif HAVE_LOCALTIME_R
+ tzset();
+ localtime_r(t, tm);
+#else
+ memcpy(tm, localtime(t), sizeof(*tm));
+#endif
+}
+
+/*
+ * Date and Time Format.
+ * ISO9660 Standard 8.4.26.1
+ */
+static void
+set_date_time(unsigned char *p, time_t t)
+{
+ struct tm tm;
+
+ get_tmfromtime(&tm, &t);
+ set_digit(p, 4, tm.tm_year + 1900);
+ set_digit(p+4, 2, tm.tm_mon + 1);
+ set_digit(p+6, 2, tm.tm_mday);
+ set_digit(p+8, 2, tm.tm_hour);
+ set_digit(p+10, 2, tm.tm_min);
+ set_digit(p+12, 2, tm.tm_sec);
+ set_digit(p+14, 2, 0);
+ set_num_712(p+16, (char)(get_gmoffset(&tm)/(60*15)));
+}
+
+static void
+set_date_time_null(unsigned char *p)
+{
+ memset(p, (int)'0', 16);
+ p[16] = 0;
+}
+
+static void
+set_time_915(unsigned char *p, time_t t)
+{
+ struct tm tm;
+
+ get_tmfromtime(&tm, &t);
+ set_num_711(p+0, tm.tm_year);
+ set_num_711(p+1, tm.tm_mon+1);
+ set_num_711(p+2, tm.tm_mday);
+ set_num_711(p+3, tm.tm_hour);
+ set_num_711(p+4, tm.tm_min);
+ set_num_711(p+5, tm.tm_sec);
+ set_num_712(p+6, (char)(get_gmoffset(&tm)/(60*15)));
+}
+
+
+/*
+ * Write SUSP "CE" System Use Entry.
+ */
+static int
+set_SUSP_CE(unsigned char *p, int location, int offset, int size)
+{
+ unsigned char *bp = p -1;
+ /* Extend the System Use Area
+ * "CE" Format:
+ * len ver
+ * +----+----+----+----+-----------+-----------+
+ * | 'C'| 'E'| 1C | 01 | LOCATION1 | LOCATION2 |
+ * +----+----+----+----+-----------+-----------+
+ * 0 1 2 3 4 12 20
+ * +-----------+
+ * | LOCATION3 |
+ * +-----------+
+ * 20 28
+ * LOCATION1 : Location of Continuation of System Use Area.
+ * LOCATION2 : Offset to Start of Continuation.
+ * LOCATION3 : Length of the Continuation.
+ */
+
+ bp[1] = 'C';
+ bp[2] = 'E';
+ bp[3] = RR_CE_SIZE; /* length */
+ bp[4] = 1; /* version */
+ set_num_733(bp+5, location);
+ set_num_733(bp+13, offset);
+ set_num_733(bp+21, size);
+ return (RR_CE_SIZE);
+}
+
+/*
+ * The functions, which names are beginning with extra_, are used to
+ * control extra records.
+ * The maximum size of a Directory Record is 254. When a filename is
+ * very long, all of RRIP data of a file won't stored to the Directory
+ * Record and so remaining RRIP data store to an extra record instead.
+ */
+static unsigned char *
+extra_open_record(unsigned char *bp, int dr_len, struct isoent *isoent,
+ struct ctl_extr_rec *ctl)
+{
+ ctl->bp = bp;
+ if (bp != NULL)
+ bp += dr_len;
+ ctl->use_extr = 0;
+ ctl->isoent = isoent;
+ ctl->ce_ptr = NULL;
+ ctl->cur_len = ctl->dr_len = dr_len;
+ ctl->limit = DR_LIMIT;
+
+ return (bp);
+}
+
+static void
+extra_close_record(struct ctl_extr_rec *ctl, int ce_size)
+{
+ int padding = 0;
+
+ if (ce_size > 0)
+ extra_tell_used_size(ctl, ce_size);
+ /* Padding. */
+ if (ctl->cur_len & 0x01) {
+ ctl->cur_len++;
+ if (ctl->bp != NULL)
+ ctl->bp[ctl->cur_len] = 0;
+ padding = 1;
+ }
+ if (ctl->use_extr) {
+ if (ctl->ce_ptr != NULL)
+ set_SUSP_CE(ctl->ce_ptr, ctl->extr_loc,
+ ctl->extr_off, ctl->cur_len - padding);
+ } else
+ ctl->dr_len = ctl->cur_len;
+}
+
+#define extra_space(ctl) ((ctl)->limit - (ctl)->cur_len)
+
+static unsigned char *
+extra_next_record(struct ctl_extr_rec *ctl, int length)
+{
+ int cur_len = ctl->cur_len;/* save cur_len */
+
+ /* Close the current extra record or Directory Record. */
+ extra_close_record(ctl, RR_CE_SIZE);
+
+ /* Get a next extra record. */
+ ctl->use_extr = 1;
+ if (ctl->bp != NULL) {
+ /* Storing data into an extra record. */
+ unsigned char *p;
+
+ /* Save the pointer where a CE extension will be
+ * stored to. */
+ ctl->ce_ptr = &ctl->bp[cur_len+1];
+ p = extra_get_record(ctl->isoent,
+ &ctl->limit, &ctl->extr_off, &ctl->extr_loc);
+ ctl->bp = p - 1;/* the base of bp offset is 1. */
+ } else
+ /* Calculating the size of an extra record. */
+ (void)extra_get_record(ctl->isoent,
+ &ctl->limit, NULL, NULL);
+ ctl->cur_len = 0;
+ /* Check if an extra record is almost full.
+ * If so, get a next one. */
+ if (extra_space(ctl) < length)
+ (void)extra_next_record(ctl, length);
+
+ return (ctl->bp);
+}
+
+static inline struct extr_rec *
+extra_last_record(struct isoent *isoent)
+{
+ if (isoent->extr_rec_list.first == NULL)
+ return (NULL);
+ return ((struct extr_rec *)(void *)
+ ((char *)(isoent->extr_rec_list.last)
+ - offsetof(struct extr_rec, next)));
+}
+
+static unsigned char *
+extra_get_record(struct isoent *isoent, int *space, int *off, int *loc)
+{
+ struct extr_rec *rec;
+
+ isoent = isoent->parent;
+ if (off != NULL) {
+ /* Storing data into an extra record. */
+ rec = isoent->extr_rec_list.current;
+ if (DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset)
+ rec = rec->next;
+ } else {
+ /* Calculating the size of an extra record. */
+ rec = extra_last_record(isoent);
+ if (rec == NULL ||
+ DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset) {
+ rec = malloc(sizeof(*rec));
+ if (rec == NULL)
+ return (NULL);
+ rec->location = 0;
+ rec->offset = 0;
+ /* Insert `rec` into the tail of isoent->extr_rec_list */
+ rec->next = NULL;
+ /*
+ * Note: testing isoent->extr_rec_list.last == NULL
+ * here is really unneeded since it has been already
+ * initialized at isoent_new function but Clang Static
+ * Analyzer claims that it is dereference of null
+ * pointer.
+ */
+ if (isoent->extr_rec_list.last == NULL)
+ isoent->extr_rec_list.last =
+ &(isoent->extr_rec_list.first);
+ *isoent->extr_rec_list.last = rec;
+ isoent->extr_rec_list.last = &(rec->next);
+ }
+ }
+ *space = LOGICAL_BLOCK_SIZE - rec->offset - DR_SAFETY;
+ if (*space & 0x01)
+ *space -= 1;/* Keep padding space. */
+ if (off != NULL)
+ *off = rec->offset;
+ if (loc != NULL)
+ *loc = rec->location;
+ isoent->extr_rec_list.current = rec;
+
+ return (&rec->buf[rec->offset]);
+}
+
+static void
+extra_tell_used_size(struct ctl_extr_rec *ctl, int size)
+{
+ struct isoent *isoent;
+ struct extr_rec *rec;
+
+ if (ctl->use_extr) {
+ isoent = ctl->isoent->parent;
+ rec = isoent->extr_rec_list.current;
+ if (rec != NULL)
+ rec->offset += size;
+ }
+ ctl->cur_len += size;
+}
+
+static int
+extra_setup_location(struct isoent *isoent, int location)
+{
+ struct extr_rec *rec;
+ int cnt;
+
+ cnt = 0;
+ rec = isoent->extr_rec_list.first;
+ isoent->extr_rec_list.current = rec;
+ while (rec) {
+ cnt++;
+ rec->location = location++;
+ rec->offset = 0;
+ rec = rec->next;
+ }
+ return (cnt);
+}
+
+/*
+ * Create the RRIP entries.
+ */
+static int
+set_directory_record_rr(unsigned char *bp, int dr_len,
+ struct isoent *isoent, struct iso9660 *iso9660, enum dir_rec_type t)
+{
+ /* Flags(BP 5) of the Rockridge "RR" System Use Field */
+ unsigned char rr_flag;
+#define RR_USE_PX 0x01
+#define RR_USE_PN 0x02
+#define RR_USE_SL 0x04
+#define RR_USE_NM 0x08
+#define RR_USE_CL 0x10
+#define RR_USE_PL 0x20
+#define RR_USE_RE 0x40
+#define RR_USE_TF 0x80
+ int length;
+ struct ctl_extr_rec ctl;
+ struct isoent *rr_parent, *pxent;
+ struct isofile *file;
+
+ bp = extra_open_record(bp, dr_len, isoent, &ctl);
+
+ if (t == DIR_REC_PARENT) {
+ rr_parent = isoent->rr_parent;
+ pxent = isoent->parent;
+ if (rr_parent != NULL)
+ isoent = rr_parent;
+ else
+ isoent = isoent->parent;
+ } else {
+ rr_parent = NULL;
+ pxent = isoent;
+ }
+ file = isoent->file;
+
+ if (t != DIR_REC_NORMAL) {
+ rr_flag = RR_USE_PX | RR_USE_TF;
+ if (rr_parent != NULL)
+ rr_flag |= RR_USE_PL;
+ } else {
+ rr_flag = RR_USE_PX | RR_USE_NM | RR_USE_TF;
+ if (archive_entry_filetype(file->entry) == AE_IFLNK)
+ rr_flag |= RR_USE_SL;
+ if (isoent->rr_parent != NULL)
+ rr_flag |= RR_USE_RE;
+ if (isoent->rr_child != NULL)
+ rr_flag |= RR_USE_CL;
+ if (archive_entry_filetype(file->entry) == AE_IFCHR ||
+ archive_entry_filetype(file->entry) == AE_IFBLK)
+ rr_flag |= RR_USE_PN;
+#ifdef COMPAT_MKISOFS
+ /*
+ * mkisofs 2.01.01a63 records "RE" extension to
+ * the entry of "rr_moved" directory.
+ * I don't understand this behavior.
+ */
+ if (isoent->virtual &&
+ isoent->parent == iso9660->primary.rootent &&
+ strcmp(isoent->file->basename.s, "rr_moved") == 0)
+ rr_flag |= RR_USE_RE;
+#endif
+ }
+
+ /* Write "SP" System Use Entry. */
+ if (t == DIR_REC_SELF && isoent == isoent->parent) {
+ length = 7;
+ if (bp != NULL) {
+ bp[1] = 'S';
+ bp[2] = 'P';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ bp[5] = 0xBE; /* Check Byte */
+ bp[6] = 0xEF; /* Check Byte */
+ bp[7] = 0;
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "RR" System Use Entry. */
+ length = 5;
+ if (extra_space(&ctl) < length)
+ bp = extra_next_record(&ctl, length);
+ if (bp != NULL) {
+ bp[1] = 'R';
+ bp[2] = 'R';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ bp[5] = rr_flag;
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+
+ /* Write "NM" System Use Entry. */
+ if (rr_flag & RR_USE_NM) {
+ /*
+ * "NM" Format:
+ * e.g. a basename is 'foo'
+ * len ver flg
+ * +----+----+----+----+----+----+----+----+
+ * | 'N'| 'M'| 08 | 01 | 00 | 'f'| 'o'| 'o'|
+ * +----+----+----+----+----+----+----+----+
+ * <----------------- len ----------------->
+ */
+ size_t nmlen = file->basename.length;
+ const char *nm = file->basename.s;
+ size_t nmmax;
+
+ if (extra_space(&ctl) < 6)
+ bp = extra_next_record(&ctl, 6);
+ if (bp != NULL) {
+ bp[1] = 'N';
+ bp[2] = 'M';
+ bp[4] = 1; /* version */
+ }
+ nmmax = extra_space(&ctl);
+ if (nmmax > 0xff)
+ nmmax = 0xff;
+ while (nmlen + 5 > nmmax) {
+ length = (int)nmmax;
+ if (bp != NULL) {
+ bp[3] = length;
+ bp[5] = 0x01;/* Alternate Name continues
+ * in next "NM" field */
+ memcpy(bp+6, nm, length - 5);
+ bp += length;
+ }
+ nmlen -= length - 5;
+ nm += length - 5;
+ extra_tell_used_size(&ctl, length);
+ if (extra_space(&ctl) < 6) {
+ bp = extra_next_record(&ctl, 6);
+ nmmax = extra_space(&ctl);
+ if (nmmax > 0xff)
+ nmmax = 0xff;
+ }
+ if (bp != NULL) {
+ bp[1] = 'N';
+ bp[2] = 'M';
+ bp[4] = 1; /* version */
+ }
+ }
+ length = 5 + (int)nmlen;
+ if (bp != NULL) {
+ bp[3] = length;
+ bp[5] = 0;
+ memcpy(bp+6, nm, nmlen);
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "PX" System Use Entry. */
+ if (rr_flag & RR_USE_PX) {
+ /*
+ * "PX" Format:
+ * len ver
+ * +----+----+----+----+-----------+-----------+
+ * | 'P'| 'X'| 2C | 01 | FILE MODE | LINKS |
+ * +----+----+----+----+-----------+-----------+
+ * 0 1 2 3 4 12 20
+ * +-----------+-----------+------------------+
+ * | USER ID | GROUP ID |FILE SERIAL NUMBER|
+ * +-----------+-----------+------------------+
+ * 20 28 36 44
+ */
+ length = 44;
+ if (extra_space(&ctl) < length)
+ bp = extra_next_record(&ctl, length);
+ if (bp != NULL) {
+ mode_t mode;
+ int64_t uid;
+ int64_t gid;
+
+ mode = archive_entry_mode(file->entry);
+ uid = archive_entry_uid(file->entry);
+ gid = archive_entry_gid(file->entry);
+ if (iso9660->opt.rr == OPT_RR_USEFUL) {
+ /*
+ * This action is similar to mkisofs -r option
+ * but our rockridge=useful option does not
+ * set a zero to uid and gid.
+ */
+ /* set all read bit ON */
+ mode |= 0444;
+#if !defined(_WIN32) && !defined(__CYGWIN__)
+ if (mode & 0111)
+#endif
+ /* set all exec bit ON */
+ mode |= 0111;
+ /* clear all write bits. */
+ mode &= ~0222;
+ /* clear setuid,setgid,sticky bits. */
+ mode &= ~07000;
+ }
+
+ bp[1] = 'P';
+ bp[2] = 'X';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ /* file mode */
+ set_num_733(bp+5, mode);
+ /* file links (stat.st_nlink) */
+ set_num_733(bp+13,
+ archive_entry_nlink(file->entry));
+ set_num_733(bp+21, (uint32_t)uid);
+ set_num_733(bp+29, (uint32_t)gid);
+ /* File Serial Number */
+ if (pxent->dir)
+ set_num_733(bp+37, pxent->dir_location);
+ else if (file->hardlink_target != NULL)
+ set_num_733(bp+37,
+ file->hardlink_target->cur_content->location);
+ else
+ set_num_733(bp+37,
+ file->cur_content->location);
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "SL" System Use Entry. */
+ if (rr_flag & RR_USE_SL) {
+ /*
+ * "SL" Format:
+ * e.g. a symbolic name is 'foo/bar'
+ * len ver flg
+ * +----+----+----+----+----+------------+
+ * | 'S'| 'L'| 0F | 01 | 00 | components |
+ * +----+----+----+----+----+-----+------+
+ * 0 1 2 3 4 5 ...|... 15
+ * <----------------- len --------+------>
+ * components : |
+ * cflg clen |
+ * +----+----+----+----+----+ |
+ * | 00 | 03 | 'f'| 'o'| 'o'| <---+
+ * +----+----+----+----+----+ |
+ * 5 6 7 8 9 10 |
+ * cflg clen |
+ * +----+----+----+----+----+ |
+ * | 00 | 03 | 'b'| 'a'| 'r'| <---+
+ * +----+----+----+----+----+
+ * 10 11 12 13 14 15
+ *
+ * - cflg : flag of component
+ * - clen : length of component
+ */
+ const char *sl;
+ char sl_last;
+
+ if (extra_space(&ctl) < 7)
+ bp = extra_next_record(&ctl, 7);
+ sl = file->symlink.s;
+ sl_last = '\0';
+ if (bp != NULL) {
+ bp[1] = 'S';
+ bp[2] = 'L';
+ bp[4] = 1; /* version */
+ }
+ for (;;) {
+ unsigned char *nc, *cf, *cl, cldmy = 0;
+ int sllen, slmax;
+
+ slmax = extra_space(&ctl);
+ if (slmax > 0xff)
+ slmax = 0xff;
+ if (bp != NULL)
+ nc = &bp[6];
+ else
+ nc = NULL;
+ cf = cl = NULL;
+ sllen = 0;
+ while (*sl && sllen + 11 < slmax) {
+ if (sl_last == '\0' && sl[0] == '/') {
+ /*
+ * flg len
+ * +----+----+
+ * | 08 | 00 | ROOT component.
+ * +----+----+ ("/")
+ *
+ * Root component has to appear
+ * at the first component only.
+ */
+ if (nc != NULL) {
+ cf = nc++;
+ *cf = 0x08; /* ROOT */
+ *nc++ = 0;
+ }
+ sllen += 2;
+ sl++;
+ sl_last = '/';
+ cl = NULL;
+ continue;
+ }
+ if (((sl_last == '\0' || sl_last == '/') &&
+ sl[0] == '.' && sl[1] == '.' &&
+ (sl[2] == '/' || sl[2] == '\0')) ||
+ (sl[0] == '/' &&
+ sl[1] == '.' && sl[2] == '.' &&
+ (sl[3] == '/' || sl[3] == '\0'))) {
+ /*
+ * flg len
+ * +----+----+
+ * | 04 | 00 | PARENT component.
+ * +----+----+ ("..")
+ */
+ if (nc != NULL) {
+ cf = nc++;
+ *cf = 0x04; /* PARENT */
+ *nc++ = 0;
+ }
+ sllen += 2;
+ if (sl[0] == '/')
+ sl += 3;/* skip "/.." */
+ else
+ sl += 2;/* skip ".." */
+ sl_last = '.';
+ cl = NULL;
+ continue;
+ }
+ if (((sl_last == '\0' || sl_last == '/') &&
+ sl[0] == '.' &&
+ (sl[1] == '/' || sl[1] == '\0')) ||
+ (sl[0] == '/' && sl[1] == '.' &&
+ (sl[2] == '/' || sl[2] == '\0'))) {
+ /*
+ * flg len
+ * +----+----+
+ * | 02 | 00 | CURRENT component.
+ * +----+----+ (".")
+ */
+ if (nc != NULL) {
+ cf = nc++;
+ *cf = 0x02; /* CURRENT */
+ *nc++ = 0;
+ }
+ sllen += 2;
+ if (sl[0] == '/')
+ sl += 2;/* skip "/." */
+ else
+ sl ++; /* skip "." */
+ sl_last = '.';
+ cl = NULL;
+ continue;
+ }
+ if (sl[0] == '/' || cl == NULL) {
+ if (nc != NULL) {
+ cf = nc++;
+ *cf = 0;
+ cl = nc++;
+ *cl = 0;
+ } else
+ cl = &cldmy;
+ sllen += 2;
+ if (sl[0] == '/') {
+ sl_last = *sl++;
+ continue;
+ }
+ }
+ sl_last = *sl++;
+ if (nc != NULL) {
+ *nc++ = sl_last;
+ (*cl) ++;
+ }
+ sllen++;
+ }
+ if (*sl) {
+ length = 5 + sllen;
+ if (bp != NULL) {
+ /*
+ * Mark flg as CONTINUE component.
+ */
+ *cf |= 0x01;
+ /*
+ * len ver flg
+ * +----+----+----+----+----+-
+ * | 'S'| 'L'| XX | 01 | 01 |
+ * +----+----+----+----+----+-
+ * ^
+ * continues in next "SL"
+ */
+ bp[3] = length;
+ bp[5] = 0x01;/* This Symbolic Link
+ * continues in next
+ * "SL" field */
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ if (extra_space(&ctl) < 11)
+ bp = extra_next_record(&ctl, 11);
+ if (bp != NULL) {
+ /* Next 'SL' */
+ bp[1] = 'S';
+ bp[2] = 'L';
+ bp[4] = 1; /* version */
+ }
+ } else {
+ length = 5 + sllen;
+ if (bp != NULL) {
+ bp[3] = length;
+ bp[5] = 0;
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ break;
+ }
+ }
+ }
+
+ /* Write "TF" System Use Entry. */
+ if (rr_flag & RR_USE_TF) {
+ /*
+ * "TF" Format:
+ * len ver
+ * +----+----+----+----+-----+-------------+
+ * | 'T'| 'F'| XX | 01 |FLAGS| TIME STAMPS |
+ * +----+----+----+----+-----+-------------+
+ * 0 1 2 3 4 5 XX
+ * TIME STAMPS : ISO 9660 Standard 9.1.5.
+ * If TF_LONG_FORM FLAGS is set,
+ * use ISO9660 Standard 8.4.26.1.
+ */
+#define TF_CREATION 0x01 /* Creation time recorded */
+#define TF_MODIFY 0x02 /* Modification time recorded */
+#define TF_ACCESS 0x04 /* Last Access time recorded */
+#define TF_ATTRIBUTES 0x08 /* Last Attribute Change time recorded */
+#define TF_BACKUP 0x10 /* Last Backup time recorded */
+#define TF_EXPIRATION 0x20 /* Expiration time recorded */
+#define TF_EFFECTIVE 0x40 /* Effective time recorded */
+#define TF_LONG_FORM 0x80 /* ISO 9660 17-byte time format used */
+ unsigned char tf_flags;
+
+ length = 5;
+ tf_flags = 0;
+#ifndef COMPAT_MKISOFS
+ if (archive_entry_birthtime_is_set(file->entry) &&
+ archive_entry_birthtime(file->entry) <=
+ archive_entry_mtime(file->entry)) {
+ length += 7;
+ tf_flags |= TF_CREATION;
+ }
+#endif
+ if (archive_entry_mtime_is_set(file->entry)) {
+ length += 7;
+ tf_flags |= TF_MODIFY;
+ }
+ if (archive_entry_atime_is_set(file->entry)) {
+ length += 7;
+ tf_flags |= TF_ACCESS;
+ }
+ if (archive_entry_ctime_is_set(file->entry)) {
+ length += 7;
+ tf_flags |= TF_ATTRIBUTES;
+ }
+ if (extra_space(&ctl) < length)
+ bp = extra_next_record(&ctl, length);
+ if (bp != NULL) {
+ bp[1] = 'T';
+ bp[2] = 'F';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ bp[5] = tf_flags;
+ bp += 5;
+ /* Creation time */
+ if (tf_flags & TF_CREATION) {
+ set_time_915(bp+1,
+ archive_entry_birthtime(file->entry));
+ bp += 7;
+ }
+ /* Modification time */
+ if (tf_flags & TF_MODIFY) {
+ set_time_915(bp+1,
+ archive_entry_mtime(file->entry));
+ bp += 7;
+ }
+ /* Last Access time */
+ if (tf_flags & TF_ACCESS) {
+ set_time_915(bp+1,
+ archive_entry_atime(file->entry));
+ bp += 7;
+ }
+ /* Last Attribute Change time */
+ if (tf_flags & TF_ATTRIBUTES) {
+ set_time_915(bp+1,
+ archive_entry_ctime(file->entry));
+ bp += 7;
+ }
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "RE" System Use Entry. */
+ if (rr_flag & RR_USE_RE) {
+ /*
+ * "RE" Format:
+ * len ver
+ * +----+----+----+----+
+ * | 'R'| 'E'| 04 | 01 |
+ * +----+----+----+----+
+ * 0 1 2 3 4
+ */
+ length = 4;
+ if (extra_space(&ctl) < length)
+ bp = extra_next_record(&ctl, length);
+ if (bp != NULL) {
+ bp[1] = 'R';
+ bp[2] = 'E';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "PL" System Use Entry. */
+ if (rr_flag & RR_USE_PL) {
+ /*
+ * "PL" Format:
+ * len ver
+ * +----+----+----+----+------------+
+ * | 'P'| 'L'| 0C | 01 | *LOCATION |
+ * +----+----+----+----+------------+
+ * 0 1 2 3 4 12
+ * *LOCATION: location of parent directory
+ */
+ length = 12;
+ if (extra_space(&ctl) < length)
+ bp = extra_next_record(&ctl, length);
+ if (bp != NULL) {
+ bp[1] = 'P';
+ bp[2] = 'L';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ set_num_733(bp + 5,
+ rr_parent->dir_location);
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "CL" System Use Entry. */
+ if (rr_flag & RR_USE_CL) {
+ /*
+ * "CL" Format:
+ * len ver
+ * +----+----+----+----+------------+
+ * | 'C'| 'L'| 0C | 01 | *LOCATION |
+ * +----+----+----+----+------------+
+ * 0 1 2 3 4 12
+ * *LOCATION: location of child directory
+ */
+ length = 12;
+ if (extra_space(&ctl) < length)
+ bp = extra_next_record(&ctl, length);
+ if (bp != NULL) {
+ bp[1] = 'C';
+ bp[2] = 'L';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ set_num_733(bp + 5,
+ isoent->rr_child->dir_location);
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "PN" System Use Entry. */
+ if (rr_flag & RR_USE_PN) {
+ /*
+ * "PN" Format:
+ * len ver
+ * +----+----+----+----+------------+------------+
+ * | 'P'| 'N'| 14 | 01 | dev_t high | dev_t low |
+ * +----+----+----+----+------------+------------+
+ * 0 1 2 3 4 12 20
+ */
+ length = 20;
+ if (extra_space(&ctl) < length)
+ bp = extra_next_record(&ctl, length);
+ if (bp != NULL) {
+ uint64_t dev;
+
+ bp[1] = 'P';
+ bp[2] = 'N';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ dev = (uint64_t)archive_entry_rdev(file->entry);
+ set_num_733(bp + 5, (uint32_t)(dev >> 32));
+ set_num_733(bp + 13, (uint32_t)(dev & 0xFFFFFFFF));
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "ZF" System Use Entry. */
+ if (file->zisofs.header_size) {
+ /*
+ * "ZF" Format:
+ * len ver
+ * +----+----+----+----+----+----+-------------+
+ * | 'Z'| 'F'| 10 | 01 | 'p'| 'z'| Header Size |
+ * +----+----+----+----+----+----+-------------+
+ * 0 1 2 3 4 5 6 7
+ * +--------------------+-------------------+
+ * | Log2 of block Size | Uncompressed Size |
+ * +--------------------+-------------------+
+ * 7 8 16
+ */
+ length = 16;
+ if (extra_space(&ctl) < length)
+ bp = extra_next_record(&ctl, length);
+ if (bp != NULL) {
+ bp[1] = 'Z';
+ bp[2] = 'F';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ bp[5] = 'p';
+ bp[6] = 'z';
+ bp[7] = file->zisofs.header_size;
+ bp[8] = file->zisofs.log2_bs;
+ set_num_733(bp + 9, file->zisofs.uncompressed_size);
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "CE" System Use Entry. */
+ if (t == DIR_REC_SELF && isoent == isoent->parent) {
+ length = RR_CE_SIZE;
+ if (bp != NULL)
+ set_SUSP_CE(bp+1, iso9660->location_rrip_er,
+ 0, RRIP_ER_SIZE);
+ extra_tell_used_size(&ctl, length);
+ }
+
+ extra_close_record(&ctl, 0);
+
+ return (ctl.dr_len);
+}
+
+/*
+ * Write data of a Directory Record or calculate writing bytes itself.
+ * If parameter `p' is NULL, calculates the size of writing data, which
+ * a Directory Record needs to write, then it saved and return
+ * the calculated size.
+ * Parameter `n' is a remaining size of buffer. when parameter `p' is
+ * not NULL, check whether that `n' is not less than the saved size.
+ * if that `n' is small, return zero.
+ *
+ * This format of the Directory Record is according to
+ * ISO9660 Standard 9.1
+ */
+static int
+set_directory_record(unsigned char *p, size_t n, struct isoent *isoent,
+ struct iso9660 *iso9660, enum dir_rec_type t,
+ enum vdd_type vdd_type)
+{
+ unsigned char *bp;
+ size_t dr_len;
+ size_t fi_len;
+
+ if (p != NULL) {
+ /*
+ * Check whether a write buffer size is less than the
+ * saved size which is needed to write this Directory
+ * Record.
+ */
+ switch (t) {
+ case DIR_REC_VD:
+ dr_len = isoent->dr_len.vd; break;
+ case DIR_REC_SELF:
+ dr_len = isoent->dr_len.self; break;
+ case DIR_REC_PARENT:
+ dr_len = isoent->dr_len.parent; break;
+ case DIR_REC_NORMAL:
+ default:
+ dr_len = isoent->dr_len.normal; break;
+ }
+ if (dr_len > n)
+ return (0);/* Needs more buffer size. */
+ }
+
+ if (t == DIR_REC_NORMAL && isoent->identifier != NULL)
+ fi_len = isoent->id_len;
+ else
+ fi_len = 1;
+
+ if (p != NULL) {
+ struct isoent *xisoent;
+ struct isofile *file;
+ unsigned char flag;
+
+ if (t == DIR_REC_PARENT)
+ xisoent = isoent->parent;
+ else
+ xisoent = isoent;
+ file = isoent->file;
+ if (file->hardlink_target != NULL)
+ file = file->hardlink_target;
+ /* Make a file flag. */
+ if (xisoent->dir)
+ flag = FILE_FLAG_DIRECTORY;
+ else {
+ if (file->cur_content->next != NULL)
+ flag = FILE_FLAG_MULTI_EXTENT;
+ else
+ flag = 0;
+ }
+
+ bp = p -1;
+ /* Extended Attribute Record Length */
+ set_num_711(bp+2, 0);
+ /* Location of Extent */
+ if (xisoent->dir)
+ set_num_733(bp+3, xisoent->dir_location);
+ else
+ set_num_733(bp+3, file->cur_content->location);
+ /* Data Length */
+ if (xisoent->dir)
+ set_num_733(bp+11,
+ xisoent->dir_block * LOGICAL_BLOCK_SIZE);
+ else
+ set_num_733(bp+11, (uint32_t)file->cur_content->size);
+ /* Recording Date and Time */
+ /* NOTE:
+ * If a file type is symbolic link, you are seeing this
+ * field value is different from a value mkisofs makes.
+ * libarchive uses lstat to get this one, but it
+ * seems mkisofs uses stat to get.
+ */
+ set_time_915(bp+19,
+ archive_entry_mtime(xisoent->file->entry));
+ /* File Flags */
+ bp[26] = flag;
+ /* File Unit Size */
+ set_num_711(bp+27, 0);
+ /* Interleave Gap Size */
+ set_num_711(bp+28, 0);
+ /* Volume Sequence Number */
+ set_num_723(bp+29, iso9660->volume_sequence_number);
+ /* Length of File Identifier */
+ set_num_711(bp+33, (unsigned char)fi_len);
+ /* File Identifier */
+ switch (t) {
+ case DIR_REC_VD:
+ case DIR_REC_SELF:
+ set_num_711(bp+34, 0);
+ break;
+ case DIR_REC_PARENT:
+ set_num_711(bp+34, 1);
+ break;
+ case DIR_REC_NORMAL:
+ if (isoent->identifier != NULL)
+ memcpy(bp+34, isoent->identifier, fi_len);
+ else
+ set_num_711(bp+34, 0);
+ break;
+ }
+ } else
+ bp = NULL;
+ dr_len = 33 + fi_len;
+ /* Padding Field */
+ if (dr_len & 0x01) {
+ dr_len ++;
+ if (p != NULL)
+ bp[dr_len] = 0;
+ }
+
+ /* Volume Descriptor does not record extension. */
+ if (t == DIR_REC_VD) {
+ if (p != NULL)
+ /* Length of Directory Record */
+ set_num_711(p, (unsigned char)dr_len);
+ else
+ isoent->dr_len.vd = (int)dr_len;
+ return ((int)dr_len);
+ }
+
+ /* Rockridge */
+ if (iso9660->opt.rr && vdd_type != VDD_JOLIET)
+ dr_len = set_directory_record_rr(bp, (int)dr_len,
+ isoent, iso9660, t);
+
+ if (p != NULL)
+ /* Length of Directory Record */
+ set_num_711(p, (unsigned char)dr_len);
+ else {
+ /*
+ * Save the size which is needed to write this
+ * Directory Record.
+ */
+ switch (t) {
+ case DIR_REC_VD:
+ /* This case does not come, but compiler
+ * complains that DIR_REC_VD not handled
+ * in switch .... */
+ break;
+ case DIR_REC_SELF:
+ isoent->dr_len.self = (int)dr_len; break;
+ case DIR_REC_PARENT:
+ isoent->dr_len.parent = (int)dr_len; break;
+ case DIR_REC_NORMAL:
+ isoent->dr_len.normal = (int)dr_len; break;
+ }
+ }
+
+ return ((int)dr_len);
+}
+
+/*
+ * Calculate the size of a directory record.
+ */
+static inline int
+get_dir_rec_size(struct iso9660 *iso9660, struct isoent *isoent,
+ enum dir_rec_type t, enum vdd_type vdd_type)
+{
+
+ return (set_directory_record(NULL, SIZE_MAX,
+ isoent, iso9660, t, vdd_type));
+}
+
+/*
+ * Manage to write ISO-image data with wbuff to reduce calling
+ * __archive_write_output() for performance.
+ */
+
+
+static inline unsigned char *
+wb_buffptr(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+
+ return (&(iso9660->wbuff[sizeof(iso9660->wbuff)
+ - iso9660->wbuff_remaining]));
+}
+
+static int
+wb_write_out(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+ size_t wsize, nw;
+ int r;
+
+ wsize = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining;
+ nw = wsize % LOGICAL_BLOCK_SIZE;
+ if (iso9660->wbuff_type == WB_TO_STREAM)
+ r = __archive_write_output(a, iso9660->wbuff, wsize - nw);
+ else
+ r = write_to_temp(a, iso9660->wbuff, wsize - nw);
+ /* Increase the offset. */
+ iso9660->wbuff_offset += wsize - nw;
+ if (iso9660->wbuff_offset > iso9660->wbuff_written)
+ iso9660->wbuff_written = iso9660->wbuff_offset;
+ iso9660->wbuff_remaining = sizeof(iso9660->wbuff);
+ if (nw) {
+ iso9660->wbuff_remaining -= nw;
+ memmove(iso9660->wbuff, iso9660->wbuff + wsize - nw, nw);
+ }
+ return (r);
+}
+
+static int
+wb_consume(struct archive_write *a, size_t size)
+{
+ struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+
+ if (size > iso9660->wbuff_remaining ||
+ iso9660->wbuff_remaining == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Internal Programming error: iso9660:wb_consume()"
+ " size=%jd, wbuff_remaining=%jd",
+ (intmax_t)size, (intmax_t)iso9660->wbuff_remaining);
+ return (ARCHIVE_FATAL);
+ }
+ iso9660->wbuff_remaining -= size;
+ if (iso9660->wbuff_remaining < LOGICAL_BLOCK_SIZE)
+ return (wb_write_out(a));
+ return (ARCHIVE_OK);
+}
+
+#ifdef HAVE_ZLIB_H
+
+static int
+wb_set_offset(struct archive_write *a, int64_t off)
+{
+ struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+ int64_t used, ext_bytes;
+
+ if (iso9660->wbuff_type != WB_TO_TEMP) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Internal Programming error: iso9660:wb_set_offset()");
+ return (ARCHIVE_FATAL);
+ }
+
+ used = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining;
+ if (iso9660->wbuff_offset + used > iso9660->wbuff_tail)
+ iso9660->wbuff_tail = iso9660->wbuff_offset + used;
+ if (iso9660->wbuff_offset < iso9660->wbuff_written) {
+ if (used > 0 &&
+ write_to_temp(a, iso9660->wbuff, (size_t)used) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ iso9660->wbuff_offset = iso9660->wbuff_written;
+ lseek(iso9660->temp_fd, iso9660->wbuff_offset, SEEK_SET);
+ iso9660->wbuff_remaining = sizeof(iso9660->wbuff);
+ used = 0;
+ }
+ if (off < iso9660->wbuff_offset) {
+ /*
+ * Write out waiting data.
+ */
+ if (used > 0) {
+ if (wb_write_out(a) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ lseek(iso9660->temp_fd, off, SEEK_SET);
+ iso9660->wbuff_offset = off;
+ iso9660->wbuff_remaining = sizeof(iso9660->wbuff);
+ } else if (off <= iso9660->wbuff_tail) {
+ iso9660->wbuff_remaining = (size_t)
+ (sizeof(iso9660->wbuff) - (off - iso9660->wbuff_offset));
+ } else {
+ ext_bytes = off - iso9660->wbuff_tail;
+ iso9660->wbuff_remaining = (size_t)(sizeof(iso9660->wbuff)
+ - (iso9660->wbuff_tail - iso9660->wbuff_offset));
+ while (ext_bytes >= (int64_t)iso9660->wbuff_remaining) {
+ if (write_null(a, (size_t)iso9660->wbuff_remaining)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ ext_bytes -= iso9660->wbuff_remaining;
+ }
+ if (ext_bytes > 0) {
+ if (write_null(a, (size_t)ext_bytes) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_ZLIB_H */
+
+static int
+write_null(struct archive_write *a, size_t size)
+{
+ size_t remaining;
+ unsigned char *p, *old;
+ int r;
+
+ remaining = wb_remaining(a);
+ p = wb_buffptr(a);
+ if (size <= remaining) {
+ memset(p, 0, size);
+ return (wb_consume(a, size));
+ }
+ memset(p, 0, remaining);
+ r = wb_consume(a, remaining);
+ if (r != ARCHIVE_OK)
+ return (r);
+ size -= remaining;
+ old = p;
+ p = wb_buffptr(a);
+ memset(p, 0, old - p);
+ remaining = wb_remaining(a);
+ while (size) {
+ size_t wsize = size;
+
+ if (wsize > remaining)
+ wsize = remaining;
+ r = wb_consume(a, wsize);
+ if (r != ARCHIVE_OK)
+ return (r);
+ size -= wsize;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Write Volume Descriptor Set Terminator
+ */
+static int
+write_VD_terminator(struct archive_write *a)
+{
+ unsigned char *bp;
+
+ bp = wb_buffptr(a) -1;
+ set_VD_bp(bp, VDT_TERMINATOR, 1);
+ set_unused_field_bp(bp, 8, LOGICAL_BLOCK_SIZE);
+
+ return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+static int
+set_file_identifier(unsigned char *bp, int from, int to, enum vdc vdc,
+ struct archive_write *a, struct vdd *vdd, struct archive_string *id,
+ const char *label, int leading_under, enum char_type char_type)
+{
+ char identifier[256];
+ struct isoent *isoent;
+ const char *ids;
+ size_t len;
+ int r;
+
+ if (id->length > 0 && leading_under && id->s[0] != '_') {
+ if (char_type == A_CHAR)
+ r = set_str_a_characters_bp(a, bp, from, to, id->s, vdc);
+ else
+ r = set_str_d_characters_bp(a, bp, from, to, id->s, vdc);
+ } else if (id->length > 0) {
+ ids = id->s;
+ if (leading_under)
+ ids++;
+ isoent = isoent_find_entry(vdd->rootent, ids);
+ if (isoent == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Not Found %s `%s'.",
+ label, ids);
+ return (ARCHIVE_FATAL);
+ }
+ len = isoent->ext_off + isoent->ext_len;
+ if (vdd->vdd_type == VDD_JOLIET) {
+ if (len > sizeof(identifier)-2)
+ len = sizeof(identifier)-2;
+ } else {
+ if (len > sizeof(identifier)-1)
+ len = sizeof(identifier)-1;
+ }
+ memcpy(identifier, isoent->identifier, len);
+ identifier[len] = '\0';
+ if (vdd->vdd_type == VDD_JOLIET) {
+ identifier[len+1] = 0;
+ vdc = VDC_UCS2_DIRECT;
+ }
+ if (char_type == A_CHAR)
+ r = set_str_a_characters_bp(a, bp, from, to,
+ identifier, vdc);
+ else
+ r = set_str_d_characters_bp(a, bp, from, to,
+ identifier, vdc);
+ } else {
+ if (char_type == A_CHAR)
+ r = set_str_a_characters_bp(a, bp, from, to, NULL, vdc);
+ else
+ r = set_str_d_characters_bp(a, bp, from, to, NULL, vdc);
+ }
+ return (r);
+}
+
+/*
+ * Write Primary/Supplementary Volume Descriptor
+ */
+static int
+write_VD(struct archive_write *a, struct vdd *vdd)
+{
+ struct iso9660 *iso9660;
+ unsigned char *bp;
+ uint16_t volume_set_size = 1;
+ char identifier[256];
+ enum VD_type vdt;
+ enum vdc vdc;
+ unsigned char vd_ver, fst_ver;
+ int r;
+
+ iso9660 = a->format_data;
+ switch (vdd->vdd_type) {
+ case VDD_JOLIET:
+ vdt = VDT_SUPPLEMENTARY;
+ vd_ver = fst_ver = 1;
+ vdc = VDC_UCS2;
+ break;
+ case VDD_ENHANCED:
+ vdt = VDT_SUPPLEMENTARY;
+ vd_ver = fst_ver = 2;
+ vdc = VDC_LOWERCASE;
+ break;
+ case VDD_PRIMARY:
+ default:
+ vdt = VDT_PRIMARY;
+ vd_ver = fst_ver = 1;
+#ifdef COMPAT_MKISOFS
+ vdc = VDC_LOWERCASE;
+#else
+ vdc = VDC_STD;
+#endif
+ break;
+ }
+
+ bp = wb_buffptr(a) -1;
+ /* Volume Descriptor Type */
+ set_VD_bp(bp, vdt, vd_ver);
+ /* Unused Field */
+ set_unused_field_bp(bp, 8, 8);
+ /* System Identifier */
+ get_system_identitier(identifier, sizeof(identifier));
+ r = set_str_a_characters_bp(a, bp, 9, 40, identifier, vdc);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Volume Identifier */
+ r = set_str_d_characters_bp(a, bp, 41, 72,
+ iso9660->volume_identifier.s, vdc);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Unused Field */
+ set_unused_field_bp(bp, 73, 80);
+ /* Volume Space Size */
+ set_num_733(bp+81, iso9660->volume_space_size);
+ if (vdd->vdd_type == VDD_JOLIET) {
+ /* Escape Sequences */
+ bp[89] = 0x25;/* UCS-2 Level 3 */
+ bp[90] = 0x2F;
+ bp[91] = 0x45;
+ memset(bp + 92, 0, 120 - 92 + 1);
+ } else {
+ /* Unused Field */
+ set_unused_field_bp(bp, 89, 120);
+ }
+ /* Volume Set Size */
+ set_num_723(bp+121, volume_set_size);
+ /* Volume Sequence Number */
+ set_num_723(bp+125, iso9660->volume_sequence_number);
+ /* Logical Block Size */
+ set_num_723(bp+129, LOGICAL_BLOCK_SIZE);
+ /* Path Table Size */
+ set_num_733(bp+133, vdd->path_table_size);
+ /* Location of Occurrence of Type L Path Table */
+ set_num_731(bp+141, vdd->location_type_L_path_table);
+ /* Location of Optional Occurrence of Type L Path Table */
+ set_num_731(bp+145, 0);
+ /* Location of Occurrence of Type M Path Table */
+ set_num_732(bp+149, vdd->location_type_M_path_table);
+ /* Location of Optional Occurrence of Type M Path Table */
+ set_num_732(bp+153, 0);
+ /* Directory Record for Root Directory(BP 157 to 190) */
+ set_directory_record(bp+157, 190-157+1, vdd->rootent,
+ iso9660, DIR_REC_VD, vdd->vdd_type);
+ /* Volume Set Identifier */
+ r = set_str_d_characters_bp(a, bp, 191, 318, "", vdc);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Publisher Identifier */
+ r = set_file_identifier(bp, 319, 446, vdc, a, vdd,
+ &(iso9660->publisher_identifier),
+ "Publisher File", 1, A_CHAR);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Data Preparer Identifier */
+ r = set_file_identifier(bp, 447, 574, vdc, a, vdd,
+ &(iso9660->data_preparer_identifier),
+ "Data Preparer File", 1, A_CHAR);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Application Identifier */
+ r = set_file_identifier(bp, 575, 702, vdc, a, vdd,
+ &(iso9660->application_identifier),
+ "Application File", 1, A_CHAR);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Copyright File Identifier */
+ r = set_file_identifier(bp, 703, 739, vdc, a, vdd,
+ &(iso9660->copyright_file_identifier),
+ "Copyright File", 0, D_CHAR);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Abstract File Identifier */
+ r = set_file_identifier(bp, 740, 776, vdc, a, vdd,
+ &(iso9660->abstract_file_identifier),
+ "Abstract File", 0, D_CHAR);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Bibliographic File Identifier */
+ r = set_file_identifier(bp, 777, 813, vdc, a, vdd,
+ &(iso9660->bibliographic_file_identifier),
+ "Bibliongraphic File", 0, D_CHAR);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Volume Creation Date and Time */
+ set_date_time(bp+814, iso9660->birth_time);
+ /* Volume Modification Date and Time */
+ set_date_time(bp+831, iso9660->birth_time);
+ /* Volume Expiration Date and Time(obsolete) */
+ set_date_time_null(bp+848);
+ /* Volume Effective Date and Time */
+ set_date_time(bp+865, iso9660->birth_time);
+ /* File Structure Version */
+ bp[882] = fst_ver;
+ /* Reserved */
+ bp[883] = 0;
+ /* Application Use */
+ memset(bp + 884, 0x20, 1395 - 884 + 1);
+ /* Reserved */
+ set_unused_field_bp(bp, 1396, LOGICAL_BLOCK_SIZE);
+
+ return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+/*
+ * Write Boot Record Volume Descriptor
+ */
+static int
+write_VD_boot_record(struct archive_write *a)
+{
+ struct iso9660 *iso9660;
+ unsigned char *bp;
+
+ iso9660 = a->format_data;
+ bp = wb_buffptr(a) -1;
+ /* Volume Descriptor Type */
+ set_VD_bp(bp, VDT_BOOT_RECORD, 1);
+ /* Boot System Identifier */
+ memcpy(bp+8, "EL TORITO SPECIFICATION", 23);
+ set_unused_field_bp(bp, 8+23, 39);
+ /* Unused */
+ set_unused_field_bp(bp, 40, 71);
+ /* Absolute pointer to first sector of Boot Catalog */
+ set_num_731(bp+72,
+ iso9660->el_torito.catalog->file->content.location);
+ /* Unused */
+ set_unused_field_bp(bp, 76, LOGICAL_BLOCK_SIZE);
+
+ return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+enum keytype {
+ KEY_FLG,
+ KEY_STR,
+ KEY_INT,
+ KEY_HEX
+};
+static void
+set_option_info(struct archive_string *info, int *opt, const char *key,
+ enum keytype type, ...)
+{
+ va_list ap;
+ char prefix;
+ const char *s;
+ int d;
+
+ prefix = (*opt==0)? ' ':',';
+ va_start(ap, type);
+ switch (type) {
+ case KEY_FLG:
+ d = va_arg(ap, int);
+ archive_string_sprintf(info, "%c%s%s",
+ prefix, (d == 0)?"!":"", key);
+ break;
+ case KEY_STR:
+ s = va_arg(ap, const char *);
+ archive_string_sprintf(info, "%c%s=%s",
+ prefix, key, s);
+ break;
+ case KEY_INT:
+ d = va_arg(ap, int);
+ archive_string_sprintf(info, "%c%s=%d",
+ prefix, key, d);
+ break;
+ case KEY_HEX:
+ d = va_arg(ap, int);
+ archive_string_sprintf(info, "%c%s=%x",
+ prefix, key, d);
+ break;
+ }
+ va_end(ap);
+
+ *opt = 1;
+}
+
+/*
+ * Make Non-ISO File System Information
+ */
+static int
+write_information_block(struct archive_write *a)
+{
+ struct iso9660 *iso9660;
+ char buf[128];
+ const char *v;
+ int opt, r;
+ struct archive_string info;
+ size_t info_size = LOGICAL_BLOCK_SIZE *
+ NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK;
+
+ iso9660 = (struct iso9660 *)a->format_data;
+ if (info_size > wb_remaining(a)) {
+ r = wb_write_out(a);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ archive_string_init(&info);
+ if (archive_string_ensure(&info, info_size) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ memset(info.s, 0, info_size);
+ opt = 0;
+#if defined(HAVE_CTIME_S)
+ ctime_s(buf, sizeof(buf), &(iso9660->birth_time));
+#elif defined(HAVE_CTIME_R)
+ ctime_r(&(iso9660->birth_time), buf);
+#else
+ strncpy(buf, ctime(&(iso9660->birth_time)), sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+#endif
+ archive_string_sprintf(&info,
+ "INFO %s%s", buf, archive_version_string());
+ if (iso9660->opt.abstract_file != OPT_ABSTRACT_FILE_DEFAULT)
+ set_option_info(&info, &opt, "abstract-file",
+ KEY_STR, iso9660->abstract_file_identifier.s);
+ if (iso9660->opt.application_id != OPT_APPLICATION_ID_DEFAULT)
+ set_option_info(&info, &opt, "application-id",
+ KEY_STR, iso9660->application_identifier.s);
+ if (iso9660->opt.allow_vernum != OPT_ALLOW_VERNUM_DEFAULT)
+ set_option_info(&info, &opt, "allow-vernum",
+ KEY_FLG, iso9660->opt.allow_vernum);
+ if (iso9660->opt.biblio_file != OPT_BIBLIO_FILE_DEFAULT)
+ set_option_info(&info, &opt, "biblio-file",
+ KEY_STR, iso9660->bibliographic_file_identifier.s);
+ if (iso9660->opt.boot != OPT_BOOT_DEFAULT)
+ set_option_info(&info, &opt, "boot",
+ KEY_STR, iso9660->el_torito.boot_filename.s);
+ if (iso9660->opt.boot_catalog != OPT_BOOT_CATALOG_DEFAULT)
+ set_option_info(&info, &opt, "boot-catalog",
+ KEY_STR, iso9660->el_torito.catalog_filename.s);
+ if (iso9660->opt.boot_info_table != OPT_BOOT_INFO_TABLE_DEFAULT)
+ set_option_info(&info, &opt, "boot-info-table",
+ KEY_FLG, iso9660->opt.boot_info_table);
+ if (iso9660->opt.boot_load_seg != OPT_BOOT_LOAD_SEG_DEFAULT)
+ set_option_info(&info, &opt, "boot-load-seg",
+ KEY_HEX, iso9660->el_torito.boot_load_seg);
+ if (iso9660->opt.boot_load_size != OPT_BOOT_LOAD_SIZE_DEFAULT)
+ set_option_info(&info, &opt, "boot-load-size",
+ KEY_INT, iso9660->el_torito.boot_load_size);
+ if (iso9660->opt.boot_type != OPT_BOOT_TYPE_DEFAULT) {
+ v = "no-emulation";
+ if (iso9660->opt.boot_type == OPT_BOOT_TYPE_FD)
+ v = "fd";
+ if (iso9660->opt.boot_type == OPT_BOOT_TYPE_HARD_DISK)
+ v = "hard-disk";
+ set_option_info(&info, &opt, "boot-type",
+ KEY_STR, v);
+ }
+#ifdef HAVE_ZLIB_H
+ if (iso9660->opt.compression_level != OPT_COMPRESSION_LEVEL_DEFAULT)
+ set_option_info(&info, &opt, "compression-level",
+ KEY_INT, iso9660->zisofs.compression_level);
+#endif
+ if (iso9660->opt.copyright_file != OPT_COPYRIGHT_FILE_DEFAULT)
+ set_option_info(&info, &opt, "copyright-file",
+ KEY_STR, iso9660->copyright_file_identifier.s);
+ if (iso9660->opt.iso_level != OPT_ISO_LEVEL_DEFAULT)
+ set_option_info(&info, &opt, "iso-level",
+ KEY_INT, iso9660->opt.iso_level);
+ if (iso9660->opt.joliet != OPT_JOLIET_DEFAULT) {
+ if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME)
+ set_option_info(&info, &opt, "joliet",
+ KEY_STR, "long");
+ else
+ set_option_info(&info, &opt, "joliet",
+ KEY_FLG, iso9660->opt.joliet);
+ }
+ if (iso9660->opt.limit_depth != OPT_LIMIT_DEPTH_DEFAULT)
+ set_option_info(&info, &opt, "limit-depth",
+ KEY_FLG, iso9660->opt.limit_depth);
+ if (iso9660->opt.limit_dirs != OPT_LIMIT_DIRS_DEFAULT)
+ set_option_info(&info, &opt, "limit-dirs",
+ KEY_FLG, iso9660->opt.limit_dirs);
+ if (iso9660->opt.pad != OPT_PAD_DEFAULT)
+ set_option_info(&info, &opt, "pad",
+ KEY_FLG, iso9660->opt.pad);
+ if (iso9660->opt.publisher != OPT_PUBLISHER_DEFAULT)
+ set_option_info(&info, &opt, "publisher",
+ KEY_STR, iso9660->publisher_identifier.s);
+ if (iso9660->opt.rr != OPT_RR_DEFAULT) {
+ if (iso9660->opt.rr == OPT_RR_DISABLED)
+ set_option_info(&info, &opt, "rockridge",
+ KEY_FLG, iso9660->opt.rr);
+ else if (iso9660->opt.rr == OPT_RR_STRICT)
+ set_option_info(&info, &opt, "rockridge",
+ KEY_STR, "strict");
+ else if (iso9660->opt.rr == OPT_RR_USEFUL)
+ set_option_info(&info, &opt, "rockridge",
+ KEY_STR, "useful");
+ }
+ if (iso9660->opt.volume_id != OPT_VOLUME_ID_DEFAULT)
+ set_option_info(&info, &opt, "volume-id",
+ KEY_STR, iso9660->volume_identifier.s);
+ if (iso9660->opt.zisofs != OPT_ZISOFS_DEFAULT)
+ set_option_info(&info, &opt, "zisofs",
+ KEY_FLG, iso9660->opt.zisofs);
+
+ memcpy(wb_buffptr(a), info.s, info_size);
+ archive_string_free(&info);
+ return (wb_consume(a, info_size));
+}
+
+static int
+write_rr_ER(struct archive_write *a)
+{
+ unsigned char *p;
+
+ p = wb_buffptr(a);
+
+ memset(p, 0, LOGICAL_BLOCK_SIZE);
+ p[0] = 'E';
+ p[1] = 'R';
+ p[3] = 0x01;
+ p[2] = RRIP_ER_SIZE;
+ p[4] = RRIP_ER_ID_SIZE;
+ p[5] = RRIP_ER_DSC_SIZE;
+ p[6] = RRIP_ER_SRC_SIZE;
+ p[7] = 0x01;
+ memcpy(&p[8], rrip_identifier, p[4]);
+ memcpy(&p[8+p[4]], rrip_descriptor, p[5]);
+ memcpy(&p[8+p[4]+p[5]], rrip_source, p[6]);
+
+ return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+static void
+calculate_path_table_size(struct vdd *vdd)
+{
+ int depth, size;
+ struct path_table *pt;
+
+ pt = vdd->pathtbl;
+ size = 0;
+ for (depth = 0; depth < vdd->max_depth; depth++) {
+ struct isoent **ptbl;
+ int i, cnt;
+
+ if ((cnt = pt[depth].cnt) == 0)
+ break;
+
+ ptbl = pt[depth].sorted;
+ for (i = 0; i < cnt; i++) {
+ int len;
+
+ if (ptbl[i]->identifier == NULL)
+ len = 1; /* root directory */
+ else
+ len = ptbl[i]->id_len;
+ if (len & 0x01)
+ len++; /* Padding Field */
+ size += 8 + len;
+ }
+ }
+ vdd->path_table_size = size;
+ vdd->path_table_block =
+ ((size + PATH_TABLE_BLOCK_SIZE -1) /
+ PATH_TABLE_BLOCK_SIZE) *
+ (PATH_TABLE_BLOCK_SIZE / LOGICAL_BLOCK_SIZE);
+}
+
+static int
+_write_path_table(struct archive_write *a, int type_m, int depth,
+ struct vdd *vdd)
+{
+ unsigned char *bp, *wb;
+ struct isoent **ptbl;
+ size_t wbremaining;
+ int i, r, wsize;
+
+ if (vdd->pathtbl[depth].cnt == 0)
+ return (0);
+
+ wsize = 0;
+ wb = wb_buffptr(a);
+ wbremaining = wb_remaining(a);
+ bp = wb - 1;
+ ptbl = vdd->pathtbl[depth].sorted;
+ for (i = 0; i < vdd->pathtbl[depth].cnt; i++) {
+ struct isoent *np;
+ size_t len;
+
+ np = ptbl[i];
+ if (np->identifier == NULL)
+ len = 1; /* root directory */
+ else
+ len = np->id_len;
+ if (wbremaining - ((bp+1) - wb) < (len + 1 + 8)) {
+ r = wb_consume(a, (bp+1) - wb);
+ if (r < 0)
+ return (r);
+ wb = wb_buffptr(a);
+ wbremaining = wb_remaining(a);
+ bp = wb -1;
+ }
+ /* Length of Directory Identifier */
+ set_num_711(bp+1, (unsigned char)len);
+ /* Extended Attribute Record Length */
+ set_num_711(bp+2, 0);
+ /* Location of Extent */
+ if (type_m)
+ set_num_732(bp+3, np->dir_location);
+ else
+ set_num_731(bp+3, np->dir_location);
+ /* Parent Directory Number */
+ if (type_m)
+ set_num_722(bp+7, np->parent->dir_number);
+ else
+ set_num_721(bp+7, np->parent->dir_number);
+ /* Directory Identifier */
+ if (np->identifier == NULL)
+ bp[9] = 0;
+ else
+ memcpy(&bp[9], np->identifier, len);
+ if (len & 0x01) {
+ /* Padding Field */
+ bp[9+len] = 0;
+ len++;
+ }
+ wsize += 8 + (int)len;
+ bp += 8 + len;
+ }
+ if ((bp + 1) > wb) {
+ r = wb_consume(a, (bp+1)-wb);
+ if (r < 0)
+ return (r);
+ }
+ return (wsize);
+}
+
+static int
+write_path_table(struct archive_write *a, int type_m, struct vdd *vdd)
+{
+ int depth, r;
+ size_t path_table_size;
+
+ r = ARCHIVE_OK;
+ path_table_size = 0;
+ for (depth = 0; depth < vdd->max_depth; depth++) {
+ r = _write_path_table(a, type_m, depth, vdd);
+ if (r < 0)
+ return (r);
+ path_table_size += r;
+ }
+
+ /* Write padding data. */
+ path_table_size = path_table_size % PATH_TABLE_BLOCK_SIZE;
+ if (path_table_size > 0)
+ r = write_null(a, PATH_TABLE_BLOCK_SIZE - path_table_size);
+ return (r);
+}
+
+static int
+calculate_directory_descriptors(struct iso9660 *iso9660, struct vdd *vdd,
+ struct isoent *isoent, int depth)
+{
+ struct isoent **enttbl;
+ int bs, block, i;
+
+ block = 1;
+ bs = get_dir_rec_size(iso9660, isoent, DIR_REC_SELF, vdd->vdd_type);
+ bs += get_dir_rec_size(iso9660, isoent, DIR_REC_PARENT, vdd->vdd_type);
+
+ if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET &&
+ !iso9660->opt.rr && depth + 1 >= vdd->max_depth))
+ return (block);
+
+ enttbl = isoent->children_sorted;
+ for (i = 0; i < isoent->children.cnt; i++) {
+ struct isoent *np = enttbl[i];
+ struct isofile *file;
+
+ file = np->file;
+ if (file->hardlink_target != NULL)
+ file = file->hardlink_target;
+ file->cur_content = &(file->content);
+ do {
+ int dr_l;
+
+ dr_l = get_dir_rec_size(iso9660, np, DIR_REC_NORMAL,
+ vdd->vdd_type);
+ if ((bs + dr_l) > LOGICAL_BLOCK_SIZE) {
+ block ++;
+ bs = dr_l;
+ } else
+ bs += dr_l;
+ file->cur_content = file->cur_content->next;
+ } while (file->cur_content != NULL);
+ }
+ return (block);
+}
+
+static int
+_write_directory_descriptors(struct archive_write *a, struct vdd *vdd,
+ struct isoent *isoent, int depth)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isoent **enttbl;
+ unsigned char *p, *wb;
+ int i, r;
+ int dr_l;
+
+ p = wb = wb_buffptr(a);
+#define WD_REMAINING (LOGICAL_BLOCK_SIZE - (p - wb))
+ p += set_directory_record(p, WD_REMAINING, isoent,
+ iso9660, DIR_REC_SELF, vdd->vdd_type);
+ p += set_directory_record(p, WD_REMAINING, isoent,
+ iso9660, DIR_REC_PARENT, vdd->vdd_type);
+
+ if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET &&
+ !iso9660->opt.rr && depth + 1 >= vdd->max_depth)) {
+ memset(p, 0, WD_REMAINING);
+ return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+ }
+
+ enttbl = isoent->children_sorted;
+ for (i = 0; i < isoent->children.cnt; i++) {
+ struct isoent *np = enttbl[i];
+ struct isofile *file = np->file;
+
+ if (file->hardlink_target != NULL)
+ file = file->hardlink_target;
+ file->cur_content = &(file->content);
+ do {
+ dr_l = set_directory_record(p, WD_REMAINING,
+ np, iso9660, DIR_REC_NORMAL,
+ vdd->vdd_type);
+ if (dr_l == 0) {
+ memset(p, 0, WD_REMAINING);
+ r = wb_consume(a, LOGICAL_BLOCK_SIZE);
+ if (r < 0)
+ return (r);
+ p = wb = wb_buffptr(a);
+ dr_l = set_directory_record(p,
+ WD_REMAINING, np, iso9660,
+ DIR_REC_NORMAL, vdd->vdd_type);
+ }
+ p += dr_l;
+ file->cur_content = file->cur_content->next;
+ } while (file->cur_content != NULL);
+ }
+ memset(p, 0, WD_REMAINING);
+ return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+static int
+write_directory_descriptors(struct archive_write *a, struct vdd *vdd)
+{
+ struct isoent *np;
+ int depth, r;
+
+ depth = 0;
+ np = vdd->rootent;
+ do {
+ struct extr_rec *extr;
+
+ r = _write_directory_descriptors(a, vdd, np, depth);
+ if (r < 0)
+ return (r);
+ if (vdd->vdd_type != VDD_JOLIET) {
+ /*
+ * This extract record is used by SUSP,RRIP.
+ * Not for joliet.
+ */
+ for (extr = np->extr_rec_list.first;
+ extr != NULL;
+ extr = extr->next) {
+ unsigned char *wb;
+
+ wb = wb_buffptr(a);
+ memcpy(wb, extr->buf, extr->offset);
+ memset(wb + extr->offset, 0,
+ LOGICAL_BLOCK_SIZE - extr->offset);
+ r = wb_consume(a, LOGICAL_BLOCK_SIZE);
+ if (r < 0)
+ return (r);
+ }
+ }
+
+ if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) {
+ /* Enter to sub directories. */
+ np = np->subdirs.first;
+ depth++;
+ continue;
+ }
+ while (np != np->parent) {
+ if (np->drnext == NULL) {
+ /* Return to the parent directory. */
+ np = np->parent;
+ depth--;
+ } else {
+ np = np->drnext;
+ break;
+ }
+ }
+ } while (np != np->parent);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Read file contents from the temporary file, and write it.
+ */
+static int
+write_file_contents(struct archive_write *a, int64_t offset, int64_t size)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ int r;
+
+ lseek(iso9660->temp_fd, offset, SEEK_SET);
+
+ while (size) {
+ size_t rsize;
+ ssize_t rs;
+ unsigned char *wb;
+
+ wb = wb_buffptr(a);
+ rsize = wb_remaining(a);
+ if (rsize > (size_t)size)
+ rsize = (size_t)size;
+ rs = read(iso9660->temp_fd, wb, rsize);
+ if (rs <= 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't read temporary file(%jd)", (intmax_t)rs);
+ return (ARCHIVE_FATAL);
+ }
+ size -= rs;
+ r = wb_consume(a, rs);
+ if (r < 0)
+ return (r);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+write_file_descriptors(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isofile *file;
+ int64_t blocks, offset;
+ int r;
+
+ blocks = 0;
+ offset = 0;
+
+ /* Make the boot catalog contents, and write it. */
+ if (iso9660->el_torito.catalog != NULL) {
+ r = make_boot_catalog(a);
+ if (r < 0)
+ return (r);
+ }
+
+ /* Write the boot file contents. */
+ if (iso9660->el_torito.boot != NULL) {
+ file = iso9660->el_torito.boot->file;
+ blocks = file->content.blocks;
+ offset = file->content.offset_of_temp;
+ if (offset != 0) {
+ r = write_file_contents(a, offset,
+ blocks << LOGICAL_BLOCK_BITS);
+ if (r < 0)
+ return (r);
+ blocks = 0;
+ offset = 0;
+ }
+ }
+
+ /* Write out all file contents. */
+ for (file = iso9660->data_file_list.first;
+ file != NULL; file = file->datanext) {
+
+ if (!file->write_content)
+ continue;
+
+ if ((offset + (blocks << LOGICAL_BLOCK_BITS)) <
+ file->content.offset_of_temp) {
+ if (blocks > 0) {
+ r = write_file_contents(a, offset,
+ blocks << LOGICAL_BLOCK_BITS);
+ if (r < 0)
+ return (r);
+ }
+ blocks = 0;
+ offset = file->content.offset_of_temp;
+ }
+
+ file->cur_content = &(file->content);
+ do {
+ blocks += file->cur_content->blocks;
+ /* Next fragment */
+ file->cur_content = file->cur_content->next;
+ } while (file->cur_content != NULL);
+ }
+
+ /* Flush out remaining blocks. */
+ if (blocks > 0) {
+ r = write_file_contents(a, offset,
+ blocks << LOGICAL_BLOCK_BITS);
+ if (r < 0)
+ return (r);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static void
+isofile_init_entry_list(struct iso9660 *iso9660)
+{
+ iso9660->all_file_list.first = NULL;
+ iso9660->all_file_list.last = &(iso9660->all_file_list.first);
+}
+
+static void
+isofile_add_entry(struct iso9660 *iso9660, struct isofile *file)
+{
+ file->allnext = NULL;
+ *iso9660->all_file_list.last = file;
+ iso9660->all_file_list.last = &(file->allnext);
+}
+
+static void
+isofile_free_all_entries(struct iso9660 *iso9660)
+{
+ struct isofile *file, *file_next;
+
+ file = iso9660->all_file_list.first;
+ while (file != NULL) {
+ file_next = file->allnext;
+ isofile_free(file);
+ file = file_next;
+ }
+}
+
+static void
+isofile_init_entry_data_file_list(struct iso9660 *iso9660)
+{
+ iso9660->data_file_list.first = NULL;
+ iso9660->data_file_list.last = &(iso9660->data_file_list.first);
+}
+
+static void
+isofile_add_data_file(struct iso9660 *iso9660, struct isofile *file)
+{
+ file->datanext = NULL;
+ *iso9660->data_file_list.last = file;
+ iso9660->data_file_list.last = &(file->datanext);
+}
+
+
+static struct isofile *
+isofile_new(struct archive_write *a, struct archive_entry *entry)
+{
+ struct isofile *file;
+
+ file = calloc(1, sizeof(*file));
+ if (file == NULL)
+ return (NULL);
+
+ if (entry != NULL)
+ file->entry = archive_entry_clone(entry);
+ else
+ file->entry = archive_entry_new2(&a->archive);
+ if (file->entry == NULL) {
+ free(file);
+ return (NULL);
+ }
+ archive_string_init(&(file->parentdir));
+ archive_string_init(&(file->basename));
+ archive_string_init(&(file->basename_utf16));
+ archive_string_init(&(file->symlink));
+ file->cur_content = &(file->content);
+
+ return (file);
+}
+
+static void
+isofile_free(struct isofile *file)
+{
+ struct content *con, *tmp;
+
+ con = file->content.next;
+ while (con != NULL) {
+ tmp = con;
+ con = con->next;
+ free(tmp);
+ }
+ archive_entry_free(file->entry);
+ archive_string_free(&(file->parentdir));
+ archive_string_free(&(file->basename));
+ archive_string_free(&(file->basename_utf16));
+ archive_string_free(&(file->symlink));
+ free(file);
+}
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+static int
+cleanup_backslash_1(char *p)
+{
+ int mb, dos;
+
+ mb = dos = 0;
+ while (*p) {
+ if (*(unsigned char *)p > 127)
+ mb = 1;
+ if (*p == '\\') {
+ /* If we have not met any multi-byte characters,
+ * we can replace '\' with '/'. */
+ if (!mb)
+ *p = '/';
+ dos = 1;
+ }
+ p++;
+ }
+ if (!mb || !dos)
+ return (0);
+ return (-1);
+}
+
+static void
+cleanup_backslash_2(wchar_t *p)
+{
+
+ /* Convert a path-separator from '\' to '/' */
+ while (*p != L'\0') {
+ if (*p == L'\\')
+ *p = L'/';
+ p++;
+ }
+}
+#endif
+
+/*
+ * Generate a parent directory name and a base name from a pathname.
+ */
+static int
+isofile_gen_utility_names(struct archive_write *a, struct isofile *file)
+{
+ struct iso9660 *iso9660;
+ const char *pathname;
+ char *p, *dirname, *slash;
+ size_t len;
+ int ret = ARCHIVE_OK;
+
+ iso9660 = a->format_data;
+
+ archive_string_empty(&(file->parentdir));
+ archive_string_empty(&(file->basename));
+ archive_string_empty(&(file->basename_utf16));
+ archive_string_empty(&(file->symlink));
+
+ pathname = archive_entry_pathname(file->entry);
+ if (pathname == NULL || pathname[0] == '\0') {/* virtual root */
+ file->dircnt = 0;
+ return (ret);
+ }
+
+ /*
+ * Make a UTF-16BE basename if Joliet extension enabled.
+ */
+ if (iso9660->opt.joliet) {
+ const char *u16, *ulast;
+ size_t u16len, ulen_last;
+
+ if (iso9660->sconv_to_utf16be == NULL) {
+ iso9660->sconv_to_utf16be =
+ archive_string_conversion_to_charset(
+ &(a->archive), "UTF-16BE", 1);
+ if (iso9660->sconv_to_utf16be == NULL)
+ /* Couldn't allocate memory */
+ return (ARCHIVE_FATAL);
+ iso9660->sconv_from_utf16be =
+ archive_string_conversion_from_charset(
+ &(a->archive), "UTF-16BE", 1);
+ if (iso9660->sconv_from_utf16be == NULL)
+ /* Couldn't allocate memory */
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Convert a filename to UTF-16BE.
+ */
+ if (0 > archive_entry_pathname_l(file->entry, &u16, &u16len,
+ iso9660->sconv_to_utf16be)) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for UTF-16BE");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "A filename cannot be converted to UTF-16BE;"
+ "You should disable making Joliet extension");
+ ret = ARCHIVE_WARN;
+ }
+
+ /*
+ * Make sure a path separator is not in the last;
+ * Remove trailing '/'.
+ */
+ while (u16len >= 2) {
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (u16[u16len-2] == 0 &&
+ (u16[u16len-1] == '/' || u16[u16len-1] == '\\'))
+#else
+ if (u16[u16len-2] == 0 && u16[u16len-1] == '/')
+#endif
+ {
+ u16len -= 2;
+ } else
+ break;
+ }
+
+ /*
+ * Find a basename in UTF-16BE.
+ */
+ ulast = u16;
+ u16len >>= 1;
+ ulen_last = u16len;
+ while (u16len > 0) {
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (u16[0] == 0 && (u16[1] == '/' || u16[1] == '\\'))
+#else
+ if (u16[0] == 0 && u16[1] == '/')
+#endif
+ {
+ ulast = u16 + 2;
+ ulen_last = u16len -1;
+ }
+ u16 += 2;
+ u16len --;
+ }
+ ulen_last <<= 1;
+ if (archive_string_ensure(&(file->basename_utf16),
+ ulen_last) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for UTF-16BE");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Set UTF-16BE basename.
+ */
+ memcpy(file->basename_utf16.s, ulast, ulen_last);
+ file->basename_utf16.length = ulen_last;
+ }
+
+ archive_strcpy(&(file->parentdir), pathname);
+#if defined(_WIN32) || defined(__CYGWIN__)
+ /*
+ * Convert a path-separator from '\' to '/'
+ */
+ if (cleanup_backslash_1(file->parentdir.s) != 0) {
+ const wchar_t *wp = archive_entry_pathname_w(file->entry);
+ struct archive_wstring ws;
+
+ if (wp != NULL) {
+ int r;
+ archive_string_init(&ws);
+ archive_wstrcpy(&ws, wp);
+ cleanup_backslash_2(ws.s);
+ archive_string_empty(&(file->parentdir));
+ r = archive_string_append_from_wcs(&(file->parentdir),
+ ws.s, ws.length);
+ archive_wstring_free(&ws);
+ if (r < 0 && errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ }
+#endif
+
+ len = file->parentdir.length;
+ p = dirname = file->parentdir.s;
+
+ /*
+ * Remove leading '/', '../' and './' elements
+ */
+ while (*p) {
+ if (p[0] == '/') {
+ p++;
+ len--;
+ } else if (p[0] != '.')
+ break;
+ else if (p[1] == '.' && p[2] == '/') {
+ p += 3;
+ len -= 3;
+ } else if (p[1] == '/' || (p[1] == '.' && p[2] == '\0')) {
+ p += 2;
+ len -= 2;
+ } else if (p[1] == '\0') {
+ p++;
+ len--;
+ } else
+ break;
+ }
+ if (p != dirname) {
+ memmove(dirname, p, len+1);
+ p = dirname;
+ }
+ /*
+ * Remove "/","/." and "/.." elements from tail.
+ */
+ while (len > 0) {
+ size_t ll = len;
+
+ if (len > 0 && p[len-1] == '/') {
+ p[len-1] = '\0';
+ len--;
+ }
+ if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {
+ p[len-2] = '\0';
+ len -= 2;
+ }
+ if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&
+ p[len-1] == '.') {
+ p[len-3] = '\0';
+ len -= 3;
+ }
+ if (ll == len)
+ break;
+ }
+ while (*p) {
+ if (p[0] == '/') {
+ if (p[1] == '/')
+ /* Convert '//' --> '/' */
+ memmove(p, p+1, strlen(p+1) + 1);
+ else if (p[1] == '.' && p[2] == '/')
+ /* Convert '/./' --> '/' */
+ memmove(p, p+2, strlen(p+2) + 1);
+ else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {
+ /* Convert 'dir/dir1/../dir2/'
+ * --> 'dir/dir2/'
+ */
+ char *rp = p -1;
+ while (rp >= dirname) {
+ if (*rp == '/')
+ break;
+ --rp;
+ }
+ if (rp > dirname) {
+ strcpy(rp, p+3);
+ p = rp;
+ } else {
+ strcpy(dirname, p+4);
+ p = dirname;
+ }
+ } else
+ p++;
+ } else
+ p++;
+ }
+ p = dirname;
+ len = strlen(p);
+
+ if (archive_entry_filetype(file->entry) == AE_IFLNK) {
+ /* Convert symlink name too. */
+ pathname = archive_entry_symlink(file->entry);
+ archive_strcpy(&(file->symlink), pathname);
+#if defined(_WIN32) || defined(__CYGWIN__)
+ /*
+ * Convert a path-separator from '\' to '/'
+ */
+ if (archive_strlen(&(file->symlink)) > 0 &&
+ cleanup_backslash_1(file->symlink.s) != 0) {
+ const wchar_t *wp =
+ archive_entry_symlink_w(file->entry);
+ struct archive_wstring ws;
+
+ if (wp != NULL) {
+ int r;
+ archive_string_init(&ws);
+ archive_wstrcpy(&ws, wp);
+ cleanup_backslash_2(ws.s);
+ archive_string_empty(&(file->symlink));
+ r = archive_string_append_from_wcs(
+ &(file->symlink),
+ ws.s, ws.length);
+ archive_wstring_free(&ws);
+ if (r < 0 && errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ }
+#endif
+ }
+ /*
+ * - Count up directory elements.
+ * - Find out the position which points the last position of
+ * path separator('/').
+ */
+ slash = NULL;
+ file->dircnt = 0;
+ for (; *p != '\0'; p++)
+ if (*p == '/') {
+ slash = p;
+ file->dircnt++;
+ }
+ if (slash == NULL) {
+ /* The pathname doesn't have a parent directory. */
+ file->parentdir.length = len;
+ archive_string_copy(&(file->basename), &(file->parentdir));
+ archive_string_empty(&(file->parentdir));
+ *file->parentdir.s = '\0';
+ return (ret);
+ }
+
+ /* Make a basename from dirname and slash */
+ *slash = '\0';
+ file->parentdir.length = slash - dirname;
+ archive_strcpy(&(file->basename), slash + 1);
+ if (archive_entry_filetype(file->entry) == AE_IFDIR)
+ file->dircnt ++;
+ return (ret);
+}
+
+/*
+ * Register a entry to get a hardlink target.
+ */
+static int
+isofile_register_hardlink(struct archive_write *a, struct isofile *file)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct hardlink *hl;
+ const char *pathname;
+
+ archive_entry_set_nlink(file->entry, 1);
+ pathname = archive_entry_hardlink(file->entry);
+ if (pathname == NULL) {
+ /* This `file` is a hardlink target. */
+ hl = malloc(sizeof(*hl));
+ if (hl == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ hl->nlink = 1;
+ /* A hardlink target must be the first position. */
+ file->hlnext = NULL;
+ hl->file_list.first = file;
+ hl->file_list.last = &(file->hlnext);
+ __archive_rb_tree_insert_node(&(iso9660->hardlink_rbtree),
+ (struct archive_rb_node *)hl);
+ } else {
+ hl = (struct hardlink *)__archive_rb_tree_find_node(
+ &(iso9660->hardlink_rbtree), pathname);
+ if (hl != NULL) {
+ /* Insert `file` entry into the tail. */
+ file->hlnext = NULL;
+ *hl->file_list.last = file;
+ hl->file_list.last = &(file->hlnext);
+ hl->nlink++;
+ }
+ archive_entry_unset_size(file->entry);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Hardlinked files have to have the same location of extent.
+ * We have to find out hardlink target entries for the entries
+ * which have a hardlink target name.
+ */
+static void
+isofile_connect_hardlink_files(struct iso9660 *iso9660)
+{
+ struct archive_rb_node *n;
+ struct hardlink *hl;
+ struct isofile *target, *nf;
+
+ ARCHIVE_RB_TREE_FOREACH(n, &(iso9660->hardlink_rbtree)) {
+ hl = (struct hardlink *)n;
+
+ /* The first entry must be a hardlink target. */
+ target = hl->file_list.first;
+ archive_entry_set_nlink(target->entry, hl->nlink);
+ /* Set a hardlink target to reference entries. */
+ for (nf = target->hlnext;
+ nf != NULL; nf = nf->hlnext) {
+ nf->hardlink_target = target;
+ archive_entry_set_nlink(nf->entry, hl->nlink);
+ }
+ }
+}
+
+static int
+isofile_hd_cmp_node(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct hardlink *h1 = (const struct hardlink *)n1;
+ const struct hardlink *h2 = (const struct hardlink *)n2;
+
+ return (strcmp(archive_entry_pathname(h1->file_list.first->entry),
+ archive_entry_pathname(h2->file_list.first->entry)));
+}
+
+static int
+isofile_hd_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ const struct hardlink *h = (const struct hardlink *)n;
+
+ return (strcmp(archive_entry_pathname(h->file_list.first->entry),
+ (const char *)key));
+}
+
+static void
+isofile_init_hardlinks(struct iso9660 *iso9660)
+{
+ static const struct archive_rb_tree_ops rb_ops = {
+ isofile_hd_cmp_node, isofile_hd_cmp_key,
+ };
+
+ __archive_rb_tree_init(&(iso9660->hardlink_rbtree), &rb_ops);
+}
+
+static void
+isofile_free_hardlinks(struct iso9660 *iso9660)
+{
+ struct archive_rb_node *n, *tmp;
+
+ ARCHIVE_RB_TREE_FOREACH_SAFE(n, &(iso9660->hardlink_rbtree), tmp) {
+ __archive_rb_tree_remove_node(&(iso9660->hardlink_rbtree), n);
+ free(n);
+ }
+}
+
+static struct isoent *
+isoent_new(struct isofile *file)
+{
+ struct isoent *isoent;
+ static const struct archive_rb_tree_ops rb_ops = {
+ isoent_cmp_node, isoent_cmp_key,
+ };
+
+ isoent = calloc(1, sizeof(*isoent));
+ if (isoent == NULL)
+ return (NULL);
+ isoent->file = file;
+ isoent->children.first = NULL;
+ isoent->children.last = &(isoent->children.first);
+ __archive_rb_tree_init(&(isoent->rbtree), &rb_ops);
+ isoent->subdirs.first = NULL;
+ isoent->subdirs.last = &(isoent->subdirs.first);
+ isoent->extr_rec_list.first = NULL;
+ isoent->extr_rec_list.last = &(isoent->extr_rec_list.first);
+ isoent->extr_rec_list.current = NULL;
+ if (archive_entry_filetype(file->entry) == AE_IFDIR)
+ isoent->dir = 1;
+
+ return (isoent);
+}
+
+static inline struct isoent *
+isoent_clone(struct isoent *src)
+{
+ return (isoent_new(src->file));
+}
+
+static void
+_isoent_free(struct isoent *isoent)
+{
+ struct extr_rec *er, *er_next;
+
+ free(isoent->children_sorted);
+ free(isoent->identifier);
+ er = isoent->extr_rec_list.first;
+ while (er != NULL) {
+ er_next = er->next;
+ free(er);
+ er = er_next;
+ }
+ free(isoent);
+}
+
+static void
+isoent_free_all(struct isoent *isoent)
+{
+ struct isoent *np, *np_temp;
+
+ if (isoent == NULL)
+ return;
+ np = isoent;
+ for (;;) {
+ if (np->dir) {
+ if (np->children.first != NULL) {
+ /* Enter to sub directories. */
+ np = np->children.first;
+ continue;
+ }
+ }
+ for (;;) {
+ np_temp = np;
+ if (np->chnext == NULL) {
+ /* Return to the parent directory. */
+ np = np->parent;
+ _isoent_free(np_temp);
+ if (np == np_temp)
+ return;
+ } else {
+ np = np->chnext;
+ _isoent_free(np_temp);
+ break;
+ }
+ }
+ }
+}
+
+static struct isoent *
+isoent_create_virtual_dir(struct archive_write *a, struct iso9660 *iso9660, const char *pathname)
+{
+ struct isofile *file;
+ struct isoent *isoent;
+
+ file = isofile_new(a, NULL);
+ if (file == NULL)
+ return (NULL);
+ archive_entry_set_pathname(file->entry, pathname);
+ archive_entry_unset_mtime(file->entry);
+ archive_entry_unset_atime(file->entry);
+ archive_entry_unset_ctime(file->entry);
+ archive_entry_set_uid(file->entry, getuid());
+ archive_entry_set_gid(file->entry, getgid());
+ archive_entry_set_mode(file->entry, 0555 | AE_IFDIR);
+ archive_entry_set_nlink(file->entry, 2);
+ if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) {
+ isofile_free(file);
+ return (NULL);
+ }
+ isofile_add_entry(iso9660, file);
+
+ isoent = isoent_new(file);
+ if (isoent == NULL)
+ return (NULL);
+ isoent->dir = 1;
+ isoent->virtual = 1;
+
+ return (isoent);
+}
+
+static int
+isoent_cmp_node(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct isoent *e1 = (const struct isoent *)n1;
+ const struct isoent *e2 = (const struct isoent *)n2;
+
+ return (strcmp(e1->file->basename.s, e2->file->basename.s));
+}
+
+static int
+isoent_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ const struct isoent *e = (const struct isoent *)n;
+
+ return (strcmp(e->file->basename.s, (const char *)key));
+}
+
+static int
+isoent_add_child_head(struct isoent *parent, struct isoent *child)
+{
+
+ if (!__archive_rb_tree_insert_node(
+ &(parent->rbtree), (struct archive_rb_node *)child))
+ return (0);
+ if ((child->chnext = parent->children.first) == NULL)
+ parent->children.last = &(child->chnext);
+ parent->children.first = child;
+ parent->children.cnt++;
+ child->parent = parent;
+
+ /* Add a child to a sub-directory chain */
+ if (child->dir) {
+ if ((child->drnext = parent->subdirs.first) == NULL)
+ parent->subdirs.last = &(child->drnext);
+ parent->subdirs.first = child;
+ parent->subdirs.cnt++;
+ child->parent = parent;
+ } else
+ child->drnext = NULL;
+ return (1);
+}
+
+static int
+isoent_add_child_tail(struct isoent *parent, struct isoent *child)
+{
+
+ if (!__archive_rb_tree_insert_node(
+ &(parent->rbtree), (struct archive_rb_node *)child))
+ return (0);
+ child->chnext = NULL;
+ *parent->children.last = child;
+ parent->children.last = &(child->chnext);
+ parent->children.cnt++;
+ child->parent = parent;
+
+ /* Add a child to a sub-directory chain */
+ child->drnext = NULL;
+ if (child->dir) {
+ *parent->subdirs.last = child;
+ parent->subdirs.last = &(child->drnext);
+ parent->subdirs.cnt++;
+ child->parent = parent;
+ }
+ return (1);
+}
+
+static void
+isoent_remove_child(struct isoent *parent, struct isoent *child)
+{
+ struct isoent *ent;
+
+ /* Remove a child entry from children chain. */
+ ent = parent->children.first;
+ while (ent->chnext != child)
+ ent = ent->chnext;
+ if ((ent->chnext = ent->chnext->chnext) == NULL)
+ parent->children.last = &(ent->chnext);
+ parent->children.cnt--;
+
+ if (child->dir) {
+ /* Remove a child entry from sub-directory chain. */
+ ent = parent->subdirs.first;
+ while (ent->drnext != child)
+ ent = ent->drnext;
+ if ((ent->drnext = ent->drnext->drnext) == NULL)
+ parent->subdirs.last = &(ent->drnext);
+ parent->subdirs.cnt--;
+ }
+
+ __archive_rb_tree_remove_node(&(parent->rbtree),
+ (struct archive_rb_node *)child);
+}
+
+static int
+isoent_clone_tree(struct archive_write *a, struct isoent **nroot,
+ struct isoent *root)
+{
+ struct isoent *np, *xroot, *newent;
+
+ np = root;
+ xroot = NULL;
+ do {
+ newent = isoent_clone(np);
+ if (newent == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ if (xroot == NULL) {
+ *nroot = xroot = newent;
+ newent->parent = xroot;
+ } else
+ isoent_add_child_tail(xroot, newent);
+ if (np->dir && np->children.first != NULL) {
+ /* Enter to sub directories. */
+ np = np->children.first;
+ xroot = newent;
+ continue;
+ }
+ while (np != np->parent) {
+ if (np->chnext == NULL) {
+ /* Return to the parent directory. */
+ np = np->parent;
+ xroot = xroot->parent;
+ } else {
+ np = np->chnext;
+ break;
+ }
+ }
+ } while (np != np->parent);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Setup directory locations.
+ */
+static void
+isoent_setup_directory_location(struct iso9660 *iso9660, int location,
+ struct vdd *vdd)
+{
+ struct isoent *np;
+ int depth;
+
+ vdd->total_dir_block = 0;
+ depth = 0;
+ np = vdd->rootent;
+ do {
+ int block;
+
+ np->dir_block = calculate_directory_descriptors(
+ iso9660, vdd, np, depth);
+ vdd->total_dir_block += np->dir_block;
+ np->dir_location = location;
+ location += np->dir_block;
+ block = extra_setup_location(np, location);
+ vdd->total_dir_block += block;
+ location += block;
+
+ if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) {
+ /* Enter to sub directories. */
+ np = np->subdirs.first;
+ depth++;
+ continue;
+ }
+ while (np != np->parent) {
+ if (np->drnext == NULL) {
+ /* Return to the parent directory. */
+ np = np->parent;
+ depth--;
+ } else {
+ np = np->drnext;
+ break;
+ }
+ }
+ } while (np != np->parent);
+}
+
+static void
+_isoent_file_location(struct iso9660 *iso9660, struct isoent *isoent,
+ int *symlocation)
+{
+ struct isoent **children;
+ int n;
+
+ if (isoent->children.cnt == 0)
+ return;
+
+ children = isoent->children_sorted;
+ for (n = 0; n < isoent->children.cnt; n++) {
+ struct isoent *np;
+ struct isofile *file;
+
+ np = children[n];
+ if (np->dir)
+ continue;
+ if (np == iso9660->el_torito.boot)
+ continue;
+ file = np->file;
+ if (file->boot || file->hardlink_target != NULL)
+ continue;
+ if (archive_entry_filetype(file->entry) == AE_IFLNK ||
+ file->content.size == 0) {
+ /*
+ * Do not point a valid location.
+ * Make sure entry is not hardlink file.
+ */
+ file->content.location = (*symlocation)--;
+ continue;
+ }
+
+ file->write_content = 1;
+ }
+}
+
+/*
+ * Setup file locations.
+ */
+static void
+isoent_setup_file_location(struct iso9660 *iso9660, int location)
+{
+ struct isoent *isoent;
+ struct isoent *np;
+ struct isofile *file;
+ size_t size;
+ int block;
+ int depth;
+ int joliet;
+ int symlocation;
+ int total_block;
+
+ iso9660->total_file_block = 0;
+ if ((isoent = iso9660->el_torito.catalog) != NULL) {
+ isoent->file->content.location = location;
+ block = (int)((archive_entry_size(isoent->file->entry) +
+ LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS);
+ location += block;
+ iso9660->total_file_block += block;
+ }
+ if ((isoent = iso9660->el_torito.boot) != NULL) {
+ isoent->file->content.location = location;
+ size = fd_boot_image_size(iso9660->el_torito.media_type);
+ if (size == 0)
+ size = (size_t)archive_entry_size(isoent->file->entry);
+ block = ((int)size + LOGICAL_BLOCK_SIZE -1)
+ >> LOGICAL_BLOCK_BITS;
+ location += block;
+ iso9660->total_file_block += block;
+ isoent->file->content.blocks = block;
+ }
+
+ depth = 0;
+ symlocation = -16;
+ if (!iso9660->opt.rr && iso9660->opt.joliet) {
+ joliet = 1;
+ np = iso9660->joliet.rootent;
+ } else {
+ joliet = 0;
+ np = iso9660->primary.rootent;
+ }
+ do {
+ _isoent_file_location(iso9660, np, &symlocation);
+
+ if (np->subdirs.first != NULL &&
+ (joliet ||
+ ((iso9660->opt.rr == OPT_RR_DISABLED &&
+ depth + 2 < iso9660->primary.max_depth) ||
+ (iso9660->opt.rr &&
+ depth + 1 < iso9660->primary.max_depth)))) {
+ /* Enter to sub directories. */
+ np = np->subdirs.first;
+ depth++;
+ continue;
+ }
+ while (np != np->parent) {
+ if (np->drnext == NULL) {
+ /* Return to the parent directory. */
+ np = np->parent;
+ depth--;
+ } else {
+ np = np->drnext;
+ break;
+ }
+ }
+ } while (np != np->parent);
+
+ total_block = 0;
+ for (file = iso9660->data_file_list.first;
+ file != NULL; file = file->datanext) {
+
+ if (!file->write_content)
+ continue;
+
+ file->cur_content = &(file->content);
+ do {
+ file->cur_content->location = location;
+ location += file->cur_content->blocks;
+ total_block += file->cur_content->blocks;
+ /* Next fragment */
+ file->cur_content = file->cur_content->next;
+ } while (file->cur_content != NULL);
+ }
+ iso9660->total_file_block += total_block;
+}
+
+static int
+get_path_component(char *name, size_t n, const char *fn)
+{
+ char *p;
+ size_t l;
+
+ p = strchr(fn, '/');
+ if (p == NULL) {
+ if ((l = strlen(fn)) == 0)
+ return (0);
+ } else
+ l = p - fn;
+ if (l > n -1)
+ return (-1);
+ memcpy(name, fn, l);
+ name[l] = '\0';
+
+ return ((int)l);
+}
+
+/*
+ * Add a new entry into the tree.
+ */
+static int
+isoent_tree(struct archive_write *a, struct isoent **isoentpp)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ char name[_MAX_FNAME];/* Included null terminator size. */
+#elif defined(NAME_MAX) && NAME_MAX >= 255
+ char name[NAME_MAX+1];
+#else
+ char name[256];
+#endif
+ struct iso9660 *iso9660 = a->format_data;
+ struct isoent *dent, *isoent, *np;
+ struct isofile *f1, *f2;
+ const char *fn, *p;
+ int l;
+
+ isoent = *isoentpp;
+ dent = iso9660->primary.rootent;
+ if (isoent->file->parentdir.length > 0)
+ fn = p = isoent->file->parentdir.s;
+ else
+ fn = p = "";
+
+ /*
+ * If the path of the parent directory of `isoent' entry is
+ * the same as the path of `cur_dirent', add isoent to
+ * `cur_dirent'.
+ */
+ if (archive_strlen(&(iso9660->cur_dirstr))
+ == archive_strlen(&(isoent->file->parentdir)) &&
+ strcmp(iso9660->cur_dirstr.s, fn) == 0) {
+ if (!isoent_add_child_tail(iso9660->cur_dirent, isoent)) {
+ np = (struct isoent *)__archive_rb_tree_find_node(
+ &(iso9660->cur_dirent->rbtree),
+ isoent->file->basename.s);
+ goto same_entry;
+ }
+ return (ARCHIVE_OK);
+ }
+
+ for (;;) {
+ l = get_path_component(name, sizeof(name), fn);
+ if (l == 0) {
+ np = NULL;
+ break;
+ }
+ if (l < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "A name buffer is too small");
+ _isoent_free(isoent);
+ return (ARCHIVE_FATAL);
+ }
+
+ np = isoent_find_child(dent, name);
+ if (np == NULL || fn[0] == '\0')
+ break;
+
+ /* Find next subdirectory. */
+ if (!np->dir) {
+ /* NOT Directory! */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "`%s' is not directory, we cannot insert `%s' ",
+ archive_entry_pathname(np->file->entry),
+ archive_entry_pathname(isoent->file->entry));
+ _isoent_free(isoent);
+ *isoentpp = NULL;
+ return (ARCHIVE_FAILED);
+ }
+ fn += l;
+ if (fn[0] == '/')
+ fn++;
+ dent = np;
+ }
+ if (np == NULL) {
+ /*
+ * Create virtual parent directories.
+ */
+ while (fn[0] != '\0') {
+ struct isoent *vp;
+ struct archive_string as;
+
+ archive_string_init(&as);
+ archive_strncat(&as, p, fn - p + l);
+ if (as.s[as.length-1] == '/') {
+ as.s[as.length-1] = '\0';
+ as.length--;
+ }
+ vp = isoent_create_virtual_dir(a, iso9660, as.s);
+ if (vp == NULL) {
+ archive_string_free(&as);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ _isoent_free(isoent);
+ *isoentpp = NULL;
+ return (ARCHIVE_FATAL);
+ }
+ archive_string_free(&as);
+
+ if (vp->file->dircnt > iso9660->dircnt_max)
+ iso9660->dircnt_max = vp->file->dircnt;
+ isoent_add_child_tail(dent, vp);
+ np = vp;
+
+ fn += l;
+ if (fn[0] == '/')
+ fn++;
+ l = get_path_component(name, sizeof(name), fn);
+ if (l < 0) {
+ archive_string_free(&as);
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "A name buffer is too small");
+ _isoent_free(isoent);
+ *isoentpp = NULL;
+ return (ARCHIVE_FATAL);
+ }
+ dent = np;
+ }
+
+ /* Found out the parent directory where isoent can be
+ * inserted. */
+ iso9660->cur_dirent = dent;
+ archive_string_empty(&(iso9660->cur_dirstr));
+ archive_string_ensure(&(iso9660->cur_dirstr),
+ archive_strlen(&(dent->file->parentdir)) +
+ archive_strlen(&(dent->file->basename)) + 2);
+ if (archive_strlen(&(dent->file->parentdir)) +
+ archive_strlen(&(dent->file->basename)) == 0)
+ iso9660->cur_dirstr.s[0] = 0;
+ else {
+ if (archive_strlen(&(dent->file->parentdir)) > 0) {
+ archive_string_copy(&(iso9660->cur_dirstr),
+ &(dent->file->parentdir));
+ archive_strappend_char(&(iso9660->cur_dirstr), '/');
+ }
+ archive_string_concat(&(iso9660->cur_dirstr),
+ &(dent->file->basename));
+ }
+
+ if (!isoent_add_child_tail(dent, isoent)) {
+ np = (struct isoent *)__archive_rb_tree_find_node(
+ &(dent->rbtree), isoent->file->basename.s);
+ goto same_entry;
+ }
+ return (ARCHIVE_OK);
+ }
+
+same_entry:
+ /*
+ * We have already has the entry the filename of which is
+ * the same.
+ */
+ f1 = np->file;
+ f2 = isoent->file;
+
+ /* If the file type of entries is different,
+ * we cannot handle it. */
+ if (archive_entry_filetype(f1->entry) !=
+ archive_entry_filetype(f2->entry)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Found duplicate entries `%s' and its file type is "
+ "different",
+ archive_entry_pathname(f1->entry));
+ _isoent_free(isoent);
+ *isoentpp = NULL;
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Swap file entries. */
+ np->file = f2;
+ isoent->file = f1;
+ np->virtual = 0;
+
+ _isoent_free(isoent);
+ *isoentpp = np;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Find a entry from `isoent'
+ */
+static struct isoent *
+isoent_find_child(struct isoent *isoent, const char *child_name)
+{
+ struct isoent *np;
+
+ np = (struct isoent *)__archive_rb_tree_find_node(
+ &(isoent->rbtree), child_name);
+ return (np);
+}
+
+/*
+ * Find a entry full-path of which is specified by `fn' parameter,
+ * in the tree.
+ */
+static struct isoent *
+isoent_find_entry(struct isoent *rootent, const char *fn)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ char name[_MAX_FNAME];/* Included null terminator size. */
+#elif defined(NAME_MAX) && NAME_MAX >= 255
+ char name[NAME_MAX+1];
+#else
+ char name[256];
+#endif
+ struct isoent *isoent, *np;
+ int l;
+
+ isoent = rootent;
+ np = NULL;
+ for (;;) {
+ l = get_path_component(name, sizeof(name), fn);
+ if (l == 0)
+ break;
+ fn += l;
+ if (fn[0] == '/')
+ fn++;
+
+ np = isoent_find_child(isoent, name);
+ if (np == NULL)
+ break;
+ if (fn[0] == '\0')
+ break;/* We found out the entry */
+
+ /* Try sub directory. */
+ isoent = np;
+ np = NULL;
+ if (!isoent->dir)
+ break;/* Not directory */
+ }
+
+ return (np);
+}
+
+/*
+ * Following idr_* functions are used for resolving duplicated filenames
+ * and unreceivable filenames to generate ISO9660/Joliet Identifiers.
+ */
+
+static void
+idr_relaxed_filenames(char *map)
+{
+ int i;
+
+ for (i = 0x21; i <= 0x2F; i++)
+ map[i] = 1;
+ for (i = 0x3A; i <= 0x41; i++)
+ map[i] = 1;
+ for (i = 0x5B; i <= 0x5E; i++)
+ map[i] = 1;
+ map[0x60] = 1;
+ for (i = 0x7B; i <= 0x7E; i++)
+ map[i] = 1;
+}
+
+static void
+idr_init(struct iso9660 *iso9660, struct vdd *vdd, struct idr *idr)
+{
+
+ idr->idrent_pool = NULL;
+ idr->pool_size = 0;
+ if (vdd->vdd_type != VDD_JOLIET) {
+ if (iso9660->opt.iso_level <= 3) {
+ memcpy(idr->char_map, d_characters_map,
+ sizeof(idr->char_map));
+ } else {
+ memcpy(idr->char_map, d1_characters_map,
+ sizeof(idr->char_map));
+ idr_relaxed_filenames(idr->char_map);
+ }
+ }
+}
+
+static void
+idr_cleanup(struct idr *idr)
+{
+ free(idr->idrent_pool);
+}
+
+static int
+idr_ensure_poolsize(struct archive_write *a, struct idr *idr,
+ int cnt)
+{
+
+ if (idr->pool_size < cnt) {
+ void *p;
+ const int bk = (1 << 7) - 1;
+ int psize;
+
+ psize = (cnt + bk) & ~bk;
+ p = realloc(idr->idrent_pool, sizeof(struct idrent) * psize);
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ idr->idrent_pool = (struct idrent *)p;
+ idr->pool_size = psize;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+idr_start(struct archive_write *a, struct idr *idr, int cnt, int ffmax,
+ int num_size, int null_size, const struct archive_rb_tree_ops *rbt_ops)
+{
+ int r;
+
+ (void)ffmax; /* UNUSED */
+
+ r = idr_ensure_poolsize(a, idr, cnt);
+ if (r != ARCHIVE_OK)
+ return (r);
+ __archive_rb_tree_init(&(idr->rbtree), rbt_ops);
+ idr->wait_list.first = NULL;
+ idr->wait_list.last = &(idr->wait_list.first);
+ idr->pool_idx = 0;
+ idr->num_size = num_size;
+ idr->null_size = null_size;
+ return (ARCHIVE_OK);
+}
+
+static void
+idr_register(struct idr *idr, struct isoent *isoent, int weight, int noff)
+{
+ struct idrent *idrent, *n;
+
+ idrent = &(idr->idrent_pool[idr->pool_idx++]);
+ idrent->wnext = idrent->avail = NULL;
+ idrent->isoent = isoent;
+ idrent->weight = weight;
+ idrent->noff = noff;
+ idrent->rename_num = 0;
+
+ if (!__archive_rb_tree_insert_node(&(idr->rbtree), &(idrent->rbnode))) {
+ n = (struct idrent *)__archive_rb_tree_find_node(
+ &(idr->rbtree), idrent->isoent);
+ if (n != NULL) {
+ /* this `idrent' needs to rename. */
+ idrent->avail = n;
+ *idr->wait_list.last = idrent;
+ idr->wait_list.last = &(idrent->wnext);
+ }
+ }
+}
+
+static void
+idr_extend_identifier(struct idrent *wnp, int numsize, int nullsize)
+{
+ unsigned char *p;
+ int wnp_ext_off;
+
+ wnp_ext_off = wnp->isoent->ext_off;
+ if (wnp->noff + numsize != wnp_ext_off) {
+ p = (unsigned char *)wnp->isoent->identifier;
+ /* Extend the filename; foo.c --> foo___.c */
+ memmove(p + wnp->noff + numsize, p + wnp_ext_off,
+ wnp->isoent->ext_len + nullsize);
+ wnp->isoent->ext_off = wnp_ext_off = wnp->noff + numsize;
+ wnp->isoent->id_len = wnp_ext_off + wnp->isoent->ext_len;
+ }
+}
+
+static void
+idr_resolve(struct idr *idr, void (*fsetnum)(unsigned char *p, int num))
+{
+ struct idrent *n;
+ unsigned char *p;
+
+ for (n = idr->wait_list.first; n != NULL; n = n->wnext) {
+ idr_extend_identifier(n, idr->num_size, idr->null_size);
+ p = (unsigned char *)n->isoent->identifier + n->noff;
+ do {
+ fsetnum(p, n->avail->rename_num++);
+ } while (!__archive_rb_tree_insert_node(
+ &(idr->rbtree), &(n->rbnode)));
+ }
+}
+
+static void
+idr_set_num(unsigned char *p, int num)
+{
+ static const char xdig[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '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'
+ };
+
+ num %= sizeof(xdig) * sizeof(xdig) * sizeof(xdig);
+ p[0] = xdig[(num / (sizeof(xdig) * sizeof(xdig)))];
+ num %= sizeof(xdig) * sizeof(xdig);
+ p[1] = xdig[ (num / sizeof(xdig))];
+ num %= sizeof(xdig);
+ p[2] = xdig[num];
+}
+
+static void
+idr_set_num_beutf16(unsigned char *p, int num)
+{
+ static const uint16_t xdig[] = {
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,
+ 0x0036, 0x0037, 0x0038, 0x0039,
+ 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046,
+ 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C,
+ 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052,
+ 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
+ 0x0059, 0x005A
+ };
+#define XDIG_CNT (sizeof(xdig)/sizeof(xdig[0]))
+
+ num %= XDIG_CNT * XDIG_CNT * XDIG_CNT;
+ archive_be16enc(p, xdig[(num / (XDIG_CNT * XDIG_CNT))]);
+ num %= XDIG_CNT * XDIG_CNT;
+ archive_be16enc(p+2, xdig[ (num / XDIG_CNT)]);
+ num %= XDIG_CNT;
+ archive_be16enc(p+4, xdig[num]);
+}
+
+/*
+ * Generate ISO9660 Identifier.
+ */
+static int
+isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent,
+ struct idr *idr)
+{
+ struct iso9660 *iso9660;
+ struct isoent *np;
+ char *p;
+ int l, r;
+ const char *char_map;
+ char allow_ldots, allow_multidot, allow_period, allow_vernum;
+ int fnmax, ffmax, dnmax;
+ static const struct archive_rb_tree_ops rb_ops = {
+ isoent_cmp_node_iso9660, isoent_cmp_key_iso9660
+ };
+
+ if (isoent->children.cnt == 0)
+ return (0);
+
+ iso9660 = a->format_data;
+ char_map = idr->char_map;
+ if (iso9660->opt.iso_level <= 3) {
+ allow_ldots = 0;
+ allow_multidot = 0;
+ allow_period = 1;
+ allow_vernum = iso9660->opt.allow_vernum;
+ if (iso9660->opt.iso_level == 1) {
+ fnmax = 8;
+ ffmax = 12;/* fnmax + '.' + 3 */
+ dnmax = 8;
+ } else {
+ fnmax = 30;
+ ffmax = 31;
+ dnmax = 31;
+ }
+ } else {
+ allow_ldots = allow_multidot = 1;
+ allow_period = allow_vernum = 0;
+ if (iso9660->opt.rr)
+ /*
+ * MDR : The maximum size of Directory Record(254).
+ * DRL : A Directory Record Length(33).
+ * CE : A size of SUSP CE System Use Entry(28).
+ * MDR - DRL - CE = 254 - 33 - 28 = 193.
+ */
+ fnmax = ffmax = dnmax = 193;
+ else
+ /*
+ * XA : CD-ROM XA System Use Extension
+ * Information(14).
+ * MDR - DRL - XA = 254 - 33 -14 = 207.
+ */
+ fnmax = ffmax = dnmax = 207;
+ }
+
+ r = idr_start(a, idr, isoent->children.cnt, ffmax, 3, 1, &rb_ops);
+ if (r < 0)
+ return (r);
+
+ for (np = isoent->children.first; np != NULL; np = np->chnext) {
+ char *dot, *xdot;
+ int ext_off, noff, weight;
+
+ l = (int)np->file->basename.length;
+ p = malloc(l+31+2+1);
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(p, np->file->basename.s, l);
+ p[l] = '\0';
+ np->identifier = p;
+
+ dot = xdot = NULL;
+ if (!allow_ldots) {
+ /*
+ * If there is a '.' character at the first byte,
+ * it has to be replaced by '_' character.
+ */
+ if (*p == '.')
+ *p++ = '_';
+ }
+ for (;*p; p++) {
+ if (*p & 0x80) {
+ *p = '_';
+ continue;
+ }
+ if (char_map[(unsigned char)*p]) {
+ /* if iso-level is '4', a character '.' is
+ * allowed by char_map. */
+ if (*p == '.') {
+ xdot = dot;
+ dot = p;
+ }
+ continue;
+ }
+ if (*p >= 'a' && *p <= 'z') {
+ *p -= 'a' - 'A';
+ continue;
+ }
+ if (*p == '.') {
+ xdot = dot;
+ dot = p;
+ if (allow_multidot)
+ continue;
+ }
+ *p = '_';
+ }
+ p = np->identifier;
+ weight = -1;
+ if (dot == NULL) {
+ int nammax;
+
+ if (np->dir)
+ nammax = dnmax;
+ else
+ nammax = fnmax;
+
+ if (l > nammax) {
+ p[nammax] = '\0';
+ weight = nammax;
+ ext_off = nammax;
+ } else
+ ext_off = l;
+ } else {
+ *dot = '.';
+ ext_off = (int)(dot - p);
+
+ if (iso9660->opt.iso_level == 1) {
+ if (dot - p <= 8) {
+ if (strlen(dot) > 4) {
+ /* A length of a file extension
+ * must be less than 4 */
+ dot[4] = '\0';
+ weight = 0;
+ }
+ } else {
+ p[8] = dot[0];
+ p[9] = dot[1];
+ p[10] = dot[2];
+ p[11] = dot[3];
+ p[12] = '\0';
+ weight = 8;
+ ext_off = 8;
+ }
+ } else if (np->dir) {
+ if (l > dnmax) {
+ p[dnmax] = '\0';
+ weight = dnmax;
+ if (ext_off > dnmax)
+ ext_off = dnmax;
+ }
+ } else if (l > ffmax) {
+ int extlen = (int)strlen(dot);
+ int xdoff;
+
+ if (xdot != NULL)
+ xdoff = (int)(xdot - p);
+ else
+ xdoff = 0;
+
+ if (extlen > 1 && xdoff < fnmax-1) {
+ int off;
+
+ if (extlen > ffmax)
+ extlen = ffmax;
+ off = ffmax - extlen;
+ if (off == 0) {
+ /* A dot('.') character
+ * doesn't place to the first
+ * byte of identifier. */
+ off ++;
+ extlen --;
+ }
+ memmove(p+off, dot, extlen);
+ p[ffmax] = '\0';
+ ext_off = off;
+ weight = off;
+#ifdef COMPAT_MKISOFS
+ } else if (xdoff >= fnmax-1) {
+ /* Simulate a bug(?) of mkisofs. */
+ p[fnmax-1] = '\0';
+ ext_off = fnmax-1;
+ weight = fnmax-1;
+#endif
+ } else {
+ p[fnmax] = '\0';
+ ext_off = fnmax;
+ weight = fnmax;
+ }
+ }
+ }
+ /* Save an offset of a file name extension to sort files. */
+ np->ext_off = ext_off;
+ np->ext_len = (int)strlen(&p[ext_off]);
+ np->id_len = l = ext_off + np->ext_len;
+
+ /* Make an offset of the number which is used to be set
+ * hexadecimal number to avoid duplicate identifier. */
+ if (iso9660->opt.iso_level == 1) {
+ if (ext_off >= 5)
+ noff = 5;
+ else
+ noff = ext_off;
+ } else {
+ if (l == ffmax)
+ noff = ext_off - 3;
+ else if (l == ffmax-1)
+ noff = ext_off - 2;
+ else if (l == ffmax-2)
+ noff = ext_off - 1;
+ else
+ noff = ext_off;
+ }
+ /* Register entry to the identifier resolver. */
+ idr_register(idr, np, weight, noff);
+ }
+
+ /* Resolve duplicate identifier. */
+ idr_resolve(idr, idr_set_num);
+
+ /* Add a period and a version number to identifiers. */
+ for (np = isoent->children.first; np != NULL; np = np->chnext) {
+ if (!np->dir && np->rr_child == NULL) {
+ p = np->identifier + np->ext_off + np->ext_len;
+ if (np->ext_len == 0 && allow_period) {
+ *p++ = '.';
+ np->ext_len = 1;
+ }
+ if (np->ext_len == 1 && !allow_period) {
+ *--p = '\0';
+ np->ext_len = 0;
+ }
+ np->id_len = np->ext_off + np->ext_len;
+ if (allow_vernum) {
+ *p++ = ';';
+ *p++ = '1';
+ np->id_len += 2;
+ }
+ *p = '\0';
+ } else
+ np->id_len = np->ext_off + np->ext_len;
+ np->mb_len = np->id_len;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Generate Joliet Identifier.
+ */
+static int
+isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent,
+ struct idr *idr)
+{
+ struct iso9660 *iso9660;
+ struct isoent *np;
+ unsigned char *p;
+ size_t l;
+ int r;
+ size_t ffmax, parent_len;
+ static const struct archive_rb_tree_ops rb_ops = {
+ isoent_cmp_node_joliet, isoent_cmp_key_joliet
+ };
+
+ if (isoent->children.cnt == 0)
+ return (0);
+
+ iso9660 = a->format_data;
+ if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME)
+ ffmax = 206;
+ else
+ ffmax = 128;
+
+ r = idr_start(a, idr, isoent->children.cnt, (int)ffmax, 6, 2, &rb_ops);
+ if (r < 0)
+ return (r);
+
+ parent_len = 1;
+ for (np = isoent; np->parent != np; np = np->parent)
+ parent_len += np->mb_len + 1;
+
+ for (np = isoent->children.first; np != NULL; np = np->chnext) {
+ unsigned char *dot;
+ int ext_off, noff, weight;
+ size_t lt;
+
+ if ((l = np->file->basename_utf16.length) > ffmax)
+ l = ffmax;
+
+ p = malloc((l+1)*2);
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(p, np->file->basename_utf16.s, l);
+ p[l] = 0;
+ p[l+1] = 0;
+
+ np->identifier = (char *)p;
+ lt = l;
+ dot = p + l;
+ weight = 0;
+ while (lt > 0) {
+ if (!joliet_allowed_char(p[0], p[1]))
+ archive_be16enc(p, 0x005F); /* '_' */
+ else if (p[0] == 0 && p[1] == 0x2E) /* '.' */
+ dot = p;
+ p += 2;
+ lt -= 2;
+ }
+ ext_off = (int)(dot - (unsigned char *)np->identifier);
+ np->ext_off = ext_off;
+ np->ext_len = (int)l - ext_off;
+ np->id_len = (int)l;
+
+ /*
+ * Get a length of MBS of a full-pathname.
+ */
+ if (np->file->basename_utf16.length > ffmax) {
+ if (archive_strncpy_l(&iso9660->mbs,
+ (const char *)np->identifier, l,
+ iso9660->sconv_from_utf16be) != 0 &&
+ errno == ENOMEM) {
+ archive_set_error(&a->archive, errno,
+ "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ np->mb_len = (int)iso9660->mbs.length;
+ if (np->mb_len != (int)np->file->basename.length)
+ weight = np->mb_len;
+ } else
+ np->mb_len = (int)np->file->basename.length;
+
+ /* If a length of full-pathname is longer than 240 bytes,
+ * it violates Joliet extensions regulation. */
+ if (parent_len > 240
+ || np->mb_len > 240
+ || parent_len + np->mb_len > 240) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "The regulation of Joliet extensions;"
+ " A length of a full-pathname of `%s' is "
+ "longer than 240 bytes, (p=%d, b=%d)",
+ archive_entry_pathname(np->file->entry),
+ (int)parent_len, (int)np->mb_len);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Make an offset of the number which is used to be set
+ * hexadecimal number to avoid duplicate identifier. */
+ if (l == ffmax)
+ noff = ext_off - 6;
+ else if (l == ffmax-2)
+ noff = ext_off - 4;
+ else if (l == ffmax-4)
+ noff = ext_off - 2;
+ else
+ noff = ext_off;
+ /* Register entry to the identifier resolver. */
+ idr_register(idr, np, weight, noff);
+ }
+
+ /* Resolve duplicate identifier with Joliet Volume. */
+ idr_resolve(idr, idr_set_num_beutf16);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * This comparing rule is according to ISO9660 Standard 9.3
+ */
+static int
+isoent_cmp_iso9660_identifier(const struct isoent *p1, const struct isoent *p2)
+{
+ const char *s1, *s2;
+ int cmp;
+ int l;
+
+ s1 = p1->identifier;
+ s2 = p2->identifier;
+
+ /* Compare File Name */
+ l = p1->ext_off;
+ if (l > p2->ext_off)
+ l = p2->ext_off;
+ cmp = memcmp(s1, s2, l);
+ if (cmp != 0)
+ return (cmp);
+ if (p1->ext_off < p2->ext_off) {
+ s2 += l;
+ l = p2->ext_off - p1->ext_off;
+ while (l--)
+ if (0x20 != *s2++)
+ return (0x20
+ - *(const unsigned char *)(s2 - 1));
+ } else if (p1->ext_off > p2->ext_off) {
+ s1 += l;
+ l = p1->ext_off - p2->ext_off;
+ while (l--)
+ if (0x20 != *s1++)
+ return (*(const unsigned char *)(s1 - 1)
+ - 0x20);
+ }
+ /* Compare File Name Extension */
+ if (p1->ext_len == 0 && p2->ext_len == 0)
+ return (0);
+ if (p1->ext_len == 1 && p2->ext_len == 1)
+ return (0);
+ if (p1->ext_len <= 1)
+ return (-1);
+ if (p2->ext_len <= 1)
+ return (1);
+ l = p1->ext_len;
+ if (l > p2->ext_len)
+ l = p2->ext_len;
+ s1 = p1->identifier + p1->ext_off;
+ s2 = p2->identifier + p2->ext_off;
+ if (l > 1) {
+ cmp = memcmp(s1, s2, l);
+ if (cmp != 0)
+ return (cmp);
+ }
+ if (p1->ext_len < p2->ext_len) {
+ s2 += l;
+ l = p2->ext_len - p1->ext_len;
+ while (l--)
+ if (0x20 != *s2++)
+ return (0x20
+ - *(const unsigned char *)(s2 - 1));
+ } else if (p1->ext_len > p2->ext_len) {
+ s1 += l;
+ l = p1->ext_len - p2->ext_len;
+ while (l--)
+ if (0x20 != *s1++)
+ return (*(const unsigned char *)(s1 - 1)
+ - 0x20);
+ }
+ /* Compare File Version Number */
+ /* No operation. The File Version Number is always one. */
+
+ return (cmp);
+}
+
+static int
+isoent_cmp_node_iso9660(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct idrent *e1 = (const struct idrent *)n1;
+ const struct idrent *e2 = (const struct idrent *)n2;
+
+ return (isoent_cmp_iso9660_identifier(e2->isoent, e1->isoent));
+}
+
+static int
+isoent_cmp_key_iso9660(const struct archive_rb_node *node, const void *key)
+{
+ const struct isoent *isoent = (const struct isoent *)key;
+ const struct idrent *idrent = (const struct idrent *)node;
+
+ return (isoent_cmp_iso9660_identifier(isoent, idrent->isoent));
+}
+
+static int
+isoent_cmp_joliet_identifier(const struct isoent *p1, const struct isoent *p2)
+{
+ const unsigned char *s1, *s2;
+ int cmp;
+ int l;
+
+ s1 = (const unsigned char *)p1->identifier;
+ s2 = (const unsigned char *)p2->identifier;
+
+ /* Compare File Name */
+ l = p1->ext_off;
+ if (l > p2->ext_off)
+ l = p2->ext_off;
+ cmp = memcmp(s1, s2, l);
+ if (cmp != 0)
+ return (cmp);
+ if (p1->ext_off < p2->ext_off) {
+ s2 += l;
+ l = p2->ext_off - p1->ext_off;
+ while (l--)
+ if (0 != *s2++)
+ return (- *(const unsigned char *)(s2 - 1));
+ } else if (p1->ext_off > p2->ext_off) {
+ s1 += l;
+ l = p1->ext_off - p2->ext_off;
+ while (l--)
+ if (0 != *s1++)
+ return (*(const unsigned char *)(s1 - 1));
+ }
+ /* Compare File Name Extension */
+ if (p1->ext_len == 0 && p2->ext_len == 0)
+ return (0);
+ if (p1->ext_len == 2 && p2->ext_len == 2)
+ return (0);
+ if (p1->ext_len <= 2)
+ return (-1);
+ if (p2->ext_len <= 2)
+ return (1);
+ l = p1->ext_len;
+ if (l > p2->ext_len)
+ l = p2->ext_len;
+ s1 = (unsigned char *)(p1->identifier + p1->ext_off);
+ s2 = (unsigned char *)(p2->identifier + p2->ext_off);
+ if (l > 1) {
+ cmp = memcmp(s1, s2, l);
+ if (cmp != 0)
+ return (cmp);
+ }
+ if (p1->ext_len < p2->ext_len) {
+ s2 += l;
+ l = p2->ext_len - p1->ext_len;
+ while (l--)
+ if (0 != *s2++)
+ return (- *(const unsigned char *)(s2 - 1));
+ } else if (p1->ext_len > p2->ext_len) {
+ s1 += l;
+ l = p1->ext_len - p2->ext_len;
+ while (l--)
+ if (0 != *s1++)
+ return (*(const unsigned char *)(s1 - 1));
+ }
+ /* Compare File Version Number */
+ /* No operation. The File Version Number is always one. */
+
+ return (cmp);
+}
+
+static int
+isoent_cmp_node_joliet(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct idrent *e1 = (const struct idrent *)n1;
+ const struct idrent *e2 = (const struct idrent *)n2;
+
+ return (isoent_cmp_joliet_identifier(e2->isoent, e1->isoent));
+}
+
+static int
+isoent_cmp_key_joliet(const struct archive_rb_node *node, const void *key)
+{
+ const struct isoent *isoent = (const struct isoent *)key;
+ const struct idrent *idrent = (const struct idrent *)node;
+
+ return (isoent_cmp_joliet_identifier(isoent, idrent->isoent));
+}
+
+static int
+isoent_make_sorted_files(struct archive_write *a, struct isoent *isoent,
+ struct idr *idr)
+{
+ struct archive_rb_node *rn;
+ struct isoent **children;
+
+ children = malloc(isoent->children.cnt * sizeof(struct isoent *));
+ if (children == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ isoent->children_sorted = children;
+
+ ARCHIVE_RB_TREE_FOREACH(rn, &(idr->rbtree)) {
+ struct idrent *idrent = (struct idrent *)rn;
+ *children ++ = idrent->isoent;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * - Generate ISO9660 and Joliet identifiers from basenames.
+ * - Sort files by each directory.
+ */
+static int
+isoent_traverse_tree(struct archive_write *a, struct vdd* vdd)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isoent *np;
+ struct idr idr;
+ int depth;
+ int r;
+ int (*genid)(struct archive_write *, struct isoent *, struct idr *);
+
+ idr_init(iso9660, vdd, &idr);
+ np = vdd->rootent;
+ depth = 0;
+ if (vdd->vdd_type == VDD_JOLIET)
+ genid = isoent_gen_joliet_identifier;
+ else
+ genid = isoent_gen_iso9660_identifier;
+ do {
+ if (np->virtual &&
+ !archive_entry_mtime_is_set(np->file->entry)) {
+ /* Set properly times to virtual directory */
+ archive_entry_set_mtime(np->file->entry,
+ iso9660->birth_time, 0);
+ archive_entry_set_atime(np->file->entry,
+ iso9660->birth_time, 0);
+ archive_entry_set_ctime(np->file->entry,
+ iso9660->birth_time, 0);
+ }
+ if (np->children.first != NULL) {
+ if (vdd->vdd_type != VDD_JOLIET &&
+ !iso9660->opt.rr && depth + 1 >= vdd->max_depth) {
+ if (np->children.cnt > 0)
+ iso9660->directories_too_deep = np;
+ } else {
+ /* Generate Identifier */
+ r = genid(a, np, &idr);
+ if (r < 0)
+ goto exit_traverse_tree;
+ r = isoent_make_sorted_files(a, np, &idr);
+ if (r < 0)
+ goto exit_traverse_tree;
+
+ if (np->subdirs.first != NULL &&
+ depth + 1 < vdd->max_depth) {
+ /* Enter to sub directories. */
+ np = np->subdirs.first;
+ depth++;
+ continue;
+ }
+ }
+ }
+ while (np != np->parent) {
+ if (np->drnext == NULL) {
+ /* Return to the parent directory. */
+ np = np->parent;
+ depth--;
+ } else {
+ np = np->drnext;
+ break;
+ }
+ }
+ } while (np != np->parent);
+
+ r = ARCHIVE_OK;
+exit_traverse_tree:
+ idr_cleanup(&idr);
+
+ return (r);
+}
+
+/*
+ * Collect directory entries into path_table by a directory depth.
+ */
+static int
+isoent_collect_dirs(struct vdd *vdd, struct isoent *rootent, int depth)
+{
+ struct isoent *np;
+
+ if (rootent == NULL)
+ rootent = vdd->rootent;
+ np = rootent;
+ do {
+ /* Register current directory to pathtable. */
+ path_table_add_entry(&(vdd->pathtbl[depth]), np);
+
+ if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) {
+ /* Enter to sub directories. */
+ np = np->subdirs.first;
+ depth++;
+ continue;
+ }
+ while (np != rootent) {
+ if (np->drnext == NULL) {
+ /* Return to the parent directory. */
+ np = np->parent;
+ depth--;
+ } else {
+ np = np->drnext;
+ break;
+ }
+ }
+ } while (np != rootent);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * The entry whose number of levels in a directory hierarchy is
+ * large than eight relocate to rr_move directory.
+ */
+static int
+isoent_rr_move_dir(struct archive_write *a, struct isoent **rr_moved,
+ struct isoent *curent, struct isoent **newent)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isoent *rrmoved, *mvent, *np;
+
+ if ((rrmoved = *rr_moved) == NULL) {
+ struct isoent *rootent = iso9660->primary.rootent;
+ /* There isn't rr_move entry.
+ * Create rr_move entry and insert it into the root entry.
+ */
+ rrmoved = isoent_create_virtual_dir(a, iso9660, "rr_moved");
+ if (rrmoved == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ /* Add "rr_moved" entry to the root entry. */
+ isoent_add_child_head(rootent, rrmoved);
+ archive_entry_set_nlink(rootent->file->entry,
+ archive_entry_nlink(rootent->file->entry) + 1);
+ /* Register "rr_moved" entry to second level pathtable. */
+ path_table_add_entry(&(iso9660->primary.pathtbl[1]), rrmoved);
+ /* Save rr_moved. */
+ *rr_moved = rrmoved;
+ }
+ /*
+ * Make a clone of curent which is going to be relocated
+ * to rr_moved.
+ */
+ mvent = isoent_clone(curent);
+ if (mvent == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ /* linking.. and use for creating "CL", "PL" and "RE" */
+ mvent->rr_parent = curent->parent;
+ curent->rr_child = mvent;
+ /*
+ * Move subdirectories from the curent to mvent
+ */
+ if (curent->children.first != NULL) {
+ *mvent->children.last = curent->children.first;
+ mvent->children.last = curent->children.last;
+ }
+ for (np = mvent->children.first; np != NULL; np = np->chnext)
+ np->parent = mvent;
+ mvent->children.cnt = curent->children.cnt;
+ curent->children.cnt = 0;
+ curent->children.first = NULL;
+ curent->children.last = &curent->children.first;
+
+ if (curent->subdirs.first != NULL) {
+ *mvent->subdirs.last = curent->subdirs.first;
+ mvent->subdirs.last = curent->subdirs.last;
+ }
+ mvent->subdirs.cnt = curent->subdirs.cnt;
+ curent->subdirs.cnt = 0;
+ curent->subdirs.first = NULL;
+ curent->subdirs.last = &curent->subdirs.first;
+
+ /*
+ * The mvent becomes a child of the rr_moved entry.
+ */
+ isoent_add_child_tail(rrmoved, mvent);
+ archive_entry_set_nlink(rrmoved->file->entry,
+ archive_entry_nlink(rrmoved->file->entry) + 1);
+ /*
+ * This entry which relocated to the rr_moved directory
+ * has to set the flag as a file.
+ * See also RRIP 4.1.5.1 Description of the "CL" System Use Entry.
+ */
+ curent->dir = 0;
+
+ *newent = mvent;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+isoent_rr_move(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct path_table *pt;
+ struct isoent *rootent, *rr_moved;
+ struct isoent *np, *last;
+ int r;
+
+ pt = &(iso9660->primary.pathtbl[MAX_DEPTH-1]);
+ /* There aren't level 8 directories reaching a deeper level. */
+ if (pt->cnt == 0)
+ return (ARCHIVE_OK);
+
+ rootent = iso9660->primary.rootent;
+ /* If "rr_moved" directory is already existing,
+ * we have to use it. */
+ rr_moved = isoent_find_child(rootent, "rr_moved");
+ if (rr_moved != NULL &&
+ rr_moved != rootent->children.first) {
+ /*
+ * It's necessary that rr_move is the first entry
+ * of the root.
+ */
+ /* Remove "rr_moved" entry from children chain. */
+ isoent_remove_child(rootent, rr_moved);
+
+ /* Add "rr_moved" entry into the head of children chain. */
+ isoent_add_child_head(rootent, rr_moved);
+ }
+
+ /*
+ * Check level 8 path_table.
+ * If find out sub directory entries, that entries move to rr_move.
+ */
+ np = pt->first;
+ while (np != NULL) {
+ last = path_table_last_entry(pt);
+ for (; np != NULL; np = np->ptnext) {
+ struct isoent *mvent;
+ struct isoent *newent;
+
+ if (!np->dir)
+ continue;
+ for (mvent = np->subdirs.first;
+ mvent != NULL; mvent = mvent->drnext) {
+ r = isoent_rr_move_dir(a, &rr_moved,
+ mvent, &newent);
+ if (r < 0)
+ return (r);
+ isoent_collect_dirs(&(iso9660->primary),
+ newent, 2);
+ }
+ }
+ /* If new entries are added to level 8 path_talbe,
+ * its sub directory entries move to rr_move too.
+ */
+ np = last->ptnext;
+ }
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * This comparing rule is according to ISO9660 Standard 6.9.1
+ */
+static int
+__LA_LIBC_CC
+_compare_path_table(const void *v1, const void *v2)
+{
+ const struct isoent *p1, *p2;
+ const char *s1, *s2;
+ int cmp, l;
+
+ p1 = *((const struct isoent **)(uintptr_t)v1);
+ p2 = *((const struct isoent **)(uintptr_t)v2);
+
+ /* Compare parent directory number */
+ cmp = p1->parent->dir_number - p2->parent->dir_number;
+ if (cmp != 0)
+ return (cmp);
+
+ /* Compare identifier */
+ s1 = p1->identifier;
+ s2 = p2->identifier;
+ l = p1->ext_off;
+ if (l > p2->ext_off)
+ l = p2->ext_off;
+ cmp = strncmp(s1, s2, l);
+ if (cmp != 0)
+ return (cmp);
+ if (p1->ext_off < p2->ext_off) {
+ s2 += l;
+ l = p2->ext_off - p1->ext_off;
+ while (l--)
+ if (0x20 != *s2++)
+ return (0x20
+ - *(const unsigned char *)(s2 - 1));
+ } else if (p1->ext_off > p2->ext_off) {
+ s1 += l;
+ l = p1->ext_off - p2->ext_off;
+ while (l--)
+ if (0x20 != *s1++)
+ return (*(const unsigned char *)(s1 - 1)
+ - 0x20);
+ }
+ return (0);
+}
+
+static int
+__LA_LIBC_CC
+_compare_path_table_joliet(const void *v1, const void *v2)
+{
+ const struct isoent *p1, *p2;
+ const unsigned char *s1, *s2;
+ int cmp, l;
+
+ p1 = *((const struct isoent **)(uintptr_t)v1);
+ p2 = *((const struct isoent **)(uintptr_t)v2);
+
+ /* Compare parent directory number */
+ cmp = p1->parent->dir_number - p2->parent->dir_number;
+ if (cmp != 0)
+ return (cmp);
+
+ /* Compare identifier */
+ s1 = (const unsigned char *)p1->identifier;
+ s2 = (const unsigned char *)p2->identifier;
+ l = p1->ext_off;
+ if (l > p2->ext_off)
+ l = p2->ext_off;
+ cmp = memcmp(s1, s2, l);
+ if (cmp != 0)
+ return (cmp);
+ if (p1->ext_off < p2->ext_off) {
+ s2 += l;
+ l = p2->ext_off - p1->ext_off;
+ while (l--)
+ if (0 != *s2++)
+ return (- *(const unsigned char *)(s2 - 1));
+ } else if (p1->ext_off > p2->ext_off) {
+ s1 += l;
+ l = p1->ext_off - p2->ext_off;
+ while (l--)
+ if (0 != *s1++)
+ return (*(const unsigned char *)(s1 - 1));
+ }
+ return (0);
+}
+
+static inline void
+path_table_add_entry(struct path_table *pathtbl, struct isoent *ent)
+{
+ ent->ptnext = NULL;
+ *pathtbl->last = ent;
+ pathtbl->last = &(ent->ptnext);
+ pathtbl->cnt ++;
+}
+
+static inline struct isoent *
+path_table_last_entry(struct path_table *pathtbl)
+{
+ if (pathtbl->first == NULL)
+ return (NULL);
+ return (((struct isoent *)(void *)
+ ((char *)(pathtbl->last) - offsetof(struct isoent, ptnext))));
+}
+
+/*
+ * Sort directory entries in path_table
+ * and assign directory number to each entries.
+ */
+static int
+isoent_make_path_table_2(struct archive_write *a, struct vdd *vdd,
+ int depth, int *dir_number)
+{
+ struct isoent *np;
+ struct isoent **enttbl;
+ struct path_table *pt;
+ int i;
+
+ pt = &vdd->pathtbl[depth];
+ if (pt->cnt == 0) {
+ pt->sorted = NULL;
+ return (ARCHIVE_OK);
+ }
+ enttbl = malloc(pt->cnt * sizeof(struct isoent *));
+ if (enttbl == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ pt->sorted = enttbl;
+ for (np = pt->first; np != NULL; np = np->ptnext)
+ *enttbl ++ = np;
+ enttbl = pt->sorted;
+
+ switch (vdd->vdd_type) {
+ case VDD_PRIMARY:
+ case VDD_ENHANCED:
+#ifdef __COMPAR_FN_T
+ qsort(enttbl, pt->cnt, sizeof(struct isoent *),
+ (__compar_fn_t)_compare_path_table);
+#else
+ qsort(enttbl, pt->cnt, sizeof(struct isoent *),
+ _compare_path_table);
+#endif
+ break;
+ case VDD_JOLIET:
+#ifdef __COMPAR_FN_T
+ qsort(enttbl, pt->cnt, sizeof(struct isoent *),
+ (__compar_fn_t)_compare_path_table_joliet);
+#else
+ qsort(enttbl, pt->cnt, sizeof(struct isoent *),
+ _compare_path_table_joliet);
+#endif
+ break;
+ }
+ for (i = 0; i < pt->cnt; i++)
+ enttbl[i]->dir_number = (*dir_number)++;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+isoent_alloc_path_table(struct archive_write *a, struct vdd *vdd,
+ int max_depth)
+{
+ int i;
+
+ vdd->max_depth = max_depth;
+ vdd->pathtbl = malloc(sizeof(*vdd->pathtbl) * vdd->max_depth);
+ if (vdd->pathtbl == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ for (i = 0; i < vdd->max_depth; i++) {
+ vdd->pathtbl[i].first = NULL;
+ vdd->pathtbl[i].last = &(vdd->pathtbl[i].first);
+ vdd->pathtbl[i].sorted = NULL;
+ vdd->pathtbl[i].cnt = 0;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Make Path Tables
+ */
+static int
+isoent_make_path_table(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ int depth, r;
+ int dir_number;
+
+ /*
+ * Init Path Table.
+ */
+ if (iso9660->dircnt_max >= MAX_DEPTH &&
+ (!iso9660->opt.limit_depth || iso9660->opt.iso_level == 4))
+ r = isoent_alloc_path_table(a, &(iso9660->primary),
+ iso9660->dircnt_max + 1);
+ else
+ /* The number of levels in the hierarchy cannot exceed
+ * eight. */
+ r = isoent_alloc_path_table(a, &(iso9660->primary),
+ MAX_DEPTH);
+ if (r < 0)
+ return (r);
+ if (iso9660->opt.joliet) {
+ r = isoent_alloc_path_table(a, &(iso9660->joliet),
+ iso9660->dircnt_max + 1);
+ if (r < 0)
+ return (r);
+ }
+
+ /* Step 0.
+ * - Collect directories for primary and joliet.
+ */
+ isoent_collect_dirs(&(iso9660->primary), NULL, 0);
+ if (iso9660->opt.joliet)
+ isoent_collect_dirs(&(iso9660->joliet), NULL, 0);
+ /*
+ * Rockridge; move deeper depth directories to rr_moved.
+ */
+ if (iso9660->opt.rr) {
+ r = isoent_rr_move(a);
+ if (r < 0)
+ return (r);
+ }
+
+ /* Update nlink. */
+ isofile_connect_hardlink_files(iso9660);
+
+ /* Step 1.
+ * - Renew a value of the depth of that directories.
+ * - Resolve hardlinks.
+ * - Convert pathnames to ISO9660 name or UCS2(joliet).
+ * - Sort files by each directory.
+ */
+ r = isoent_traverse_tree(a, &(iso9660->primary));
+ if (r < 0)
+ return (r);
+ if (iso9660->opt.joliet) {
+ r = isoent_traverse_tree(a, &(iso9660->joliet));
+ if (r < 0)
+ return (r);
+ }
+
+ /* Step 2.
+ * - Sort directories.
+ * - Assign all directory number.
+ */
+ dir_number = 1;
+ for (depth = 0; depth < iso9660->primary.max_depth; depth++) {
+ r = isoent_make_path_table_2(a, &(iso9660->primary),
+ depth, &dir_number);
+ if (r < 0)
+ return (r);
+ }
+ if (iso9660->opt.joliet) {
+ dir_number = 1;
+ for (depth = 0; depth < iso9660->joliet.max_depth; depth++) {
+ r = isoent_make_path_table_2(a, &(iso9660->joliet),
+ depth, &dir_number);
+ if (r < 0)
+ return (r);
+ }
+ }
+ if (iso9660->opt.limit_dirs && dir_number > 0xffff) {
+ /*
+ * Maximum number of directories is 65535(0xffff)
+ * doe to size(16bit) of Parent Directory Number of
+ * the Path Table.
+ * See also ISO9660 Standard 9.4.
+ */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Too many directories(%d) over 65535.", dir_number);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Get the size of the Path Table. */
+ calculate_path_table_size(&(iso9660->primary));
+ if (iso9660->opt.joliet)
+ calculate_path_table_size(&(iso9660->joliet));
+
+ return (ARCHIVE_OK);
+}
+
+static int
+isoent_find_out_boot_file(struct archive_write *a, struct isoent *rootent)
+{
+ struct iso9660 *iso9660 = a->format_data;
+
+ /* Find a isoent of the boot file. */
+ iso9660->el_torito.boot = isoent_find_entry(rootent,
+ iso9660->el_torito.boot_filename.s);
+ if (iso9660->el_torito.boot == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't find the boot image file ``%s''",
+ iso9660->el_torito.boot_filename.s);
+ return (ARCHIVE_FATAL);
+ }
+ iso9660->el_torito.boot->file->boot = BOOT_IMAGE;
+ return (ARCHIVE_OK);
+}
+
+static int
+isoent_create_boot_catalog(struct archive_write *a, struct isoent *rootent)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isofile *file;
+ struct isoent *isoent;
+ struct archive_entry *entry;
+
+ (void)rootent; /* UNUSED */
+ /*
+ * Create the entry which is the "boot.catalog" file.
+ */
+ file = isofile_new(a, NULL);
+ if (file == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ archive_entry_set_pathname(file->entry,
+ iso9660->el_torito.catalog_filename.s);
+ archive_entry_set_size(file->entry, LOGICAL_BLOCK_SIZE);
+ archive_entry_set_mtime(file->entry, iso9660->birth_time, 0);
+ archive_entry_set_atime(file->entry, iso9660->birth_time, 0);
+ archive_entry_set_ctime(file->entry, iso9660->birth_time, 0);
+ archive_entry_set_uid(file->entry, getuid());
+ archive_entry_set_gid(file->entry, getgid());
+ archive_entry_set_mode(file->entry, AE_IFREG | 0444);
+ archive_entry_set_nlink(file->entry, 1);
+
+ if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) {
+ isofile_free(file);
+ return (ARCHIVE_FATAL);
+ }
+ file->boot = BOOT_CATALOG;
+ file->content.size = LOGICAL_BLOCK_SIZE;
+ isofile_add_entry(iso9660, file);
+
+ isoent = isoent_new(file);
+ if (isoent == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ isoent->virtual = 1;
+
+ /* Add the "boot.catalog" entry into tree */
+ if (isoent_tree(a, &isoent) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ iso9660->el_torito.catalog = isoent;
+ /*
+ * Get a boot media type.
+ */
+ switch (iso9660->opt.boot_type) {
+ default:
+ case OPT_BOOT_TYPE_AUTO:
+ /* Try detecting a media type of the boot image. */
+ entry = iso9660->el_torito.boot->file->entry;
+ if (archive_entry_size(entry) == FD_1_2M_SIZE)
+ iso9660->el_torito.media_type =
+ BOOT_MEDIA_1_2M_DISKETTE;
+ else if (archive_entry_size(entry) == FD_1_44M_SIZE)
+ iso9660->el_torito.media_type =
+ BOOT_MEDIA_1_44M_DISKETTE;
+ else if (archive_entry_size(entry) == FD_2_88M_SIZE)
+ iso9660->el_torito.media_type =
+ BOOT_MEDIA_2_88M_DISKETTE;
+ else
+ /* We cannot decide whether the boot image is
+ * hard-disk. */
+ iso9660->el_torito.media_type =
+ BOOT_MEDIA_NO_EMULATION;
+ break;
+ case OPT_BOOT_TYPE_NO_EMU:
+ iso9660->el_torito.media_type = BOOT_MEDIA_NO_EMULATION;
+ break;
+ case OPT_BOOT_TYPE_HARD_DISK:
+ iso9660->el_torito.media_type = BOOT_MEDIA_HARD_DISK;
+ break;
+ case OPT_BOOT_TYPE_FD:
+ entry = iso9660->el_torito.boot->file->entry;
+ if (archive_entry_size(entry) <= FD_1_2M_SIZE)
+ iso9660->el_torito.media_type =
+ BOOT_MEDIA_1_2M_DISKETTE;
+ else if (archive_entry_size(entry) <= FD_1_44M_SIZE)
+ iso9660->el_torito.media_type =
+ BOOT_MEDIA_1_44M_DISKETTE;
+ else if (archive_entry_size(entry) <= FD_2_88M_SIZE)
+ iso9660->el_torito.media_type =
+ BOOT_MEDIA_2_88M_DISKETTE;
+ else {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Boot image file(``%s'') size is too big "
+ "for fd type.",
+ iso9660->el_torito.boot_filename.s);
+ return (ARCHIVE_FATAL);
+ }
+ break;
+ }
+
+ /*
+ * Get a system type.
+ * TODO: `El Torito' specification says "A copy of byte 5 from the
+ * Partition Table found in the boot image".
+ */
+ iso9660->el_torito.system_type = 0;
+
+ /*
+ * Get an ID.
+ */
+ if (iso9660->opt.publisher)
+ archive_string_copy(&(iso9660->el_torito.id),
+ &(iso9660->publisher_identifier));
+
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * If a media type is floppy, return its image size.
+ * otherwise return 0.
+ */
+static size_t
+fd_boot_image_size(int media_type)
+{
+ switch (media_type) {
+ case BOOT_MEDIA_1_2M_DISKETTE:
+ return (FD_1_2M_SIZE);
+ case BOOT_MEDIA_1_44M_DISKETTE:
+ return (FD_1_44M_SIZE);
+ case BOOT_MEDIA_2_88M_DISKETTE:
+ return (FD_2_88M_SIZE);
+ default:
+ return (0);
+ }
+}
+
+/*
+ * Make a boot catalog image data.
+ */
+static int
+make_boot_catalog(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ unsigned char *block;
+ unsigned char *p;
+ uint16_t sum, *wp;
+
+ block = wb_buffptr(a);
+ memset(block, 0, LOGICAL_BLOCK_SIZE);
+ p = block;
+ /*
+ * Validation Entry
+ */
+ /* Header ID */
+ p[0] = 1;
+ /* Platform ID */
+ p[1] = iso9660->el_torito.platform_id;
+ /* Reserved */
+ p[2] = p[3] = 0;
+ /* ID */
+ if (archive_strlen(&(iso9660->el_torito.id)) > 0)
+ strncpy((char *)p+4, iso9660->el_torito.id.s, 23);
+ p[27] = 0;
+ /* Checksum */
+ p[28] = p[29] = 0;
+ /* Key */
+ p[30] = 0x55;
+ p[31] = 0xAA;
+
+ sum = 0;
+ wp = (uint16_t *)block;
+ while (wp < (uint16_t *)&block[32])
+ sum += archive_le16dec(wp++);
+ set_num_721(&block[28], (~sum) + 1);
+
+ /*
+ * Initial/Default Entry
+ */
+ p = &block[32];
+ /* Boot Indicator */
+ p[0] = 0x88;
+ /* Boot media type */
+ p[1] = iso9660->el_torito.media_type;
+ /* Load Segment */
+ if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION)
+ set_num_721(&p[2], iso9660->el_torito.boot_load_seg);
+ else
+ set_num_721(&p[2], 0);
+ /* System Type */
+ p[4] = iso9660->el_torito.system_type;
+ /* Unused */
+ p[5] = 0;
+ /* Sector Count */
+ if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION)
+ set_num_721(&p[6], iso9660->el_torito.boot_load_size);
+ else
+ set_num_721(&p[6], 1);
+ /* Load RBA */
+ set_num_731(&p[8],
+ iso9660->el_torito.boot->file->content.location);
+ /* Unused */
+ memset(&p[12], 0, 20);
+
+ return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+static int
+setup_boot_information(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isoent *np;
+ int64_t size;
+ uint32_t sum;
+ unsigned char buff[4096];
+
+ np = iso9660->el_torito.boot;
+ lseek(iso9660->temp_fd,
+ np->file->content.offset_of_temp + 64, SEEK_SET);
+ size = archive_entry_size(np->file->entry) - 64;
+ if (size <= 0) {
+ archive_set_error(&a->archive, errno,
+ "Boot file(%jd) is too small", (intmax_t)size + 64);
+ return (ARCHIVE_FATAL);
+ }
+ sum = 0;
+ while (size > 0) {
+ size_t rsize;
+ ssize_t i, rs;
+
+ if (size > (int64_t)sizeof(buff))
+ rsize = sizeof(buff);
+ else
+ rsize = (size_t)size;
+
+ rs = read(iso9660->temp_fd, buff, rsize);
+ if (rs <= 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't read temporary file(%jd)",
+ (intmax_t)rs);
+ return (ARCHIVE_FATAL);
+ }
+ for (i = 0; i < rs; i += 4)
+ sum += archive_le32dec(buff + i);
+ size -= rs;
+ }
+ /* Set the location of Primary Volume Descriptor. */
+ set_num_731(buff, SYSTEM_AREA_BLOCK);
+ /* Set the location of the boot file. */
+ set_num_731(buff+4, np->file->content.location);
+ /* Set the size of the boot file. */
+ size = fd_boot_image_size(iso9660->el_torito.media_type);
+ if (size == 0)
+ size = archive_entry_size(np->file->entry);
+ set_num_731(buff+8, (uint32_t)size);
+ /* Set the sum of the boot file. */
+ set_num_731(buff+12, sum);
+ /* Clear reserved bytes. */
+ memset(buff+16, 0, 40);
+
+ /* Overwrite the boot file. */
+ lseek(iso9660->temp_fd,
+ np->file->content.offset_of_temp + 8, SEEK_SET);
+ return (write_to_temp(a, buff, 56));
+}
+
+#ifdef HAVE_ZLIB_H
+
+static int
+zisofs_init_zstream(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ int r;
+
+ iso9660->zisofs.stream.next_in = NULL;
+ iso9660->zisofs.stream.avail_in = 0;
+ iso9660->zisofs.stream.total_in = 0;
+ iso9660->zisofs.stream.total_out = 0;
+ if (iso9660->zisofs.stream_valid)
+ r = deflateReset(&(iso9660->zisofs.stream));
+ else {
+ r = deflateInit(&(iso9660->zisofs.stream),
+ iso9660->zisofs.compression_level);
+ iso9660->zisofs.stream_valid = 1;
+ }
+ switch (r) {
+ case Z_OK:
+ break;
+ default:
+ case Z_STREAM_ERROR:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing "
+ "compression library: invalid setup parameter");
+ return (ARCHIVE_FATAL);
+ case Z_MEM_ERROR:
+ archive_set_error(&a->archive, ENOMEM,
+ "Internal error initializing "
+ "compression library");
+ return (ARCHIVE_FATAL);
+ case Z_VERSION_ERROR:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing "
+ "compression library: invalid library version");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_ZLIB_H */
+
+static int
+zisofs_init(struct archive_write *a, struct isofile *file)
+{
+ struct iso9660 *iso9660 = a->format_data;
+#ifdef HAVE_ZLIB_H
+ uint64_t tsize;
+ size_t _ceil, bpsize;
+ int r;
+#endif
+
+ iso9660->zisofs.detect_magic = 0;
+ iso9660->zisofs.making = 0;
+
+ if (!iso9660->opt.rr || !iso9660->opt.zisofs)
+ return (ARCHIVE_OK);
+
+ if (archive_entry_size(file->entry) >= 24 &&
+ archive_entry_size(file->entry) < MULTI_EXTENT_SIZE) {
+ /* Acceptable file size for zisofs. */
+ iso9660->zisofs.detect_magic = 1;
+ iso9660->zisofs.magic_cnt = 0;
+ }
+ if (!iso9660->zisofs.detect_magic)
+ return (ARCHIVE_OK);
+
+#ifdef HAVE_ZLIB_H
+ /* The number of Logical Blocks which uncompressed data
+ * will use in iso-image file is the same as the number of
+ * Logical Blocks which zisofs(compressed) data will use
+ * in ISO-image file. It won't reduce iso-image file size. */
+ if (archive_entry_size(file->entry) <= LOGICAL_BLOCK_SIZE)
+ return (ARCHIVE_OK);
+
+ /* Initialize compression library */
+ r = zisofs_init_zstream(a);
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Mark file->zisofs to create RRIP 'ZF' Use Entry. */
+ file->zisofs.header_size = ZF_HEADER_SIZE >> 2;
+ file->zisofs.log2_bs = ZF_LOG2_BS;
+ file->zisofs.uncompressed_size =
+ (uint32_t)archive_entry_size(file->entry);
+
+ /* Calculate a size of Block Pointers of zisofs. */
+ _ceil = (file->zisofs.uncompressed_size + ZF_BLOCK_SIZE -1)
+ >> file->zisofs.log2_bs;
+ iso9660->zisofs.block_pointers_cnt = (int)_ceil + 1;
+ iso9660->zisofs.block_pointers_idx = 0;
+
+ /* Ensure a buffer size used for Block Pointers */
+ bpsize = iso9660->zisofs.block_pointers_cnt *
+ sizeof(iso9660->zisofs.block_pointers[0]);
+ if (iso9660->zisofs.block_pointers_allocated < bpsize) {
+ free(iso9660->zisofs.block_pointers);
+ iso9660->zisofs.block_pointers = malloc(bpsize);
+ if (iso9660->zisofs.block_pointers == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate data");
+ return (ARCHIVE_FATAL);
+ }
+ iso9660->zisofs.block_pointers_allocated = bpsize;
+ }
+
+ /*
+ * Skip zisofs header and Block Pointers, which we will write
+ * after all compressed data of a file written to the temporary
+ * file.
+ */
+ tsize = ZF_HEADER_SIZE + bpsize;
+ if (write_null(a, (size_t)tsize) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /*
+ * Initialize some variables to make zisofs.
+ */
+ archive_le32enc(&(iso9660->zisofs.block_pointers[0]),
+ (uint32_t)tsize);
+ iso9660->zisofs.remaining = file->zisofs.uncompressed_size;
+ iso9660->zisofs.making = 1;
+ iso9660->zisofs.allzero = 1;
+ iso9660->zisofs.block_offset = tsize;
+ iso9660->zisofs.total_size = tsize;
+ iso9660->cur_file->cur_content->size = tsize;
+#endif
+
+ return (ARCHIVE_OK);
+}
+
+static void
+zisofs_detect_magic(struct archive_write *a, const void *buff, size_t s)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isofile *file = iso9660->cur_file;
+ const unsigned char *p, *endp;
+ const unsigned char *magic_buff;
+ uint32_t uncompressed_size;
+ unsigned char header_size;
+ unsigned char log2_bs;
+ size_t _ceil, doff;
+ uint32_t bst, bed;
+ int magic_max;
+ int64_t entry_size;
+
+ entry_size = archive_entry_size(file->entry);
+ if ((int64_t)sizeof(iso9660->zisofs.magic_buffer) > entry_size)
+ magic_max = (int)entry_size;
+ else
+ magic_max = sizeof(iso9660->zisofs.magic_buffer);
+
+ if (iso9660->zisofs.magic_cnt == 0 && s >= (size_t)magic_max)
+ /* It's unnecessary we copy buffer. */
+ magic_buff = buff;
+ else {
+ if (iso9660->zisofs.magic_cnt < magic_max) {
+ size_t l;
+
+ l = sizeof(iso9660->zisofs.magic_buffer)
+ - iso9660->zisofs.magic_cnt;
+ if (l > s)
+ l = s;
+ memcpy(iso9660->zisofs.magic_buffer
+ + iso9660->zisofs.magic_cnt, buff, l);
+ iso9660->zisofs.magic_cnt += (int)l;
+ if (iso9660->zisofs.magic_cnt < magic_max)
+ return;
+ }
+ magic_buff = iso9660->zisofs.magic_buffer;
+ }
+ iso9660->zisofs.detect_magic = 0;
+ p = magic_buff;
+
+ /* Check the magic code of zisofs. */
+ if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0)
+ /* This is not zisofs file which made by mkzftree. */
+ return;
+ p += sizeof(zisofs_magic);
+
+ /* Read a zisofs header. */
+ uncompressed_size = archive_le32dec(p);
+ header_size = p[4];
+ log2_bs = p[5];
+ if (uncompressed_size < 24 || header_size != 4 ||
+ log2_bs > 30 || log2_bs < 7)
+ return;/* Invalid or not supported header. */
+
+ /* Calculate a size of Block Pointers of zisofs. */
+ _ceil = (uncompressed_size +
+ (ARCHIVE_LITERAL_LL(1) << log2_bs) -1) >> log2_bs;
+ doff = (_ceil + 1) * 4 + 16;
+ if (entry_size < (int64_t)doff)
+ return;/* Invalid data. */
+
+ /* Check every Block Pointer has valid value. */
+ p = magic_buff + 16;
+ endp = magic_buff + magic_max;
+ while (_ceil && p + 8 <= endp) {
+ bst = archive_le32dec(p);
+ if (bst != doff)
+ return;/* Invalid data. */
+ p += 4;
+ bed = archive_le32dec(p);
+ if (bed < bst || bed > entry_size)
+ return;/* Invalid data. */
+ doff += bed - bst;
+ _ceil--;
+ }
+
+ file->zisofs.uncompressed_size = uncompressed_size;
+ file->zisofs.header_size = header_size;
+ file->zisofs.log2_bs = log2_bs;
+
+ /* Disable making a zisofs image. */
+ iso9660->zisofs.making = 0;
+}
+
+#ifdef HAVE_ZLIB_H
+
+/*
+ * Compress data and write it to a temporary file.
+ */
+static int
+zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isofile *file = iso9660->cur_file;
+ const unsigned char *b;
+ z_stream *zstrm;
+ size_t avail, csize;
+ int flush, r;
+
+ zstrm = &(iso9660->zisofs.stream);
+ zstrm->next_out = wb_buffptr(a);
+ zstrm->avail_out = (uInt)wb_remaining(a);
+ b = (const unsigned char *)buff;
+ do {
+ avail = ZF_BLOCK_SIZE - zstrm->total_in;
+ if (s < avail) {
+ avail = s;
+ flush = Z_NO_FLUSH;
+ } else
+ flush = Z_FINISH;
+ iso9660->zisofs.remaining -= avail;
+ if (iso9660->zisofs.remaining <= 0)
+ flush = Z_FINISH;
+
+ zstrm->next_in = (Bytef *)(uintptr_t)(const void *)b;
+ zstrm->avail_in = (uInt)avail;
+
+ /*
+ * Check if current data block are all zero.
+ */
+ if (iso9660->zisofs.allzero) {
+ const unsigned char *nonzero = b;
+ const unsigned char *nonzeroend = b + avail;
+
+ while (nonzero < nonzeroend)
+ if (*nonzero++) {
+ iso9660->zisofs.allzero = 0;
+ break;
+ }
+ }
+ b += avail;
+ s -= avail;
+
+ /*
+ * If current data block are all zero, we do not use
+ * compressed data.
+ */
+ if (flush == Z_FINISH && iso9660->zisofs.allzero &&
+ avail + zstrm->total_in == ZF_BLOCK_SIZE) {
+ if (iso9660->zisofs.block_offset !=
+ file->cur_content->size) {
+ int64_t diff;
+
+ r = wb_set_offset(a,
+ file->cur_content->offset_of_temp +
+ iso9660->zisofs.block_offset);
+ if (r != ARCHIVE_OK)
+ return (r);
+ diff = file->cur_content->size -
+ iso9660->zisofs.block_offset;
+ file->cur_content->size -= diff;
+ iso9660->zisofs.total_size -= diff;
+ }
+ zstrm->avail_in = 0;
+ }
+
+ /*
+ * Compress file data.
+ */
+ while (zstrm->avail_in > 0) {
+ csize = zstrm->total_out;
+ r = deflate(zstrm, flush);
+ switch (r) {
+ case Z_OK:
+ case Z_STREAM_END:
+ csize = zstrm->total_out - csize;
+ if (wb_consume(a, csize) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ iso9660->zisofs.total_size += csize;
+ iso9660->cur_file->cur_content->size += csize;
+ zstrm->next_out = wb_buffptr(a);
+ zstrm->avail_out = (uInt)wb_remaining(a);
+ break;
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Compression failed:"
+ " deflate() call returned status %d",
+ r);
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ if (flush == Z_FINISH) {
+ /*
+ * Save the information of one zisofs block.
+ */
+ iso9660->zisofs.block_pointers_idx ++;
+ archive_le32enc(&(iso9660->zisofs.block_pointers[
+ iso9660->zisofs.block_pointers_idx]),
+ (uint32_t)iso9660->zisofs.total_size);
+ r = zisofs_init_zstream(a);
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ iso9660->zisofs.allzero = 1;
+ iso9660->zisofs.block_offset = file->cur_content->size;
+ }
+ } while (s);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+zisofs_finish_entry(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isofile *file = iso9660->cur_file;
+ unsigned char buff[16];
+ size_t s;
+ int64_t tail;
+
+ /* Direct temp file stream to zisofs temp file stream. */
+ archive_entry_set_size(file->entry, iso9660->zisofs.total_size);
+
+ /*
+ * Save a file pointer which points the end of current zisofs data.
+ */
+ tail = wb_offset(a);
+
+ /*
+ * Make a header.
+ *
+ * +-----------------+----------------+-----------------+
+ * | Header 16 bytes | Block Pointers | Compressed data |
+ * +-----------------+----------------+-----------------+
+ * 0 16 +X
+ * Block Pointers :
+ * 4 * (((Uncompressed file size + block_size -1) / block_size) + 1)
+ *
+ * Write zisofs header.
+ * Magic number
+ * +----+----+----+----+----+----+----+----+
+ * | 37 | E4 | 53 | 96 | C9 | DB | D6 | 07 |
+ * +----+----+----+----+----+----+----+----+
+ * 0 1 2 3 4 5 6 7 8
+ *
+ * +------------------------+------------------+
+ * | Uncompressed file size | header_size >> 2 |
+ * +------------------------+------------------+
+ * 8 12 13
+ *
+ * +-----------------+----------------+
+ * | log2 block_size | Reserved(0000) |
+ * +-----------------+----------------+
+ * 13 14 16
+ */
+ memcpy(buff, zisofs_magic, 8);
+ set_num_731(buff+8, file->zisofs.uncompressed_size);
+ buff[12] = file->zisofs.header_size;
+ buff[13] = file->zisofs.log2_bs;
+ buff[14] = buff[15] = 0;/* Reserved */
+
+ /* Move to the right position to write the header. */
+ wb_set_offset(a, file->content.offset_of_temp);
+
+ /* Write the header. */
+ if (wb_write_to_temp(a, buff, 16) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /*
+ * Write zisofs Block Pointers.
+ */
+ s = iso9660->zisofs.block_pointers_cnt *
+ sizeof(iso9660->zisofs.block_pointers[0]);
+ if (wb_write_to_temp(a, iso9660->zisofs.block_pointers, s)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Set a file pointer back to the end of the temporary file. */
+ wb_set_offset(a, tail);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+zisofs_free(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ int ret = ARCHIVE_OK;
+
+ free(iso9660->zisofs.block_pointers);
+ if (iso9660->zisofs.stream_valid &&
+ deflateEnd(&(iso9660->zisofs.stream)) != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up compressor");
+ ret = ARCHIVE_FATAL;
+ }
+ iso9660->zisofs.block_pointers = NULL;
+ iso9660->zisofs.stream_valid = 0;
+ return (ret);
+}
+
+struct zisofs_extract {
+ int pz_log2_bs; /* Log2 of block size */
+ uint64_t pz_uncompressed_size;
+ size_t uncompressed_buffer_size;
+
+ unsigned int initialized:1;
+ unsigned int header_passed:1;
+
+ uint32_t pz_offset;
+ unsigned char *block_pointers;
+ size_t block_pointers_size;
+ size_t block_pointers_avail;
+ size_t block_off;
+ uint32_t block_avail;
+
+ z_stream stream;
+ int stream_valid;
+};
+
+static ssize_t
+zisofs_extract_init(struct archive_write *a, struct zisofs_extract *zisofs,
+ const unsigned char *p, size_t bytes)
+{
+ size_t avail = bytes;
+ size_t _ceil, xsize;
+
+ /* Allocate block pointers buffer. */
+ _ceil = (size_t)((zisofs->pz_uncompressed_size +
+ (((int64_t)1) << zisofs->pz_log2_bs) - 1)
+ >> zisofs->pz_log2_bs);
+ xsize = (_ceil + 1) * 4;
+ if (zisofs->block_pointers == NULL) {
+ size_t alloc = ((xsize >> 10) + 1) << 10;
+ zisofs->block_pointers = malloc(alloc);
+ if (zisofs->block_pointers == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for zisofs decompression");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ zisofs->block_pointers_size = xsize;
+
+ /* Allocate uncompressed data buffer. */
+ zisofs->uncompressed_buffer_size = (size_t)1UL << zisofs->pz_log2_bs;
+
+ /*
+ * Read the file header, and check the magic code of zisofs.
+ */
+ if (!zisofs->header_passed) {
+ int err = 0;
+ if (avail < 16) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0)
+ err = 1;
+ else if (archive_le32dec(p + 8) != zisofs->pz_uncompressed_size)
+ err = 1;
+ else if (p[12] != 4 || p[13] != zisofs->pz_log2_bs)
+ err = 1;
+ if (err) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs file body");
+ return (ARCHIVE_FATAL);
+ }
+ avail -= 16;
+ p += 16;
+ zisofs->header_passed = 1;
+ }
+
+ /*
+ * Read block pointers.
+ */
+ if (zisofs->header_passed &&
+ zisofs->block_pointers_avail < zisofs->block_pointers_size) {
+ xsize = zisofs->block_pointers_size
+ - zisofs->block_pointers_avail;
+ if (avail < xsize)
+ xsize = avail;
+ memcpy(zisofs->block_pointers
+ + zisofs->block_pointers_avail, p, xsize);
+ zisofs->block_pointers_avail += xsize;
+ avail -= xsize;
+ if (zisofs->block_pointers_avail
+ == zisofs->block_pointers_size) {
+ /* We've got all block pointers and initialize
+ * related variables. */
+ zisofs->block_off = 0;
+ zisofs->block_avail = 0;
+ /* Complete a initialization */
+ zisofs->initialized = 1;
+ }
+ }
+ return ((ssize_t)avail);
+}
+
+static ssize_t
+zisofs_extract(struct archive_write *a, struct zisofs_extract *zisofs,
+ const unsigned char *p, size_t bytes)
+{
+ size_t avail;
+ int r;
+
+ if (!zisofs->initialized) {
+ ssize_t rs = zisofs_extract_init(a, zisofs, p, bytes);
+ if (rs < 0)
+ return (rs);
+ if (!zisofs->initialized) {
+ /* We need more data. */
+ zisofs->pz_offset += (uint32_t)bytes;
+ return (bytes);
+ }
+ avail = rs;
+ p += bytes - avail;
+ } else
+ avail = bytes;
+
+ /*
+ * Get block offsets from block pointers.
+ */
+ if (zisofs->block_avail == 0) {
+ uint32_t bst, bed;
+
+ if (zisofs->block_off + 4 >= zisofs->block_pointers_size) {
+ /* There isn't a pair of offsets. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs block pointers");
+ return (ARCHIVE_FATAL);
+ }
+ bst = archive_le32dec(
+ zisofs->block_pointers + zisofs->block_off);
+ if (bst != zisofs->pz_offset + (bytes - avail)) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs block pointers(cannot seek)");
+ return (ARCHIVE_FATAL);
+ }
+ bed = archive_le32dec(
+ zisofs->block_pointers + zisofs->block_off + 4);
+ if (bed < bst) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs block pointers");
+ return (ARCHIVE_FATAL);
+ }
+ zisofs->block_avail = bed - bst;
+ zisofs->block_off += 4;
+
+ /* Initialize compression library for new block. */
+ if (zisofs->stream_valid)
+ r = inflateReset(&zisofs->stream);
+ else
+ r = inflateInit(&zisofs->stream);
+ if (r != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't initialize zisofs decompression.");
+ return (ARCHIVE_FATAL);
+ }
+ zisofs->stream_valid = 1;
+ zisofs->stream.total_in = 0;
+ zisofs->stream.total_out = 0;
+ }
+
+ /*
+ * Make uncompressed data.
+ */
+ if (zisofs->block_avail == 0) {
+ /*
+ * It's basically 32K bytes NUL data.
+ */
+ unsigned char *wb;
+ size_t size, wsize;
+
+ size = zisofs->uncompressed_buffer_size;
+ while (size) {
+ wb = wb_buffptr(a);
+ if (size > wb_remaining(a))
+ wsize = wb_remaining(a);
+ else
+ wsize = size;
+ memset(wb, 0, wsize);
+ r = wb_consume(a, wsize);
+ if (r < 0)
+ return (r);
+ size -= wsize;
+ }
+ } else {
+ zisofs->stream.next_in = (Bytef *)(uintptr_t)(const void *)p;
+ if (avail > zisofs->block_avail)
+ zisofs->stream.avail_in = zisofs->block_avail;
+ else
+ zisofs->stream.avail_in = (uInt)avail;
+ zisofs->stream.next_out = wb_buffptr(a);
+ zisofs->stream.avail_out = (uInt)wb_remaining(a);
+
+ r = inflate(&zisofs->stream, 0);
+ switch (r) {
+ case Z_OK: /* Decompressor made some progress.*/
+ case Z_STREAM_END: /* Found end of stream. */
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "zisofs decompression failed (%d)", r);
+ return (ARCHIVE_FATAL);
+ }
+ avail -= zisofs->stream.next_in - p;
+ zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p);
+ r = wb_consume(a, wb_remaining(a) - zisofs->stream.avail_out);
+ if (r < 0)
+ return (r);
+ }
+ zisofs->pz_offset += (uint32_t)bytes;
+ return (bytes - avail);
+}
+
+static int
+zisofs_rewind_boot_file(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isofile *file;
+ unsigned char *rbuff;
+ ssize_t r;
+ size_t remaining, rbuff_size;
+ struct zisofs_extract zext;
+ int64_t read_offset, write_offset, new_offset;
+ int fd, ret = ARCHIVE_OK;
+
+ file = iso9660->el_torito.boot->file;
+ /*
+ * There is nothing to do if this boot file does not have
+ * zisofs header.
+ */
+ if (file->zisofs.header_size == 0)
+ return (ARCHIVE_OK);
+
+ /*
+ * Uncompress the zisofs'ed file contents.
+ */
+ memset(&zext, 0, sizeof(zext));
+ zext.pz_uncompressed_size = file->zisofs.uncompressed_size;
+ zext.pz_log2_bs = file->zisofs.log2_bs;
+
+ fd = iso9660->temp_fd;
+ new_offset = wb_offset(a);
+ read_offset = file->content.offset_of_temp;
+ remaining = (size_t)file->content.size;
+ if (remaining > 1024 * 32)
+ rbuff_size = 1024 * 32;
+ else
+ rbuff_size = remaining;
+
+ rbuff = malloc(rbuff_size);
+ if (rbuff == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ while (remaining) {
+ size_t rsize;
+ ssize_t rs;
+
+ /* Get the current file pointer. */
+ write_offset = lseek(fd, 0, SEEK_CUR);
+
+ /* Change the file pointer to read. */
+ lseek(fd, read_offset, SEEK_SET);
+
+ rsize = rbuff_size;
+ if (rsize > remaining)
+ rsize = remaining;
+ rs = read(iso9660->temp_fd, rbuff, rsize);
+ if (rs <= 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't read temporary file(%jd)", (intmax_t)rs);
+ ret = ARCHIVE_FATAL;
+ break;
+ }
+ remaining -= rs;
+ read_offset += rs;
+
+ /* Put the file pointer back to write. */
+ lseek(fd, write_offset, SEEK_SET);
+
+ r = zisofs_extract(a, &zext, rbuff, rs);
+ if (r < 0) {
+ ret = (int)r;
+ break;
+ }
+ }
+
+ if (ret == ARCHIVE_OK) {
+ /*
+ * Change the boot file content from zisofs'ed data
+ * to plain data.
+ */
+ file->content.offset_of_temp = new_offset;
+ file->content.size = file->zisofs.uncompressed_size;
+ archive_entry_set_size(file->entry, file->content.size);
+ /* Set to be no zisofs. */
+ file->zisofs.header_size = 0;
+ file->zisofs.log2_bs = 0;
+ file->zisofs.uncompressed_size = 0;
+ r = wb_write_padding_to_temp(a, file->content.size);
+ if (r < 0)
+ ret = ARCHIVE_FATAL;
+ }
+
+ /*
+ * Free the resource we used in this function only.
+ */
+ free(rbuff);
+ free(zext.block_pointers);
+ if (zext.stream_valid && inflateEnd(&(zext.stream)) != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up compressor");
+ ret = ARCHIVE_FATAL;
+ }
+
+ return (ret);
+}
+
+#else
+
+static int
+zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+ (void)buff; /* UNUSED */
+ (void)s; /* UNUSED */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Programming error");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+zisofs_rewind_boot_file(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+
+ if (iso9660->el_torito.boot->file->zisofs.header_size != 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "We cannot extract the zisofs imaged boot file;"
+ " this may not boot in being zisofs imaged");
+ return (ARCHIVE_FAILED);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+zisofs_finish_entry(struct archive_write *a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+static int
+zisofs_free(struct archive_write *a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_ZLIB_H */
+
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_mtree.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_mtree.c
new file mode 100644
index 00000000000..619b7714eeb
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_mtree.c
@@ -0,0 +1,2217 @@
+/*-
+ * Copyright (c) 2008 Joerg Sonnenberger
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_mtree.c 201171 2009-12-29 06:39:07Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "archive.h"
+#include "archive_digest_private.h"
+#include "archive_entry.h"
+#include "archive_entry_private.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+#define INDENTNAMELEN 15
+#define MAXLINELEN 80
+#define SET_KEYS \
+ (F_FLAGS | F_GID | F_GNAME | F_MODE | F_TYPE | F_UID | F_UNAME)
+
+struct attr_counter {
+ struct attr_counter *prev;
+ struct attr_counter *next;
+ struct mtree_entry *m_entry;
+ int count;
+};
+
+struct att_counter_set {
+ struct attr_counter *uid_list;
+ struct attr_counter *gid_list;
+ struct attr_counter *mode_list;
+ struct attr_counter *flags_list;
+};
+
+struct mtree_chain {
+ struct mtree_entry *first;
+ struct mtree_entry **last;
+};
+
+/*
+ * The Data only for a directory file.
+ */
+struct dir_info {
+ struct archive_rb_tree rbtree;
+ struct mtree_chain children;
+ struct mtree_entry *chnext;
+ int virtual;
+};
+
+/*
+ * The Data only for a regular file.
+ */
+struct reg_info {
+ int compute_sum;
+ uint32_t crc;
+ struct ae_digest digest;
+};
+
+struct mtree_entry {
+ struct archive_rb_node rbnode;
+ struct mtree_entry *next;
+ struct mtree_entry *parent;
+ struct dir_info *dir_info;
+ struct reg_info *reg_info;
+
+ struct archive_string parentdir;
+ struct archive_string basename;
+ struct archive_string pathname;
+ struct archive_string symlink;
+ struct archive_string uname;
+ struct archive_string gname;
+ struct archive_string fflags_text;
+ unsigned int nlink;
+ mode_t filetype;
+ mode_t mode;
+ int64_t size;
+ int64_t uid;
+ int64_t gid;
+ time_t mtime;
+ long mtime_nsec;
+ unsigned long fflags_set;
+ unsigned long fflags_clear;
+ dev_t rdevmajor;
+ dev_t rdevminor;
+ dev_t devmajor;
+ dev_t devminor;
+ int64_t ino;
+};
+
+struct mtree_writer {
+ struct mtree_entry *mtree_entry;
+ struct mtree_entry *root;
+ struct mtree_entry *cur_dirent;
+ struct archive_string cur_dirstr;
+ struct mtree_chain file_list;
+
+ struct archive_string ebuf;
+ struct archive_string buf;
+ int first;
+ uint64_t entry_bytes_remaining;
+
+ /*
+ * Set global value.
+ */
+ struct {
+ int processing;
+ mode_t type;
+ int keys;
+ int64_t uid;
+ int64_t gid;
+ mode_t mode;
+ unsigned long fflags_set;
+ unsigned long fflags_clear;
+ } set;
+ struct att_counter_set acs;
+ int classic;
+ int depth;
+
+ /* check sum */
+ int compute_sum;
+ uint32_t crc;
+ uint64_t crc_len;
+#ifdef ARCHIVE_HAS_MD5
+ archive_md5_ctx md5ctx;
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+ archive_rmd160_ctx rmd160ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+ archive_sha1_ctx sha1ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+ archive_sha256_ctx sha256ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+ archive_sha384_ctx sha384ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ archive_sha512_ctx sha512ctx;
+#endif
+ /* Keyword options */
+ int keys;
+#define F_CKSUM 0x00000001 /* checksum */
+#define F_DEV 0x00000002 /* device type */
+#define F_DONE 0x00000004 /* directory done */
+#define F_FLAGS 0x00000008 /* file flags */
+#define F_GID 0x00000010 /* gid */
+#define F_GNAME 0x00000020 /* group name */
+#define F_IGN 0x00000040 /* ignore */
+#define F_MAGIC 0x00000080 /* name has magic chars */
+#define F_MD5 0x00000100 /* MD5 digest */
+#define F_MODE 0x00000200 /* mode */
+#define F_NLINK 0x00000400 /* number of links */
+#define F_NOCHANGE 0x00000800 /* If owner/mode "wrong", do
+ * not change */
+#define F_OPT 0x00001000 /* existence optional */
+#define F_RMD160 0x00002000 /* RIPEMD160 digest */
+#define F_SHA1 0x00004000 /* SHA-1 digest */
+#define F_SIZE 0x00008000 /* size */
+#define F_SLINK 0x00010000 /* symbolic link */
+#define F_TAGS 0x00020000 /* tags */
+#define F_TIME 0x00040000 /* modification time */
+#define F_TYPE 0x00080000 /* file type */
+#define F_UID 0x00100000 /* uid */
+#define F_UNAME 0x00200000 /* user name */
+#define F_VISIT 0x00400000 /* file visited */
+#define F_SHA256 0x00800000 /* SHA-256 digest */
+#define F_SHA384 0x01000000 /* SHA-384 digest */
+#define F_SHA512 0x02000000 /* SHA-512 digest */
+#define F_INO 0x04000000 /* inode number */
+#define F_RESDEV 0x08000000 /* device ID on which the
+ * entry resides */
+
+ /* Options */
+ int dironly; /* If it is set, ignore all files except
+ * directory files, like mtree(8) -d option. */
+ int indent; /* If it is set, indent output data. */
+ int output_global_set; /* If it is set, use /set keyword to set
+ * global values. When generating mtree
+ * classic format, it is set by default. */
+};
+
+#define DEFAULT_KEYS (F_DEV | F_FLAGS | F_GID | F_GNAME | F_SLINK | F_MODE\
+ | F_NLINK | F_SIZE | F_TIME | F_TYPE | F_UID\
+ | F_UNAME)
+#define attr_counter_set_reset attr_counter_set_free
+
+static void attr_counter_free(struct attr_counter **);
+static int attr_counter_inc(struct attr_counter **, struct attr_counter *,
+ struct attr_counter *, struct mtree_entry *);
+static struct attr_counter * attr_counter_new(struct mtree_entry *,
+ struct attr_counter *);
+static int attr_counter_set_collect(struct mtree_writer *,
+ struct mtree_entry *);
+static void attr_counter_set_free(struct mtree_writer *);
+static int get_global_set_keys(struct mtree_writer *, struct mtree_entry *);
+static int mtree_entry_add_child_tail(struct mtree_entry *,
+ struct mtree_entry *);
+static int mtree_entry_create_virtual_dir(struct archive_write *, const char *,
+ struct mtree_entry **);
+static int mtree_entry_cmp_node(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+static int mtree_entry_cmp_key(const struct archive_rb_node *, const void *);
+static int mtree_entry_exchange_same_entry(struct archive_write *,
+ struct mtree_entry *, struct mtree_entry *);
+static void mtree_entry_free(struct mtree_entry *);
+static int mtree_entry_new(struct archive_write *, struct archive_entry *,
+ struct mtree_entry **);
+static void mtree_entry_register_free(struct mtree_writer *);
+static void mtree_entry_register_init(struct mtree_writer *);
+static int mtree_entry_setup_filenames(struct archive_write *,
+ struct mtree_entry *, struct archive_entry *);
+static int mtree_entry_tree_add(struct archive_write *, struct mtree_entry **);
+static void sum_init(struct mtree_writer *);
+static void sum_update(struct mtree_writer *, const void *, size_t);
+static void sum_final(struct mtree_writer *, struct reg_info *);
+static void sum_write(struct archive_string *, struct reg_info *);
+static int write_mtree_entry(struct archive_write *, struct mtree_entry *);
+static int write_dot_dot_entry(struct archive_write *, struct mtree_entry *);
+
+#define COMPUTE_CRC(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]
+static const uint32_t crctab[] = {
+ 0x0,
+ 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+ 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
+ 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+ 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
+ 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
+ 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
+ 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+ 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
+ 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
+ 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
+ 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+ 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
+ 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+ 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
+ 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
+ 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
+ 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
+ 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
+ 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+ 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
+ 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
+ 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
+ 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
+ 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
+ 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+ 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
+ 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+ 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
+ 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
+ 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
+ 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+ 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
+ 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
+ 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
+ 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+ 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
+ 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+ 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
+ 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
+ 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
+ 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
+ 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
+ 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+ 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
+ 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
+ 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
+ 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
+ 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
+ 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+ 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
+ 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+};
+
+static const unsigned char safe_char[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+ /* !"$%&'()*+,-./ EXCLUSION:0x20( ) 0x23(#) */
+ 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
+ /* 0123456789:;<>? EXCLUSION:0x3d(=) */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */
+ /* @ABCDEFGHIJKLMNO */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
+ /* PQRSTUVWXYZ[]^_ EXCLUSION:0x5c(\) */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* 50 - 5F */
+ /* `abcdefghijklmno */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
+ /* pqrstuvwxyz{|}~ */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
+};
+
+static void
+mtree_quote(struct archive_string *s, const char *str)
+{
+ const char *start;
+ char buf[4];
+ unsigned char c;
+
+ for (start = str; *str != '\0'; ++str) {
+ if (safe_char[*(const unsigned char *)str])
+ continue;
+ if (start != str)
+ archive_strncat(s, start, str - start);
+ c = (unsigned char)*str;
+ buf[0] = '\\';
+ buf[1] = (c / 64) + '0';
+ buf[2] = (c / 8 % 8) + '0';
+ buf[3] = (c % 8) + '0';
+ archive_strncat(s, buf, 4);
+ start = str + 1;
+ }
+
+ if (start != str)
+ archive_strncat(s, start, str - start);
+}
+
+/*
+ * Indent a line as the mtree utility does so it is readable for people.
+ */
+static void
+mtree_indent(struct mtree_writer *mtree)
+{
+ int i, fn, nd, pd;
+ const char *r, *s, *x;
+
+ if (mtree->classic) {
+ if (mtree->indent) {
+ nd = 0;
+ pd = mtree->depth * 4;
+ } else {
+ nd = mtree->depth?4:0;
+ pd = 0;
+ }
+ } else
+ nd = pd = 0;
+ fn = 1;
+ s = r = mtree->ebuf.s;
+ x = NULL;
+ while (*r == ' ')
+ r++;
+ while ((r = strchr(r, ' ')) != NULL) {
+ if (fn) {
+ fn = 0;
+ for (i = 0; i < nd + pd; i++)
+ archive_strappend_char(&mtree->buf, ' ');
+ archive_strncat(&mtree->buf, s, r - s);
+ if (nd + (r -s) > INDENTNAMELEN) {
+ archive_strncat(&mtree->buf, " \\\n", 3);
+ for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
+ archive_strappend_char(&mtree->buf, ' ');
+ } else {
+ for (i = (int)(r -s + nd);
+ i < (INDENTNAMELEN + 1); i++)
+ archive_strappend_char(&mtree->buf, ' ');
+ }
+ s = ++r;
+ x = NULL;
+ continue;
+ }
+ if (pd + (r - s) <= MAXLINELEN - 3 - INDENTNAMELEN)
+ x = r++;
+ else {
+ if (x == NULL)
+ x = r;
+ archive_strncat(&mtree->buf, s, x - s);
+ archive_strncat(&mtree->buf, " \\\n", 3);
+ for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
+ archive_strappend_char(&mtree->buf, ' ');
+ s = r = ++x;
+ x = NULL;
+ }
+ }
+ if (fn) {
+ for (i = 0; i < nd + pd; i++)
+ archive_strappend_char(&mtree->buf, ' ');
+ archive_strcat(&mtree->buf, s);
+ s += strlen(s);
+ }
+ if (x != NULL && pd + strlen(s) > MAXLINELEN - 3 - INDENTNAMELEN) {
+ /* Last keyword is longer. */
+ archive_strncat(&mtree->buf, s, x - s);
+ archive_strncat(&mtree->buf, " \\\n", 3);
+ for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
+ archive_strappend_char(&mtree->buf, ' ');
+ s = ++x;
+ }
+ archive_strcat(&mtree->buf, s);
+ archive_string_empty(&mtree->ebuf);
+}
+
+/*
+ * Write /set keyword.
+ * Set the most used value of uid, gid, mode and fflags, which are
+ * collected by the attr_counter_set_collect() function.
+ */
+static void
+write_global(struct mtree_writer *mtree)
+{
+ struct archive_string setstr;
+ struct archive_string unsetstr;
+ struct att_counter_set *acs;
+ int keys, oldkeys, effkeys;
+
+ archive_string_init(&setstr);
+ archive_string_init(&unsetstr);
+ keys = mtree->keys & SET_KEYS;
+ oldkeys = mtree->set.keys;
+ effkeys = keys;
+ acs = &mtree->acs;
+ if (mtree->set.processing) {
+ /*
+ * Check if the global data needs updating.
+ */
+ effkeys &= ~F_TYPE;
+ if (acs->uid_list == NULL)
+ effkeys &= ~(F_UNAME | F_UID);
+ else if (oldkeys & (F_UNAME | F_UID)) {
+ if (acs->uid_list->count < 2 ||
+ mtree->set.uid == acs->uid_list->m_entry->uid)
+ effkeys &= ~(F_UNAME | F_UID);
+ }
+ if (acs->gid_list == NULL)
+ effkeys &= ~(F_GNAME | F_GID);
+ else if (oldkeys & (F_GNAME | F_GID)) {
+ if (acs->gid_list->count < 2 ||
+ mtree->set.gid == acs->gid_list->m_entry->gid)
+ effkeys &= ~(F_GNAME | F_GID);
+ }
+ if (acs->mode_list == NULL)
+ effkeys &= ~F_MODE;
+ else if (oldkeys & F_MODE) {
+ if (acs->mode_list->count < 2 ||
+ mtree->set.mode == acs->mode_list->m_entry->mode)
+ effkeys &= ~F_MODE;
+ }
+ if (acs->flags_list == NULL)
+ effkeys &= ~F_FLAGS;
+ else if ((oldkeys & F_FLAGS) != 0) {
+ if (acs->flags_list->count < 2 ||
+ (acs->flags_list->m_entry->fflags_set ==
+ mtree->set.fflags_set &&
+ acs->flags_list->m_entry->fflags_clear ==
+ mtree->set.fflags_clear))
+ effkeys &= ~F_FLAGS;
+ }
+ } else {
+ if (acs->uid_list == NULL)
+ keys &= ~(F_UNAME | F_UID);
+ if (acs->gid_list == NULL)
+ keys &= ~(F_GNAME | F_GID);
+ if (acs->mode_list == NULL)
+ keys &= ~F_MODE;
+ if (acs->flags_list == NULL)
+ keys &= ~F_FLAGS;
+ }
+ if ((keys & effkeys & F_TYPE) != 0) {
+ if (mtree->dironly) {
+ archive_strcat(&setstr, " type=dir");
+ mtree->set.type = AE_IFDIR;
+ } else {
+ archive_strcat(&setstr, " type=file");
+ mtree->set.type = AE_IFREG;
+ }
+ }
+ if ((keys & effkeys & F_UNAME) != 0) {
+ if (archive_strlen(&(acs->uid_list->m_entry->uname)) > 0) {
+ archive_strcat(&setstr, " uname=");
+ mtree_quote(&setstr, acs->uid_list->m_entry->uname.s);
+ } else {
+ keys &= ~F_UNAME;
+ if ((oldkeys & F_UNAME) != 0)
+ archive_strcat(&unsetstr, " uname");
+ }
+ }
+ if ((keys & effkeys & F_UID) != 0) {
+ mtree->set.uid = acs->uid_list->m_entry->uid;
+ archive_string_sprintf(&setstr, " uid=%jd",
+ (intmax_t)mtree->set.uid);
+ }
+ if ((keys & effkeys & F_GNAME) != 0) {
+ if (archive_strlen(&(acs->gid_list->m_entry->gname)) > 0) {
+ archive_strcat(&setstr, " gname=");
+ mtree_quote(&setstr, acs->gid_list->m_entry->gname.s);
+ } else {
+ keys &= ~F_GNAME;
+ if ((oldkeys & F_GNAME) != 0)
+ archive_strcat(&unsetstr, " gname");
+ }
+ }
+ if ((keys & effkeys & F_GID) != 0) {
+ mtree->set.gid = acs->gid_list->m_entry->gid;
+ archive_string_sprintf(&setstr, " gid=%jd",
+ (intmax_t)mtree->set.gid);
+ }
+ if ((keys & effkeys & F_MODE) != 0) {
+ mtree->set.mode = acs->mode_list->m_entry->mode;
+ archive_string_sprintf(&setstr, " mode=%o",
+ (unsigned int)mtree->set.mode);
+ }
+ if ((keys & effkeys & F_FLAGS) != 0) {
+ if (archive_strlen(
+ &(acs->flags_list->m_entry->fflags_text)) > 0) {
+ archive_strcat(&setstr, " flags=");
+ mtree_quote(&setstr,
+ acs->flags_list->m_entry->fflags_text.s);
+ mtree->set.fflags_set =
+ acs->flags_list->m_entry->fflags_set;
+ mtree->set.fflags_clear =
+ acs->flags_list->m_entry->fflags_clear;
+ } else {
+ keys &= ~F_FLAGS;
+ if ((oldkeys & F_FLAGS) != 0)
+ archive_strcat(&unsetstr, " flags");
+ }
+ }
+ if (unsetstr.length > 0)
+ archive_string_sprintf(&mtree->buf, "/unset%s\n", unsetstr.s);
+ archive_string_free(&unsetstr);
+ if (setstr.length > 0)
+ archive_string_sprintf(&mtree->buf, "/set%s\n", setstr.s);
+ archive_string_free(&setstr);
+ mtree->set.keys = keys;
+ mtree->set.processing = 1;
+}
+
+static struct attr_counter *
+attr_counter_new(struct mtree_entry *me, struct attr_counter *prev)
+{
+ struct attr_counter *ac;
+
+ ac = malloc(sizeof(*ac));
+ if (ac != NULL) {
+ ac->prev = prev;
+ ac->next = NULL;
+ ac->count = 1;
+ ac->m_entry = me;
+ }
+ return (ac);
+}
+
+static void
+attr_counter_free(struct attr_counter **top)
+{
+ struct attr_counter *ac, *tac;
+
+ if (*top == NULL)
+ return;
+ ac = *top;
+ while (ac != NULL) {
+ tac = ac->next;
+ free(ac);
+ ac = tac;
+ }
+ *top = NULL;
+}
+
+static int
+attr_counter_inc(struct attr_counter **top, struct attr_counter *ac,
+ struct attr_counter *last, struct mtree_entry *me)
+{
+ struct attr_counter *pac;
+
+ if (ac != NULL) {
+ ac->count++;
+ if (*top == ac || ac->prev->count >= ac->count)
+ return (0);
+ for (pac = ac->prev; pac; pac = pac->prev) {
+ if (pac->count >= ac->count)
+ break;
+ }
+ ac->prev->next = ac->next;
+ if (ac->next != NULL)
+ ac->next->prev = ac->prev;
+ if (pac != NULL) {
+ ac->prev = pac;
+ ac->next = pac->next;
+ pac->next = ac;
+ if (ac->next != NULL)
+ ac->next->prev = ac;
+ } else {
+ ac->prev = NULL;
+ ac->next = *top;
+ *top = ac;
+ ac->next->prev = ac;
+ }
+ } else if (last != NULL) {
+ ac = attr_counter_new(me, last);
+ if (ac == NULL)
+ return (-1);
+ last->next = ac;
+ }
+ return (0);
+}
+
+/*
+ * Tabulate uid, gid, mode and fflags of a entry in order to be used for /set.
+ */
+static int
+attr_counter_set_collect(struct mtree_writer *mtree, struct mtree_entry *me)
+{
+ struct attr_counter *ac, *last;
+ struct att_counter_set *acs = &mtree->acs;
+ int keys = mtree->keys;
+
+ if (keys & (F_UNAME | F_UID)) {
+ if (acs->uid_list == NULL) {
+ acs->uid_list = attr_counter_new(me, NULL);
+ if (acs->uid_list == NULL)
+ return (-1);
+ } else {
+ last = NULL;
+ for (ac = acs->uid_list; ac; ac = ac->next) {
+ if (ac->m_entry->uid == me->uid)
+ break;
+ last = ac;
+ }
+ if (attr_counter_inc(&acs->uid_list, ac, last, me) < 0)
+ return (-1);
+ }
+ }
+ if (keys & (F_GNAME | F_GID)) {
+ if (acs->gid_list == NULL) {
+ acs->gid_list = attr_counter_new(me, NULL);
+ if (acs->gid_list == NULL)
+ return (-1);
+ } else {
+ last = NULL;
+ for (ac = acs->gid_list; ac; ac = ac->next) {
+ if (ac->m_entry->gid == me->gid)
+ break;
+ last = ac;
+ }
+ if (attr_counter_inc(&acs->gid_list, ac, last, me) < 0)
+ return (-1);
+ }
+ }
+ if (keys & F_MODE) {
+ if (acs->mode_list == NULL) {
+ acs->mode_list = attr_counter_new(me, NULL);
+ if (acs->mode_list == NULL)
+ return (-1);
+ } else {
+ last = NULL;
+ for (ac = acs->mode_list; ac; ac = ac->next) {
+ if (ac->m_entry->mode == me->mode)
+ break;
+ last = ac;
+ }
+ if (attr_counter_inc(&acs->mode_list, ac, last, me) < 0)
+ return (-1);
+ }
+ }
+ if (keys & F_FLAGS) {
+ if (acs->flags_list == NULL) {
+ acs->flags_list = attr_counter_new(me, NULL);
+ if (acs->flags_list == NULL)
+ return (-1);
+ } else {
+ last = NULL;
+ for (ac = acs->flags_list; ac; ac = ac->next) {
+ if (ac->m_entry->fflags_set == me->fflags_set &&
+ ac->m_entry->fflags_clear ==
+ me->fflags_clear)
+ break;
+ last = ac;
+ }
+ if (attr_counter_inc(&acs->flags_list, ac, last, me) < 0)
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static void
+attr_counter_set_free(struct mtree_writer *mtree)
+{
+ struct att_counter_set *acs = &mtree->acs;
+
+ attr_counter_free(&acs->uid_list);
+ attr_counter_free(&acs->gid_list);
+ attr_counter_free(&acs->mode_list);
+ attr_counter_free(&acs->flags_list);
+}
+
+static int
+get_global_set_keys(struct mtree_writer *mtree, struct mtree_entry *me)
+{
+ int keys;
+
+ keys = mtree->keys;
+
+ /*
+ * If a keyword has been set by /set, we do not need to
+ * output it.
+ */
+ if (mtree->set.keys == 0)
+ return (keys);/* /set is not used. */
+
+ if ((mtree->set.keys & (F_GNAME | F_GID)) != 0 &&
+ mtree->set.gid == me->gid)
+ keys &= ~(F_GNAME | F_GID);
+ if ((mtree->set.keys & (F_UNAME | F_UID)) != 0 &&
+ mtree->set.uid == me->uid)
+ keys &= ~(F_UNAME | F_UID);
+ if (mtree->set.keys & F_FLAGS) {
+ if (mtree->set.fflags_set == me->fflags_set &&
+ mtree->set.fflags_clear == me->fflags_clear)
+ keys &= ~F_FLAGS;
+ }
+ if ((mtree->set.keys & F_MODE) != 0 && mtree->set.mode == me->mode)
+ keys &= ~F_MODE;
+
+ switch (me->filetype) {
+ case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR:
+ case AE_IFBLK: case AE_IFIFO:
+ break;
+ case AE_IFDIR:
+ if ((mtree->set.keys & F_TYPE) != 0 &&
+ mtree->set.type == AE_IFDIR)
+ keys &= ~F_TYPE;
+ break;
+ case AE_IFREG:
+ default: /* Handle unknown file types as regular files. */
+ if ((mtree->set.keys & F_TYPE) != 0 &&
+ mtree->set.type == AE_IFREG)
+ keys &= ~F_TYPE;
+ break;
+ }
+
+ return (keys);
+}
+
+static int
+mtree_entry_new(struct archive_write *a, struct archive_entry *entry,
+ struct mtree_entry **m_entry)
+{
+ struct mtree_entry *me;
+ const char *s;
+ int r;
+ static const struct archive_rb_tree_ops rb_ops = {
+ mtree_entry_cmp_node, mtree_entry_cmp_key
+ };
+
+ me = calloc(1, sizeof(*me));
+ if (me == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for a mtree entry");
+ *m_entry = NULL;
+ return (ARCHIVE_FATAL);
+ }
+
+ r = mtree_entry_setup_filenames(a, me, entry);
+ if (r < ARCHIVE_WARN) {
+ mtree_entry_free(me);
+ *m_entry = NULL;
+ return (r);
+ }
+
+ if ((s = archive_entry_symlink(entry)) != NULL)
+ archive_strcpy(&me->symlink, s);
+ me->nlink = archive_entry_nlink(entry);
+ me->filetype = archive_entry_filetype(entry);
+ me->mode = archive_entry_mode(entry) & 07777;
+ me->uid = archive_entry_uid(entry);
+ me->gid = archive_entry_gid(entry);
+ if ((s = archive_entry_uname(entry)) != NULL)
+ archive_strcpy(&me->uname, s);
+ if ((s = archive_entry_gname(entry)) != NULL)
+ archive_strcpy(&me->gname, s);
+ if ((s = archive_entry_fflags_text(entry)) != NULL)
+ archive_strcpy(&me->fflags_text, s);
+ archive_entry_fflags(entry, &me->fflags_set, &me->fflags_clear);
+ me->mtime = archive_entry_mtime(entry);
+ me->mtime_nsec = archive_entry_mtime_nsec(entry);
+ me->rdevmajor = archive_entry_rdevmajor(entry);
+ me->rdevminor = archive_entry_rdevminor(entry);
+ me->devmajor = archive_entry_devmajor(entry);
+ me->devminor = archive_entry_devminor(entry);
+ me->ino = archive_entry_ino(entry);
+ me->size = archive_entry_size(entry);
+ if (me->filetype == AE_IFDIR) {
+ me->dir_info = calloc(1, sizeof(*me->dir_info));
+ if (me->dir_info == NULL) {
+ mtree_entry_free(me);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for a mtree entry");
+ *m_entry = NULL;
+ return (ARCHIVE_FATAL);
+ }
+ __archive_rb_tree_init(&me->dir_info->rbtree, &rb_ops);
+ me->dir_info->children.first = NULL;
+ me->dir_info->children.last = &(me->dir_info->children.first);
+ me->dir_info->chnext = NULL;
+ } else if (me->filetype == AE_IFREG) {
+ me->reg_info = calloc(1, sizeof(*me->reg_info));
+ if (me->reg_info == NULL) {
+ mtree_entry_free(me);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for a mtree entry");
+ *m_entry = NULL;
+ return (ARCHIVE_FATAL);
+ }
+ me->reg_info->compute_sum = 0;
+ }
+
+ *m_entry = me;
+ return (ARCHIVE_OK);
+}
+
+static void
+mtree_entry_free(struct mtree_entry *me)
+{
+ archive_string_free(&me->parentdir);
+ archive_string_free(&me->basename);
+ archive_string_free(&me->pathname);
+ archive_string_free(&me->symlink);
+ archive_string_free(&me->uname);
+ archive_string_free(&me->gname);
+ archive_string_free(&me->fflags_text);
+ free(me->dir_info);
+ free(me->reg_info);
+ free(me);
+}
+
+static int
+archive_write_mtree_header(struct archive_write *a,
+ struct archive_entry *entry)
+{
+ struct mtree_writer *mtree= a->format_data;
+ struct mtree_entry *mtree_entry;
+ int r, r2;
+
+ if (mtree->first) {
+ mtree->first = 0;
+ archive_strcat(&mtree->buf, "#mtree\n");
+ if ((mtree->keys & SET_KEYS) == 0)
+ mtree->output_global_set = 0;/* Disabled. */
+ }
+
+ mtree->entry_bytes_remaining = archive_entry_size(entry);
+
+ /* While directory only mode, we do not handle non directory files. */
+ if (mtree->dironly && archive_entry_filetype(entry) != AE_IFDIR)
+ return (ARCHIVE_OK);
+
+ r2 = mtree_entry_new(a, entry, &mtree_entry);
+ if (r2 < ARCHIVE_WARN)
+ return (r2);
+ r = mtree_entry_tree_add(a, &mtree_entry);
+ if (r < ARCHIVE_WARN) {
+ mtree_entry_free(mtree_entry);
+ return (r);
+ }
+ mtree->mtree_entry = mtree_entry;
+
+ /* If the current file is a regular file, we have to
+ * compute the sum of its content.
+ * Initialize a bunch of checksum context. */
+ if (mtree_entry->reg_info)
+ sum_init(mtree);
+
+ return (r2);
+}
+
+static int
+write_mtree_entry(struct archive_write *a, struct mtree_entry *me)
+{
+ struct mtree_writer *mtree = a->format_data;
+ struct archive_string *str;
+ int keys, ret;
+
+ if (me->dir_info) {
+ if (mtree->classic) {
+ /*
+ * Output a comment line to describe the full
+ * pathname of the entry as mtree utility does
+ * while generating classic format.
+ */
+ if (!mtree->dironly)
+ archive_strappend_char(&mtree->buf, '\n');
+ if (me->parentdir.s)
+ archive_string_sprintf(&mtree->buf,
+ "# %s/%s\n",
+ me->parentdir.s, me->basename.s);
+ else
+ archive_string_sprintf(&mtree->buf,
+ "# %s\n",
+ me->basename.s);
+ }
+ if (mtree->output_global_set)
+ write_global(mtree);
+ }
+ archive_string_empty(&mtree->ebuf);
+ str = (mtree->indent || mtree->classic)? &mtree->ebuf : &mtree->buf;
+
+ if (!mtree->classic && me->parentdir.s) {
+ /*
+ * If generating format is not classic one(v1), output
+ * a full pathname.
+ */
+ mtree_quote(str, me->parentdir.s);
+ archive_strappend_char(str, '/');
+ }
+ mtree_quote(str, me->basename.s);
+
+ keys = get_global_set_keys(mtree, me);
+ if ((keys & F_NLINK) != 0 &&
+ me->nlink != 1 && me->filetype != AE_IFDIR)
+ archive_string_sprintf(str, " nlink=%u", me->nlink);
+
+ if ((keys & F_GNAME) != 0 && archive_strlen(&me->gname) > 0) {
+ archive_strcat(str, " gname=");
+ mtree_quote(str, me->gname.s);
+ }
+ if ((keys & F_UNAME) != 0 && archive_strlen(&me->uname) > 0) {
+ archive_strcat(str, " uname=");
+ mtree_quote(str, me->uname.s);
+ }
+ if ((keys & F_FLAGS) != 0) {
+ if (archive_strlen(&me->fflags_text) > 0) {
+ archive_strcat(str, " flags=");
+ mtree_quote(str, me->fflags_text.s);
+ } else if (mtree->set.processing &&
+ (mtree->set.keys & F_FLAGS) != 0)
+ /* Overwrite the global parameter. */
+ archive_strcat(str, " flags=none");
+ }
+ if ((keys & F_TIME) != 0)
+ archive_string_sprintf(str, " time=%jd.%jd",
+ (intmax_t)me->mtime, (intmax_t)me->mtime_nsec);
+ if ((keys & F_MODE) != 0)
+ archive_string_sprintf(str, " mode=%o", (unsigned int)me->mode);
+ if ((keys & F_GID) != 0)
+ archive_string_sprintf(str, " gid=%jd", (intmax_t)me->gid);
+ if ((keys & F_UID) != 0)
+ archive_string_sprintf(str, " uid=%jd", (intmax_t)me->uid);
+
+ if ((keys & F_INO) != 0)
+ archive_string_sprintf(str, " inode=%jd", (intmax_t)me->ino);
+ if ((keys & F_RESDEV) != 0) {
+ archive_string_sprintf(str,
+ " resdevice=native,%ju,%ju",
+ (uintmax_t)me->devmajor,
+ (uintmax_t)me->devminor);
+ }
+
+ switch (me->filetype) {
+ case AE_IFLNK:
+ if ((keys & F_TYPE) != 0)
+ archive_strcat(str, " type=link");
+ if ((keys & F_SLINK) != 0) {
+ archive_strcat(str, " link=");
+ mtree_quote(str, me->symlink.s);
+ }
+ break;
+ case AE_IFSOCK:
+ if ((keys & F_TYPE) != 0)
+ archive_strcat(str, " type=socket");
+ break;
+ case AE_IFCHR:
+ if ((keys & F_TYPE) != 0)
+ archive_strcat(str, " type=char");
+ if ((keys & F_DEV) != 0) {
+ archive_string_sprintf(str,
+ " device=native,%ju,%ju",
+ (uintmax_t)me->rdevmajor,
+ (uintmax_t)me->rdevminor);
+ }
+ break;
+ case AE_IFBLK:
+ if ((keys & F_TYPE) != 0)
+ archive_strcat(str, " type=block");
+ if ((keys & F_DEV) != 0) {
+ archive_string_sprintf(str,
+ " device=native,%ju,%ju",
+ (uintmax_t)me->rdevmajor,
+ (uintmax_t)me->rdevminor);
+ }
+ break;
+ case AE_IFDIR:
+ if ((keys & F_TYPE) != 0)
+ archive_strcat(str, " type=dir");
+ break;
+ case AE_IFIFO:
+ if ((keys & F_TYPE) != 0)
+ archive_strcat(str, " type=fifo");
+ break;
+ case AE_IFREG:
+ default: /* Handle unknown file types as regular files. */
+ if ((keys & F_TYPE) != 0)
+ archive_strcat(str, " type=file");
+ if ((keys & F_SIZE) != 0)
+ archive_string_sprintf(str, " size=%jd",
+ (intmax_t)me->size);
+ break;
+ }
+
+ /* Write a bunch of sum. */
+ if (me->reg_info)
+ sum_write(str, me->reg_info);
+
+ archive_strappend_char(str, '\n');
+ if (mtree->indent || mtree->classic)
+ mtree_indent(mtree);
+
+ if (mtree->buf.length > 32768) {
+ ret = __archive_write_output(
+ a, mtree->buf.s, mtree->buf.length);
+ archive_string_empty(&mtree->buf);
+ } else
+ ret = ARCHIVE_OK;
+ return (ret);
+}
+
+static int
+write_dot_dot_entry(struct archive_write *a, struct mtree_entry *n)
+{
+ struct mtree_writer *mtree = a->format_data;
+ int ret;
+
+ if (n->parentdir.s) {
+ if (mtree->indent) {
+ int i, pd = mtree->depth * 4;
+ for (i = 0; i < pd; i++)
+ archive_strappend_char(&mtree->buf, ' ');
+ }
+ archive_string_sprintf(&mtree->buf, "# %s/%s\n",
+ n->parentdir.s, n->basename.s);
+ }
+
+ if (mtree->indent) {
+ archive_string_empty(&mtree->ebuf);
+ archive_strncat(&mtree->ebuf, "..\n\n", (mtree->dironly)?3:4);
+ mtree_indent(mtree);
+ } else
+ archive_strncat(&mtree->buf, "..\n\n", (mtree->dironly)?3:4);
+
+ if (mtree->buf.length > 32768) {
+ ret = __archive_write_output(
+ a, mtree->buf.s, mtree->buf.length);
+ archive_string_empty(&mtree->buf);
+ } else
+ ret = ARCHIVE_OK;
+ return (ret);
+}
+
+/*
+ * Write mtree entries saved at attr_counter_set_collect() function.
+ */
+static int
+write_mtree_entry_tree(struct archive_write *a)
+{
+ struct mtree_writer *mtree = a->format_data;
+ struct mtree_entry *np = mtree->root;
+ struct archive_rb_node *n;
+ int ret;
+
+ do {
+ if (mtree->output_global_set) {
+ /*
+ * Collect attribute information to know which value
+ * is frequently used among the children.
+ */
+ attr_counter_set_reset(mtree);
+ ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) {
+ struct mtree_entry *e = (struct mtree_entry *)n;
+ if (attr_counter_set_collect(mtree, e) < 0) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ }
+ if (!np->dir_info->virtual || mtree->classic) {
+ ret = write_mtree_entry(a, np);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } else {
+ /* Whenever output_global_set is enabled
+ * output global value(/set keywords)
+ * even if the directory entry is not allowed
+ * to be written because the global values
+ * can be used for the children. */
+ if (mtree->output_global_set)
+ write_global(mtree);
+ }
+ /*
+ * Output the attribute of all files except directory files.
+ */
+ mtree->depth++;
+ ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) {
+ struct mtree_entry *e = (struct mtree_entry *)n;
+
+ if (e->dir_info)
+ mtree_entry_add_child_tail(np, e);
+ else {
+ ret = write_mtree_entry(a, e);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ }
+ mtree->depth--;
+
+ if (np->dir_info->children.first != NULL) {
+ /*
+ * Descend the tree.
+ */
+ np = np->dir_info->children.first;
+ if (mtree->indent)
+ mtree->depth++;
+ continue;
+ } else if (mtree->classic) {
+ /*
+ * While printing mtree classic, if there are not
+ * any directory files(except "." and "..") in the
+ * directory, output two dots ".." as returning
+ * the parent directory.
+ */
+ ret = write_dot_dot_entry(a, np);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ while (np != np->parent) {
+ if (np->dir_info->chnext == NULL) {
+ /*
+ * Ascend the tree; go back to the parent.
+ */
+ if (mtree->indent)
+ mtree->depth--;
+ if (mtree->classic) {
+ ret = write_dot_dot_entry(a,
+ np->parent);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ np = np->parent;
+ } else {
+ /*
+ * Switch to next mtree entry in the directory.
+ */
+ np = np->dir_info->chnext;
+ break;
+ }
+ }
+ } while (np != np->parent);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_mtree_finish_entry(struct archive_write *a)
+{
+ struct mtree_writer *mtree = a->format_data;
+ struct mtree_entry *me;
+
+ if ((me = mtree->mtree_entry) == NULL)
+ return (ARCHIVE_OK);
+ mtree->mtree_entry = NULL;
+
+ if (me->reg_info)
+ sum_final(mtree, me->reg_info);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_mtree_close(struct archive_write *a)
+{
+ struct mtree_writer *mtree= a->format_data;
+ int ret;
+
+ if (mtree->root != NULL) {
+ ret = write_mtree_entry_tree(a);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ archive_write_set_bytes_in_last_block(&a->archive, 1);
+
+ return __archive_write_output(a, mtree->buf.s, mtree->buf.length);
+}
+
+static ssize_t
+archive_write_mtree_data(struct archive_write *a, const void *buff, size_t n)
+{
+ struct mtree_writer *mtree= a->format_data;
+
+ if (n > mtree->entry_bytes_remaining)
+ n = (size_t)mtree->entry_bytes_remaining;
+ mtree->entry_bytes_remaining -= n;
+
+ /* We don't need to compute a regular file sum */
+ if (mtree->mtree_entry == NULL)
+ return (n);
+
+ if (mtree->mtree_entry->filetype == AE_IFREG)
+ sum_update(mtree, buff, n);
+
+ return (n);
+}
+
+static int
+archive_write_mtree_free(struct archive_write *a)
+{
+ struct mtree_writer *mtree= a->format_data;
+
+ if (mtree == NULL)
+ return (ARCHIVE_OK);
+
+ /* Make sure we do not leave any entries. */
+ mtree_entry_register_free(mtree);
+ archive_string_free(&mtree->cur_dirstr);
+ archive_string_free(&mtree->ebuf);
+ archive_string_free(&mtree->buf);
+ attr_counter_set_free(mtree);
+ free(mtree);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_mtree_options(struct archive_write *a, const char *key,
+ const char *value)
+{
+ struct mtree_writer *mtree= a->format_data;
+ int keybit = 0;
+
+ switch (key[0]) {
+ case 'a':
+ if (strcmp(key, "all") == 0)
+ keybit = ~0;
+ break;
+ case 'c':
+ if (strcmp(key, "cksum") == 0)
+ keybit = F_CKSUM;
+ break;
+ case 'd':
+ if (strcmp(key, "device") == 0)
+ keybit = F_DEV;
+ else if (strcmp(key, "dironly") == 0) {
+ mtree->dironly = (value != NULL)? 1: 0;
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'f':
+ if (strcmp(key, "flags") == 0)
+ keybit = F_FLAGS;
+ break;
+ case 'g':
+ if (strcmp(key, "gid") == 0)
+ keybit = F_GID;
+ else if (strcmp(key, "gname") == 0)
+ keybit = F_GNAME;
+ break;
+ case 'i':
+ if (strcmp(key, "indent") == 0) {
+ mtree->indent = (value != NULL)? 1: 0;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "inode") == 0) {
+ keybit = F_INO;
+ }
+ break;
+ case 'l':
+ if (strcmp(key, "link") == 0)
+ keybit = F_SLINK;
+ break;
+ case 'm':
+ if (strcmp(key, "md5") == 0 ||
+ strcmp(key, "md5digest") == 0)
+ keybit = F_MD5;
+ if (strcmp(key, "mode") == 0)
+ keybit = F_MODE;
+ break;
+ case 'n':
+ if (strcmp(key, "nlink") == 0)
+ keybit = F_NLINK;
+ break;
+ case 'r':
+ if (strcmp(key, "resdevice") == 0) {
+ keybit = F_RESDEV;
+ } else if (strcmp(key, "ripemd160digest") == 0 ||
+ strcmp(key, "rmd160") == 0 ||
+ strcmp(key, "rmd160digest") == 0)
+ keybit = F_RMD160;
+ break;
+ case 's':
+ if (strcmp(key, "sha1") == 0 ||
+ strcmp(key, "sha1digest") == 0)
+ keybit = F_SHA1;
+ if (strcmp(key, "sha256") == 0 ||
+ strcmp(key, "sha256digest") == 0)
+ keybit = F_SHA256;
+ if (strcmp(key, "sha384") == 0 ||
+ strcmp(key, "sha384digest") == 0)
+ keybit = F_SHA384;
+ if (strcmp(key, "sha512") == 0 ||
+ strcmp(key, "sha512digest") == 0)
+ keybit = F_SHA512;
+ if (strcmp(key, "size") == 0)
+ keybit = F_SIZE;
+ break;
+ case 't':
+ if (strcmp(key, "time") == 0)
+ keybit = F_TIME;
+ else if (strcmp(key, "type") == 0)
+ keybit = F_TYPE;
+ break;
+ case 'u':
+ if (strcmp(key, "uid") == 0)
+ keybit = F_UID;
+ else if (strcmp(key, "uname") == 0)
+ keybit = F_UNAME;
+ else if (strcmp(key, "use-set") == 0) {
+ mtree->output_global_set = (value != NULL)? 1: 0;
+ return (ARCHIVE_OK);
+ }
+ break;
+ }
+ if (keybit != 0) {
+ if (value != NULL)
+ mtree->keys |= keybit;
+ else
+ mtree->keys &= ~keybit;
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_set_format_mtree_default(struct archive *_a, const char *fn)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct mtree_writer *mtree;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, fn);
+
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ if ((mtree = calloc(1, sizeof(*mtree))) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate mtree data");
+ return (ARCHIVE_FATAL);
+ }
+
+ mtree->mtree_entry = NULL;
+ mtree->first = 1;
+ memset(&(mtree->set), 0, sizeof(mtree->set));
+ mtree->keys = DEFAULT_KEYS;
+ mtree->dironly = 0;
+ mtree->indent = 0;
+ archive_string_init(&mtree->ebuf);
+ archive_string_init(&mtree->buf);
+ mtree_entry_register_init(mtree);
+ a->format_data = mtree;
+ a->format_free = archive_write_mtree_free;
+ a->format_name = "mtree";
+ a->format_options = archive_write_mtree_options;
+ a->format_write_header = archive_write_mtree_header;
+ a->format_close = archive_write_mtree_close;
+ a->format_write_data = archive_write_mtree_data;
+ a->format_finish_entry = archive_write_mtree_finish_entry;
+ a->archive.archive_format = ARCHIVE_FORMAT_MTREE;
+ a->archive.archive_format_name = "mtree";
+
+ return (ARCHIVE_OK);
+}
+
+int
+archive_write_set_format_mtree(struct archive *_a)
+{
+ return archive_write_set_format_mtree_default(_a,
+ "archive_write_set_format_mtree");
+}
+
+int
+archive_write_set_format_mtree_classic(struct archive *_a)
+{
+ int r;
+
+ r = archive_write_set_format_mtree_default(_a,
+ "archive_write_set_format_mtree_classic");
+ if (r == ARCHIVE_OK) {
+ struct archive_write *a = (struct archive_write *)_a;
+ struct mtree_writer *mtree;
+
+ mtree = (struct mtree_writer *)a->format_data;
+
+ /* Set to output a mtree archive in classic format. */
+ mtree->classic = 1;
+ /* Basically, mtree classic format uses '/set' global
+ * value. */
+ mtree->output_global_set = 1;
+ }
+ return (r);
+}
+
+static void
+sum_init(struct mtree_writer *mtree)
+{
+
+ mtree->compute_sum = 0;
+
+ if (mtree->keys & F_CKSUM) {
+ mtree->compute_sum |= F_CKSUM;
+ mtree->crc = 0;
+ mtree->crc_len = 0;
+ }
+#ifdef ARCHIVE_HAS_MD5
+ if (mtree->keys & F_MD5) {
+ if (archive_md5_init(&mtree->md5ctx) == ARCHIVE_OK)
+ mtree->compute_sum |= F_MD5;
+ else
+ mtree->keys &= ~F_MD5;/* Not supported. */
+ }
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+ if (mtree->keys & F_RMD160) {
+ if (archive_rmd160_init(&mtree->rmd160ctx) == ARCHIVE_OK)
+ mtree->compute_sum |= F_RMD160;
+ else
+ mtree->keys &= ~F_RMD160;/* Not supported. */
+ }
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+ if (mtree->keys & F_SHA1) {
+ if (archive_sha1_init(&mtree->sha1ctx) == ARCHIVE_OK)
+ mtree->compute_sum |= F_SHA1;
+ else
+ mtree->keys &= ~F_SHA1;/* Not supported. */
+ }
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+ if (mtree->keys & F_SHA256) {
+ if (archive_sha256_init(&mtree->sha256ctx) == ARCHIVE_OK)
+ mtree->compute_sum |= F_SHA256;
+ else
+ mtree->keys &= ~F_SHA256;/* Not supported. */
+ }
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+ if (mtree->keys & F_SHA384) {
+ if (archive_sha384_init(&mtree->sha384ctx) == ARCHIVE_OK)
+ mtree->compute_sum |= F_SHA384;
+ else
+ mtree->keys &= ~F_SHA384;/* Not supported. */
+ }
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ if (mtree->keys & F_SHA512) {
+ if (archive_sha512_init(&mtree->sha512ctx) == ARCHIVE_OK)
+ mtree->compute_sum |= F_SHA512;
+ else
+ mtree->keys &= ~F_SHA512;/* Not supported. */
+ }
+#endif
+}
+
+static void
+sum_update(struct mtree_writer *mtree, const void *buff, size_t n)
+{
+ if (mtree->compute_sum & F_CKSUM) {
+ /*
+ * Compute a POSIX 1003.2 checksum
+ */
+ const unsigned char *p;
+ size_t nn;
+
+ for (nn = n, p = buff; nn--; ++p)
+ COMPUTE_CRC(mtree->crc, *p);
+ mtree->crc_len += n;
+ }
+#ifdef ARCHIVE_HAS_MD5
+ if (mtree->compute_sum & F_MD5)
+ archive_md5_update(&mtree->md5ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+ if (mtree->compute_sum & F_RMD160)
+ archive_rmd160_update(&mtree->rmd160ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+ if (mtree->compute_sum & F_SHA1)
+ archive_sha1_update(&mtree->sha1ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+ if (mtree->compute_sum & F_SHA256)
+ archive_sha256_update(&mtree->sha256ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+ if (mtree->compute_sum & F_SHA384)
+ archive_sha384_update(&mtree->sha384ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ if (mtree->compute_sum & F_SHA512)
+ archive_sha512_update(&mtree->sha512ctx, buff, n);
+#endif
+}
+
+static void
+sum_final(struct mtree_writer *mtree, struct reg_info *reg)
+{
+
+ if (mtree->compute_sum & F_CKSUM) {
+ uint64_t len;
+ /* Include the length of the file. */
+ for (len = mtree->crc_len; len != 0; len >>= 8)
+ COMPUTE_CRC(mtree->crc, len & 0xff);
+ reg->crc = ~mtree->crc;
+ }
+#ifdef ARCHIVE_HAS_MD5
+ if (mtree->compute_sum & F_MD5)
+ archive_md5_final(&mtree->md5ctx, reg->digest.md5);
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+ if (mtree->compute_sum & F_RMD160)
+ archive_rmd160_final(&mtree->rmd160ctx, reg->digest.rmd160);
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+ if (mtree->compute_sum & F_SHA1)
+ archive_sha1_final(&mtree->sha1ctx, reg->digest.sha1);
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+ if (mtree->compute_sum & F_SHA256)
+ archive_sha256_final(&mtree->sha256ctx, reg->digest.sha256);
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+ if (mtree->compute_sum & F_SHA384)
+ archive_sha384_final(&mtree->sha384ctx, reg->digest.sha384);
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ if (mtree->compute_sum & F_SHA512)
+ archive_sha512_final(&mtree->sha512ctx, reg->digest.sha512);
+#endif
+ /* Save what types of sum are computed. */
+ reg->compute_sum = mtree->compute_sum;
+}
+
+#if defined(ARCHIVE_HAS_MD5) || defined(ARCHIVE_HAS_RMD160) || \
+ defined(ARCHIVE_HAS_SHA1) || defined(ARCHIVE_HAS_SHA256) || \
+ defined(ARCHIVE_HAS_SHA384) || defined(ARCHIVE_HAS_SHA512)
+static void
+strappend_bin(struct archive_string *s, const unsigned char *bin, int n)
+{
+ static const char hex[] = "0123456789abcdef";
+ int i;
+
+ for (i = 0; i < n; i++) {
+ archive_strappend_char(s, hex[bin[i] >> 4]);
+ archive_strappend_char(s, hex[bin[i] & 0x0f]);
+ }
+}
+#endif
+
+static void
+sum_write(struct archive_string *str, struct reg_info *reg)
+{
+
+ if (reg->compute_sum & F_CKSUM) {
+ archive_string_sprintf(str, " cksum=%ju",
+ (uintmax_t)reg->crc);
+ }
+
+#define append_digest(_s, _r, _t) \
+ strappend_bin(_s, _r->digest._t, sizeof(_r->digest._t))
+
+#ifdef ARCHIVE_HAS_MD5
+ if (reg->compute_sum & F_MD5) {
+ archive_strcat(str, " md5digest=");
+ append_digest(str, reg, md5);
+ }
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+ if (reg->compute_sum & F_RMD160) {
+ archive_strcat(str, " rmd160digest=");
+ append_digest(str, reg, rmd160);
+ }
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+ if (reg->compute_sum & F_SHA1) {
+ archive_strcat(str, " sha1digest=");
+ append_digest(str, reg, sha1);
+ }
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+ if (reg->compute_sum & F_SHA256) {
+ archive_strcat(str, " sha256digest=");
+ append_digest(str, reg, sha256);
+ }
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+ if (reg->compute_sum & F_SHA384) {
+ archive_strcat(str, " sha384digest=");
+ append_digest(str, reg, sha384);
+ }
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ if (reg->compute_sum & F_SHA512) {
+ archive_strcat(str, " sha512digest=");
+ append_digest(str, reg, sha512);
+ }
+#endif
+#undef append_digest
+}
+
+static int
+mtree_entry_cmp_node(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct mtree_entry *e1 = (const struct mtree_entry *)n1;
+ const struct mtree_entry *e2 = (const struct mtree_entry *)n2;
+
+ return (strcmp(e2->basename.s, e1->basename.s));
+}
+
+static int
+mtree_entry_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ const struct mtree_entry *e = (const struct mtree_entry *)n;
+
+ return (strcmp((const char *)key, e->basename.s));
+}
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+static int
+cleanup_backslash_1(char *p)
+{
+ int mb, dos;
+
+ mb = dos = 0;
+ while (*p) {
+ if (*(unsigned char *)p > 127)
+ mb = 1;
+ if (*p == '\\') {
+ /* If we have not met any multi-byte characters,
+ * we can replace '\' with '/'. */
+ if (!mb)
+ *p = '/';
+ dos = 1;
+ }
+ p++;
+ }
+ if (!mb || !dos)
+ return (0);
+ return (-1);
+}
+
+static void
+cleanup_backslash_2(wchar_t *p)
+{
+
+ /* Convert a path-separator from '\' to '/' */
+ while (*p != L'\0') {
+ if (*p == L'\\')
+ *p = L'/';
+ p++;
+ }
+}
+#endif
+
+/*
+ * Generate a parent directory name and a base name from a pathname.
+ */
+static int
+mtree_entry_setup_filenames(struct archive_write *a, struct mtree_entry *file,
+ struct archive_entry *entry)
+{
+ const char *pathname;
+ char *p, *dirname, *slash;
+ size_t len;
+ int ret = ARCHIVE_OK;
+
+ archive_strcpy(&file->pathname, archive_entry_pathname(entry));
+#if defined(_WIN32) || defined(__CYGWIN__)
+ /*
+ * Convert a path-separator from '\' to '/'
+ */
+ if (cleanup_backslash_1(file->pathname.s) != 0) {
+ const wchar_t *wp = archive_entry_pathname_w(entry);
+ struct archive_wstring ws;
+
+ if (wp != NULL) {
+ int r;
+ archive_string_init(&ws);
+ archive_wstrcpy(&ws, wp);
+ cleanup_backslash_2(ws.s);
+ archive_string_empty(&(file->pathname));
+ r = archive_string_append_from_wcs(&(file->pathname),
+ ws.s, ws.length);
+ archive_wstring_free(&ws);
+ if (r < 0 && errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ }
+#else
+ (void)a; /* UNUSED */
+#endif
+ pathname = file->pathname.s;
+ if (strcmp(pathname, ".") == 0) {
+ archive_strcpy(&file->basename, ".");
+ return (ARCHIVE_OK);
+ }
+
+ archive_strcpy(&(file->parentdir), pathname);
+
+ len = file->parentdir.length;
+ p = dirname = file->parentdir.s;
+
+ /*
+ * Remove leading '/' and '../' elements
+ */
+ while (*p) {
+ if (p[0] == '/') {
+ p++;
+ len--;
+ } else if (p[0] != '.')
+ break;
+ else if (p[1] == '.' && p[2] == '/') {
+ p += 3;
+ len -= 3;
+ } else
+ break;
+ }
+ if (p != dirname) {
+ memmove(dirname, p, len+1);
+ p = dirname;
+ }
+ /*
+ * Remove "/","/." and "/.." elements from tail.
+ */
+ while (len > 0) {
+ size_t ll = len;
+
+ if (len > 0 && p[len-1] == '/') {
+ p[len-1] = '\0';
+ len--;
+ }
+ if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {
+ p[len-2] = '\0';
+ len -= 2;
+ }
+ if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&
+ p[len-1] == '.') {
+ p[len-3] = '\0';
+ len -= 3;
+ }
+ if (ll == len)
+ break;
+ }
+ while (*p) {
+ if (p[0] == '/') {
+ if (p[1] == '/')
+ /* Convert '//' --> '/' */
+ memmove(p, p+1, strlen(p+1) + 1);
+ else if (p[1] == '.' && p[2] == '/')
+ /* Convert '/./' --> '/' */
+ memmove(p, p+2, strlen(p+2) + 1);
+ else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {
+ /* Convert 'dir/dir1/../dir2/'
+ * --> 'dir/dir2/'
+ */
+ char *rp = p -1;
+ while (rp >= dirname) {
+ if (*rp == '/')
+ break;
+ --rp;
+ }
+ if (rp > dirname) {
+ strcpy(rp, p+3);
+ p = rp;
+ } else {
+ strcpy(dirname, p+4);
+ p = dirname;
+ }
+ } else
+ p++;
+ } else
+ p++;
+ }
+ p = dirname;
+ len = strlen(p);
+
+ /*
+ * Add "./" prefix.
+ * NOTE: If the pathname does not have a path separator, we have
+ * to add "./" to the head of the pathname because mtree reader
+ * will suppose that it is v1(a.k.a classic) mtree format and
+ * change the directory unexpectedly and so it will make a wrong
+ * path.
+ */
+ if (strcmp(p, ".") != 0 && strncmp(p, "./", 2) != 0) {
+ struct archive_string as;
+ archive_string_init(&as);
+ archive_strcpy(&as, "./");
+ archive_strncat(&as, p, len);
+ archive_string_empty(&file->parentdir);
+ archive_string_concat(&file->parentdir, &as);
+ archive_string_free(&as);
+ p = file->parentdir.s;
+ len = archive_strlen(&file->parentdir);
+ }
+
+ /*
+ * Find out the position which points the last position of
+ * path separator('/').
+ */
+ slash = NULL;
+ for (; *p != '\0'; p++) {
+ if (*p == '/')
+ slash = p;
+ }
+ if (slash == NULL) {
+ /* The pathname doesn't have a parent directory. */
+ file->parentdir.length = len;
+ archive_string_copy(&(file->basename), &(file->parentdir));
+ archive_string_empty(&(file->parentdir));
+ *file->parentdir.s = '\0';
+ return (ret);
+ }
+
+ /* Make a basename from file->parentdir.s and slash */
+ *slash = '\0';
+ file->parentdir.length = slash - file->parentdir.s;
+ archive_strcpy(&(file->basename), slash + 1);
+ return (ret);
+}
+
+static int
+mtree_entry_create_virtual_dir(struct archive_write *a, const char *pathname,
+ struct mtree_entry **m_entry)
+{
+ struct archive_entry *entry;
+ struct mtree_entry *file;
+ int r;
+
+ entry = archive_entry_new();
+ if (entry == NULL) {
+ *m_entry = NULL;
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ archive_entry_copy_pathname(entry, pathname);
+ archive_entry_set_mode(entry, AE_IFDIR | 0755);
+ archive_entry_set_mtime(entry, time(NULL), 0);
+
+ r = mtree_entry_new(a, entry, &file);
+ archive_entry_free(entry);
+ if (r < ARCHIVE_WARN) {
+ *m_entry = NULL;
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ file->dir_info->virtual = 1;
+
+ *m_entry = file;
+ return (ARCHIVE_OK);
+}
+
+static void
+mtree_entry_register_add(struct mtree_writer *mtree, struct mtree_entry *file)
+{
+ file->next = NULL;
+ *mtree->file_list.last = file;
+ mtree->file_list.last = &(file->next);
+}
+
+static void
+mtree_entry_register_init(struct mtree_writer *mtree)
+{
+ mtree->file_list.first = NULL;
+ mtree->file_list.last = &(mtree->file_list.first);
+}
+
+static void
+mtree_entry_register_free(struct mtree_writer *mtree)
+{
+ struct mtree_entry *file, *file_next;
+
+ file = mtree->file_list.first;
+ while (file != NULL) {
+ file_next = file->next;
+ mtree_entry_free(file);
+ file = file_next;
+ }
+}
+
+static int
+mtree_entry_add_child_tail(struct mtree_entry *parent,
+ struct mtree_entry *child)
+{
+ child->dir_info->chnext = NULL;
+ *parent->dir_info->children.last = child;
+ parent->dir_info->children.last = &(child->dir_info->chnext);
+ return (1);
+}
+
+/*
+ * Find a entry from a parent entry with the name.
+ */
+static struct mtree_entry *
+mtree_entry_find_child(struct mtree_entry *parent, const char *child_name)
+{
+ struct mtree_entry *np;
+
+ if (parent == NULL)
+ return (NULL);
+ np = (struct mtree_entry *)__archive_rb_tree_find_node(
+ &(parent->dir_info->rbtree), child_name);
+ return (np);
+}
+
+static int
+get_path_component(char *name, size_t n, const char *fn)
+{
+ char *p;
+ size_t l;
+
+ p = strchr(fn, '/');
+ if (p == NULL) {
+ if ((l = strlen(fn)) == 0)
+ return (0);
+ } else
+ l = p - fn;
+ if (l > n -1)
+ return (-1);
+ memcpy(name, fn, l);
+ name[l] = '\0';
+
+ return ((int)l);
+}
+
+/*
+ * Add a new entry into the tree.
+ */
+static int
+mtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ char name[_MAX_FNAME];/* Included null terminator size. */
+#elif defined(NAME_MAX) && NAME_MAX >= 255
+ char name[NAME_MAX+1];
+#else
+ char name[256];
+#endif
+ struct mtree_writer *mtree = (struct mtree_writer *)a->format_data;
+ struct mtree_entry *dent, *file, *np;
+ const char *fn, *p;
+ int l, r;
+
+ file = *filep;
+ if (file->parentdir.length == 0 && file->basename.length == 1 &&
+ file->basename.s[0] == '.') {
+ file->parent = file;
+ if (mtree->root != NULL) {
+ np = mtree->root;
+ goto same_entry;
+ }
+ mtree->root = file;
+ mtree_entry_register_add(mtree, file);
+ return (ARCHIVE_OK);
+ }
+
+ if (file->parentdir.length == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Internal programming error "
+ "in generating canonical name for %s",
+ file->pathname.s);
+ return (ARCHIVE_FAILED);
+ }
+
+ fn = p = file->parentdir.s;
+
+ /*
+ * If the path of the parent directory of `file' entry is
+ * the same as the path of `cur_dirent', add `file' entry to
+ * `cur_dirent'.
+ */
+ if (archive_strlen(&(mtree->cur_dirstr))
+ == archive_strlen(&(file->parentdir)) &&
+ strcmp(mtree->cur_dirstr.s, fn) == 0) {
+ if (!__archive_rb_tree_insert_node(
+ &(mtree->cur_dirent->dir_info->rbtree),
+ (struct archive_rb_node *)file)) {
+ /* There is the same name in the tree. */
+ np = (struct mtree_entry *)__archive_rb_tree_find_node(
+ &(mtree->cur_dirent->dir_info->rbtree),
+ file->basename.s);
+ goto same_entry;
+ }
+ file->parent = mtree->cur_dirent;
+ mtree_entry_register_add(mtree, file);
+ return (ARCHIVE_OK);
+ }
+
+ dent = mtree->root;
+ for (;;) {
+ l = get_path_component(name, sizeof(name), fn);
+ if (l == 0) {
+ np = NULL;
+ break;
+ }
+ if (l < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "A name buffer is too small");
+ return (ARCHIVE_FATAL);
+ }
+ if (l == 1 && name[0] == '.' && dent != NULL &&
+ dent == mtree->root) {
+ fn += l;
+ if (fn[0] == '/')
+ fn++;
+ continue;
+ }
+
+ np = mtree_entry_find_child(dent, name);
+ if (np == NULL || fn[0] == '\0')
+ break;
+
+ /* Find next sub directory. */
+ if (!np->dir_info) {
+ /* NOT Directory! */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "`%s' is not directory, we cannot insert `%s' ",
+ np->pathname.s, file->pathname.s);
+ return (ARCHIVE_FAILED);
+ }
+ fn += l;
+ if (fn[0] == '/')
+ fn++;
+ dent = np;
+ }
+ if (np == NULL) {
+ /*
+ * Create virtual parent directories.
+ */
+ while (fn[0] != '\0') {
+ struct mtree_entry *vp;
+ struct archive_string as;
+
+ archive_string_init(&as);
+ archive_strncat(&as, p, fn - p + l);
+ if (as.s[as.length-1] == '/') {
+ as.s[as.length-1] = '\0';
+ as.length--;
+ }
+ r = mtree_entry_create_virtual_dir(a, as.s, &vp);
+ archive_string_free(&as);
+ if (r < ARCHIVE_WARN)
+ return (r);
+
+ if (strcmp(vp->pathname.s, ".") == 0) {
+ vp->parent = vp;
+ mtree->root = vp;
+ } else {
+ __archive_rb_tree_insert_node(
+ &(dent->dir_info->rbtree),
+ (struct archive_rb_node *)vp);
+ vp->parent = dent;
+ }
+ mtree_entry_register_add(mtree, vp);
+ np = vp;
+
+ fn += l;
+ if (fn[0] == '/')
+ fn++;
+ l = get_path_component(name, sizeof(name), fn);
+ if (l < 0) {
+ archive_string_free(&as);
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "A name buffer is too small");
+ return (ARCHIVE_FATAL);
+ }
+ dent = np;
+ }
+
+ /* Found out the parent directory where `file' can be
+ * inserted. */
+ mtree->cur_dirent = dent;
+ archive_string_empty(&(mtree->cur_dirstr));
+ archive_string_ensure(&(mtree->cur_dirstr),
+ archive_strlen(&(dent->parentdir)) +
+ archive_strlen(&(dent->basename)) + 2);
+ if (archive_strlen(&(dent->parentdir)) +
+ archive_strlen(&(dent->basename)) == 0)
+ mtree->cur_dirstr.s[0] = 0;
+ else {
+ if (archive_strlen(&(dent->parentdir)) > 0) {
+ archive_string_copy(&(mtree->cur_dirstr),
+ &(dent->parentdir));
+ archive_strappend_char(
+ &(mtree->cur_dirstr), '/');
+ }
+ archive_string_concat(&(mtree->cur_dirstr),
+ &(dent->basename));
+ }
+
+ if (!__archive_rb_tree_insert_node(
+ &(dent->dir_info->rbtree),
+ (struct archive_rb_node *)file)) {
+ np = (struct mtree_entry *)__archive_rb_tree_find_node(
+ &(dent->dir_info->rbtree), file->basename.s);
+ goto same_entry;
+ }
+ file->parent = dent;
+ mtree_entry_register_add(mtree, file);
+ return (ARCHIVE_OK);
+ }
+
+same_entry:
+ /*
+ * We have already has the entry the filename of which is
+ * the same.
+ */
+ r = mtree_entry_exchange_same_entry(a, np, file);
+ if (r < ARCHIVE_WARN)
+ return (r);
+ if (np->dir_info)
+ np->dir_info->virtual = 0;
+ *filep = np;
+ mtree_entry_free(file);
+ return (ARCHIVE_WARN);
+}
+
+static int
+mtree_entry_exchange_same_entry(struct archive_write *a, struct mtree_entry *np,
+ struct mtree_entry *file)
+{
+
+ if ((np->mode & AE_IFMT) != (file->mode & AE_IFMT)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Found duplicate entries `%s' and its file type is "
+ "different",
+ np->pathname.s);
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Update the existent mtree entry's attributes by the new one's. */
+ archive_string_empty(&np->symlink);
+ archive_string_concat(&np->symlink, &file->symlink);
+ archive_string_empty(&np->uname);
+ archive_string_concat(&np->uname, &file->uname);
+ archive_string_empty(&np->gname);
+ archive_string_concat(&np->gname, &file->gname);
+ archive_string_empty(&np->fflags_text);
+ archive_string_concat(&np->fflags_text, &file->fflags_text);
+ np->nlink = file->nlink;
+ np->filetype = file->filetype;
+ np->mode = file->mode;
+ np->size = file->size;
+ np->uid = file->uid;
+ np->gid = file->gid;
+ np->fflags_set = file->fflags_set;
+ np->fflags_clear = file->fflags_clear;
+ np->mtime = file->mtime;
+ np->mtime_nsec = file->mtime_nsec;
+ np->rdevmajor = file->rdevmajor;
+ np->rdevminor = file->rdevminor;
+ np->devmajor = file->devmajor;
+ np->devminor = file->devminor;
+ np->ino = file->ino;
+
+ return (ARCHIVE_WARN);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_pax.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_pax.c
new file mode 100644
index 00000000000..c9c15916468
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_pax.c
@@ -0,0 +1,2063 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2016 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_pax.c 201162 2009-12-29 05:47:46Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct sparse_block {
+ struct sparse_block *next;
+ int is_hole;
+ uint64_t offset;
+ uint64_t remaining;
+};
+
+struct pax {
+ uint64_t entry_bytes_remaining;
+ uint64_t entry_padding;
+ struct archive_string l_url_encoded_name;
+ struct archive_string pax_header;
+ struct archive_string sparse_map;
+ size_t sparse_map_padding;
+ struct sparse_block *sparse_list;
+ struct sparse_block *sparse_tail;
+ struct archive_string_conv *sconv_utf8;
+ int opt_binary;
+
+ unsigned flags;
+#define WRITE_SCHILY_XATTR (1 << 0)
+#define WRITE_LIBARCHIVE_XATTR (1 << 1)
+};
+
+static void add_pax_attr(struct archive_string *, const char *key,
+ const char *value);
+static void add_pax_attr_binary(struct archive_string *,
+ const char *key,
+ const char *value, size_t value_len);
+static void add_pax_attr_int(struct archive_string *,
+ const char *key, int64_t value);
+static void add_pax_attr_time(struct archive_string *,
+ const char *key, int64_t sec,
+ unsigned long nanos);
+static int add_pax_acl(struct archive_write *,
+ struct archive_entry *, struct pax *, int);
+static ssize_t archive_write_pax_data(struct archive_write *,
+ const void *, size_t);
+static int archive_write_pax_close(struct archive_write *);
+static int archive_write_pax_free(struct archive_write *);
+static int archive_write_pax_finish_entry(struct archive_write *);
+static int archive_write_pax_header(struct archive_write *,
+ struct archive_entry *);
+static int archive_write_pax_options(struct archive_write *,
+ const char *, const char *);
+static char *base64_encode(const char *src, size_t len);
+static char *build_gnu_sparse_name(char *dest, const char *src);
+static char *build_pax_attribute_name(char *dest, const char *src);
+static char *build_ustar_entry_name(char *dest, const char *src,
+ size_t src_length, const char *insert);
+static char *format_int(char *dest, int64_t);
+static int has_non_ASCII(const char *);
+static void sparse_list_clear(struct pax *);
+static int sparse_list_add(struct pax *, int64_t, int64_t);
+static char *url_encode(const char *in);
+static time_t get_ustar_max_mtime(void);
+
+/*
+ * Set output format to 'restricted pax' format.
+ *
+ * This is the same as normal 'pax', but tries to suppress
+ * the pax header whenever possible. This is the default for
+ * bsdtar, for instance.
+ */
+int
+archive_write_set_format_pax_restricted(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_pax_restricted");
+
+ r = archive_write_set_format_pax(&a->archive);
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED;
+ a->archive.archive_format_name = "restricted POSIX pax interchange";
+ return (r);
+}
+
+/*
+ * Set output format to 'pax' format.
+ */
+int
+archive_write_set_format_pax(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct pax *pax;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_pax");
+
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ pax = (struct pax *)calloc(1, sizeof(*pax));
+ if (pax == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate pax data");
+ return (ARCHIVE_FATAL);
+ }
+ pax->flags = WRITE_LIBARCHIVE_XATTR | WRITE_SCHILY_XATTR;
+
+ a->format_data = pax;
+ a->format_name = "pax";
+ a->format_options = archive_write_pax_options;
+ a->format_write_header = archive_write_pax_header;
+ a->format_write_data = archive_write_pax_data;
+ a->format_close = archive_write_pax_close;
+ a->format_free = archive_write_pax_free;
+ a->format_finish_entry = archive_write_pax_finish_entry;
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+ a->archive.archive_format_name = "POSIX pax interchange";
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_pax_options(struct archive_write *a, const char *key,
+ const char *val)
+{
+ struct pax *pax = (struct pax *)a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ if (strcmp(key, "hdrcharset") == 0) {
+ /*
+ * The character-set we can use are defined in
+ * IEEE Std 1003.1-2001
+ */
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "pax: hdrcharset option needs a character-set name");
+ else if (strcmp(val, "BINARY") == 0 ||
+ strcmp(val, "binary") == 0) {
+ /*
+ * Specify binary mode. We will not convert
+ * filenames, uname and gname to any charsets.
+ */
+ pax->opt_binary = 1;
+ ret = ARCHIVE_OK;
+ } else if (strcmp(val, "UTF-8") == 0) {
+ /*
+ * Specify UTF-8 character-set to be used for
+ * filenames. This is almost the test that
+ * running platform supports the string conversion.
+ * Especially libarchive_test needs this trick for
+ * its test.
+ */
+ pax->sconv_utf8 = archive_string_conversion_to_charset(
+ &(a->archive), "UTF-8", 0);
+ if (pax->sconv_utf8 == NULL)
+ ret = ARCHIVE_FATAL;
+ else
+ ret = ARCHIVE_OK;
+ } else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "pax: invalid charset name");
+ return (ret);
+ } else if (strcmp(key, "xattrheader") == 0) {
+ if (val == NULL || val[0] == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "pax: xattrheader requires a value");
+ } else if (strcmp(val, "ALL") == 0 ||
+ strcmp(val, "all") == 0) {
+ pax->flags |= WRITE_LIBARCHIVE_XATTR | WRITE_SCHILY_XATTR;
+ ret = ARCHIVE_OK;
+ } else if (strcmp(val, "SCHILY") == 0 ||
+ strcmp(val, "schily") == 0) {
+ pax->flags |= WRITE_SCHILY_XATTR;
+ pax->flags &= ~WRITE_LIBARCHIVE_XATTR;
+ ret = ARCHIVE_OK;
+ } else if (strcmp(val, "LIBARCHIVE") == 0 ||
+ strcmp(val, "libarchive") == 0) {
+ pax->flags |= WRITE_LIBARCHIVE_XATTR;
+ pax->flags &= ~WRITE_SCHILY_XATTR;
+ ret = ARCHIVE_OK;
+ } else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "pax: invalid xattr header name");
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Note: This code assumes that 'nanos' has the same sign as 'sec',
+ * which implies that sec=-1, nanos=200000000 represents -1.2 seconds
+ * and not -0.8 seconds. This is a pretty pedantic point, as we're
+ * unlikely to encounter many real files created before Jan 1, 1970,
+ * much less ones with timestamps recorded to sub-second resolution.
+ */
+static void
+add_pax_attr_time(struct archive_string *as, const char *key,
+ int64_t sec, unsigned long nanos)
+{
+ int digit, i;
+ char *t;
+ /*
+ * Note that each byte contributes fewer than 3 base-10
+ * digits, so this will always be big enough.
+ */
+ char tmp[1 + 3*sizeof(sec) + 1 + 3*sizeof(nanos)];
+
+ tmp[sizeof(tmp) - 1] = 0;
+ t = tmp + sizeof(tmp) - 1;
+
+ /* Skip trailing zeros in the fractional part. */
+ for (digit = 0, i = 10; i > 0 && digit == 0; i--) {
+ digit = nanos % 10;
+ nanos /= 10;
+ }
+
+ /* Only format the fraction if it's non-zero. */
+ if (i > 0) {
+ while (i > 0) {
+ *--t = "0123456789"[digit];
+ digit = nanos % 10;
+ nanos /= 10;
+ i--;
+ }
+ *--t = '.';
+ }
+ t = format_int(t, sec);
+
+ add_pax_attr(as, key, t);
+}
+
+static char *
+format_int(char *t, int64_t i)
+{
+ uint64_t ui;
+
+ if (i < 0)
+ ui = (i == INT64_MIN) ? (uint64_t)(INT64_MAX) + 1 : (uint64_t)(-i);
+ else
+ ui = i;
+
+ do {
+ *--t = "0123456789"[ui % 10];
+ } while (ui /= 10);
+ if (i < 0)
+ *--t = '-';
+ return (t);
+}
+
+static void
+add_pax_attr_int(struct archive_string *as, const char *key, int64_t value)
+{
+ char tmp[1 + 3 * sizeof(value)];
+
+ tmp[sizeof(tmp) - 1] = 0;
+ add_pax_attr(as, key, format_int(tmp + sizeof(tmp) - 1, value));
+}
+
+/*
+ * Add a key/value attribute to the pax header. This function handles
+ * the length field and various other syntactic requirements.
+ */
+static void
+add_pax_attr(struct archive_string *as, const char *key, const char *value)
+{
+ add_pax_attr_binary(as, key, value, strlen(value));
+}
+
+/*
+ * Add a key/value attribute to the pax header. This function handles
+ * binary values.
+ */
+static void
+add_pax_attr_binary(struct archive_string *as, const char *key,
+ const char *value, size_t value_len)
+{
+ int digits, i, len, next_ten;
+ char tmp[1 + 3 * sizeof(int)]; /* < 3 base-10 digits per byte */
+
+ /*-
+ * PAX attributes have the following layout:
+ * <len> <space> <key> <=> <value> <nl>
+ */
+ len = 1 + (int)strlen(key) + 1 + (int)value_len + 1;
+
+ /*
+ * The <len> field includes the length of the <len> field, so
+ * computing the correct length is tricky. I start by
+ * counting the number of base-10 digits in 'len' and
+ * computing the next higher power of 10.
+ */
+ next_ten = 1;
+ digits = 0;
+ i = len;
+ while (i > 0) {
+ i = i / 10;
+ digits++;
+ next_ten = next_ten * 10;
+ }
+ /*
+ * For example, if string without the length field is 99
+ * chars, then adding the 2 digit length "99" will force the
+ * total length past 100, requiring an extra digit. The next
+ * statement adjusts for this effect.
+ */
+ if (len + digits >= next_ten)
+ digits++;
+
+ /* Now, we have the right length so we can build the line. */
+ tmp[sizeof(tmp) - 1] = 0; /* Null-terminate the work area. */
+ archive_strcat(as, format_int(tmp + sizeof(tmp) - 1, len + digits));
+ archive_strappend_char(as, ' ');
+ archive_strcat(as, key);
+ archive_strappend_char(as, '=');
+ archive_array_append(as, value, value_len);
+ archive_strappend_char(as, '\n');
+}
+
+static void
+archive_write_pax_header_xattr(struct pax *pax, const char *encoded_name,
+ const void *value, size_t value_len)
+{
+ struct archive_string s;
+ char *encoded_value;
+
+ if (pax->flags & WRITE_LIBARCHIVE_XATTR) {
+ encoded_value = base64_encode((const char *)value, value_len);
+
+ if (encoded_name != NULL && encoded_value != NULL) {
+ archive_string_init(&s);
+ archive_strcpy(&s, "LIBARCHIVE.xattr.");
+ archive_strcat(&s, encoded_name);
+ add_pax_attr(&(pax->pax_header), s.s, encoded_value);
+ archive_string_free(&s);
+ }
+ free(encoded_value);
+ }
+ if (pax->flags & WRITE_SCHILY_XATTR) {
+ archive_string_init(&s);
+ archive_strcpy(&s, "SCHILY.xattr.");
+ archive_strcat(&s, encoded_name);
+ add_pax_attr_binary(&(pax->pax_header), s.s, value, value_len);
+ archive_string_free(&s);
+ }
+}
+
+static int
+archive_write_pax_header_xattrs(struct archive_write *a,
+ struct pax *pax, struct archive_entry *entry)
+{
+ int i = archive_entry_xattr_reset(entry);
+
+ while (i--) {
+ const char *name;
+ const void *value;
+ char *url_encoded_name = NULL, *encoded_name = NULL;
+ size_t size;
+ int r;
+
+ archive_entry_xattr_next(entry, &name, &value, &size);
+ url_encoded_name = url_encode(name);
+ if (url_encoded_name != NULL) {
+ /* Convert narrow-character to UTF-8. */
+ r = archive_strcpy_l(&(pax->l_url_encoded_name),
+ url_encoded_name, pax->sconv_utf8);
+ free(url_encoded_name); /* Done with this. */
+ if (r == 0)
+ encoded_name = pax->l_url_encoded_name.s;
+ else if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ archive_write_pax_header_xattr(pax, encoded_name,
+ value, size);
+
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+get_entry_hardlink(struct archive_write *a, struct archive_entry *entry,
+ const char **name, size_t *length, struct archive_string_conv *sc)
+{
+ int r;
+
+ r = archive_entry_hardlink_l(entry, name, length, sc);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+get_entry_pathname(struct archive_write *a, struct archive_entry *entry,
+ const char **name, size_t *length, struct archive_string_conv *sc)
+{
+ int r;
+
+ r = archive_entry_pathname_l(entry, name, length, sc);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+get_entry_uname(struct archive_write *a, struct archive_entry *entry,
+ const char **name, size_t *length, struct archive_string_conv *sc)
+{
+ int r;
+
+ r = archive_entry_uname_l(entry, name, length, sc);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Uname");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+get_entry_gname(struct archive_write *a, struct archive_entry *entry,
+ const char **name, size_t *length, struct archive_string_conv *sc)
+{
+ int r;
+
+ r = archive_entry_gname_l(entry, name, length, sc);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Gname");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+get_entry_symlink(struct archive_write *a, struct archive_entry *entry,
+ const char **name, size_t *length, struct archive_string_conv *sc)
+{
+ int r;
+
+ r = archive_entry_symlink_l(entry, name, length, sc);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+/* Add ACL to pax header */
+static int
+add_pax_acl(struct archive_write *a,
+ struct archive_entry *entry, struct pax *pax, int flags)
+{
+ char *p;
+ const char *attr;
+ int acl_types;
+
+ acl_types = archive_entry_acl_types(entry);
+
+ if ((acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0)
+ attr = "SCHILY.acl.ace";
+ else if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
+ attr = "SCHILY.acl.access";
+ else if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+ attr = "SCHILY.acl.default";
+ else
+ return (ARCHIVE_FATAL);
+
+ p = archive_entry_acl_to_text_l(entry, NULL, flags, pax->sconv_utf8);
+ if (p == NULL) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM, "%s %s",
+ "Can't allocate memory for ", attr);
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT, "%s %s %s",
+ "Can't translate ", attr, " to UTF-8");
+ return(ARCHIVE_WARN);
+ }
+
+ if (*p != '\0') {
+ add_pax_attr(&(pax->pax_header),
+ attr, p);
+ }
+ free(p);
+ return(ARCHIVE_OK);
+}
+
+/*
+ * TODO: Consider adding 'comment' and 'charset' fields to
+ * archive_entry so that clients can specify them. Also, consider
+ * adding generic key/value tags so clients can add arbitrary
+ * key/value data.
+ *
+ * TODO: Break up this 700-line function!!!! Yowza!
+ */
+static int
+archive_write_pax_header(struct archive_write *a,
+ struct archive_entry *entry_original)
+{
+ struct archive_entry *entry_main;
+ const char *p;
+ const char *suffix;
+ int need_extension, r, ret;
+ int acl_types;
+ int sparse_count;
+ uint64_t sparse_total, real_size;
+ struct pax *pax;
+ const char *hardlink;
+ const char *path = NULL, *linkpath = NULL;
+ const char *uname = NULL, *gname = NULL;
+ const void *mac_metadata;
+ size_t mac_metadata_size;
+ struct archive_string_conv *sconv;
+ size_t hardlink_length, path_length, linkpath_length;
+ size_t uname_length, gname_length;
+
+ char paxbuff[512];
+ char ustarbuff[512];
+ char ustar_entry_name[256];
+ char pax_entry_name[256];
+ char gnu_sparse_name[256];
+ struct archive_string entry_name;
+
+ ret = ARCHIVE_OK;
+ need_extension = 0;
+ pax = (struct pax *)a->format_data;
+
+ const time_t ustar_max_mtime = get_ustar_max_mtime();
+
+ /* Sanity check. */
+ if (archive_entry_pathname(entry_original) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't record entry in tar file without pathname");
+ return (ARCHIVE_FAILED);
+ }
+
+ /*
+ * Choose a header encoding.
+ */
+ if (pax->opt_binary)
+ sconv = NULL;/* Binary mode. */
+ else {
+ /* Header encoding is UTF-8. */
+ if (pax->sconv_utf8 == NULL) {
+ /* Initialize the string conversion object
+ * we must need */
+ pax->sconv_utf8 = archive_string_conversion_to_charset(
+ &(a->archive), "UTF-8", 1);
+ if (pax->sconv_utf8 == NULL)
+ /* Couldn't allocate memory */
+ return (ARCHIVE_FAILED);
+ }
+ sconv = pax->sconv_utf8;
+ }
+
+ r = get_entry_hardlink(a, entry_original, &hardlink,
+ &hardlink_length, sconv);
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ else if (r != ARCHIVE_OK) {
+ r = get_entry_hardlink(a, entry_original, &hardlink,
+ &hardlink_length, NULL);
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s", hardlink,
+ archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ sconv = NULL;/* The header charset switches to binary mode. */
+ }
+
+ /* Make sure this is a type of entry that we can handle here */
+ if (hardlink == NULL) {
+ switch (archive_entry_filetype(entry_original)) {
+ case AE_IFBLK:
+ case AE_IFCHR:
+ case AE_IFIFO:
+ case AE_IFLNK:
+ case AE_IFREG:
+ break;
+ case AE_IFDIR:
+ {
+ /*
+ * Ensure a trailing '/'. Modify the original
+ * entry so the client sees the change.
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ const wchar_t *wp;
+
+ wp = archive_entry_pathname_w(entry_original);
+ if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
+ struct archive_wstring ws;
+
+ archive_string_init(&ws);
+ path_length = wcslen(wp);
+ if (archive_wstring_ensure(&ws,
+ path_length + 2) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate pax data");
+ archive_wstring_free(&ws);
+ return(ARCHIVE_FATAL);
+ }
+ /* Should we keep '\' ? */
+ if (wp[path_length -1] == L'\\')
+ path_length--;
+ archive_wstrncpy(&ws, wp, path_length);
+ archive_wstrappend_wchar(&ws, L'/');
+ archive_entry_copy_pathname_w(
+ entry_original, ws.s);
+ archive_wstring_free(&ws);
+ p = NULL;
+ } else
+#endif
+ p = archive_entry_pathname(entry_original);
+ /*
+ * On Windows, this is a backup operation just in
+ * case getting WCS failed. On POSIX, this is a
+ * normal operation.
+ */
+ if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {
+ struct archive_string as;
+
+ archive_string_init(&as);
+ path_length = strlen(p);
+ if (archive_string_ensure(&as,
+ path_length + 2) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate pax data");
+ archive_string_free(&as);
+ return(ARCHIVE_FATAL);
+ }
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* NOTE: This might break the pathname
+ * if the current code page is CP932 and
+ * the pathname includes a character '\'
+ * as a part of its multibyte pathname. */
+ if (p[strlen(p) -1] == '\\')
+ path_length--;
+ else
+#endif
+ archive_strncpy(&as, p, path_length);
+ archive_strappend_char(&as, '/');
+ archive_entry_copy_pathname(
+ entry_original, as.s);
+ archive_string_free(&as);
+ }
+ break;
+ }
+ default: /* AE_IFSOCK and unknown */
+ __archive_write_entry_filetype_unsupported(
+ &a->archive, entry_original, "pax");
+ return (ARCHIVE_FAILED);
+ }
+ }
+
+ /*
+ * If Mac OS metadata blob is here, recurse to write that
+ * as a separate entry. This is really a pretty poor design:
+ * In particular, it doubles the overhead for long filenames.
+ * TODO: Help Apple folks design something better and figure
+ * out how to transition from this legacy format.
+ *
+ * Note that this code is present on every platform; clients
+ * on non-Mac are unlikely to ever provide this data, but
+ * applications that copy entries from one archive to another
+ * should not lose data just because the local filesystem
+ * can't store it.
+ */
+ mac_metadata =
+ archive_entry_mac_metadata(entry_original, &mac_metadata_size);
+ if (mac_metadata != NULL) {
+ const char *oname;
+ char *name, *bname;
+ size_t name_length;
+ struct archive_entry *extra = archive_entry_new2(&a->archive);
+
+ oname = archive_entry_pathname(entry_original);
+ name_length = strlen(oname);
+ name = malloc(name_length + 3);
+ if (name == NULL || extra == NULL) {
+ /* XXX error message */
+ archive_entry_free(extra);
+ free(name);
+ return (ARCHIVE_FAILED);
+ }
+ strcpy(name, oname);
+ /* Find last '/'; strip trailing '/' characters */
+ bname = strrchr(name, '/');
+ while (bname != NULL && bname[1] == '\0') {
+ *bname = '\0';
+ bname = strrchr(name, '/');
+ }
+ if (bname == NULL) {
+ memmove(name + 2, name, name_length + 1);
+ memmove(name, "._", 2);
+ } else {
+ bname += 1;
+ memmove(bname + 2, bname, strlen(bname) + 1);
+ memmove(bname, "._", 2);
+ }
+ archive_entry_copy_pathname(extra, name);
+ free(name);
+
+ archive_entry_set_size(extra, mac_metadata_size);
+ archive_entry_set_filetype(extra, AE_IFREG);
+ archive_entry_set_perm(extra,
+ archive_entry_perm(entry_original));
+ archive_entry_set_mtime(extra,
+ archive_entry_mtime(entry_original),
+ archive_entry_mtime_nsec(entry_original));
+ archive_entry_set_gid(extra,
+ archive_entry_gid(entry_original));
+ archive_entry_set_gname(extra,
+ archive_entry_gname(entry_original));
+ archive_entry_set_uid(extra,
+ archive_entry_uid(entry_original));
+ archive_entry_set_uname(extra,
+ archive_entry_uname(entry_original));
+
+ /* Recurse to write the special copyfile entry. */
+ r = archive_write_pax_header(a, extra);
+ archive_entry_free(extra);
+ if (r < ARCHIVE_WARN)
+ return (r);
+ if (r < ret)
+ ret = r;
+ r = (int)archive_write_pax_data(a, mac_metadata,
+ mac_metadata_size);
+ if (r < ARCHIVE_WARN)
+ return (r);
+ if (r < ret)
+ ret = r;
+ r = archive_write_pax_finish_entry(a);
+ if (r < ARCHIVE_WARN)
+ return (r);
+ if (r < ret)
+ ret = r;
+ }
+
+ /* Copy entry so we can modify it as needed. */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Make sure the path separators in pathname, hardlink and symlink
+ * are all slash '/', not the Windows path separator '\'. */
+ entry_main = __la_win_entry_in_posix_pathseparator(entry_original);
+ if (entry_main == entry_original)
+ entry_main = archive_entry_clone(entry_original);
+#else
+ entry_main = archive_entry_clone(entry_original);
+#endif
+ if (entry_main == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate pax data");
+ return(ARCHIVE_FATAL);
+ }
+ archive_string_empty(&(pax->pax_header)); /* Blank our work area. */
+ archive_string_empty(&(pax->sparse_map));
+ sparse_total = 0;
+ sparse_list_clear(pax);
+
+ if (hardlink == NULL &&
+ archive_entry_filetype(entry_main) == AE_IFREG)
+ sparse_count = archive_entry_sparse_reset(entry_main);
+ else
+ sparse_count = 0;
+ if (sparse_count) {
+ int64_t offset, length, last_offset = 0;
+ /* Get the last entry of sparse block. */
+ while (archive_entry_sparse_next(
+ entry_main, &offset, &length) == ARCHIVE_OK)
+ last_offset = offset + length;
+
+ /* If the last sparse block does not reach the end of file,
+ * We have to add a empty sparse block as the last entry to
+ * manage storing file data. */
+ if (last_offset < archive_entry_size(entry_main))
+ archive_entry_sparse_add_entry(entry_main,
+ archive_entry_size(entry_main), 0);
+ sparse_count = archive_entry_sparse_reset(entry_main);
+ }
+
+ /*
+ * First, check the name fields and see if any of them
+ * require binary coding. If any of them does, then all of
+ * them do.
+ */
+ r = get_entry_pathname(a, entry_main, &path, &path_length, sconv);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ } else if (r != ARCHIVE_OK) {
+ r = get_entry_pathname(a, entry_main, &path,
+ &path_length, NULL);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s", path,
+ archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ sconv = NULL;/* The header charset switches to binary mode. */
+ }
+ r = get_entry_uname(a, entry_main, &uname, &uname_length, sconv);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ } else if (r != ARCHIVE_OK) {
+ r = get_entry_uname(a, entry_main, &uname, &uname_length, NULL);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate uname '%s' to %s", uname,
+ archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ sconv = NULL;/* The header charset switches to binary mode. */
+ }
+ r = get_entry_gname(a, entry_main, &gname, &gname_length, sconv);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ } else if (r != ARCHIVE_OK) {
+ r = get_entry_gname(a, entry_main, &gname, &gname_length, NULL);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate gname '%s' to %s", gname,
+ archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ sconv = NULL;/* The header charset switches to binary mode. */
+ }
+ linkpath = hardlink;
+ linkpath_length = hardlink_length;
+ if (linkpath == NULL) {
+ r = get_entry_symlink(a, entry_main, &linkpath,
+ &linkpath_length, sconv);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ } else if (r != ARCHIVE_OK) {
+ r = get_entry_symlink(a, entry_main, &linkpath,
+ &linkpath_length, NULL);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s", linkpath,
+ archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ sconv = NULL;
+ }
+ }
+
+ /* If any string conversions failed, get all attributes
+ * in binary-mode. */
+ if (sconv == NULL && !pax->opt_binary) {
+ if (hardlink != NULL) {
+ r = get_entry_hardlink(a, entry_main, &hardlink,
+ &hardlink_length, NULL);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ }
+ linkpath = hardlink;
+ linkpath_length = hardlink_length;
+ }
+ r = get_entry_pathname(a, entry_main, &path,
+ &path_length, NULL);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ }
+ r = get_entry_uname(a, entry_main, &uname, &uname_length, NULL);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ }
+ r = get_entry_gname(a, entry_main, &gname, &gname_length, NULL);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ }
+ }
+
+ /* Store the header encoding first, to be nice to readers. */
+ if (sconv == NULL)
+ add_pax_attr(&(pax->pax_header), "hdrcharset", "BINARY");
+
+
+ /*
+ * If name is too long, or has non-ASCII characters, add
+ * 'path' to pax extended attrs. (Note that an unconvertible
+ * name must have non-ASCII characters.)
+ */
+ if (has_non_ASCII(path)) {
+ /* We have non-ASCII characters. */
+ add_pax_attr(&(pax->pax_header), "path", path);
+ archive_entry_set_pathname(entry_main,
+ build_ustar_entry_name(ustar_entry_name,
+ path, path_length, NULL));
+ need_extension = 1;
+ } else {
+ /* We have an all-ASCII path; we'd like to just store
+ * it in the ustar header if it will fit. Yes, this
+ * duplicates some of the logic in
+ * archive_write_set_format_ustar.c
+ */
+ if (path_length <= 100) {
+ /* Fits in the old 100-char tar name field. */
+ } else {
+ /* Find largest suffix that will fit. */
+ /* Note: strlen() > 100, so strlen() - 100 - 1 >= 0 */
+ suffix = strchr(path + path_length - 100 - 1, '/');
+ /* Don't attempt an empty prefix. */
+ if (suffix == path)
+ suffix = strchr(suffix + 1, '/');
+ /* We can put it in the ustar header if it's
+ * all ASCII and it's either <= 100 characters
+ * or can be split at a '/' into a prefix <=
+ * 155 chars and a suffix <= 100 chars. (Note
+ * the strchr() above will return NULL exactly
+ * when the path can't be split.)
+ */
+ if (suffix == NULL /* Suffix > 100 chars. */
+ || suffix[1] == '\0' /* empty suffix */
+ || suffix - path > 155) /* Prefix > 155 chars */
+ {
+ add_pax_attr(&(pax->pax_header), "path", path);
+ archive_entry_set_pathname(entry_main,
+ build_ustar_entry_name(ustar_entry_name,
+ path, path_length, NULL));
+ need_extension = 1;
+ }
+ }
+ }
+
+ if (linkpath != NULL) {
+ /* If link name is too long or has non-ASCII characters, add
+ * 'linkpath' to pax extended attrs. */
+ if (linkpath_length > 100 || has_non_ASCII(linkpath)) {
+ add_pax_attr(&(pax->pax_header), "linkpath", linkpath);
+ if (linkpath_length > 100) {
+ if (hardlink != NULL)
+ archive_entry_set_hardlink(entry_main,
+ "././@LongHardLink");
+ else
+ archive_entry_set_symlink(entry_main,
+ "././@LongSymLink");
+ }
+ need_extension = 1;
+ }
+ }
+ /* Save a pathname since it will be renamed if `entry_main` has
+ * sparse blocks. */
+ archive_string_init(&entry_name);
+ archive_strcpy(&entry_name, archive_entry_pathname(entry_main));
+
+ /* If file size is too large, we need pax extended attrs. */
+ if (archive_entry_size(entry_main) >= (((int64_t)1) << 33)) {
+ need_extension = 1;
+ }
+
+ /* If numeric GID is too large, add 'gid' to pax extended attrs. */
+ if ((unsigned int)archive_entry_gid(entry_main) >= (1 << 18)) {
+ add_pax_attr_int(&(pax->pax_header), "gid",
+ archive_entry_gid(entry_main));
+ need_extension = 1;
+ }
+
+ /* If group name is too large or has non-ASCII characters, add
+ * 'gname' to pax extended attrs. */
+ if (gname != NULL) {
+ if (gname_length > 31 || has_non_ASCII(gname)) {
+ add_pax_attr(&(pax->pax_header), "gname", gname);
+ need_extension = 1;
+ }
+ }
+
+ /* If numeric UID is too large, add 'uid' to pax extended attrs. */
+ if ((unsigned int)archive_entry_uid(entry_main) >= (1 << 18)) {
+ add_pax_attr_int(&(pax->pax_header), "uid",
+ archive_entry_uid(entry_main));
+ need_extension = 1;
+ }
+
+ /* Add 'uname' to pax extended attrs if necessary. */
+ if (uname != NULL) {
+ if (uname_length > 31 || has_non_ASCII(uname)) {
+ add_pax_attr(&(pax->pax_header), "uname", uname);
+ need_extension = 1;
+ }
+ }
+
+ /*
+ * POSIX/SUSv3 doesn't provide a standard key for large device
+ * numbers. I use the same keys here that Joerg Schilling
+ * used for 'star.' (Which, somewhat confusingly, are called
+ * "devXXX" even though they code "rdev" values.) No doubt,
+ * other implementations use other keys. Note that there's no
+ * reason we can't write the same information into a number of
+ * different keys.
+ *
+ * Of course, this is only needed for block or char device entries.
+ */
+ if (archive_entry_filetype(entry_main) == AE_IFBLK
+ || archive_entry_filetype(entry_main) == AE_IFCHR) {
+ /*
+ * If rdevmajor is too large, add 'SCHILY.devmajor' to
+ * extended attributes.
+ */
+ int rdevmajor, rdevminor;
+ rdevmajor = archive_entry_rdevmajor(entry_main);
+ rdevminor = archive_entry_rdevminor(entry_main);
+ if (rdevmajor >= (1 << 18)) {
+ add_pax_attr_int(&(pax->pax_header), "SCHILY.devmajor",
+ rdevmajor);
+ /*
+ * Non-strict formatting below means we don't
+ * have to truncate here. Not truncating improves
+ * the chance that some more modern tar archivers
+ * (such as GNU tar 1.13) can restore the full
+ * value even if they don't understand the pax
+ * extended attributes. See my rant below about
+ * file size fields for additional details.
+ */
+ /* archive_entry_set_rdevmajor(entry_main,
+ rdevmajor & ((1 << 18) - 1)); */
+ need_extension = 1;
+ }
+
+ /*
+ * If devminor is too large, add 'SCHILY.devminor' to
+ * extended attributes.
+ */
+ if (rdevminor >= (1 << 18)) {
+ add_pax_attr_int(&(pax->pax_header), "SCHILY.devminor",
+ rdevminor);
+ /* Truncation is not necessary here, either. */
+ /* archive_entry_set_rdevminor(entry_main,
+ rdevminor & ((1 << 18) - 1)); */
+ need_extension = 1;
+ }
+ }
+
+ /*
+ * Yes, this check is duplicated just below; this helps to
+ * avoid writing an mtime attribute just to handle a
+ * high-resolution timestamp in "restricted pax" mode.
+ */
+ if (!need_extension &&
+ ((archive_entry_mtime(entry_main) < 0)
+ || (archive_entry_mtime(entry_main) >= ustar_max_mtime)))
+ need_extension = 1;
+
+ /* I use a star-compatible file flag attribute. */
+ p = archive_entry_fflags_text(entry_main);
+ if (!need_extension && p != NULL && *p != '\0')
+ need_extension = 1;
+
+ /* If there are extended attributes, we need an extension */
+ if (!need_extension && archive_entry_xattr_count(entry_original) > 0)
+ need_extension = 1;
+
+ /* If there are sparse info, we need an extension */
+ if (!need_extension && sparse_count > 0)
+ need_extension = 1;
+
+ acl_types = archive_entry_acl_types(entry_original);
+
+ /* If there are any ACL entries, we need an extension */
+ if (!need_extension && acl_types != 0)
+ need_extension = 1;
+
+ /* If the symlink type is defined, we need an extension */
+ if (!need_extension && archive_entry_symlink_type(entry_main) > 0)
+ need_extension = 1;
+
+ /*
+ * Libarchive used to include these in extended headers for
+ * restricted pax format, but that confused people who
+ * expected ustar-like time semantics. So now we only include
+ * them in full pax format.
+ */
+ if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED) {
+ if (archive_entry_ctime(entry_main) != 0 ||
+ archive_entry_ctime_nsec(entry_main) != 0)
+ add_pax_attr_time(&(pax->pax_header), "ctime",
+ archive_entry_ctime(entry_main),
+ archive_entry_ctime_nsec(entry_main));
+
+ if (archive_entry_atime(entry_main) != 0 ||
+ archive_entry_atime_nsec(entry_main) != 0)
+ add_pax_attr_time(&(pax->pax_header), "atime",
+ archive_entry_atime(entry_main),
+ archive_entry_atime_nsec(entry_main));
+
+ /* Store birth/creationtime only if it's earlier than mtime */
+ if (archive_entry_birthtime_is_set(entry_main) &&
+ archive_entry_birthtime(entry_main)
+ < archive_entry_mtime(entry_main))
+ add_pax_attr_time(&(pax->pax_header),
+ "LIBARCHIVE.creationtime",
+ archive_entry_birthtime(entry_main),
+ archive_entry_birthtime_nsec(entry_main));
+ }
+
+ /*
+ * The following items are handled differently in "pax
+ * restricted" format. In particular, in "pax restricted"
+ * format they won't be added unless need_extension is
+ * already set (we're already generating an extended header, so
+ * may as well include these).
+ */
+ if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED ||
+ need_extension) {
+ if (archive_entry_mtime(entry_main) < 0 ||
+ archive_entry_mtime(entry_main) >= ustar_max_mtime ||
+ archive_entry_mtime_nsec(entry_main) != 0)
+ add_pax_attr_time(&(pax->pax_header), "mtime",
+ archive_entry_mtime(entry_main),
+ archive_entry_mtime_nsec(entry_main));
+
+ /* I use a star-compatible file flag attribute. */
+ p = archive_entry_fflags_text(entry_main);
+ if (p != NULL && *p != '\0')
+ add_pax_attr(&(pax->pax_header), "SCHILY.fflags", p);
+
+ /* I use star-compatible ACL attributes. */
+ if ((acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+ ret = add_pax_acl(a, entry_original, pax,
+ ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |
+ ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA |
+ ARCHIVE_ENTRY_ACL_STYLE_COMPACT);
+ if (ret == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+ }
+ if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+ ret = add_pax_acl(a, entry_original, pax,
+ ARCHIVE_ENTRY_ACL_TYPE_ACCESS |
+ ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |
+ ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA);
+ if (ret == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+ }
+ if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) {
+ ret = add_pax_acl(a, entry_original, pax,
+ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT |
+ ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |
+ ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA);
+ if (ret == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ /* We use GNU-tar-compatible sparse attributes. */
+ if (sparse_count > 0) {
+ int64_t soffset, slength;
+
+ add_pax_attr_int(&(pax->pax_header),
+ "GNU.sparse.major", 1);
+ add_pax_attr_int(&(pax->pax_header),
+ "GNU.sparse.minor", 0);
+ /*
+ * Make sure to store the original path, since
+ * truncation to ustar limit happened already.
+ */
+ add_pax_attr(&(pax->pax_header),
+ "GNU.sparse.name", path);
+ add_pax_attr_int(&(pax->pax_header),
+ "GNU.sparse.realsize",
+ archive_entry_size(entry_main));
+
+ /* Rename the file name which will be used for
+ * ustar header to a special name, which GNU
+ * PAX Format 1.0 requires */
+ archive_entry_set_pathname(entry_main,
+ build_gnu_sparse_name(gnu_sparse_name,
+ entry_name.s));
+
+ /*
+ * - Make a sparse map, which will precede a file data.
+ * - Get the total size of available data of sparse.
+ */
+ archive_string_sprintf(&(pax->sparse_map), "%d\n",
+ sparse_count);
+ while (archive_entry_sparse_next(entry_main,
+ &soffset, &slength) == ARCHIVE_OK) {
+ archive_string_sprintf(&(pax->sparse_map),
+ "%jd\n%jd\n",
+ (intmax_t)soffset,
+ (intmax_t)slength);
+ sparse_total += slength;
+ if (sparse_list_add(pax, soffset, slength)
+ != ARCHIVE_OK) {
+ archive_set_error(&a->archive,
+ ENOMEM,
+ "Can't allocate memory");
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+ }
+ }
+
+ /* Store extended attributes */
+ if (archive_write_pax_header_xattrs(a, pax, entry_original)
+ == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Store extended symlink information */
+ if (archive_entry_symlink_type(entry_main) ==
+ AE_SYMLINK_TYPE_FILE) {
+ add_pax_attr(&(pax->pax_header),
+ "LIBARCHIVE.symlinktype", "file");
+ } else if (archive_entry_symlink_type(entry_main) ==
+ AE_SYMLINK_TYPE_DIRECTORY) {
+ add_pax_attr(&(pax->pax_header),
+ "LIBARCHIVE.symlinktype", "dir");
+ }
+ }
+
+ /* Only regular files have data. */
+ if (archive_entry_filetype(entry_main) != AE_IFREG)
+ archive_entry_set_size(entry_main, 0);
+
+ /*
+ * Pax-restricted does not store data for hardlinks, in order
+ * to improve compatibility with ustar.
+ */
+ if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE &&
+ hardlink != NULL)
+ archive_entry_set_size(entry_main, 0);
+
+ /*
+ * XXX Full pax interchange format does permit a hardlink
+ * entry to have data associated with it. I'm not supporting
+ * that here because the client expects me to tell them whether
+ * or not this format expects data for hardlinks. If I
+ * don't check here, then every pax archive will end up with
+ * duplicated data for hardlinks. Someday, there may be
+ * need to select this behavior, in which case the following
+ * will need to be revisited. XXX
+ */
+ if (hardlink != NULL)
+ archive_entry_set_size(entry_main, 0);
+
+ /* Save a real file size. */
+ real_size = archive_entry_size(entry_main);
+ /*
+ * Overwrite a file size by the total size of sparse blocks and
+ * the size of sparse map info. That file size is the length of
+ * the data, which we will exactly store into an archive file.
+ */
+ if (archive_strlen(&(pax->sparse_map))) {
+ size_t mapsize = archive_strlen(&(pax->sparse_map));
+ pax->sparse_map_padding = 0x1ff & (-(ssize_t)mapsize);
+ archive_entry_set_size(entry_main,
+ mapsize + pax->sparse_map_padding + sparse_total);
+ }
+
+ /* If file size is too large, add 'size' to pax extended attrs. */
+ if (archive_entry_size(entry_main) >= (((int64_t)1) << 33)) {
+ add_pax_attr_int(&(pax->pax_header), "size",
+ archive_entry_size(entry_main));
+ }
+
+ /* Format 'ustar' header for main entry.
+ *
+ * The trouble with file size: If the reader can't understand
+ * the file size, they may not be able to locate the next
+ * entry and the rest of the archive is toast. Pax-compliant
+ * readers are supposed to ignore the file size in the main
+ * header, so the question becomes how to maximize portability
+ * for readers that don't support pax attribute extensions.
+ * For maximum compatibility, I permit numeric extensions in
+ * the main header so that the file size stored will always be
+ * correct, even if it's in a format that only some
+ * implementations understand. The technique used here is:
+ *
+ * a) If possible, follow the standard exactly. This handles
+ * files up to 8 gigabytes minus 1.
+ *
+ * b) If that fails, try octal but omit the field terminator.
+ * That handles files up to 64 gigabytes minus 1.
+ *
+ * c) Otherwise, use base-256 extensions. That handles files
+ * up to 2^63 in this implementation, with the potential to
+ * go up to 2^94. That should hold us for a while. ;-)
+ *
+ * The non-strict formatter uses similar logic for other
+ * numeric fields, though they're less critical.
+ */
+ if (__archive_write_format_header_ustar(a, ustarbuff, entry_main, -1, 0,
+ NULL) == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* If we built any extended attributes, write that entry first. */
+ if (archive_strlen(&(pax->pax_header)) > 0) {
+ struct archive_entry *pax_attr_entry;
+ time_t s;
+ int64_t uid, gid;
+ int mode;
+
+ pax_attr_entry = archive_entry_new2(&a->archive);
+ p = entry_name.s;
+ archive_entry_set_pathname(pax_attr_entry,
+ build_pax_attribute_name(pax_entry_name, p));
+ archive_entry_set_size(pax_attr_entry,
+ archive_strlen(&(pax->pax_header)));
+ /* Copy uid/gid (but clip to ustar limits). */
+ uid = archive_entry_uid(entry_main);
+ if (uid >= 1 << 18)
+ uid = (1 << 18) - 1;
+ archive_entry_set_uid(pax_attr_entry, uid);
+ gid = archive_entry_gid(entry_main);
+ if (gid >= 1 << 18)
+ gid = (1 << 18) - 1;
+ archive_entry_set_gid(pax_attr_entry, gid);
+ /* Copy mode over (but not setuid/setgid bits) */
+ mode = archive_entry_mode(entry_main);
+#ifdef S_ISUID
+ mode &= ~S_ISUID;
+#endif
+#ifdef S_ISGID
+ mode &= ~S_ISGID;
+#endif
+#ifdef S_ISVTX
+ mode &= ~S_ISVTX;
+#endif
+ archive_entry_set_mode(pax_attr_entry, mode);
+
+ /* Copy uname/gname. */
+ archive_entry_set_uname(pax_attr_entry,
+ archive_entry_uname(entry_main));
+ archive_entry_set_gname(pax_attr_entry,
+ archive_entry_gname(entry_main));
+
+ /* Copy mtime, but clip to ustar limits. */
+ s = archive_entry_mtime(entry_main);
+ if (s < 0) { s = 0; }
+ if (s > ustar_max_mtime) { s = ustar_max_mtime; }
+ archive_entry_set_mtime(pax_attr_entry, s, 0);
+
+ /* Standard ustar doesn't support atime. */
+ archive_entry_set_atime(pax_attr_entry, 0, 0);
+
+ /* Standard ustar doesn't support ctime. */
+ archive_entry_set_ctime(pax_attr_entry, 0, 0);
+
+ r = __archive_write_format_header_ustar(a, paxbuff,
+ pax_attr_entry, 'x', 1, NULL);
+
+ archive_entry_free(pax_attr_entry);
+
+ /* Note that the 'x' header shouldn't ever fail to format */
+ if (r < ARCHIVE_WARN) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "archive_write_pax_header: "
+ "'x' header failed?! This can't happen.\n");
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ } else if (r < ret)
+ ret = r;
+ r = __archive_write_output(a, paxbuff, 512);
+ if (r != ARCHIVE_OK) {
+ sparse_list_clear(pax);
+ pax->entry_bytes_remaining = 0;
+ pax->entry_padding = 0;
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+
+ pax->entry_bytes_remaining = archive_strlen(&(pax->pax_header));
+ pax->entry_padding =
+ 0x1ff & (-(int64_t)pax->entry_bytes_remaining);
+
+ r = __archive_write_output(a, pax->pax_header.s,
+ archive_strlen(&(pax->pax_header)));
+ if (r != ARCHIVE_OK) {
+ /* If a write fails, we're pretty much toast. */
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+ /* Pad out the end of the entry. */
+ r = __archive_write_nulls(a, (size_t)pax->entry_padding);
+ if (r != ARCHIVE_OK) {
+ /* If a write fails, we're pretty much toast. */
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+ pax->entry_bytes_remaining = pax->entry_padding = 0;
+ }
+
+ /* Write the header for main entry. */
+ r = __archive_write_output(a, ustarbuff, 512);
+ if (r != ARCHIVE_OK) {
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (r);
+ }
+
+ /*
+ * Inform the client of the on-disk size we're using, so
+ * they can avoid unnecessarily writing a body for something
+ * that we're just going to ignore.
+ */
+ archive_entry_set_size(entry_original, real_size);
+ if (pax->sparse_list == NULL && real_size > 0) {
+ /* This is not a sparse file but we handle its data as
+ * a sparse block. */
+ sparse_list_add(pax, 0, real_size);
+ sparse_total = real_size;
+ }
+ pax->entry_padding = 0x1ff & (-(int64_t)sparse_total);
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+
+ return (ret);
+}
+
+/*
+ * We need a valid name for the regular 'ustar' entry. This routine
+ * tries to hack something more-or-less reasonable.
+ *
+ * The approach here tries to preserve leading dir names. We do so by
+ * working with four sections:
+ * 1) "prefix" directory names,
+ * 2) "suffix" directory names,
+ * 3) inserted dir name (optional),
+ * 4) filename.
+ *
+ * These sections must satisfy the following requirements:
+ * * Parts 1 & 2 together form an initial portion of the dir name.
+ * * Part 3 is specified by the caller. (It should not contain a leading
+ * or trailing '/'.)
+ * * Part 4 forms an initial portion of the base filename.
+ * * The filename must be <= 99 chars to fit the ustar 'name' field.
+ * * Parts 2, 3, 4 together must be <= 99 chars to fit the ustar 'name' fld.
+ * * Part 1 must be <= 155 chars to fit the ustar 'prefix' field.
+ * * If the original name ends in a '/', the new name must also end in a '/'
+ * * Trailing '/.' sequences may be stripped.
+ *
+ * Note: Recall that the ustar format does not store the '/' separating
+ * parts 1 & 2, but does store the '/' separating parts 2 & 3.
+ */
+static char *
+build_ustar_entry_name(char *dest, const char *src, size_t src_length,
+ const char *insert)
+{
+ const char *prefix, *prefix_end;
+ const char *suffix, *suffix_end;
+ const char *filename, *filename_end;
+ char *p;
+ int need_slash = 0; /* Was there a trailing slash? */
+ size_t suffix_length = 99;
+ size_t insert_length;
+
+ /* Length of additional dir element to be added. */
+ if (insert == NULL)
+ insert_length = 0;
+ else
+ /* +2 here allows for '/' before and after the insert. */
+ insert_length = strlen(insert) + 2;
+
+ /* Step 0: Quick bailout in a common case. */
+ if (src_length < 100 && insert == NULL) {
+ strncpy(dest, src, src_length);
+ dest[src_length] = '\0';
+ return (dest);
+ }
+
+ /* Step 1: Locate filename and enforce the length restriction. */
+ filename_end = src + src_length;
+ /* Remove trailing '/' chars and '/.' pairs. */
+ for (;;) {
+ if (filename_end > src && filename_end[-1] == '/') {
+ filename_end --;
+ need_slash = 1; /* Remember to restore trailing '/'. */
+ continue;
+ }
+ if (filename_end > src + 1 && filename_end[-1] == '.'
+ && filename_end[-2] == '/') {
+ filename_end -= 2;
+ need_slash = 1; /* "foo/." will become "foo/" */
+ continue;
+ }
+ break;
+ }
+ if (need_slash)
+ suffix_length--;
+ /* Find start of filename. */
+ filename = filename_end - 1;
+ while ((filename > src) && (*filename != '/'))
+ filename --;
+ if ((*filename == '/') && (filename < filename_end - 1))
+ filename ++;
+ /* Adjust filename_end so that filename + insert fits in 99 chars. */
+ suffix_length -= insert_length;
+ if (filename_end > filename + suffix_length)
+ filename_end = filename + suffix_length;
+ /* Calculate max size for "suffix" section (#3 above). */
+ suffix_length -= filename_end - filename;
+
+ /* Step 2: Locate the "prefix" section of the dirname, including
+ * trailing '/'. */
+ prefix = src;
+ prefix_end = prefix + 155;
+ if (prefix_end > filename)
+ prefix_end = filename;
+ while (prefix_end > prefix && *prefix_end != '/')
+ prefix_end--;
+ if ((prefix_end < filename) && (*prefix_end == '/'))
+ prefix_end++;
+
+ /* Step 3: Locate the "suffix" section of the dirname,
+ * including trailing '/'. */
+ suffix = prefix_end;
+ suffix_end = suffix + suffix_length; /* Enforce limit. */
+ if (suffix_end > filename)
+ suffix_end = filename;
+ if (suffix_end < suffix)
+ suffix_end = suffix;
+ while (suffix_end > suffix && *suffix_end != '/')
+ suffix_end--;
+ if ((suffix_end < filename) && (*suffix_end == '/'))
+ suffix_end++;
+
+ /* Step 4: Build the new name. */
+ /* The OpenBSD strlcpy function is safer, but less portable. */
+ /* Rather than maintain two versions, just use the strncpy version. */
+ p = dest;
+ if (prefix_end > prefix) {
+ strncpy(p, prefix, prefix_end - prefix);
+ p += prefix_end - prefix;
+ }
+ if (suffix_end > suffix) {
+ strncpy(p, suffix, suffix_end - suffix);
+ p += suffix_end - suffix;
+ }
+ if (insert != NULL) {
+ /* Note: assume insert does not have leading or trailing '/' */
+ strcpy(p, insert);
+ p += strlen(insert);
+ *p++ = '/';
+ }
+ strncpy(p, filename, filename_end - filename);
+ p += filename_end - filename;
+ if (need_slash)
+ *p++ = '/';
+ *p = '\0';
+
+ return (dest);
+}
+
+/*
+ * The ustar header for the pax extended attributes must have a
+ * reasonable name: SUSv3 requires 'dirname'/PaxHeader.'pid'/'filename'
+ * where 'pid' is the PID of the archiving process. Unfortunately,
+ * that makes testing a pain since the output varies for each run,
+ * so I'm sticking with the simpler 'dirname'/PaxHeader/'filename'
+ * for now. (Someday, I'll make this settable. Then I can use the
+ * SUS recommendation as default and test harnesses can override it
+ * to get predictable results.)
+ *
+ * Joerg Schilling has argued that this is unnecessary because, in
+ * practice, if the pax extended attributes get extracted as regular
+ * files, no one is going to bother reading those attributes to
+ * manually restore them. Based on this, 'star' uses
+ * /tmp/PaxHeader/'basename' as the ustar header name. This is a
+ * tempting argument, in part because it's simpler than the SUSv3
+ * recommendation, but I'm not entirely convinced. I'm also
+ * uncomfortable with the fact that "/tmp" is a Unix-ism.
+ *
+ * The following routine leverages build_ustar_entry_name() above and
+ * so is simpler than you might think. It just needs to provide the
+ * additional path element and handle a few pathological cases).
+ */
+static char *
+build_pax_attribute_name(char *dest, const char *src)
+{
+ char buff[64];
+ const char *p;
+
+ /* Handle the null filename case. */
+ if (src == NULL || *src == '\0') {
+ strcpy(dest, "PaxHeader/blank");
+ return (dest);
+ }
+
+ /* Prune final '/' and other unwanted final elements. */
+ p = src + strlen(src);
+ for (;;) {
+ /* Ends in "/", remove the '/' */
+ if (p > src && p[-1] == '/') {
+ --p;
+ continue;
+ }
+ /* Ends in "/.", remove the '.' */
+ if (p > src + 1 && p[-1] == '.'
+ && p[-2] == '/') {
+ --p;
+ continue;
+ }
+ break;
+ }
+
+ /* Pathological case: After above, there was nothing left.
+ * This includes "/." "/./." "/.//./." etc. */
+ if (p == src) {
+ strcpy(dest, "/PaxHeader/rootdir");
+ return (dest);
+ }
+
+ /* Convert unadorned "." into a suitable filename. */
+ if (*src == '.' && p == src + 1) {
+ strcpy(dest, "PaxHeader/currentdir");
+ return (dest);
+ }
+
+ /*
+ * TODO: Push this string into the 'pax' structure to avoid
+ * recomputing it every time. That will also open the door
+ * to having clients override it.
+ */
+#if HAVE_GETPID && 0 /* Disable this for now; see above comment. */
+ snprintf(buff, sizeof(buff), "PaxHeader.%d", getpid());
+#else
+ /* If the platform can't fetch the pid, don't include it. */
+ strcpy(buff, "PaxHeader");
+#endif
+ /* General case: build a ustar-compatible name adding
+ * "/PaxHeader/". */
+ build_ustar_entry_name(dest, src, p - src, buff);
+
+ return (dest);
+}
+
+/*
+ * GNU PAX Format 1.0 requires the special name, which pattern is:
+ * <dir>/GNUSparseFile.<pid>/<original file name>
+ *
+ * Since reproducible archives are more important, use 0 as pid.
+ *
+ * This function is used for only Sparse file, a file type of which
+ * is regular file.
+ */
+static char *
+build_gnu_sparse_name(char *dest, const char *src)
+{
+ const char *p;
+
+ /* Handle the null filename case. */
+ if (src == NULL || *src == '\0') {
+ strcpy(dest, "GNUSparseFile/blank");
+ return (dest);
+ }
+
+ /* Prune final '/' and other unwanted final elements. */
+ p = src + strlen(src);
+ for (;;) {
+ /* Ends in "/", remove the '/' */
+ if (p > src && p[-1] == '/') {
+ --p;
+ continue;
+ }
+ /* Ends in "/.", remove the '.' */
+ if (p > src + 1 && p[-1] == '.'
+ && p[-2] == '/') {
+ --p;
+ continue;
+ }
+ break;
+ }
+
+ /* General case: build a ustar-compatible name adding
+ * "/GNUSparseFile/". */
+ build_ustar_entry_name(dest, src, p - src, "GNUSparseFile.0");
+
+ return (dest);
+}
+
+/* Write two null blocks for the end of archive */
+static int
+archive_write_pax_close(struct archive_write *a)
+{
+ return (__archive_write_nulls(a, 512 * 2));
+}
+
+static int
+archive_write_pax_free(struct archive_write *a)
+{
+ struct pax *pax;
+
+ pax = (struct pax *)a->format_data;
+ if (pax == NULL)
+ return (ARCHIVE_OK);
+
+ archive_string_free(&pax->pax_header);
+ archive_string_free(&pax->sparse_map);
+ archive_string_free(&pax->l_url_encoded_name);
+ sparse_list_clear(pax);
+ free(pax);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_pax_finish_entry(struct archive_write *a)
+{
+ struct pax *pax;
+ uint64_t remaining;
+ int ret;
+
+ pax = (struct pax *)a->format_data;
+ remaining = pax->entry_bytes_remaining;
+ if (remaining == 0) {
+ while (pax->sparse_list) {
+ struct sparse_block *sb;
+ if (!pax->sparse_list->is_hole)
+ remaining += pax->sparse_list->remaining;
+ sb = pax->sparse_list->next;
+ free(pax->sparse_list);
+ pax->sparse_list = sb;
+ }
+ }
+ ret = __archive_write_nulls(a, (size_t)(remaining + pax->entry_padding));
+ pax->entry_bytes_remaining = pax->entry_padding = 0;
+ return (ret);
+}
+
+static ssize_t
+archive_write_pax_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct pax *pax;
+ size_t ws;
+ size_t total;
+ int ret;
+
+ pax = (struct pax *)a->format_data;
+
+ /*
+ * According to GNU PAX format 1.0, write a sparse map
+ * before the body.
+ */
+ if (archive_strlen(&(pax->sparse_map))) {
+ ret = __archive_write_output(a, pax->sparse_map.s,
+ archive_strlen(&(pax->sparse_map)));
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ ret = __archive_write_nulls(a, pax->sparse_map_padding);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ archive_string_empty(&(pax->sparse_map));
+ }
+
+ total = 0;
+ while (total < s) {
+ const unsigned char *p;
+
+ while (pax->sparse_list != NULL &&
+ pax->sparse_list->remaining == 0) {
+ struct sparse_block *sb = pax->sparse_list->next;
+ free(pax->sparse_list);
+ pax->sparse_list = sb;
+ }
+
+ if (pax->sparse_list == NULL)
+ return (total);
+
+ p = ((const unsigned char *)buff) + total;
+ ws = s - total;
+ if (ws > pax->sparse_list->remaining)
+ ws = (size_t)pax->sparse_list->remaining;
+
+ if (pax->sparse_list->is_hole) {
+ /* Current block is hole thus we do not write
+ * the body. */
+ pax->sparse_list->remaining -= ws;
+ total += ws;
+ continue;
+ }
+
+ ret = __archive_write_output(a, p, ws);
+ pax->sparse_list->remaining -= ws;
+ total += ws;
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ }
+ return (total);
+}
+
+static int
+has_non_ASCII(const char *_p)
+{
+ const unsigned char *p = (const unsigned char *)_p;
+
+ if (p == NULL)
+ return (1);
+ while (*p != '\0' && *p < 128)
+ p++;
+ return (*p != '\0');
+}
+
+/*
+ * Used by extended attribute support; encodes the name
+ * so that there will be no '=' characters in the result.
+ */
+static char *
+url_encode(const char *in)
+{
+ const char *s;
+ char *d;
+ int out_len = 0;
+ char *out;
+
+ for (s = in; *s != '\0'; s++) {
+ if (*s < 33 || *s > 126 || *s == '%' || *s == '=')
+ out_len += 3;
+ else
+ out_len++;
+ }
+
+ out = (char *)malloc(out_len + 1);
+ if (out == NULL)
+ return (NULL);
+
+ for (s = in, d = out; *s != '\0'; s++) {
+ /* encode any non-printable ASCII character or '%' or '=' */
+ if (*s < 33 || *s > 126 || *s == '%' || *s == '=') {
+ /* URL encoding is '%' followed by two hex digits */
+ *d++ = '%';
+ *d++ = "0123456789ABCDEF"[0x0f & (*s >> 4)];
+ *d++ = "0123456789ABCDEF"[0x0f & *s];
+ } else {
+ *d++ = *s;
+ }
+ }
+ *d = '\0';
+ return (out);
+}
+
+/*
+ * Encode a sequence of bytes into a C string using base-64 encoding.
+ *
+ * Returns a null-terminated C string allocated with malloc(); caller
+ * is responsible for freeing the result.
+ */
+static char *
+base64_encode(const char *s, size_t len)
+{
+ static const char digits[64] =
+ { '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','+','/' };
+ int v;
+ char *d, *out;
+
+ /* 3 bytes becomes 4 chars, but round up and allow for trailing NUL */
+ out = (char *)malloc((len * 4 + 2) / 3 + 1);
+ if (out == NULL)
+ return (NULL);
+ d = out;
+
+ /* Convert each group of 3 bytes into 4 characters. */
+ while (len >= 3) {
+ v = (((int)s[0] << 16) & 0xff0000)
+ | (((int)s[1] << 8) & 0xff00)
+ | (((int)s[2]) & 0x00ff);
+ s += 3;
+ len -= 3;
+ *d++ = digits[(v >> 18) & 0x3f];
+ *d++ = digits[(v >> 12) & 0x3f];
+ *d++ = digits[(v >> 6) & 0x3f];
+ *d++ = digits[(v) & 0x3f];
+ }
+ /* Handle final group of 1 byte (2 chars) or 2 bytes (3 chars). */
+ switch (len) {
+ case 0: break;
+ case 1:
+ v = (((int)s[0] << 16) & 0xff0000);
+ *d++ = digits[(v >> 18) & 0x3f];
+ *d++ = digits[(v >> 12) & 0x3f];
+ break;
+ case 2:
+ v = (((int)s[0] << 16) & 0xff0000)
+ | (((int)s[1] << 8) & 0xff00);
+ *d++ = digits[(v >> 18) & 0x3f];
+ *d++ = digits[(v >> 12) & 0x3f];
+ *d++ = digits[(v >> 6) & 0x3f];
+ break;
+ }
+ /* Add trailing NUL character so output is a valid C string. */
+ *d = '\0';
+ return (out);
+}
+
+static void
+sparse_list_clear(struct pax *pax)
+{
+ while (pax->sparse_list != NULL) {
+ struct sparse_block *sb = pax->sparse_list;
+ pax->sparse_list = sb->next;
+ free(sb);
+ }
+ pax->sparse_tail = NULL;
+}
+
+static int
+_sparse_list_add_block(struct pax *pax, int64_t offset, int64_t length,
+ int is_hole)
+{
+ struct sparse_block *sb;
+
+ sb = (struct sparse_block *)malloc(sizeof(*sb));
+ if (sb == NULL)
+ return (ARCHIVE_FATAL);
+ sb->next = NULL;
+ sb->is_hole = is_hole;
+ sb->offset = offset;
+ sb->remaining = length;
+ if (pax->sparse_list == NULL || pax->sparse_tail == NULL)
+ pax->sparse_list = pax->sparse_tail = sb;
+ else {
+ pax->sparse_tail->next = sb;
+ pax->sparse_tail = sb;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+sparse_list_add(struct pax *pax, int64_t offset, int64_t length)
+{
+ int64_t last_offset;
+ int r;
+
+ if (pax->sparse_tail == NULL)
+ last_offset = 0;
+ else {
+ last_offset = pax->sparse_tail->offset +
+ pax->sparse_tail->remaining;
+ }
+ if (last_offset < offset) {
+ /* Add a hole block. */
+ r = _sparse_list_add_block(pax, last_offset,
+ offset - last_offset, 1);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ /* Add data block. */
+ return (_sparse_list_add_block(pax, offset, length, 0));
+}
+
+static time_t
+get_ustar_max_mtime(void)
+{
+ /*
+ * Technically, the mtime field in the ustar header can
+ * support 33 bits. We are using all of them to keep
+ * tar/test/test_option_C_mtree.c simple and passing after 2038.
+ * For platforms that use signed 32-bit time values we
+ * use the 32-bit maximum.
+ */
+ if (sizeof(time_t) > sizeof(int32_t))
+ return (time_t)0x1ffffffff;
+ else
+ return (time_t)0x7fffffff;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_private.h b/contrib/libs/libarchive/libarchive/archive_write_set_format_private.h
new file mode 100644
index 00000000000..e20022755f8
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_private.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 2020 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_WRITE_SET_FORMAT_PRIVATE_H_INCLUDED
+#define ARCHIVE_WRITE_SET_FORMAT_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+
+void __archive_write_entry_filetype_unsupported(struct archive *a,
+ struct archive_entry *entry, const char *format);
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_raw.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_raw.c
new file mode 100644
index 00000000000..feff9369773
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_raw.c
@@ -0,0 +1,125 @@
+/*-
+ * Copyright (c) 2013 Marek Kubica
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_write_private.h"
+
+static ssize_t archive_write_raw_data(struct archive_write *,
+ const void *buff, size_t s);
+static int archive_write_raw_free(struct archive_write *);
+static int archive_write_raw_header(struct archive_write *,
+ struct archive_entry *);
+
+struct raw {
+ int entries_written;
+};
+
+/*
+ * Set output format to 'raw' format.
+ */
+int
+archive_write_set_format_raw(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct raw *raw;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_raw");
+
+ /* If someone else was already registered, unregister them. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ raw = (struct raw *)calloc(1, sizeof(*raw));
+ if (raw == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate raw data");
+ return (ARCHIVE_FATAL);
+ }
+ raw->entries_written = 0;
+ a->format_data = raw;
+ a->format_name = "raw";
+ /* no options exist for this format */
+ a->format_options = NULL;
+ a->format_write_header = archive_write_raw_header;
+ a->format_write_data = archive_write_raw_data;
+ a->format_finish_entry = NULL;
+ /* nothing needs to be done on closing */
+ a->format_close = NULL;
+ a->format_free = archive_write_raw_free;
+ a->archive.archive_format = ARCHIVE_FORMAT_RAW;
+ a->archive.archive_format_name = "RAW";
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_raw_header(struct archive_write *a, struct archive_entry *entry)
+{
+ struct raw *raw = (struct raw *)a->format_data;
+
+ if (archive_entry_filetype(entry) != AE_IFREG) {
+ archive_set_error(&a->archive, ERANGE,
+ "Raw format only supports filetype AE_IFREG");
+ return (ARCHIVE_FATAL);
+ }
+
+
+ if (raw->entries_written > 0) {
+ archive_set_error(&a->archive, ERANGE,
+ "Raw format only supports one entry per archive");
+ return (ARCHIVE_FATAL);
+ }
+ raw->entries_written++;
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+archive_write_raw_data(struct archive_write *a, const void *buff, size_t s)
+{
+ int ret;
+
+ ret = __archive_write_output(a, buff, s);
+ if (ret >= 0)
+ return (s);
+ else
+ return (ret);
+}
+
+static int
+archive_write_raw_free(struct archive_write *a)
+{
+ struct raw *raw;
+
+ raw = (struct raw *)a->format_data;
+ free(raw);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_shar.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_shar.c
new file mode 100644
index 00000000000..9e4931c95c1
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_shar.c
@@ -0,0 +1,641 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2008 Joerg Sonnenberger
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_shar.c 189438 2009-03-06 05:58:56Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct shar {
+ int dump;
+ int end_of_line;
+ struct archive_entry *entry;
+ int has_data;
+ char *last_dir;
+
+ /* Line buffer for uuencoded dump format */
+ char outbuff[45];
+ size_t outpos;
+
+ int wrote_header;
+ struct archive_string work;
+ struct archive_string quoted_name;
+};
+
+static int archive_write_shar_close(struct archive_write *);
+static int archive_write_shar_free(struct archive_write *);
+static int archive_write_shar_header(struct archive_write *,
+ struct archive_entry *);
+static ssize_t archive_write_shar_data_sed(struct archive_write *,
+ const void * buff, size_t);
+static ssize_t archive_write_shar_data_uuencode(struct archive_write *,
+ const void * buff, size_t);
+static int archive_write_shar_finish_entry(struct archive_write *);
+
+/*
+ * Copy the given string to the buffer, quoting all shell meta characters
+ * found.
+ */
+static void
+shar_quote(struct archive_string *buf, const char *str, int in_shell)
+{
+ static const char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
+ size_t len;
+
+ while (*str != '\0') {
+ if ((len = strcspn(str, meta)) != 0) {
+ archive_strncat(buf, str, len);
+ str += len;
+ } else if (*str == '\n') {
+ if (in_shell)
+ archive_strcat(buf, "\"\n\"");
+ else
+ archive_strcat(buf, "\\n");
+ ++str;
+ } else {
+ archive_strappend_char(buf, '\\');
+ archive_strappend_char(buf, *str);
+ ++str;
+ }
+ }
+}
+
+/*
+ * Set output format to 'shar' format.
+ */
+int
+archive_write_set_format_shar(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct shar *shar;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_shar");
+
+ /* If someone else was already registered, unregister them. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ shar = (struct shar *)calloc(1, sizeof(*shar));
+ if (shar == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate shar data");
+ return (ARCHIVE_FATAL);
+ }
+ archive_string_init(&shar->work);
+ archive_string_init(&shar->quoted_name);
+ a->format_data = shar;
+ a->format_name = "shar";
+ a->format_write_header = archive_write_shar_header;
+ a->format_close = archive_write_shar_close;
+ a->format_free = archive_write_shar_free;
+ a->format_write_data = archive_write_shar_data_sed;
+ a->format_finish_entry = archive_write_shar_finish_entry;
+ a->archive.archive_format = ARCHIVE_FORMAT_SHAR_BASE;
+ a->archive.archive_format_name = "shar";
+ return (ARCHIVE_OK);
+}
+
+/*
+ * An alternate 'shar' that uses uudecode instead of 'sed' to encode
+ * file contents and can therefore be used to archive binary files.
+ * In addition, this variant also attempts to restore ownership, file modes,
+ * and other extended file information.
+ */
+int
+archive_write_set_format_shar_dump(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct shar *shar;
+
+ archive_write_set_format_shar(&a->archive);
+ shar = (struct shar *)a->format_data;
+ shar->dump = 1;
+ a->format_write_data = archive_write_shar_data_uuencode;
+ a->archive.archive_format = ARCHIVE_FORMAT_SHAR_DUMP;
+ a->archive.archive_format_name = "shar dump";
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)
+{
+ const char *linkname;
+ const char *name;
+ char *p, *pp;
+ struct shar *shar;
+
+ shar = (struct shar *)a->format_data;
+ if (!shar->wrote_header) {
+ archive_strcat(&shar->work, "#!/bin/sh\n");
+ archive_strcat(&shar->work, "# This is a shell archive\n");
+ shar->wrote_header = 1;
+ }
+
+ /* Save the entry for the closing. */
+ archive_entry_free(shar->entry);
+ shar->entry = archive_entry_clone(entry);
+ name = archive_entry_pathname(entry);
+
+ /* Handle some preparatory issues. */
+ switch(archive_entry_filetype(entry)) {
+ case AE_IFREG:
+ /* Only regular files have non-zero size. */
+ break;
+ case AE_IFDIR:
+ archive_entry_set_size(entry, 0);
+ /* Don't bother trying to recreate '.' */
+ if (strcmp(name, ".") == 0 || strcmp(name, "./") == 0)
+ return (ARCHIVE_OK);
+ break;
+ case AE_IFIFO:
+ case AE_IFCHR:
+ case AE_IFBLK:
+ /* All other file types have zero size in the archive. */
+ archive_entry_set_size(entry, 0);
+ break;
+ default:
+ archive_entry_set_size(entry, 0);
+ if (archive_entry_hardlink(entry) == NULL &&
+ archive_entry_symlink(entry) == NULL) {
+ __archive_write_entry_filetype_unsupported(
+ &a->archive, entry, "shar");
+ return (ARCHIVE_WARN);
+ }
+ }
+
+ archive_string_empty(&shar->quoted_name);
+ shar_quote(&shar->quoted_name, name, 1);
+
+ /* Stock preparation for all file types. */
+ archive_string_sprintf(&shar->work, "echo x %s\n", shar->quoted_name.s);
+
+ if (archive_entry_filetype(entry) != AE_IFDIR) {
+ /* Try to create the dir. */
+ p = strdup(name);
+ pp = strrchr(p, '/');
+ /* If there is a / character, try to create the dir. */
+ if (pp != NULL) {
+ *pp = '\0';
+
+ /* Try to avoid a lot of redundant mkdir commands. */
+ if (strcmp(p, ".") == 0) {
+ /* Don't try to "mkdir ." */
+ free(p);
+ } else if (shar->last_dir == NULL) {
+ archive_strcat(&shar->work, "mkdir -p ");
+ shar_quote(&shar->work, p, 1);
+ archive_strcat(&shar->work,
+ " > /dev/null 2>&1\n");
+ shar->last_dir = p;
+ } else if (strcmp(p, shar->last_dir) == 0) {
+ /* We've already created this exact dir. */
+ free(p);
+ } else if (strlen(p) < strlen(shar->last_dir) &&
+ strncmp(p, shar->last_dir, strlen(p)) == 0) {
+ /* We've already created a subdir. */
+ free(p);
+ } else {
+ archive_strcat(&shar->work, "mkdir -p ");
+ shar_quote(&shar->work, p, 1);
+ archive_strcat(&shar->work,
+ " > /dev/null 2>&1\n");
+ shar->last_dir = p;
+ }
+ } else {
+ free(p);
+ }
+ }
+
+ /* Handle file-type specific issues. */
+ shar->has_data = 0;
+ if ((linkname = archive_entry_hardlink(entry)) != NULL) {
+ archive_strcat(&shar->work, "ln -f ");
+ shar_quote(&shar->work, linkname, 1);
+ archive_string_sprintf(&shar->work, " %s\n",
+ shar->quoted_name.s);
+ } else if ((linkname = archive_entry_symlink(entry)) != NULL) {
+ archive_strcat(&shar->work, "ln -fs ");
+ shar_quote(&shar->work, linkname, 1);
+ archive_string_sprintf(&shar->work, " %s\n",
+ shar->quoted_name.s);
+ } else {
+ switch(archive_entry_filetype(entry)) {
+ case AE_IFREG:
+ if (archive_entry_size(entry) == 0) {
+ /* More portable than "touch." */
+ archive_string_sprintf(&shar->work,
+ "test -e \"%s\" || :> \"%s\"\n",
+ shar->quoted_name.s, shar->quoted_name.s);
+ } else {
+ if (shar->dump) {
+ unsigned int mode = archive_entry_mode(entry) & 0777;
+ archive_string_sprintf(&shar->work,
+ "uudecode -p > %s << 'SHAR_END'\n",
+ shar->quoted_name.s);
+ archive_string_sprintf(&shar->work,
+ "begin %o ", mode);
+ shar_quote(&shar->work, name, 0);
+ archive_strcat(&shar->work, "\n");
+ } else {
+ archive_string_sprintf(&shar->work,
+ "sed 's/^X//' > %s << 'SHAR_END'\n",
+ shar->quoted_name.s);
+ }
+ shar->has_data = 1;
+ shar->end_of_line = 1;
+ shar->outpos = 0;
+ }
+ break;
+ case AE_IFDIR:
+ archive_string_sprintf(&shar->work,
+ "mkdir -p %s > /dev/null 2>&1\n",
+ shar->quoted_name.s);
+ /* Record that we just created this directory. */
+ free(shar->last_dir);
+
+ shar->last_dir = strdup(name);
+ /* Trim a trailing '/'. */
+ pp = strrchr(shar->last_dir, '/');
+ if (pp != NULL && pp[1] == '\0')
+ *pp = '\0';
+ /*
+ * TODO: Put dir name/mode on a list to be fixed
+ * up at end of archive.
+ */
+ break;
+ case AE_IFIFO:
+ archive_string_sprintf(&shar->work,
+ "mkfifo %s\n", shar->quoted_name.s);
+ break;
+ case AE_IFCHR:
+ archive_string_sprintf(&shar->work,
+ "mknod %s c %ju %ju\n", shar->quoted_name.s,
+ (uintmax_t)archive_entry_rdevmajor(entry),
+ (uintmax_t)archive_entry_rdevminor(entry));
+ break;
+ case AE_IFBLK:
+ archive_string_sprintf(&shar->work,
+ "mknod %s b %ju %ju\n", shar->quoted_name.s,
+ (uintmax_t)archive_entry_rdevmajor(entry),
+ (uintmax_t)archive_entry_rdevminor(entry));
+ break;
+ default:
+ return (ARCHIVE_WARN);
+ }
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n)
+{
+ static const size_t ensured = 65533;
+ struct shar *shar;
+ const char *src;
+ char *buf, *buf_end;
+ int ret;
+ size_t written = n;
+
+ shar = (struct shar *)a->format_data;
+ if (!shar->has_data || n == 0)
+ return (0);
+
+ src = (const char *)buff;
+
+ /*
+ * ensure is the number of bytes in buffer before expanding the
+ * current character. Each operation writes the current character
+ * and optionally the start-of-new-line marker. This can happen
+ * twice before entering the loop, so make sure three additional
+ * bytes can be written.
+ */
+ if (archive_string_ensure(&shar->work, ensured + 3) == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (shar->work.length > ensured) {
+ ret = __archive_write_output(a, shar->work.s,
+ shar->work.length);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ archive_string_empty(&shar->work);
+ }
+ buf = shar->work.s + shar->work.length;
+ buf_end = shar->work.s + ensured;
+
+ if (shar->end_of_line) {
+ *buf++ = 'X';
+ shar->end_of_line = 0;
+ }
+
+ while (n-- != 0) {
+ if ((*buf++ = *src++) == '\n') {
+ if (n == 0)
+ shar->end_of_line = 1;
+ else
+ *buf++ = 'X';
+ }
+
+ if (buf >= buf_end) {
+ shar->work.length = buf - shar->work.s;
+ ret = __archive_write_output(a, shar->work.s,
+ shar->work.length);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ archive_string_empty(&shar->work);
+ buf = shar->work.s;
+ }
+ }
+
+ shar->work.length = buf - shar->work.s;
+
+ return (written);
+}
+
+#define UUENC(c) (((c)!=0) ? ((c) & 077) + ' ': '`')
+
+static void
+uuencode_group(const char _in[3], char out[4])
+{
+ const unsigned char *in = (const unsigned char *)_in;
+ int t;
+
+ t = (in[0] << 16) | (in[1] << 8) | in[2];
+ out[0] = UUENC( 0x3f & (t >> 18) );
+ out[1] = UUENC( 0x3f & (t >> 12) );
+ out[2] = UUENC( 0x3f & (t >> 6) );
+ out[3] = UUENC( 0x3f & t );
+}
+
+static int
+_uuencode_line(struct archive_write *a, struct shar *shar, const char *inbuf, size_t len)
+{
+ char *buf;
+ size_t alloc_len;
+
+ /* len <= 45 -> expanded to 60 + len byte + new line */
+ alloc_len = shar->work.length + 62;
+ if (archive_string_ensure(&shar->work, alloc_len) == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ buf = shar->work.s + shar->work.length;
+ *buf++ = UUENC(len);
+ while (len >= 3) {
+ uuencode_group(inbuf, buf);
+ len -= 3;
+ inbuf += 3;
+ buf += 4;
+ }
+ if (len != 0) {
+ char tmp_buf[3];
+ tmp_buf[0] = inbuf[0];
+ if (len == 1)
+ tmp_buf[1] = '\0';
+ else
+ tmp_buf[1] = inbuf[1];
+ tmp_buf[2] = '\0';
+ uuencode_group(tmp_buf, buf);
+ buf += 4;
+ }
+ *buf++ = '\n';
+ if ((buf - shar->work.s) > (ptrdiff_t)(shar->work.length + 62)) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC, "Buffer overflow");
+ return (ARCHIVE_FATAL);
+ }
+ shar->work.length = buf - shar->work.s;
+ return (ARCHIVE_OK);
+}
+
+#define uuencode_line(__a, __shar, __inbuf, __len) \
+ do { \
+ int r = _uuencode_line(__a, __shar, __inbuf, __len); \
+ if (r != ARCHIVE_OK) \
+ return (ARCHIVE_FATAL); \
+ } while (0)
+
+static ssize_t
+archive_write_shar_data_uuencode(struct archive_write *a, const void *buff,
+ size_t length)
+{
+ struct shar *shar;
+ const char *src;
+ size_t n;
+ int ret;
+
+ shar = (struct shar *)a->format_data;
+ if (!shar->has_data)
+ return (ARCHIVE_OK);
+ src = (const char *)buff;
+
+ if (shar->outpos != 0) {
+ n = 45 - shar->outpos;
+ if (n > length)
+ n = length;
+ memcpy(shar->outbuff + shar->outpos, src, n);
+ if (shar->outpos + n < 45) {
+ shar->outpos += n;
+ return length;
+ }
+ uuencode_line(a, shar, shar->outbuff, 45);
+ src += n;
+ n = length - n;
+ } else {
+ n = length;
+ }
+
+ while (n >= 45) {
+ uuencode_line(a, shar, src, 45);
+ src += 45;
+ n -= 45;
+
+ if (shar->work.length < 65536)
+ continue;
+ ret = __archive_write_output(a, shar->work.s,
+ shar->work.length);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ archive_string_empty(&shar->work);
+ }
+ if (n != 0) {
+ memcpy(shar->outbuff, src, n);
+ shar->outpos = n;
+ }
+ return (length);
+}
+
+static int
+archive_write_shar_finish_entry(struct archive_write *a)
+{
+ const char *g, *p, *u;
+ struct shar *shar;
+ int ret;
+
+ shar = (struct shar *)a->format_data;
+ if (shar->entry == NULL)
+ return (0);
+
+ if (shar->dump) {
+ /* Finish uuencoded data. */
+ if (shar->has_data) {
+ if (shar->outpos > 0)
+ uuencode_line(a, shar, shar->outbuff,
+ shar->outpos);
+ archive_strcat(&shar->work, "`\nend\n");
+ archive_strcat(&shar->work, "SHAR_END\n");
+ }
+ /* Restore file mode, owner, flags. */
+ /*
+ * TODO: Don't immediately restore mode for
+ * directories; defer that to end of script.
+ */
+ archive_string_sprintf(&shar->work, "chmod %o ",
+ (unsigned int)(archive_entry_mode(shar->entry) & 07777));
+ shar_quote(&shar->work, archive_entry_pathname(shar->entry), 1);
+ archive_strcat(&shar->work, "\n");
+
+ u = archive_entry_uname(shar->entry);
+ g = archive_entry_gname(shar->entry);
+ if (u != NULL || g != NULL) {
+ archive_strcat(&shar->work, "chown ");
+ if (u != NULL)
+ shar_quote(&shar->work, u, 1);
+ if (g != NULL) {
+ archive_strcat(&shar->work, ":");
+ shar_quote(&shar->work, g, 1);
+ }
+ archive_strcat(&shar->work, " ");
+ shar_quote(&shar->work,
+ archive_entry_pathname(shar->entry), 1);
+ archive_strcat(&shar->work, "\n");
+ }
+
+ if ((p = archive_entry_fflags_text(shar->entry)) != NULL) {
+ archive_string_sprintf(&shar->work, "chflags %s ", p);
+ shar_quote(&shar->work,
+ archive_entry_pathname(shar->entry), 1);
+ archive_strcat(&shar->work, "\n");
+ }
+
+ /* TODO: restore ACLs */
+
+ } else {
+ if (shar->has_data) {
+ /* Finish sed-encoded data: ensure last line ends. */
+ if (!shar->end_of_line)
+ archive_strappend_char(&shar->work, '\n');
+ archive_strcat(&shar->work, "SHAR_END\n");
+ }
+ }
+
+ archive_entry_free(shar->entry);
+ shar->entry = NULL;
+
+ if (shar->work.length < 65536)
+ return (ARCHIVE_OK);
+
+ ret = __archive_write_output(a, shar->work.s, shar->work.length);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ archive_string_empty(&shar->work);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_shar_close(struct archive_write *a)
+{
+ struct shar *shar;
+ int ret;
+
+ /*
+ * TODO: Accumulate list of directory names/modes and
+ * fix them all up at end-of-archive.
+ */
+
+ shar = (struct shar *)a->format_data;
+
+ /*
+ * Only write the end-of-archive markers if the archive was
+ * actually started. This avoids problems if someone sets
+ * shar format, then sets another format (which would invoke
+ * shar_finish to free the format-specific data).
+ */
+ if (shar->wrote_header == 0)
+ return (ARCHIVE_OK);
+
+ archive_strcat(&shar->work, "exit\n");
+
+ ret = __archive_write_output(a, shar->work.s, shar->work.length);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Shar output is never padded. */
+ archive_write_set_bytes_in_last_block(&a->archive, 1);
+ /*
+ * TODO: shar should also suppress padding of
+ * uncompressed data within gzip/bzip2 streams.
+ */
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_shar_free(struct archive_write *a)
+{
+ struct shar *shar;
+
+ shar = (struct shar *)a->format_data;
+ if (shar == NULL)
+ return (ARCHIVE_OK);
+
+ archive_entry_free(shar->entry);
+ free(shar->last_dir);
+ archive_string_free(&(shar->work));
+ archive_string_free(&(shar->quoted_name));
+ free(shar);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_ustar.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_ustar.c
new file mode 100644
index 00000000000..d1a06bc4f7e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_ustar.c
@@ -0,0 +1,758 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_ustar.c 191579 2009-04-27 18:35:03Z kientzle $");
+
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct ustar {
+ uint64_t entry_bytes_remaining;
+ uint64_t entry_padding;
+
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_default;
+ int init_default_conversion;
+};
+
+/*
+ * Define structure of POSIX 'ustar' tar header.
+ */
+#define USTAR_name_offset 0
+#define USTAR_name_size 100
+#define USTAR_mode_offset 100
+#define USTAR_mode_size 6
+#define USTAR_mode_max_size 8
+#define USTAR_uid_offset 108
+#define USTAR_uid_size 6
+#define USTAR_uid_max_size 8
+#define USTAR_gid_offset 116
+#define USTAR_gid_size 6
+#define USTAR_gid_max_size 8
+#define USTAR_size_offset 124
+#define USTAR_size_size 11
+#define USTAR_size_max_size 12
+#define USTAR_mtime_offset 136
+#define USTAR_mtime_size 11
+#define USTAR_mtime_max_size 11
+#define USTAR_checksum_offset 148
+#define USTAR_checksum_size 8
+#define USTAR_typeflag_offset 156
+#define USTAR_typeflag_size 1
+#define USTAR_linkname_offset 157
+#define USTAR_linkname_size 100
+#define USTAR_magic_offset 257
+#define USTAR_magic_size 6
+#define USTAR_version_offset 263
+#define USTAR_version_size 2
+#define USTAR_uname_offset 265
+#define USTAR_uname_size 32
+#define USTAR_gname_offset 297
+#define USTAR_gname_size 32
+#define USTAR_rdevmajor_offset 329
+#define USTAR_rdevmajor_size 6
+#define USTAR_rdevmajor_max_size 8
+#define USTAR_rdevminor_offset 337
+#define USTAR_rdevminor_size 6
+#define USTAR_rdevminor_max_size 8
+#define USTAR_prefix_offset 345
+#define USTAR_prefix_size 155
+#define USTAR_padding_offset 500
+#define USTAR_padding_size 12
+
+/*
+ * A filled-in copy of the header for initialization.
+ */
+static const char template_header[] = {
+ /* name: 100 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,
+ /* Mode, space-null termination: 8 bytes */
+ '0','0','0','0','0','0', ' ','\0',
+ /* uid, space-null termination: 8 bytes */
+ '0','0','0','0','0','0', ' ','\0',
+ /* gid, space-null termination: 8 bytes */
+ '0','0','0','0','0','0', ' ','\0',
+ /* size, space termination: 12 bytes */
+ '0','0','0','0','0','0','0','0','0','0','0', ' ',
+ /* mtime, space termination: 12 bytes */
+ '0','0','0','0','0','0','0','0','0','0','0', ' ',
+ /* Initial checksum value: 8 spaces */
+ ' ',' ',' ',' ',' ',' ',' ',' ',
+ /* Typeflag: 1 byte */
+ '0', /* '0' = regular file */
+ /* Linkname: 100 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,
+ /* Magic: 6 bytes, Version: 2 bytes */
+ 'u','s','t','a','r','\0', '0','0',
+ /* Uname: 32 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ /* Gname: 32 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ /* rdevmajor + space/null padding: 8 bytes */
+ '0','0','0','0','0','0', ' ','\0',
+ /* rdevminor + space/null padding: 8 bytes */
+ '0','0','0','0','0','0', ' ','\0',
+ /* Prefix: 155 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,
+ /* Padding: 12 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0
+};
+
+static ssize_t archive_write_ustar_data(struct archive_write *a, const void *buff,
+ size_t s);
+static int archive_write_ustar_free(struct archive_write *);
+static int archive_write_ustar_close(struct archive_write *);
+static int archive_write_ustar_finish_entry(struct archive_write *);
+static int archive_write_ustar_header(struct archive_write *,
+ struct archive_entry *entry);
+static int archive_write_ustar_options(struct archive_write *,
+ const char *, const char *);
+static int format_256(int64_t, char *, int);
+static int format_number(int64_t, char *, int size, int max, int strict);
+static int format_octal(int64_t, char *, int);
+
+/*
+ * Set output format to 'ustar' format.
+ */
+int
+archive_write_set_format_ustar(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct ustar *ustar;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_ustar");
+
+ /* If someone else was already registered, unregister them. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ /* Basic internal sanity test. */
+ if (sizeof(template_header) != 512) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Internal: template_header wrong size: %zu should be 512",
+ sizeof(template_header));
+ return (ARCHIVE_FATAL);
+ }
+
+ ustar = (struct ustar *)calloc(1, sizeof(*ustar));
+ if (ustar == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ return (ARCHIVE_FATAL);
+ }
+ a->format_data = ustar;
+ a->format_name = "ustar";
+ a->format_options = archive_write_ustar_options;
+ a->format_write_header = archive_write_ustar_header;
+ a->format_write_data = archive_write_ustar_data;
+ a->format_close = archive_write_ustar_close;
+ a->format_free = archive_write_ustar_free;
+ a->format_finish_entry = archive_write_ustar_finish_entry;
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR;
+ a->archive.archive_format_name = "POSIX ustar";
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_ustar_options(struct archive_write *a, const char *key,
+ const char *val)
+{
+ struct ustar *ustar = (struct ustar *)a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: hdrcharset option needs a character-set name",
+ a->format_name);
+ else {
+ ustar->opt_sconv = archive_string_conversion_to_charset(
+ &a->archive, val, 0);
+ if (ustar->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
+{
+ char buff[512];
+ int ret, ret2;
+ struct ustar *ustar;
+ struct archive_entry *entry_main;
+ struct archive_string_conv *sconv;
+
+ ustar = (struct ustar *)a->format_data;
+
+ /* Setup default string conversion. */
+ if (ustar->opt_sconv == NULL) {
+ if (!ustar->init_default_conversion) {
+ ustar->sconv_default =
+ archive_string_default_conversion_for_write(&(a->archive));
+ ustar->init_default_conversion = 1;
+ }
+ sconv = ustar->sconv_default;
+ } else
+ sconv = ustar->opt_sconv;
+
+ /* Sanity check. */
+ if (archive_entry_pathname(entry) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't record entry in tar file without pathname");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Only regular files (not hardlinks) have data. */
+ if (archive_entry_hardlink(entry) != NULL ||
+ archive_entry_symlink(entry) != NULL ||
+ !(archive_entry_filetype(entry) == AE_IFREG))
+ archive_entry_set_size(entry, 0);
+
+ if (AE_IFDIR == archive_entry_filetype(entry)) {
+ const char *p;
+ size_t path_length;
+ /*
+ * Ensure a trailing '/'. Modify the entry so
+ * the client sees the change.
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ const wchar_t *wp;
+
+ wp = archive_entry_pathname_w(entry);
+ if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
+ struct archive_wstring ws;
+
+ archive_string_init(&ws);
+ path_length = wcslen(wp);
+ if (archive_wstring_ensure(&ws,
+ path_length + 2) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ archive_wstring_free(&ws);
+ return(ARCHIVE_FATAL);
+ }
+ /* Should we keep '\' ? */
+ if (wp[path_length -1] == L'\\')
+ path_length--;
+ archive_wstrncpy(&ws, wp, path_length);
+ archive_wstrappend_wchar(&ws, L'/');
+ archive_entry_copy_pathname_w(entry, ws.s);
+ archive_wstring_free(&ws);
+ p = NULL;
+ } else
+#endif
+ p = archive_entry_pathname(entry);
+ /*
+ * On Windows, this is a backup operation just in
+ * case getting WCS failed. On POSIX, this is a
+ * normal operation.
+ */
+ if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {
+ struct archive_string as;
+
+ archive_string_init(&as);
+ path_length = strlen(p);
+ if (archive_string_ensure(&as,
+ path_length + 2) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ archive_string_free(&as);
+ return(ARCHIVE_FATAL);
+ }
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* NOTE: This might break the pathname
+ * if the current code page is CP932 and
+ * the pathname includes a character '\'
+ * as a part of its multibyte pathname. */
+ if (p[strlen(p) -1] == '\\')
+ path_length--;
+ else
+#endif
+ archive_strncpy(&as, p, path_length);
+ archive_strappend_char(&as, '/');
+ archive_entry_copy_pathname(entry, as.s);
+ archive_string_free(&as);
+ }
+ }
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Make sure the path separators in pathname, hardlink and symlink
+ * are all slash '/', not the Windows path separator '\'. */
+ entry_main = __la_win_entry_in_posix_pathseparator(entry);
+ if (entry_main == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ return(ARCHIVE_FATAL);
+ }
+ if (entry != entry_main)
+ entry = entry_main;
+ else
+ entry_main = NULL;
+#else
+ entry_main = NULL;
+#endif
+ ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1, sconv);
+ if (ret < ARCHIVE_WARN) {
+ archive_entry_free(entry_main);
+ return (ret);
+ }
+ ret2 = __archive_write_output(a, buff, 512);
+ if (ret2 < ARCHIVE_WARN) {
+ archive_entry_free(entry_main);
+ return (ret2);
+ }
+ if (ret2 < ret)
+ ret = ret2;
+
+ ustar->entry_bytes_remaining = archive_entry_size(entry);
+ ustar->entry_padding = 0x1ff & (-(int64_t)ustar->entry_bytes_remaining);
+ archive_entry_free(entry_main);
+ return (ret);
+}
+
+/*
+ * Format a basic 512-byte "ustar" header.
+ *
+ * Returns -1 if format failed (due to field overflow).
+ * Note that this always formats as much of the header as possible.
+ * If "strict" is set to zero, it will extend numeric fields as
+ * necessary (overwriting terminators or using base-256 extensions).
+ *
+ * This is exported so that other 'tar' formats can use it.
+ */
+int
+__archive_write_format_header_ustar(struct archive_write *a, char h[512],
+ struct archive_entry *entry, int tartype, int strict,
+ struct archive_string_conv *sconv)
+{
+ unsigned int checksum;
+ int i, r, ret;
+ size_t copy_length;
+ const char *p, *pp;
+ int mytartype;
+
+ ret = 0;
+ mytartype = -1;
+ /*
+ * The "template header" already includes the "ustar"
+ * signature, various end-of-field markers and other required
+ * elements.
+ */
+ memcpy(h, &template_header, 512);
+
+ /*
+ * Because the block is already null-filled, and strings
+ * are allowed to exactly fill their destination (without null),
+ * I use memcpy(dest, src, strlen()) here a lot to copy strings.
+ */
+ r = archive_entry_pathname_l(entry, &pp, &copy_length, sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s",
+ pp, archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ if (copy_length <= USTAR_name_size)
+ memcpy(h + USTAR_name_offset, pp, copy_length);
+ else {
+ /* Store in two pieces, splitting at a '/'. */
+ p = strchr(pp + copy_length - USTAR_name_size - 1, '/');
+ /*
+ * Look for the next '/' if we chose the first character
+ * as the separator. (ustar format doesn't permit
+ * an empty prefix.)
+ */
+ if (p == pp)
+ p = strchr(p + 1, '/');
+ /* Fail if the name won't fit. */
+ if (!p) {
+ /* No separator. */
+ archive_set_error(&a->archive, ENAMETOOLONG,
+ "Pathname too long");
+ ret = ARCHIVE_FAILED;
+ } else if (p[1] == '\0') {
+ /*
+ * The only feasible separator is a final '/';
+ * this would result in a non-empty prefix and
+ * an empty name, which POSIX doesn't
+ * explicitly forbid, but it just feels wrong.
+ */
+ archive_set_error(&a->archive, ENAMETOOLONG,
+ "Pathname too long");
+ ret = ARCHIVE_FAILED;
+ } else if (p > pp + USTAR_prefix_size) {
+ /* Prefix is too long. */
+ archive_set_error(&a->archive, ENAMETOOLONG,
+ "Pathname too long");
+ ret = ARCHIVE_FAILED;
+ } else {
+ /* Copy prefix and remainder to appropriate places */
+ memcpy(h + USTAR_prefix_offset, pp, p - pp);
+ memcpy(h + USTAR_name_offset, p + 1,
+ pp + copy_length - p - 1);
+ }
+ }
+
+ r = archive_entry_hardlink_l(entry, &p, &copy_length, sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ p, archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ if (copy_length > 0)
+ mytartype = '1';
+ else {
+ r = archive_entry_symlink_l(entry, &p, &copy_length, sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ p, archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ }
+ if (copy_length > 0) {
+ if (copy_length > USTAR_linkname_size) {
+ archive_set_error(&a->archive, ENAMETOOLONG,
+ "Link contents too long");
+ ret = ARCHIVE_FAILED;
+ copy_length = USTAR_linkname_size;
+ }
+ memcpy(h + USTAR_linkname_offset, p, copy_length);
+ }
+
+ r = archive_entry_uname_l(entry, &p, &copy_length, sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Uname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate uname '%s' to %s",
+ p, archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ if (copy_length > 0) {
+ if (copy_length > USTAR_uname_size) {
+ if (tartype != 'x') {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC, "Username too long");
+ ret = ARCHIVE_FAILED;
+ }
+ copy_length = USTAR_uname_size;
+ }
+ memcpy(h + USTAR_uname_offset, p, copy_length);
+ }
+
+ r = archive_entry_gname_l(entry, &p, &copy_length, sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Gname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate gname '%s' to %s",
+ p, archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ if (copy_length > 0) {
+ if (strlen(p) > USTAR_gname_size) {
+ if (tartype != 'x') {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC, "Group name too long");
+ ret = ARCHIVE_FAILED;
+ }
+ copy_length = USTAR_gname_size;
+ }
+ memcpy(h + USTAR_gname_offset, p, copy_length);
+ }
+
+ if (format_number(archive_entry_mode(entry) & 07777,
+ h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric mode too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_uid(entry),
+ h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric user ID too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_gid(entry),
+ h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric group ID too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_size(entry),
+ h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File size out of range");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_mtime(entry),
+ h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File modification time too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (archive_entry_filetype(entry) == AE_IFBLK
+ || archive_entry_filetype(entry) == AE_IFCHR) {
+ if (format_number(archive_entry_rdevmajor(entry),
+ h + USTAR_rdevmajor_offset, USTAR_rdevmajor_size,
+ USTAR_rdevmajor_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Major device number too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_rdevminor(entry),
+ h + USTAR_rdevminor_offset, USTAR_rdevminor_size,
+ USTAR_rdevminor_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Minor device number too large");
+ ret = ARCHIVE_FAILED;
+ }
+ }
+
+ if (tartype >= 0) {
+ h[USTAR_typeflag_offset] = tartype;
+ } else if (mytartype >= 0) {
+ h[USTAR_typeflag_offset] = mytartype;
+ } else {
+ switch (archive_entry_filetype(entry)) {
+ case AE_IFREG: h[USTAR_typeflag_offset] = '0' ; break;
+ case AE_IFLNK: h[USTAR_typeflag_offset] = '2' ; break;
+ case AE_IFCHR: h[USTAR_typeflag_offset] = '3' ; break;
+ case AE_IFBLK: h[USTAR_typeflag_offset] = '4' ; break;
+ case AE_IFDIR: h[USTAR_typeflag_offset] = '5' ; break;
+ case AE_IFIFO: h[USTAR_typeflag_offset] = '6' ; break;
+ default: /* AE_IFSOCK and unknown */
+ __archive_write_entry_filetype_unsupported(
+ &a->archive, entry, "ustar");
+ ret = ARCHIVE_FAILED;
+ }
+ }
+
+ checksum = 0;
+ for (i = 0; i < 512; i++)
+ checksum += 255 & (unsigned int)h[i];
+ h[USTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */
+ /* h[USTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */
+ format_octal(checksum, h + USTAR_checksum_offset, 6);
+ return (ret);
+}
+
+/*
+ * Format a number into a field, with some intelligence.
+ */
+static int
+format_number(int64_t v, char *p, int s, int maxsize, int strict)
+{
+ int64_t limit;
+
+ limit = ((int64_t)1 << (s*3));
+
+ /* "Strict" only permits octal values with proper termination. */
+ if (strict)
+ return (format_octal(v, p, s));
+
+ /*
+ * In non-strict mode, we allow the number to overwrite one or
+ * more bytes of the field termination. Even old tar
+ * implementations should be able to handle this with no
+ * problem.
+ */
+ if (v >= 0) {
+ while (s <= maxsize) {
+ if (v < limit)
+ return (format_octal(v, p, s));
+ s++;
+ limit <<= 3;
+ }
+ }
+
+ /* Base-256 can handle any number, positive or negative. */
+ return (format_256(v, p, maxsize));
+}
+
+/*
+ * Format a number into the specified field using base-256.
+ */
+static int
+format_256(int64_t v, char *p, int s)
+{
+ p += s;
+ while (s-- > 0) {
+ *--p = (char)(v & 0xff);
+ v >>= 8;
+ }
+ *p |= 0x80; /* Set the base-256 marker bit. */
+ return (0);
+}
+
+/*
+ * Format a number into the specified field.
+ */
+static int
+format_octal(int64_t v, char *p, int s)
+{
+ int len;
+
+ len = s;
+
+ /* Octal values can't be negative, so use 0. */
+ if (v < 0) {
+ while (len-- > 0)
+ *p++ = '0';
+ return (-1);
+ }
+
+ p += s; /* Start at the end and work backwards. */
+ while (s-- > 0) {
+ *--p = (char)('0' + (v & 7));
+ v >>= 3;
+ }
+
+ if (v == 0)
+ return (0);
+
+ /* If it overflowed, fill field with max value. */
+ while (len-- > 0)
+ *p++ = '7';
+
+ return (-1);
+}
+
+static int
+archive_write_ustar_close(struct archive_write *a)
+{
+ return (__archive_write_nulls(a, 512*2));
+}
+
+static int
+archive_write_ustar_free(struct archive_write *a)
+{
+ struct ustar *ustar;
+
+ ustar = (struct ustar *)a->format_data;
+ free(ustar);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_ustar_finish_entry(struct archive_write *a)
+{
+ struct ustar *ustar;
+ int ret;
+
+ ustar = (struct ustar *)a->format_data;
+ ret = __archive_write_nulls(a,
+ (size_t)(ustar->entry_bytes_remaining + ustar->entry_padding));
+ ustar->entry_bytes_remaining = ustar->entry_padding = 0;
+ return (ret);
+}
+
+static ssize_t
+archive_write_ustar_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct ustar *ustar;
+ int ret;
+
+ ustar = (struct ustar *)a->format_data;
+ if (s > ustar->entry_bytes_remaining)
+ s = (size_t)ustar->entry_bytes_remaining;
+ ret = __archive_write_output(a, buff, s);
+ ustar->entry_bytes_remaining -= s;
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ return (s);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_v7tar.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_v7tar.c
new file mode 100644
index 00000000000..59940714412
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_v7tar.c
@@ -0,0 +1,638 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct v7tar {
+ uint64_t entry_bytes_remaining;
+ uint64_t entry_padding;
+
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_default;
+ int init_default_conversion;
+};
+
+/*
+ * Define structure of POSIX 'v7tar' tar header.
+ */
+#define V7TAR_name_offset 0
+#define V7TAR_name_size 100
+#define V7TAR_mode_offset 100
+#define V7TAR_mode_size 6
+#define V7TAR_mode_max_size 8
+#define V7TAR_uid_offset 108
+#define V7TAR_uid_size 6
+#define V7TAR_uid_max_size 8
+#define V7TAR_gid_offset 116
+#define V7TAR_gid_size 6
+#define V7TAR_gid_max_size 8
+#define V7TAR_size_offset 124
+#define V7TAR_size_size 11
+#define V7TAR_size_max_size 12
+#define V7TAR_mtime_offset 136
+#define V7TAR_mtime_size 11
+#define V7TAR_mtime_max_size 12
+#define V7TAR_checksum_offset 148
+#define V7TAR_checksum_size 8
+#define V7TAR_typeflag_offset 156
+#define V7TAR_typeflag_size 1
+#define V7TAR_linkname_offset 157
+#define V7TAR_linkname_size 100
+#define V7TAR_padding_offset 257
+#define V7TAR_padding_size 255
+
+/*
+ * A filled-in copy of the header for initialization.
+ */
+static const char template_header[] = {
+ /* name: 100 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,
+ /* Mode, space-null termination: 8 bytes */
+ '0','0','0','0','0','0', ' ','\0',
+ /* uid, space-null termination: 8 bytes */
+ '0','0','0','0','0','0', ' ','\0',
+ /* gid, space-null termination: 8 bytes */
+ '0','0','0','0','0','0', ' ','\0',
+ /* size, space termination: 12 bytes */
+ '0','0','0','0','0','0','0','0','0','0','0', ' ',
+ /* mtime, space termination: 12 bytes */
+ '0','0','0','0','0','0','0','0','0','0','0', ' ',
+ /* Initial checksum value: 8 spaces */
+ ' ',' ',' ',' ',' ',' ',' ',' ',
+ /* Typeflag: 1 byte */
+ 0,
+ /* Linkname: 100 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,
+ /* Padding: 255 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0
+};
+
+static ssize_t archive_write_v7tar_data(struct archive_write *a, const void *buff,
+ size_t s);
+static int archive_write_v7tar_free(struct archive_write *);
+static int archive_write_v7tar_close(struct archive_write *);
+static int archive_write_v7tar_finish_entry(struct archive_write *);
+static int archive_write_v7tar_header(struct archive_write *,
+ struct archive_entry *entry);
+static int archive_write_v7tar_options(struct archive_write *,
+ const char *, const char *);
+static int format_256(int64_t, char *, int);
+static int format_number(int64_t, char *, int size, int max, int strict);
+static int format_octal(int64_t, char *, int);
+static int format_header_v7tar(struct archive_write *, char h[512],
+ struct archive_entry *, int, struct archive_string_conv *);
+
+/*
+ * Set output format to 'v7tar' format.
+ */
+int
+archive_write_set_format_v7tar(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct v7tar *v7tar;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_v7tar");
+
+ /* If someone else was already registered, unregister them. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ /* Basic internal sanity test. */
+ if (sizeof(template_header) != 512) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Internal: template_header wrong size: %zu should be 512",
+ sizeof(template_header));
+ return (ARCHIVE_FATAL);
+ }
+
+ v7tar = (struct v7tar *)calloc(1, sizeof(*v7tar));
+ if (v7tar == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate v7tar data");
+ return (ARCHIVE_FATAL);
+ }
+ a->format_data = v7tar;
+ a->format_name = "tar (non-POSIX)";
+ a->format_options = archive_write_v7tar_options;
+ a->format_write_header = archive_write_v7tar_header;
+ a->format_write_data = archive_write_v7tar_data;
+ a->format_close = archive_write_v7tar_close;
+ a->format_free = archive_write_v7tar_free;
+ a->format_finish_entry = archive_write_v7tar_finish_entry;
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR;
+ a->archive.archive_format_name = "tar (non-POSIX)";
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_v7tar_options(struct archive_write *a, const char *key,
+ const char *val)
+{
+ struct v7tar *v7tar = (struct v7tar *)a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: hdrcharset option needs a character-set name",
+ a->format_name);
+ else {
+ v7tar->opt_sconv = archive_string_conversion_to_charset(
+ &a->archive, val, 0);
+ if (v7tar->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry)
+{
+ char buff[512];
+ int ret, ret2;
+ struct v7tar *v7tar;
+ struct archive_entry *entry_main;
+ struct archive_string_conv *sconv;
+
+ v7tar = (struct v7tar *)a->format_data;
+
+ /* Setup default string conversion. */
+ if (v7tar->opt_sconv == NULL) {
+ if (!v7tar->init_default_conversion) {
+ v7tar->sconv_default =
+ archive_string_default_conversion_for_write(
+ &(a->archive));
+ v7tar->init_default_conversion = 1;
+ }
+ sconv = v7tar->sconv_default;
+ } else
+ sconv = v7tar->opt_sconv;
+
+ /* Sanity check. */
+ if (archive_entry_pathname(entry) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't record entry in tar file without pathname");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Only regular files (not hardlinks) have data. */
+ if (archive_entry_hardlink(entry) != NULL ||
+ archive_entry_symlink(entry) != NULL ||
+ !(archive_entry_filetype(entry) == AE_IFREG))
+ archive_entry_set_size(entry, 0);
+
+ if (AE_IFDIR == archive_entry_filetype(entry)) {
+ const char *p;
+ size_t path_length;
+ /*
+ * Ensure a trailing '/'. Modify the entry so
+ * the client sees the change.
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ const wchar_t *wp;
+
+ wp = archive_entry_pathname_w(entry);
+ if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
+ struct archive_wstring ws;
+
+ archive_string_init(&ws);
+ path_length = wcslen(wp);
+ if (archive_wstring_ensure(&ws,
+ path_length + 2) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate v7tar data");
+ archive_wstring_free(&ws);
+ return(ARCHIVE_FATAL);
+ }
+ /* Should we keep '\' ? */
+ if (wp[path_length -1] == L'\\')
+ path_length--;
+ archive_wstrncpy(&ws, wp, path_length);
+ archive_wstrappend_wchar(&ws, L'/');
+ archive_entry_copy_pathname_w(entry, ws.s);
+ archive_wstring_free(&ws);
+ p = NULL;
+ } else
+#endif
+ p = archive_entry_pathname(entry);
+ /*
+ * On Windows, this is a backup operation just in
+ * case getting WCS failed. On POSIX, this is a
+ * normal operation.
+ */
+ if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {
+ struct archive_string as;
+
+ archive_string_init(&as);
+ path_length = strlen(p);
+ if (archive_string_ensure(&as,
+ path_length + 2) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate v7tar data");
+ archive_string_free(&as);
+ return(ARCHIVE_FATAL);
+ }
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* NOTE: This might break the pathname
+ * if the current code page is CP932 and
+ * the pathname includes a character '\'
+ * as a part of its multibyte pathname. */
+ if (p[strlen(p) -1] == '\\')
+ path_length--;
+ else
+#endif
+ archive_strncpy(&as, p, path_length);
+ archive_strappend_char(&as, '/');
+ archive_entry_copy_pathname(entry, as.s);
+ archive_string_free(&as);
+ }
+ }
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Make sure the path separators in pathname, hardlink and symlink
+ * are all slash '/', not the Windows path separator '\'. */
+ entry_main = __la_win_entry_in_posix_pathseparator(entry);
+ if (entry_main == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate v7tar data");
+ return(ARCHIVE_FATAL);
+ }
+ if (entry != entry_main)
+ entry = entry_main;
+ else
+ entry_main = NULL;
+#else
+ entry_main = NULL;
+#endif
+ ret = format_header_v7tar(a, buff, entry, 1, sconv);
+ if (ret < ARCHIVE_WARN) {
+ archive_entry_free(entry_main);
+ return (ret);
+ }
+ ret2 = __archive_write_output(a, buff, 512);
+ if (ret2 < ARCHIVE_WARN) {
+ archive_entry_free(entry_main);
+ return (ret2);
+ }
+ if (ret2 < ret)
+ ret = ret2;
+
+ v7tar->entry_bytes_remaining = archive_entry_size(entry);
+ v7tar->entry_padding = 0x1ff & (-(int64_t)v7tar->entry_bytes_remaining);
+ archive_entry_free(entry_main);
+ return (ret);
+}
+
+/*
+ * Format a basic 512-byte "v7tar" header.
+ *
+ * Returns -1 if format failed (due to field overflow).
+ * Note that this always formats as much of the header as possible.
+ * If "strict" is set to zero, it will extend numeric fields as
+ * necessary (overwriting terminators or using base-256 extensions).
+ *
+ */
+static int
+format_header_v7tar(struct archive_write *a, char h[512],
+ struct archive_entry *entry, int strict,
+ struct archive_string_conv *sconv)
+{
+ unsigned int checksum;
+ int i, r, ret;
+ size_t copy_length;
+ const char *p, *pp;
+ int mytartype;
+
+ ret = 0;
+ mytartype = -1;
+ /*
+ * The "template header" already includes the "v7tar"
+ * signature, various end-of-field markers and other required
+ * elements.
+ */
+ memcpy(h, &template_header, 512);
+
+ /*
+ * Because the block is already null-filled, and strings
+ * are allowed to exactly fill their destination (without null),
+ * I use memcpy(dest, src, strlen()) here a lot to copy strings.
+ */
+ r = archive_entry_pathname_l(entry, &pp, &copy_length, sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s",
+ pp, archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ if (strict && copy_length < V7TAR_name_size)
+ memcpy(h + V7TAR_name_offset, pp, copy_length);
+ else if (!strict && copy_length <= V7TAR_name_size)
+ memcpy(h + V7TAR_name_offset, pp, copy_length);
+ else {
+ /* Prefix is too long. */
+ archive_set_error(&a->archive, ENAMETOOLONG,
+ "Pathname too long");
+ ret = ARCHIVE_FAILED;
+ }
+
+ r = archive_entry_hardlink_l(entry, &p, &copy_length, sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ p, archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ if (copy_length > 0)
+ mytartype = '1';
+ else {
+ r = archive_entry_symlink_l(entry, &p, &copy_length, sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ p, archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ }
+ if (copy_length > 0) {
+ if (copy_length >= V7TAR_linkname_size) {
+ archive_set_error(&a->archive, ENAMETOOLONG,
+ "Link contents too long");
+ ret = ARCHIVE_FAILED;
+ copy_length = V7TAR_linkname_size;
+ }
+ memcpy(h + V7TAR_linkname_offset, p, copy_length);
+ }
+
+ if (format_number(archive_entry_mode(entry) & 07777,
+ h + V7TAR_mode_offset, V7TAR_mode_size,
+ V7TAR_mode_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric mode too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_uid(entry),
+ h + V7TAR_uid_offset, V7TAR_uid_size, V7TAR_uid_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric user ID too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_gid(entry),
+ h + V7TAR_gid_offset, V7TAR_gid_size, V7TAR_gid_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric group ID too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_size(entry),
+ h + V7TAR_size_offset, V7TAR_size_size,
+ V7TAR_size_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File size out of range");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_mtime(entry),
+ h + V7TAR_mtime_offset, V7TAR_mtime_size,
+ V7TAR_mtime_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File modification time too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (mytartype >= 0) {
+ h[V7TAR_typeflag_offset] = mytartype;
+ } else {
+ switch (archive_entry_filetype(entry)) {
+ case AE_IFREG: case AE_IFDIR:
+ break;
+ case AE_IFLNK:
+ h[V7TAR_typeflag_offset] = '2';
+ break;
+ default:
+ /* AE_IFBLK, AE_IFCHR, AE_IFIFO, AE_IFSOCK
+ * and unknown */
+ __archive_write_entry_filetype_unsupported(
+ &a->archive, entry, "v7tar");
+ ret = ARCHIVE_FAILED;
+ }
+ }
+
+ checksum = 0;
+ for (i = 0; i < 512; i++)
+ checksum += 255 & (unsigned int)h[i];
+ format_octal(checksum, h + V7TAR_checksum_offset, 6);
+ /* Can't be pre-set in the template. */
+ h[V7TAR_checksum_offset + 6] = '\0';
+ return (ret);
+}
+
+/*
+ * Format a number into a field, with some intelligence.
+ */
+static int
+format_number(int64_t v, char *p, int s, int maxsize, int strict)
+{
+ int64_t limit;
+
+ limit = ((int64_t)1 << (s*3));
+
+ /* "Strict" only permits octal values with proper termination. */
+ if (strict)
+ return (format_octal(v, p, s));
+
+ /*
+ * In non-strict mode, we allow the number to overwrite one or
+ * more bytes of the field termination. Even old tar
+ * implementations should be able to handle this with no
+ * problem.
+ */
+ if (v >= 0) {
+ while (s <= maxsize) {
+ if (v < limit)
+ return (format_octal(v, p, s));
+ s++;
+ limit <<= 3;
+ }
+ }
+
+ /* Base-256 can handle any number, positive or negative. */
+ return (format_256(v, p, maxsize));
+}
+
+/*
+ * Format a number into the specified field using base-256.
+ */
+static int
+format_256(int64_t v, char *p, int s)
+{
+ p += s;
+ while (s-- > 0) {
+ *--p = (char)(v & 0xff);
+ v >>= 8;
+ }
+ *p |= 0x80; /* Set the base-256 marker bit. */
+ return (0);
+}
+
+/*
+ * Format a number into the specified field.
+ */
+static int
+format_octal(int64_t v, char *p, int s)
+{
+ int len;
+
+ len = s;
+
+ /* Octal values can't be negative, so use 0. */
+ if (v < 0) {
+ while (len-- > 0)
+ *p++ = '0';
+ return (-1);
+ }
+
+ p += s; /* Start at the end and work backwards. */
+ while (s-- > 0) {
+ *--p = (char)('0' + (v & 7));
+ v >>= 3;
+ }
+
+ if (v == 0)
+ return (0);
+
+ /* If it overflowed, fill field with max value. */
+ while (len-- > 0)
+ *p++ = '7';
+
+ return (-1);
+}
+
+static int
+archive_write_v7tar_close(struct archive_write *a)
+{
+ return (__archive_write_nulls(a, 512*2));
+}
+
+static int
+archive_write_v7tar_free(struct archive_write *a)
+{
+ struct v7tar *v7tar;
+
+ v7tar = (struct v7tar *)a->format_data;
+ free(v7tar);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_v7tar_finish_entry(struct archive_write *a)
+{
+ struct v7tar *v7tar;
+ int ret;
+
+ v7tar = (struct v7tar *)a->format_data;
+ ret = __archive_write_nulls(a,
+ (size_t)(v7tar->entry_bytes_remaining + v7tar->entry_padding));
+ v7tar->entry_bytes_remaining = v7tar->entry_padding = 0;
+ return (ret);
+}
+
+static ssize_t
+archive_write_v7tar_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct v7tar *v7tar;
+ int ret;
+
+ v7tar = (struct v7tar *)a->format_data;
+ if (s > v7tar->entry_bytes_remaining)
+ s = (size_t)v7tar->entry_bytes_remaining;
+ ret = __archive_write_output(a, buff, s);
+ v7tar->entry_bytes_remaining -= s;
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ return (s);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_warc.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_warc.c
new file mode 100644
index 00000000000..0ef003e2fc9
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_warc.c
@@ -0,0 +1,444 @@
+/*-
+ * Copyright (c) 2014 Sebastian Freundt
+ * Author: Sebastian Freundt <devel@fresse.org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_random_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct warc_s {
+ unsigned int omit_warcinfo:1;
+
+ time_t now;
+ mode_t typ;
+ unsigned int rng;
+ /* populated size */
+ uint64_t populz;
+};
+
+static const char warcinfo[] =
+ "software: libarchive/" ARCHIVE_VERSION_ONLY_STRING "\r\n"
+ "format: WARC file version 1.0\r\n";
+
+typedef enum {
+ WT_NONE,
+ /* warcinfo */
+ WT_INFO,
+ /* metadata */
+ WT_META,
+ /* resource */
+ WT_RSRC,
+ /* request, unsupported */
+ WT_REQ,
+ /* response, unsupported */
+ WT_RSP,
+ /* revisit, unsupported */
+ WT_RVIS,
+ /* conversion, unsupported */
+ WT_CONV,
+ /* continuation, unsupported at the moment */
+ WT_CONT,
+ /* invalid type */
+ LAST_WT
+} warc_type_t;
+
+typedef struct {
+ warc_type_t type;
+ const char *tgturi;
+ const char *recid;
+ time_t rtime;
+ time_t mtime;
+ const char *cnttyp;
+ uint64_t cntlen;
+} warc_essential_hdr_t;
+
+typedef struct {
+ unsigned int u[4U];
+} warc_uuid_t;
+
+static int _warc_options(struct archive_write*, const char *key, const char *v);
+static int _warc_header(struct archive_write *a, struct archive_entry *entry);
+static ssize_t _warc_data(struct archive_write *a, const void *buf, size_t sz);
+static int _warc_finish_entry(struct archive_write *a);
+static int _warc_close(struct archive_write *a);
+static int _warc_free(struct archive_write *a);
+
+/* private routines */
+static ssize_t _popul_ehdr(struct archive_string *t, size_t z, warc_essential_hdr_t);
+static int _gen_uuid(warc_uuid_t *tgt);
+
+
+/*
+ * Set output format to ISO 28500 (aka WARC) format.
+ */
+int
+archive_write_set_format_warc(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct warc_s *w;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_warc");
+
+ /* If another format was already registered, unregister it. */
+ if (a->format_free != NULL) {
+ (a->format_free)(a);
+ }
+
+ w = malloc(sizeof(*w));
+ if (w == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate warc data");
+ return (ARCHIVE_FATAL);
+ }
+ /* by default we're emitting a file wide header */
+ w->omit_warcinfo = 0U;
+ /* obtain current time for date fields */
+ w->now = time(NULL);
+ /* reset file type info */
+ w->typ = 0;
+ /* also initialise our rng */
+ w->rng = (unsigned int)w->now;
+
+ a->format_data = w;
+ a->format_name = "WARC/1.0";
+ a->format_options = _warc_options;
+ a->format_write_header = _warc_header;
+ a->format_write_data = _warc_data;
+ a->format_close = _warc_close;
+ a->format_free = _warc_free;
+ a->format_finish_entry = _warc_finish_entry;
+ a->archive.archive_format = ARCHIVE_FORMAT_WARC;
+ a->archive.archive_format_name = "WARC/1.0";
+ return (ARCHIVE_OK);
+}
+
+
+/* archive methods */
+static int
+_warc_options(struct archive_write *a, const char *key, const char *val)
+{
+ struct warc_s *w = a->format_data;
+
+ if (strcmp(key, "omit-warcinfo") == 0) {
+ if (val == NULL || strcmp(val, "true") == 0) {
+ /* great */
+ w->omit_warcinfo = 1U;
+ return (ARCHIVE_OK);
+ }
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+_warc_header(struct archive_write *a, struct archive_entry *entry)
+{
+ struct warc_s *w = a->format_data;
+ struct archive_string hdr;
+#define MAX_HDR_SIZE 512
+
+ /* check whether warcinfo record needs outputting */
+ if (!w->omit_warcinfo) {
+ ssize_t r;
+ warc_essential_hdr_t wi = {
+ WT_INFO,
+ /*uri*/NULL,
+ /*urn*/NULL,
+ /*rtm*/0,
+ /*mtm*/0,
+ /*cty*/"application/warc-fields",
+ /*len*/sizeof(warcinfo) - 1U,
+ };
+ wi.rtime = w->now;
+ wi.mtime = w->now;
+
+ archive_string_init(&hdr);
+ r = _popul_ehdr(&hdr, MAX_HDR_SIZE, wi);
+ if (r >= 0) {
+ /* jackpot! */
+ /* now also use HDR buffer for the actual warcinfo */
+ archive_strncat(&hdr, warcinfo, sizeof(warcinfo) -1);
+
+ /* append end-of-record indicator */
+ archive_strncat(&hdr, "\r\n\r\n", 4);
+
+ /* write to output stream */
+ __archive_write_output(a, hdr.s, archive_strlen(&hdr));
+ }
+ /* indicate we're done with file header writing */
+ w->omit_warcinfo = 1U;
+ archive_string_free(&hdr);
+ }
+
+ if (archive_entry_pathname(entry) == NULL) {
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid filename");
+ return (ARCHIVE_WARN);
+ }
+
+ w->typ = archive_entry_filetype(entry);
+ w->populz = 0U;
+ if (w->typ == AE_IFREG) {
+ warc_essential_hdr_t rh = {
+ WT_RSRC,
+ /*uri*/NULL,
+ /*urn*/NULL,
+ /*rtm*/0,
+ /*mtm*/0,
+ /*cty*/NULL,
+ /*len*/0,
+ };
+ ssize_t r;
+ rh.tgturi = archive_entry_pathname(entry);
+ rh.rtime = w->now;
+ rh.mtime = archive_entry_mtime(entry);
+ rh.cntlen = (size_t)archive_entry_size(entry);
+
+ archive_string_init(&hdr);
+ r = _popul_ehdr(&hdr, MAX_HDR_SIZE, rh);
+ if (r < 0) {
+ /* don't bother */
+ archive_set_error(
+ &a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "cannot archive file");
+ return (ARCHIVE_WARN);
+ }
+ /* otherwise append to output stream */
+ __archive_write_output(a, hdr.s, r);
+ /* and let subsequent calls to _data() know about the size */
+ w->populz = rh.cntlen;
+ archive_string_free(&hdr);
+ return (ARCHIVE_OK);
+ }
+ /* just resort to erroring as per Tim's advice */
+ __archive_write_entry_filetype_unsupported(
+ &a->archive, entry, "WARC");
+ return (ARCHIVE_FAILED);
+}
+
+static ssize_t
+_warc_data(struct archive_write *a, const void *buf, size_t len)
+{
+ struct warc_s *w = a->format_data;
+
+ if (w->typ == AE_IFREG) {
+ int rc;
+
+ /* never write more bytes than announced */
+ if (len > w->populz) {
+ len = (size_t)w->populz;
+ }
+
+ /* now then, out we put the whole shebang */
+ rc = __archive_write_output(a, buf, len);
+ if (rc != ARCHIVE_OK) {
+ return rc;
+ }
+ }
+ return len;
+}
+
+static int
+_warc_finish_entry(struct archive_write *a)
+{
+ static const char _eor[] = "\r\n\r\n";
+ struct warc_s *w = a->format_data;
+
+ if (w->typ == AE_IFREG) {
+ int rc = __archive_write_output(a, _eor, sizeof(_eor) - 1U);
+
+ if (rc != ARCHIVE_OK) {
+ return rc;
+ }
+ }
+ /* reset type info */
+ w->typ = 0;
+ return (ARCHIVE_OK);
+}
+
+static int
+_warc_close(struct archive_write *a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+static int
+_warc_free(struct archive_write *a)
+{
+ struct warc_s *w = a->format_data;
+
+ free(w);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+
+/* private routines */
+static void
+xstrftime(struct archive_string *as, const char *fmt, time_t t)
+{
+/** like strftime(3) but for time_t objects */
+ struct tm *rt;
+#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
+ struct tm timeHere;
+#endif
+ char strtime[100];
+ size_t len;
+
+#if defined(HAVE_GMTIME_S)
+ rt = gmtime_s(&timeHere, &t) ? NULL : &timeHere;
+#elif defined(HAVE_GMTIME_R)
+ rt = gmtime_r(&t, &timeHere);
+#else
+ rt = gmtime(&t);
+#endif
+ if (!rt)
+ return;
+ /* leave the hard yacker to our role model strftime() */
+ len = strftime(strtime, sizeof(strtime)-1, fmt, rt);
+ archive_strncat(as, strtime, len);
+}
+
+static ssize_t
+_popul_ehdr(struct archive_string *tgt, size_t tsz, warc_essential_hdr_t hdr)
+{
+ static const char _ver[] = "WARC/1.0\r\n";
+ static const char * const _typ[LAST_WT] = {
+ NULL, "warcinfo", "metadata", "resource", NULL
+ };
+ char std_uuid[48U];
+
+ if (hdr.type == WT_NONE || hdr.type > WT_RSRC) {
+ /* brilliant, how exactly did we get here? */
+ return -1;
+ }
+
+ archive_strcpy(tgt, _ver);
+
+ archive_string_sprintf(tgt, "WARC-Type: %s\r\n", _typ[hdr.type]);
+
+ if (hdr.tgturi != NULL) {
+ /* check if there's a xyz:// */
+ static const char _uri[] = "";
+ static const char _fil[] = "file://";
+ const char *u;
+ char *chk = strchr(hdr.tgturi, ':');
+
+ if (chk != NULL && chk[1U] == '/' && chk[2U] == '/') {
+ /* yep, it's definitely a URI */
+ u = _uri;
+ } else {
+ /* hm, best to prepend file:// then */
+ u = _fil;
+ }
+ archive_string_sprintf(tgt,
+ "WARC-Target-URI: %s%s\r\n", u, hdr.tgturi);
+ }
+
+ /* record time is usually when the http is sent off,
+ * just treat the archive writing as such for a moment */
+ xstrftime(tgt, "WARC-Date: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.rtime);
+
+ /* while we're at it, record the mtime */
+ xstrftime(tgt, "Last-Modified: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.mtime);
+
+ if (hdr.recid == NULL) {
+ /* generate one, grrrr */
+ warc_uuid_t u;
+
+ _gen_uuid(&u);
+ /* Unfortunately, archive_string_sprintf does not
+ * handle the minimum number following '%'.
+ * So we have to use snprintf function here instead
+ * of archive_string_snprintf function. */
+#if defined(_WIN32) && !defined(__CYGWIN__) && !( defined(_MSC_VER) && _MSC_VER >= 1900)
+#define snprintf _snprintf
+#endif
+ snprintf(
+ std_uuid, sizeof(std_uuid),
+ "<urn:uuid:%08x-%04x-%04x-%04x-%04x%08x>",
+ u.u[0U],
+ u.u[1U] >> 16U, u.u[1U] & 0xffffU,
+ u.u[2U] >> 16U, u.u[2U] & 0xffffU,
+ u.u[3U]);
+ hdr.recid = std_uuid;
+ }
+
+ /* record-id is mandatory, fingers crossed we won't fail */
+ archive_string_sprintf(tgt, "WARC-Record-ID: %s\r\n", hdr.recid);
+
+ if (hdr.cnttyp != NULL) {
+ archive_string_sprintf(tgt, "Content-Type: %s\r\n", hdr.cnttyp);
+ }
+
+ /* next one is mandatory */
+ archive_string_sprintf(tgt, "Content-Length: %ju\r\n", (uintmax_t)hdr.cntlen);
+ /**/
+ archive_strncat(tgt, "\r\n", 2);
+
+ return (archive_strlen(tgt) >= tsz)? -1: (ssize_t)archive_strlen(tgt);
+}
+
+static int
+_gen_uuid(warc_uuid_t *tgt)
+{
+ archive_random(tgt->u, sizeof(tgt->u));
+ /* obey uuid version 4 rules */
+ tgt->u[1U] &= 0xffff0fffU;
+ tgt->u[1U] |= 0x4000U;
+ tgt->u[2U] &= 0x3fffffffU;
+ tgt->u[2U] |= 0x80000000U;
+ return 0;
+}
+
+/* archive_write_set_format_warc.c ends here */
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_xar.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_xar.c
new file mode 100644
index 00000000000..c3acf86b576
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_xar.c
@@ -0,0 +1,3255 @@
+/*-
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#include <stdlib.h>
+#if HAVE_LIBXML_XMLWRITER_H
+#error #include <libxml/xmlwriter.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#if HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_digest_private.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+/*
+ * Differences to xar utility.
+ * - Subdocument is not supported yet.
+ * - ACL is not supported yet.
+ * - When writing an XML element <link type="<file-type>">, <file-type>
+ * which is a file type a symbolic link is referencing is always marked
+ * as "broken". Xar utility uses stat(2) to get the file type, but, in
+ * libarchive format writer, we should not use it; if it is needed, we
+ * should get about it at archive_read_disk.c.
+ * - It is possible to appear both <flags> and <ext2> elements.
+ * Xar utility generates <flags> on BSD platform and <ext2> on Linux
+ * platform.
+ *
+ */
+
+#if !(defined(HAVE_LIBXML_XMLWRITER_H) && defined(LIBXML_VERSION) &&\
+ LIBXML_VERSION >= 20703) ||\
+ !defined(HAVE_ZLIB_H) || \
+ !defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1)
+/*
+ * xar needs several external libraries.
+ * o libxml2
+ * o openssl or MD5/SHA1 hash function
+ * o zlib
+ * o bzlib2 (option)
+ * o liblzma (option)
+ */
+int
+archive_write_set_format_xar(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Xar not supported on this platform");
+ return (ARCHIVE_WARN);
+}
+
+#else /* Support xar format */
+
+/*#define DEBUG_PRINT_TOC 1 */
+
+#define BAD_CAST_CONST (const xmlChar *)
+
+#define HEADER_MAGIC 0x78617221
+#define HEADER_SIZE 28
+#define HEADER_VERSION 1
+
+enum sumalg {
+ CKSUM_NONE = 0,
+ CKSUM_SHA1 = 1,
+ CKSUM_MD5 = 2
+};
+
+#define MD5_SIZE 16
+#define SHA1_SIZE 20
+#define MAX_SUM_SIZE 20
+#define MD5_NAME "md5"
+#define SHA1_NAME "sha1"
+
+enum enctype {
+ NONE,
+ GZIP,
+ BZIP2,
+ LZMA,
+ XZ,
+};
+
+struct chksumwork {
+ enum sumalg alg;
+#ifdef ARCHIVE_HAS_MD5
+ archive_md5_ctx md5ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+ archive_sha1_ctx sha1ctx;
+#endif
+};
+
+enum la_zaction {
+ ARCHIVE_Z_FINISH,
+ ARCHIVE_Z_RUN
+};
+
+/*
+ * Universal zstream.
+ */
+struct la_zstream {
+ const unsigned char *next_in;
+ size_t avail_in;
+ uint64_t total_in;
+
+ unsigned char *next_out;
+ size_t avail_out;
+ uint64_t total_out;
+
+ int valid;
+ void *real_stream;
+ int (*code) (struct archive *a,
+ struct la_zstream *lastrm,
+ enum la_zaction action);
+ int (*end)(struct archive *a,
+ struct la_zstream *lastrm);
+};
+
+struct chksumval {
+ enum sumalg alg;
+ size_t len;
+ unsigned char val[MAX_SUM_SIZE];
+};
+
+struct heap_data {
+ int id;
+ struct heap_data *next;
+ uint64_t temp_offset;
+ uint64_t length; /* archived size. */
+ uint64_t size; /* extracted size. */
+ enum enctype compression;
+ struct chksumval a_sum; /* archived checksum. */
+ struct chksumval e_sum; /* extracted checksum. */
+};
+
+struct file {
+ struct archive_rb_node rbnode;
+
+ int id;
+ struct archive_entry *entry;
+
+ struct archive_rb_tree rbtree;
+ struct file *next;
+ struct file *chnext;
+ struct file *hlnext;
+ /* For hardlinked files.
+ * Use only when archive_entry_nlink() > 1 */
+ struct file *hardlink_target;
+ struct file *parent; /* parent directory entry */
+ /*
+ * To manage sub directory files.
+ * We use 'chnext' (a member of struct file) to chain.
+ */
+ struct {
+ struct file *first;
+ struct file **last;
+ } children;
+
+ /* For making a directory tree. */
+ struct archive_string parentdir;
+ struct archive_string basename;
+ struct archive_string symlink;
+
+ int ea_idx;
+ struct {
+ struct heap_data *first;
+ struct heap_data **last;
+ } xattr;
+ struct heap_data data;
+ struct archive_string script;
+
+ unsigned int virtual:1;
+ unsigned int dir:1;
+};
+
+struct hardlink {
+ struct archive_rb_node rbnode;
+ int nlink;
+ struct {
+ struct file *first;
+ struct file **last;
+ } file_list;
+};
+
+struct xar {
+ int temp_fd;
+ uint64_t temp_offset;
+
+ int file_idx;
+ struct file *root;
+ struct file *cur_dirent;
+ struct archive_string cur_dirstr;
+ struct file *cur_file;
+ uint64_t bytes_remaining;
+ struct archive_string tstr;
+ struct archive_string vstr;
+
+ enum sumalg opt_toc_sumalg;
+ enum sumalg opt_sumalg;
+ enum enctype opt_compression;
+ int opt_compression_level;
+ uint32_t opt_threads;
+
+ struct chksumwork a_sumwrk; /* archived checksum. */
+ struct chksumwork e_sumwrk; /* extracted checksum. */
+ struct la_zstream stream;
+ struct archive_string_conv *sconv;
+ /*
+ * Compressed data buffer.
+ */
+ unsigned char wbuff[1024 * 64];
+ size_t wbuff_remaining;
+
+ struct heap_data toc;
+ /*
+ * The list of all file entries is used to manage struct file
+ * objects.
+ * We use 'next' (a member of struct file) to chain.
+ */
+ struct {
+ struct file *first;
+ struct file **last;
+ } file_list;
+ /*
+ * The list of hard-linked file entries.
+ * We use 'hlnext' (a member of struct file) to chain.
+ */
+ struct archive_rb_tree hardlink_rbtree;
+};
+
+static int xar_options(struct archive_write *,
+ const char *, const char *);
+static int xar_write_header(struct archive_write *,
+ struct archive_entry *);
+static ssize_t xar_write_data(struct archive_write *,
+ const void *, size_t);
+static int xar_finish_entry(struct archive_write *);
+static int xar_close(struct archive_write *);
+static int xar_free(struct archive_write *);
+
+static struct file *file_new(struct archive_write *a, struct archive_entry *);
+static void file_free(struct file *);
+static struct file *file_create_virtual_dir(struct archive_write *a, struct xar *,
+ const char *);
+static int file_add_child_tail(struct file *, struct file *);
+static struct file *file_find_child(struct file *, const char *);
+static int file_gen_utility_names(struct archive_write *,
+ struct file *);
+static int get_path_component(char *, int, const char *);
+static int file_tree(struct archive_write *, struct file **);
+static void file_register(struct xar *, struct file *);
+static void file_init_register(struct xar *);
+static void file_free_register(struct xar *);
+static int file_register_hardlink(struct archive_write *,
+ struct file *);
+static void file_connect_hardlink_files(struct xar *);
+static void file_init_hardlinks(struct xar *);
+static void file_free_hardlinks(struct xar *);
+
+static void checksum_init(struct chksumwork *, enum sumalg);
+static void checksum_update(struct chksumwork *, const void *, size_t);
+static void checksum_final(struct chksumwork *, struct chksumval *);
+static int compression_init_encoder_gzip(struct archive *,
+ struct la_zstream *, int, int);
+static int compression_code_gzip(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end_gzip(struct archive *, struct la_zstream *);
+static int compression_init_encoder_bzip2(struct archive *,
+ struct la_zstream *, int);
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+static int compression_code_bzip2(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end_bzip2(struct archive *, struct la_zstream *);
+#endif
+static int compression_init_encoder_lzma(struct archive *,
+ struct la_zstream *, int);
+static int compression_init_encoder_xz(struct archive *,
+ struct la_zstream *, int, int);
+#if defined(HAVE_LZMA_H)
+static int compression_code_lzma(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end_lzma(struct archive *, struct la_zstream *);
+#endif
+static int xar_compression_init_encoder(struct archive_write *);
+static int compression_code(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end(struct archive *,
+ struct la_zstream *);
+static int save_xattrs(struct archive_write *, struct file *);
+static int getalgsize(enum sumalg);
+static const char *getalgname(enum sumalg);
+
+int
+archive_write_set_format_xar(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct xar *xar;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_xar");
+
+ /* If another format was already registered, unregister it. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ xar = calloc(1, sizeof(*xar));
+ if (xar == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate xar data");
+ return (ARCHIVE_FATAL);
+ }
+ xar->temp_fd = -1;
+ file_init_register(xar);
+ file_init_hardlinks(xar);
+ archive_string_init(&(xar->tstr));
+ archive_string_init(&(xar->vstr));
+
+ /*
+ * Create the root directory.
+ */
+ xar->root = file_create_virtual_dir(a, xar, "");
+ if (xar->root == NULL) {
+ free(xar);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate xar data");
+ return (ARCHIVE_FATAL);
+ }
+ xar->root->parent = xar->root;
+ file_register(xar, xar->root);
+ xar->cur_dirent = xar->root;
+ archive_string_init(&(xar->cur_dirstr));
+ archive_string_ensure(&(xar->cur_dirstr), 1);
+ xar->cur_dirstr.s[0] = 0;
+
+ /*
+ * Initialize option.
+ */
+ /* Set default checksum type. */
+ xar->opt_toc_sumalg = CKSUM_SHA1;
+ xar->opt_sumalg = CKSUM_SHA1;
+ /* Set default compression type, level, and number of threads. */
+ xar->opt_compression = GZIP;
+ xar->opt_compression_level = 6;
+ xar->opt_threads = 1;
+
+ a->format_data = xar;
+
+ a->format_name = "xar";
+ a->format_options = xar_options;
+ a->format_write_header = xar_write_header;
+ a->format_write_data = xar_write_data;
+ a->format_finish_entry = xar_finish_entry;
+ a->format_close = xar_close;
+ a->format_free = xar_free;
+ a->archive.archive_format = ARCHIVE_FORMAT_XAR;
+ a->archive.archive_format_name = "xar";
+
+ return (ARCHIVE_OK);
+}
+
+static int
+xar_options(struct archive_write *a, const char *key, const char *value)
+{
+ struct xar *xar;
+
+ xar = (struct xar *)a->format_data;
+
+ if (strcmp(key, "checksum") == 0) {
+ if (value == NULL)
+ xar->opt_sumalg = CKSUM_NONE;
+ else if (strcmp(value, "none") == 0)
+ xar->opt_sumalg = CKSUM_NONE;
+ else if (strcmp(value, "sha1") == 0)
+ xar->opt_sumalg = CKSUM_SHA1;
+ else if (strcmp(value, "md5") == 0)
+ xar->opt_sumalg = CKSUM_MD5;
+ else {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Unknown checksum name: `%s'",
+ value);
+ return (ARCHIVE_FAILED);
+ }
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "compression") == 0) {
+ const char *name = NULL;
+
+ if (value == NULL)
+ xar->opt_compression = NONE;
+ else if (strcmp(value, "none") == 0)
+ xar->opt_compression = NONE;
+ else if (strcmp(value, "gzip") == 0)
+ xar->opt_compression = GZIP;
+ else if (strcmp(value, "bzip2") == 0)
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ xar->opt_compression = BZIP2;
+#else
+ name = "bzip2";
+#endif
+ else if (strcmp(value, "lzma") == 0)
+#if HAVE_LZMA_H
+ xar->opt_compression = LZMA;
+#else
+ name = "lzma";
+#endif
+ else if (strcmp(value, "xz") == 0)
+#if HAVE_LZMA_H
+ xar->opt_compression = XZ;
+#else
+ name = "xz";
+#endif
+ else {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Unknown compression name: `%s'",
+ value);
+ return (ARCHIVE_FAILED);
+ }
+ if (name != NULL) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "`%s' compression not supported "
+ "on this platform",
+ name);
+ return (ARCHIVE_FAILED);
+ }
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "compression-level") == 0) {
+ if (value == NULL ||
+ !(value[0] >= '0' && value[0] <= '9') ||
+ value[1] != '\0') {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Illegal value `%s'",
+ value);
+ return (ARCHIVE_FAILED);
+ }
+ xar->opt_compression_level = value[0] - '0';
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "toc-checksum") == 0) {
+ if (value == NULL)
+ xar->opt_toc_sumalg = CKSUM_NONE;
+ else if (strcmp(value, "none") == 0)
+ xar->opt_toc_sumalg = CKSUM_NONE;
+ else if (strcmp(value, "sha1") == 0)
+ xar->opt_toc_sumalg = CKSUM_SHA1;
+ else if (strcmp(value, "md5") == 0)
+ xar->opt_toc_sumalg = CKSUM_MD5;
+ else {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Unknown checksum name: `%s'",
+ value);
+ return (ARCHIVE_FAILED);
+ }
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "threads") == 0) {
+ char *endptr;
+
+ if (value == NULL)
+ return (ARCHIVE_FAILED);
+ errno = 0;
+ xar->opt_threads = (int)strtoul(value, &endptr, 10);
+ if (errno != 0 || *endptr != '\0') {
+ xar->opt_threads = 1;
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Illegal value `%s'",
+ value);
+ return (ARCHIVE_FAILED);
+ }
+ if (xar->opt_threads == 0) {
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+ xar->opt_threads = lzma_cputhreads();
+#else
+ xar->opt_threads = 1;
+#endif
+ }
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+xar_write_header(struct archive_write *a, struct archive_entry *entry)
+{
+ struct xar *xar;
+ struct file *file;
+ struct archive_entry *file_entry;
+ int r, r2;
+
+ xar = (struct xar *)a->format_data;
+ xar->cur_file = NULL;
+ xar->bytes_remaining = 0;
+
+ if (xar->sconv == NULL) {
+ xar->sconv = archive_string_conversion_to_charset(
+ &a->archive, "UTF-8", 1);
+ if (xar->sconv == NULL)
+ return (ARCHIVE_FATAL);
+ }
+
+ file = file_new(a, entry);
+ if (file == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate data");
+ return (ARCHIVE_FATAL);
+ }
+ r2 = file_gen_utility_names(a, file);
+ if (r2 < ARCHIVE_WARN)
+ return (r2);
+
+ /*
+ * Ignore a path which looks like the top of directory name
+ * since we have already made the root directory of an Xar archive.
+ */
+ if (archive_strlen(&(file->parentdir)) == 0 &&
+ archive_strlen(&(file->basename)) == 0) {
+ file_free(file);
+ return (r2);
+ }
+
+ /* Add entry into tree */
+ file_entry = file->entry;
+ r = file_tree(a, &file);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* There is the same file in tree and
+ * the current file is older than the file in tree.
+ * So we don't need the current file data anymore. */
+ if (file->entry != file_entry)
+ return (r2);
+ if (file->id == 0)
+ file_register(xar, file);
+
+ /* A virtual file, which is a directory, does not have
+ * any contents and we won't store it into a archive
+ * file other than its name. */
+ if (file->virtual)
+ return (r2);
+
+ /*
+ * Prepare to save the contents of the file.
+ */
+ if (xar->temp_fd == -1) {
+ int algsize;
+ xar->temp_offset = 0;
+ xar->temp_fd = __archive_mktemp(NULL);
+ if (xar->temp_fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't create temporary file");
+ return (ARCHIVE_FATAL);
+ }
+ algsize = getalgsize(xar->opt_toc_sumalg);
+ if (algsize > 0) {
+ if (lseek(xar->temp_fd, algsize, SEEK_SET) < 0) {
+ archive_set_error(&(a->archive), errno,
+ "lseek failed");
+ return (ARCHIVE_FATAL);
+ }
+ xar->temp_offset = algsize;
+ }
+ }
+
+ if (archive_entry_hardlink(file->entry) == NULL) {
+ r = save_xattrs(a, file);
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Non regular files contents are unneeded to be saved to
+ * a temporary file. */
+ if (archive_entry_filetype(file->entry) != AE_IFREG)
+ return (r2);
+
+ /*
+ * Set the current file to cur_file to read its contents.
+ */
+ xar->cur_file = file;
+
+ if (archive_entry_nlink(file->entry) > 1) {
+ r = file_register_hardlink(a, file);
+ if (r != ARCHIVE_OK)
+ return (r);
+ if (archive_entry_hardlink(file->entry) != NULL) {
+ archive_entry_unset_size(file->entry);
+ return (r2);
+ }
+ }
+
+ /* Save a offset of current file in temporary file. */
+ file->data.temp_offset = xar->temp_offset;
+ file->data.size = archive_entry_size(file->entry);
+ file->data.compression = xar->opt_compression;
+ xar->bytes_remaining = archive_entry_size(file->entry);
+ checksum_init(&(xar->a_sumwrk), xar->opt_sumalg);
+ checksum_init(&(xar->e_sumwrk), xar->opt_sumalg);
+ r = xar_compression_init_encoder(a);
+
+ if (r != ARCHIVE_OK)
+ return (r);
+ else
+ return (r2);
+}
+
+static int
+write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+ struct xar *xar;
+ const unsigned char *p;
+ ssize_t ws;
+
+ xar = (struct xar *)a->format_data;
+ p = (const unsigned char *)buff;
+ while (s) {
+ ws = write(xar->temp_fd, p, s);
+ if (ws < 0) {
+ archive_set_error(&(a->archive), errno,
+ "fwrite function failed");
+ return (ARCHIVE_FATAL);
+ }
+ s -= ws;
+ p += ws;
+ xar->temp_offset += ws;
+ }
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+xar_write_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct xar *xar;
+ enum la_zaction run;
+ size_t size = 0;
+ size_t rsize;
+ int r;
+
+ xar = (struct xar *)a->format_data;
+
+ if (s > xar->bytes_remaining)
+ s = (size_t)xar->bytes_remaining;
+ if (s == 0 || xar->cur_file == NULL)
+ return (0);
+ if (xar->cur_file->data.compression == NONE) {
+ checksum_update(&(xar->e_sumwrk), buff, s);
+ checksum_update(&(xar->a_sumwrk), buff, s);
+ size = rsize = s;
+ } else {
+ xar->stream.next_in = (const unsigned char *)buff;
+ xar->stream.avail_in = s;
+ if (xar->bytes_remaining > s)
+ run = ARCHIVE_Z_RUN;
+ else
+ run = ARCHIVE_Z_FINISH;
+ /* Compress file data. */
+ for (;;) {
+ r = compression_code(&(a->archive), &(xar->stream),
+ run);
+ if (r != ARCHIVE_OK && r != ARCHIVE_EOF)
+ return (ARCHIVE_FATAL);
+ if (xar->stream.avail_out == 0 ||
+ run == ARCHIVE_Z_FINISH) {
+ size = sizeof(xar->wbuff) -
+ xar->stream.avail_out;
+ checksum_update(&(xar->a_sumwrk), xar->wbuff,
+ size);
+ xar->cur_file->data.length += size;
+ if (write_to_temp(a, xar->wbuff,
+ size) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ if (r == ARCHIVE_OK) {
+ /* Output buffer was full */
+ xar->stream.next_out = xar->wbuff;
+ xar->stream.avail_out =
+ sizeof(xar->wbuff);
+ } else {
+ /* ARCHIVE_EOF - We are done */
+ break;
+ }
+ } else {
+ /* Compressor wants more input */
+ break;
+ }
+ }
+ rsize = s - xar->stream.avail_in;
+ checksum_update(&(xar->e_sumwrk), buff, rsize);
+ }
+#if !defined(_WIN32) || defined(__CYGWIN__)
+ if (xar->bytes_remaining ==
+ (uint64_t)archive_entry_size(xar->cur_file->entry)) {
+ /*
+ * Get the path of a shell script if so.
+ */
+ const unsigned char *b = (const unsigned char *)buff;
+
+ archive_string_empty(&(xar->cur_file->script));
+ if (rsize > 2 && b[0] == '#' && b[1] == '!') {
+ size_t i, end, off;
+
+ off = 2;
+ if (b[off] == ' ')
+ off++;
+#ifdef PATH_MAX
+ if ((rsize - off) > PATH_MAX)
+ end = off + PATH_MAX;
+ else
+#endif
+ end = rsize;
+ /* Find the end of a script path. */
+ for (i = off; i < end && b[i] != '\0' &&
+ b[i] != '\n' && b[i] != '\r' &&
+ b[i] != ' ' && b[i] != '\t'; i++)
+ ;
+ archive_strncpy(&(xar->cur_file->script), b + off,
+ i - off);
+ }
+ }
+#endif
+
+ if (xar->cur_file->data.compression == NONE) {
+ if (write_to_temp(a, buff, size) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ xar->cur_file->data.length += size;
+ }
+ xar->bytes_remaining -= rsize;
+
+ return (rsize);
+}
+
+static int
+xar_finish_entry(struct archive_write *a)
+{
+ struct xar *xar;
+ struct file *file;
+ size_t s;
+ ssize_t w;
+
+ xar = (struct xar *)a->format_data;
+ if (xar->cur_file == NULL)
+ return (ARCHIVE_OK);
+
+ while (xar->bytes_remaining > 0) {
+ s = (size_t)xar->bytes_remaining;
+ if (s > a->null_length)
+ s = a->null_length;
+ w = xar_write_data(a, a->nulls, s);
+ if (w > 0)
+ xar->bytes_remaining -= w;
+ else
+ return (w);
+ }
+ file = xar->cur_file;
+ checksum_final(&(xar->e_sumwrk), &(file->data.e_sum));
+ checksum_final(&(xar->a_sumwrk), &(file->data.a_sum));
+ xar->cur_file = NULL;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+xmlwrite_string_attr(struct archive_write *a, xmlTextWriterPtr writer,
+ const char *key, const char *value,
+ const char *attrkey, const char *attrvalue)
+{
+ int r;
+
+ r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(key));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ if (attrkey != NULL && attrvalue != NULL) {
+ r = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST_CONST(attrkey), BAD_CAST_CONST(attrvalue));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteAttribute() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ }
+ if (value != NULL) {
+ r = xmlTextWriterWriteString(writer, BAD_CAST_CONST(value));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteString() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ }
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+xmlwrite_string(struct archive_write *a, xmlTextWriterPtr writer,
+ const char *key, const char *value)
+{
+ int r;
+
+ if (value == NULL)
+ return (ARCHIVE_OK);
+
+ r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(key));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ if (value != NULL) {
+ r = xmlTextWriterWriteString(writer, BAD_CAST_CONST(value));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteString() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ }
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+xmlwrite_fstring(struct archive_write *a, xmlTextWriterPtr writer,
+ const char *key, const char *fmt, ...)
+{
+ struct xar *xar;
+ va_list ap;
+
+ xar = (struct xar *)a->format_data;
+ va_start(ap, fmt);
+ archive_string_empty(&xar->vstr);
+ archive_string_vsprintf(&xar->vstr, fmt, ap);
+ va_end(ap);
+ return (xmlwrite_string(a, writer, key, xar->vstr.s));
+}
+
+static int
+xmlwrite_time(struct archive_write *a, xmlTextWriterPtr writer,
+ const char *key, time_t t, int z)
+{
+ char timestr[100];
+ struct tm tm;
+
+#if defined(HAVE_GMTIME_S)
+ gmtime_s(&tm, &t);
+#elif defined(HAVE_GMTIME_R)
+ gmtime_r(&t, &tm);
+#else
+ memcpy(&tm, gmtime(&t), sizeof(tm));
+#endif
+ memset(&timestr, 0, sizeof(timestr));
+ /* Do not use %F and %T for portability. */
+ strftime(timestr, sizeof(timestr), "%Y-%m-%dT%H:%M:%S", &tm);
+ if (z)
+ strcat(timestr, "Z");
+ return (xmlwrite_string(a, writer, key, timestr));
+}
+
+static int
+xmlwrite_mode(struct archive_write *a, xmlTextWriterPtr writer,
+ const char *key, mode_t mode)
+{
+ char ms[5];
+
+ ms[0] = '0';
+ ms[1] = '0' + ((mode >> 6) & 07);
+ ms[2] = '0' + ((mode >> 3) & 07);
+ ms[3] = '0' + (mode & 07);
+ ms[4] = '\0';
+
+ return (xmlwrite_string(a, writer, key, ms));
+}
+
+static int
+xmlwrite_sum(struct archive_write *a, xmlTextWriterPtr writer,
+ const char *key, struct chksumval *sum)
+{
+ const char *algname;
+ int algsize;
+ char buff[MAX_SUM_SIZE*2 + 1];
+ char *p;
+ unsigned char *s;
+ int i, r;
+
+ if (sum->len > 0) {
+ algname = getalgname(sum->alg);
+ algsize = getalgsize(sum->alg);
+ if (algname != NULL) {
+ const char *hex = "0123456789abcdef";
+ p = buff;
+ s = sum->val;
+ for (i = 0; i < algsize; i++) {
+ *p++ = hex[(*s >> 4)];
+ *p++ = hex[(*s & 0x0f)];
+ s++;
+ }
+ *p = '\0';
+ r = xmlwrite_string_attr(a, writer,
+ key, buff,
+ "style", algname);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+xmlwrite_heap(struct archive_write *a, xmlTextWriterPtr writer,
+ struct heap_data *heap)
+{
+ const char *encname;
+ int r;
+
+ r = xmlwrite_fstring(a, writer, "length", "%ju", heap->length);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = xmlwrite_fstring(a, writer, "offset", "%ju", heap->temp_offset);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = xmlwrite_fstring(a, writer, "size", "%ju", heap->size);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ switch (heap->compression) {
+ case GZIP:
+ encname = "application/x-gzip"; break;
+ case BZIP2:
+ encname = "application/x-bzip2"; break;
+ case LZMA:
+ encname = "application/x-lzma"; break;
+ case XZ:
+ encname = "application/x-xz"; break;
+ default:
+ encname = "application/octet-stream"; break;
+ }
+ r = xmlwrite_string_attr(a, writer, "encoding", NULL,
+ "style", encname);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = xmlwrite_sum(a, writer, "archived-checksum", &(heap->a_sum));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = xmlwrite_sum(a, writer, "extracted-checksum", &(heap->e_sum));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ return (ARCHIVE_OK);
+}
+
+/*
+ * xar utility records fflags as following xml elements:
+ * <flags>
+ * <UserNoDump/>
+ * .....
+ * </flags>
+ * or
+ * <ext2>
+ * <NoDump/>
+ * .....
+ * </ext2>
+ * If xar is running on BSD platform, records <flags>..</flags>;
+ * if xar is running on linux platform, records <ext2>..</ext2>;
+ * otherwise does not record.
+ *
+ * Our implements records both <flags> and <ext2> if it's necessary.
+ */
+static int
+make_fflags_entry(struct archive_write *a, xmlTextWriterPtr writer,
+ const char *element, const char *fflags_text)
+{
+ static const struct flagentry {
+ const char *name;
+ const char *xarname;
+ }
+ flagbsd[] = {
+ { "sappnd", "SystemAppend"},
+ { "sappend", "SystemAppend"},
+ { "arch", "SystemArchived"},
+ { "archived", "SystemArchived"},
+ { "schg", "SystemImmutable"},
+ { "schange", "SystemImmutable"},
+ { "simmutable", "SystemImmutable"},
+ { "nosunlnk", "SystemNoUnlink"},
+ { "nosunlink", "SystemNoUnlink"},
+ { "snapshot", "SystemSnapshot"},
+ { "uappnd", "UserAppend"},
+ { "uappend", "UserAppend"},
+ { "uchg", "UserImmutable"},
+ { "uchange", "UserImmutable"},
+ { "uimmutable", "UserImmutable"},
+ { "nodump", "UserNoDump"},
+ { "noopaque", "UserOpaque"},
+ { "nouunlnk", "UserNoUnlink"},
+ { "nouunlink", "UserNoUnlink"},
+ { NULL, NULL}
+ },
+ flagext2[] = {
+ { "sappnd", "AppendOnly"},
+ { "sappend", "AppendOnly"},
+ { "schg", "Immutable"},
+ { "schange", "Immutable"},
+ { "simmutable", "Immutable"},
+ { "nodump", "NoDump"},
+ { "nouunlnk", "Undelete"},
+ { "nouunlink", "Undelete"},
+ { "btree", "BTree"},
+ { "comperr", "CompError"},
+ { "compress", "Compress"},
+ { "noatime", "NoAtime"},
+ { "compdirty", "CompDirty"},
+ { "comprblk", "CompBlock"},
+ { "dirsync", "DirSync"},
+ { "hashidx", "HashIndexed"},
+ { "imagic", "iMagic"},
+ { "journal", "Journaled"},
+ { "securedeletion", "SecureDeletion"},
+ { "sync", "Synchronous"},
+ { "notail", "NoTail"},
+ { "topdir", "TopDir"},
+ { "reserved", "Reserved"},
+ { NULL, NULL}
+ };
+ const struct flagentry *fe, *flagentry;
+#define FLAGENTRY_MAXSIZE ((sizeof(flagbsd)+sizeof(flagext2))/sizeof(flagbsd))
+ const struct flagentry *avail[FLAGENTRY_MAXSIZE];
+ const char *p;
+ int i, n, r;
+
+ if (strcmp(element, "ext2") == 0)
+ flagentry = flagext2;
+ else
+ flagentry = flagbsd;
+ n = 0;
+ p = fflags_text;
+ do {
+ const char *cp;
+
+ cp = strchr(p, ',');
+ if (cp == NULL)
+ cp = p + strlen(p);
+
+ for (fe = flagentry; fe->name != NULL; fe++) {
+ if (fe->name[cp - p] != '\0'
+ || p[0] != fe->name[0])
+ continue;
+ if (strncmp(p, fe->name, cp - p) == 0) {
+ avail[n++] = fe;
+ break;
+ }
+ }
+ if (*cp == ',')
+ p = cp + 1;
+ else
+ p = NULL;
+ } while (p != NULL);
+
+ if (n > 0) {
+ r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(element));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ for (i = 0; i < n; i++) {
+ r = xmlwrite_string(a, writer,
+ avail[i]->xarname, NULL);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+make_file_entry(struct archive_write *a, xmlTextWriterPtr writer,
+ struct file *file)
+{
+ struct xar *xar;
+ const char *filetype, *filelink, *fflags;
+ struct archive_string linkto;
+ struct heap_data *heap;
+ unsigned char *tmp;
+ const char *p;
+ size_t len;
+ int r, r2, l, ll;
+
+ xar = (struct xar *)a->format_data;
+ r2 = ARCHIVE_OK;
+
+ /*
+ * Make a file name entry, "<name>".
+ */
+ l = ll = archive_strlen(&(file->basename));
+ tmp = malloc(l);
+ if (tmp == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ r = UTF8Toisolat1(tmp, &l, BAD_CAST(file->basename.s), &ll);
+ free(tmp);
+ if (r < 0) {
+ r = xmlTextWriterStartElement(writer, BAD_CAST("name"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ r = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST("enctype"), BAD_CAST("base64"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteAttribute() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ r = xmlTextWriterWriteBase64(writer, file->basename.s,
+ 0, archive_strlen(&(file->basename)));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteBase64() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ } else {
+ r = xmlwrite_string(a, writer, "name", file->basename.s);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Make a file type entry, "<type>".
+ */
+ filelink = NULL;
+ archive_string_init(&linkto);
+ switch (archive_entry_filetype(file->entry)) {
+ case AE_IFDIR:
+ filetype = "directory"; break;
+ case AE_IFLNK:
+ filetype = "symlink"; break;
+ case AE_IFCHR:
+ filetype = "character special"; break;
+ case AE_IFBLK:
+ filetype = "block special"; break;
+ case AE_IFSOCK:
+ filetype = "socket"; break;
+ case AE_IFIFO:
+ filetype = "fifo"; break;
+ case AE_IFREG:
+ default:
+ if (file->hardlink_target != NULL) {
+ filetype = "hardlink";
+ filelink = "link";
+ if (file->hardlink_target == file)
+ archive_strcpy(&linkto, "original");
+ else
+ archive_string_sprintf(&linkto, "%d",
+ file->hardlink_target->id);
+ } else
+ filetype = "file";
+ break;
+ }
+ r = xmlwrite_string_attr(a, writer, "type", filetype,
+ filelink, linkto.s);
+ archive_string_free(&linkto);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+
+ /*
+ * On a virtual directory, we record "name" and "type" only.
+ */
+ if (file->virtual)
+ return (ARCHIVE_OK);
+
+ switch (archive_entry_filetype(file->entry)) {
+ case AE_IFLNK:
+ /*
+ * xar utility has checked a file type, which
+ * a symbolic-link file has referenced.
+ * For example:
+ * <link type="directory">../ref/</link>
+ * The symlink target file is "../ref/" and its
+ * file type is a directory.
+ *
+ * <link type="file">../f</link>
+ * The symlink target file is "../f" and its
+ * file type is a regular file.
+ *
+ * But our implementation cannot do it, and then we
+ * always record that a attribute "type" is "broken",
+ * for example:
+ * <link type="broken">foo/bar</link>
+ * It means "foo/bar" is not reachable.
+ */
+ r = xmlwrite_string_attr(a, writer, "link",
+ file->symlink.s,
+ "type", "broken");
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ break;
+ case AE_IFCHR:
+ case AE_IFBLK:
+ r = xmlTextWriterStartElement(writer, BAD_CAST("device"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ r = xmlwrite_fstring(a, writer, "major",
+ "%d", archive_entry_rdevmajor(file->entry));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = xmlwrite_fstring(a, writer, "minor",
+ "%d", archive_entry_rdevminor(file->entry));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Make a inode entry, "<inode>".
+ */
+ r = xmlwrite_fstring(a, writer, "inode",
+ "%jd", archive_entry_ino64(file->entry));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ if (archive_entry_dev(file->entry) != 0) {
+ r = xmlwrite_fstring(a, writer, "deviceno",
+ "%d", archive_entry_dev(file->entry));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Make a file mode entry, "<mode>".
+ */
+ r = xmlwrite_mode(a, writer, "mode",
+ archive_entry_mode(file->entry));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+
+ /*
+ * Make a user entry, "<uid>" and "<user>.
+ */
+ r = xmlwrite_fstring(a, writer, "uid",
+ "%d", archive_entry_uid(file->entry));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = archive_entry_uname_l(file->entry, &p, &len, xar->sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Uname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate uname '%s' to UTF-8",
+ archive_entry_uname(file->entry));
+ r2 = ARCHIVE_WARN;
+ }
+ if (len > 0) {
+ r = xmlwrite_string(a, writer, "user", p);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Make a group entry, "<gid>" and "<group>.
+ */
+ r = xmlwrite_fstring(a, writer, "gid",
+ "%d", archive_entry_gid(file->entry));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = archive_entry_gname_l(file->entry, &p, &len, xar->sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Gname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate gname '%s' to UTF-8",
+ archive_entry_gname(file->entry));
+ r2 = ARCHIVE_WARN;
+ }
+ if (len > 0) {
+ r = xmlwrite_string(a, writer, "group", p);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Make a ctime entry, "<ctime>".
+ */
+ if (archive_entry_ctime_is_set(file->entry)) {
+ r = xmlwrite_time(a, writer, "ctime",
+ archive_entry_ctime(file->entry), 1);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Make a mtime entry, "<mtime>".
+ */
+ if (archive_entry_mtime_is_set(file->entry)) {
+ r = xmlwrite_time(a, writer, "mtime",
+ archive_entry_mtime(file->entry), 1);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Make a atime entry, "<atime>".
+ */
+ if (archive_entry_atime_is_set(file->entry)) {
+ r = xmlwrite_time(a, writer, "atime",
+ archive_entry_atime(file->entry), 1);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Make fflags entries, "<flags>" and "<ext2>".
+ */
+ fflags = archive_entry_fflags_text(file->entry);
+ if (fflags != NULL) {
+ r = make_fflags_entry(a, writer, "flags", fflags);
+ if (r < 0)
+ return (r);
+ r = make_fflags_entry(a, writer, "ext2", fflags);
+ if (r < 0)
+ return (r);
+ }
+
+ /*
+ * Make extended attribute entries, "<ea>".
+ */
+ archive_entry_xattr_reset(file->entry);
+ for (heap = file->xattr.first; heap != NULL; heap = heap->next) {
+ const char *name;
+ const void *value;
+ size_t size;
+
+ archive_entry_xattr_next(file->entry,
+ &name, &value, &size);
+ r = xmlTextWriterStartElement(writer, BAD_CAST("ea"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ r = xmlTextWriterWriteFormatAttribute(writer,
+ BAD_CAST("id"), "%d", heap->id);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteAttribute() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ r = xmlwrite_heap(a, writer, heap);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = xmlwrite_string(a, writer, "name", name);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ /*
+ * Make a file data entry, "<data>".
+ */
+ if (file->data.length > 0) {
+ r = xmlTextWriterStartElement(writer, BAD_CAST("data"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+
+ r = xmlwrite_heap(a, writer, &(file->data));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ if (archive_strlen(&file->script) > 0) {
+ r = xmlTextWriterStartElement(writer, BAD_CAST("content"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+
+ r = xmlwrite_string(a, writer,
+ "interpreter", file->script.s);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+
+ r = xmlwrite_string(a, writer, "type", "script");
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ return (r2);
+}
+
+/*
+ * Make the TOC
+ */
+static int
+make_toc(struct archive_write *a)
+{
+ struct xar *xar;
+ struct file *np;
+ xmlBufferPtr bp;
+ xmlTextWriterPtr writer;
+ int algsize;
+ int r, ret;
+
+ xar = (struct xar *)a->format_data;
+
+ ret = ARCHIVE_FATAL;
+
+ /*
+ * Initialize xml writer.
+ */
+ writer = NULL;
+ bp = xmlBufferCreate();
+ if (bp == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "xmlBufferCreate() "
+ "couldn't create xml buffer");
+ goto exit_toc;
+ }
+ writer = xmlNewTextWriterMemory(bp, 0);
+ if (writer == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlNewTextWriterMemory() "
+ "couldn't create xml writer");
+ goto exit_toc;
+ }
+ r = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartDocument() failed: %d", r);
+ goto exit_toc;
+ }
+ r = xmlTextWriterSetIndent(writer, 4);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterSetIndent() failed: %d", r);
+ goto exit_toc;
+ }
+
+ /*
+ * Start recording TOC
+ */
+ r = xmlTextWriterStartElement(writer, BAD_CAST("xar"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ goto exit_toc;
+ }
+ r = xmlTextWriterStartElement(writer, BAD_CAST("toc"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartDocument() failed: %d", r);
+ goto exit_toc;
+ }
+
+ /*
+ * Record the creation time of the archive file.
+ */
+ r = xmlwrite_time(a, writer, "creation-time", time(NULL), 0);
+ if (r < 0)
+ goto exit_toc;
+
+ /*
+ * Record the checksum value of TOC
+ */
+ algsize = getalgsize(xar->opt_toc_sumalg);
+ if (algsize) {
+ /*
+ * Record TOC checksum
+ */
+ r = xmlTextWriterStartElement(writer, BAD_CAST("checksum"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ goto exit_toc;
+ }
+ r = xmlTextWriterWriteAttribute(writer, BAD_CAST("style"),
+ BAD_CAST_CONST(getalgname(xar->opt_toc_sumalg)));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteAttribute() failed: %d", r);
+ goto exit_toc;
+ }
+
+ /*
+ * Record the offset of the value of checksum of TOC
+ */
+ r = xmlwrite_string(a, writer, "offset", "0");
+ if (r < 0)
+ goto exit_toc;
+
+ /*
+ * Record the size of the value of checksum of TOC
+ */
+ r = xmlwrite_fstring(a, writer, "size", "%d", algsize);
+ if (r < 0)
+ goto exit_toc;
+
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ goto exit_toc;
+ }
+ }
+
+ np = xar->root;
+ do {
+ if (np != np->parent) {
+ r = make_file_entry(a, writer, np);
+ if (r != ARCHIVE_OK)
+ goto exit_toc;
+ }
+
+ if (np->dir && np->children.first != NULL) {
+ /* Enter to sub directories. */
+ np = np->children.first;
+ r = xmlTextWriterStartElement(writer,
+ BAD_CAST("file"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() "
+ "failed: %d", r);
+ goto exit_toc;
+ }
+ r = xmlTextWriterWriteFormatAttribute(
+ writer, BAD_CAST("id"), "%d", np->id);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteAttribute() "
+ "failed: %d", r);
+ goto exit_toc;
+ }
+ continue;
+ }
+ while (np != np->parent) {
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() "
+ "failed: %d", r);
+ goto exit_toc;
+ }
+ if (np->chnext == NULL) {
+ /* Return to the parent directory. */
+ np = np->parent;
+ } else {
+ np = np->chnext;
+ r = xmlTextWriterStartElement(writer,
+ BAD_CAST("file"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() "
+ "failed: %d", r);
+ goto exit_toc;
+ }
+ r = xmlTextWriterWriteFormatAttribute(
+ writer, BAD_CAST("id"), "%d", np->id);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteAttribute() "
+ "failed: %d", r);
+ goto exit_toc;
+ }
+ break;
+ }
+ }
+ } while (np != np->parent);
+
+ r = xmlTextWriterEndDocument(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndDocument() failed: %d", r);
+ goto exit_toc;
+ }
+#if DEBUG_PRINT_TOC
+ fprintf(stderr, "\n---TOC-- %d bytes --\n%s\n",
+ strlen((const char *)bp->content), bp->content);
+#endif
+
+ /*
+ * Compress the TOC and calculate the sum of the TOC.
+ */
+ xar->toc.temp_offset = xar->temp_offset;
+ xar->toc.size = bp->use;
+ checksum_init(&(xar->a_sumwrk), xar->opt_toc_sumalg);
+
+ r = compression_init_encoder_gzip(&(a->archive),
+ &(xar->stream), 6, 1);
+ if (r != ARCHIVE_OK)
+ goto exit_toc;
+ xar->stream.next_in = bp->content;
+ xar->stream.avail_in = bp->use;
+ xar->stream.total_in = 0;
+ xar->stream.next_out = xar->wbuff;
+ xar->stream.avail_out = sizeof(xar->wbuff);
+ xar->stream.total_out = 0;
+ for (;;) {
+ size_t size;
+
+ r = compression_code(&(a->archive),
+ &(xar->stream), ARCHIVE_Z_FINISH);
+ if (r != ARCHIVE_OK && r != ARCHIVE_EOF)
+ goto exit_toc;
+ size = sizeof(xar->wbuff) - xar->stream.avail_out;
+ checksum_update(&(xar->a_sumwrk), xar->wbuff, size);
+ if (write_to_temp(a, xar->wbuff, size) != ARCHIVE_OK)
+ goto exit_toc;
+ if (r == ARCHIVE_EOF)
+ break;
+ xar->stream.next_out = xar->wbuff;
+ xar->stream.avail_out = sizeof(xar->wbuff);
+ }
+ r = compression_end(&(a->archive), &(xar->stream));
+ if (r != ARCHIVE_OK)
+ goto exit_toc;
+ xar->toc.length = xar->stream.total_out;
+ xar->toc.compression = GZIP;
+ checksum_final(&(xar->a_sumwrk), &(xar->toc.a_sum));
+
+ ret = ARCHIVE_OK;
+exit_toc:
+ if (writer)
+ xmlFreeTextWriter(writer);
+ if (bp)
+ xmlBufferFree(bp);
+
+ return (ret);
+}
+
+static int
+flush_wbuff(struct archive_write *a)
+{
+ struct xar *xar;
+ int r;
+ size_t s;
+
+ xar = (struct xar *)a->format_data;
+ s = sizeof(xar->wbuff) - xar->wbuff_remaining;
+ r = __archive_write_output(a, xar->wbuff, s);
+ if (r != ARCHIVE_OK)
+ return (r);
+ xar->wbuff_remaining = sizeof(xar->wbuff);
+ return (r);
+}
+
+static int
+copy_out(struct archive_write *a, uint64_t offset, uint64_t length)
+{
+ struct xar *xar;
+ int r;
+
+ xar = (struct xar *)a->format_data;
+ if (lseek(xar->temp_fd, offset, SEEK_SET) < 0) {
+ archive_set_error(&(a->archive), errno, "lseek failed");
+ return (ARCHIVE_FATAL);
+ }
+ while (length) {
+ size_t rsize;
+ ssize_t rs;
+ unsigned char *wb;
+
+ if (length > xar->wbuff_remaining)
+ rsize = xar->wbuff_remaining;
+ else
+ rsize = (size_t)length;
+ wb = xar->wbuff + (sizeof(xar->wbuff) - xar->wbuff_remaining);
+ rs = read(xar->temp_fd, wb, rsize);
+ if (rs < 0) {
+ archive_set_error(&(a->archive), errno,
+ "Can't read temporary file(%jd)",
+ (intmax_t)rs);
+ return (ARCHIVE_FATAL);
+ }
+ if (rs == 0) {
+ archive_set_error(&(a->archive), 0,
+ "Truncated xar archive");
+ return (ARCHIVE_FATAL);
+ }
+ xar->wbuff_remaining -= rs;
+ length -= rs;
+ if (xar->wbuff_remaining == 0) {
+ r = flush_wbuff(a);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+xar_close(struct archive_write *a)
+{
+ struct xar *xar;
+ unsigned char *wb;
+ uint64_t length;
+ int r;
+
+ xar = (struct xar *)a->format_data;
+
+ /* Empty! */
+ if (xar->root->children.first == NULL)
+ return (ARCHIVE_OK);
+
+ /* Save the length of all file extended attributes and contents. */
+ length = xar->temp_offset;
+
+ /* Connect hardlinked files */
+ file_connect_hardlink_files(xar);
+
+ /* Make the TOC */
+ r = make_toc(a);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /*
+ * Make the xar header on wbuff(write buffer).
+ */
+ wb = xar->wbuff;
+ xar->wbuff_remaining = sizeof(xar->wbuff);
+ archive_be32enc(&wb[0], HEADER_MAGIC);
+ archive_be16enc(&wb[4], HEADER_SIZE);
+ archive_be16enc(&wb[6], HEADER_VERSION);
+ archive_be64enc(&wb[8], xar->toc.length);
+ archive_be64enc(&wb[16], xar->toc.size);
+ archive_be32enc(&wb[24], xar->toc.a_sum.alg);
+ xar->wbuff_remaining -= HEADER_SIZE;
+
+ /*
+ * Write the TOC
+ */
+ r = copy_out(a, xar->toc.temp_offset, xar->toc.length);
+ if (r != ARCHIVE_OK)
+ return (r);
+
+ /* Write the checksum value of the TOC. */
+ if (xar->toc.a_sum.len) {
+ if (xar->wbuff_remaining < xar->toc.a_sum.len) {
+ r = flush_wbuff(a);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ wb = xar->wbuff + (sizeof(xar->wbuff) - xar->wbuff_remaining);
+ memcpy(wb, xar->toc.a_sum.val, xar->toc.a_sum.len);
+ xar->wbuff_remaining -= xar->toc.a_sum.len;
+ }
+
+ /*
+ * Write all file extended attributes and contents.
+ */
+ r = copy_out(a, xar->toc.a_sum.len, length);
+ if (r != ARCHIVE_OK)
+ return (r);
+ r = flush_wbuff(a);
+ return (r);
+}
+
+static int
+xar_free(struct archive_write *a)
+{
+ struct xar *xar;
+
+ xar = (struct xar *)a->format_data;
+
+ /* Close the temporary file. */
+ if (xar->temp_fd >= 0)
+ close(xar->temp_fd);
+
+ archive_string_free(&(xar->cur_dirstr));
+ archive_string_free(&(xar->tstr));
+ archive_string_free(&(xar->vstr));
+ file_free_hardlinks(xar);
+ file_free_register(xar);
+ compression_end(&(a->archive), &(xar->stream));
+ free(xar);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+file_cmp_node(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct file *f1 = (const struct file *)n1;
+ const struct file *f2 = (const struct file *)n2;
+
+ return (strcmp(f1->basename.s, f2->basename.s));
+}
+
+static int
+file_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ const struct file *f = (const struct file *)n;
+
+ return (strcmp(f->basename.s, (const char *)key));
+}
+
+static struct file *
+file_new(struct archive_write *a, struct archive_entry *entry)
+{
+ struct file *file;
+ static const struct archive_rb_tree_ops rb_ops = {
+ file_cmp_node, file_cmp_key
+ };
+
+ file = calloc(1, sizeof(*file));
+ if (file == NULL)
+ return (NULL);
+
+ if (entry != NULL)
+ file->entry = archive_entry_clone(entry);
+ else
+ file->entry = archive_entry_new2(&a->archive);
+ if (file->entry == NULL) {
+ free(file);
+ return (NULL);
+ }
+ __archive_rb_tree_init(&(file->rbtree), &rb_ops);
+ file->children.first = NULL;
+ file->children.last = &(file->children.first);
+ file->xattr.first = NULL;
+ file->xattr.last = &(file->xattr.first);
+ archive_string_init(&(file->parentdir));
+ archive_string_init(&(file->basename));
+ archive_string_init(&(file->symlink));
+ archive_string_init(&(file->script));
+ if (entry != NULL && archive_entry_filetype(entry) == AE_IFDIR)
+ file->dir = 1;
+
+ return (file);
+}
+
+static void
+file_free(struct file *file)
+{
+ struct heap_data *heap, *next_heap;
+
+ heap = file->xattr.first;
+ while (heap != NULL) {
+ next_heap = heap->next;
+ free(heap);
+ heap = next_heap;
+ }
+ archive_string_free(&(file->parentdir));
+ archive_string_free(&(file->basename));
+ archive_string_free(&(file->symlink));
+ archive_string_free(&(file->script));
+ archive_entry_free(file->entry);
+ free(file);
+}
+
+static struct file *
+file_create_virtual_dir(struct archive_write *a, struct xar *xar,
+ const char *pathname)
+{
+ struct file *file;
+
+ (void)xar; /* UNUSED */
+
+ file = file_new(a, NULL);
+ if (file == NULL)
+ return (NULL);
+ archive_entry_set_pathname(file->entry, pathname);
+ archive_entry_set_mode(file->entry, 0555 | AE_IFDIR);
+
+ file->dir = 1;
+ file->virtual = 1;
+
+ return (file);
+}
+
+static int
+file_add_child_tail(struct file *parent, struct file *child)
+{
+ if (!__archive_rb_tree_insert_node(
+ &(parent->rbtree), (struct archive_rb_node *)child))
+ return (0);
+ child->chnext = NULL;
+ *parent->children.last = child;
+ parent->children.last = &(child->chnext);
+ child->parent = parent;
+ return (1);
+}
+
+/*
+ * Find a entry from `parent'
+ */
+static struct file *
+file_find_child(struct file *parent, const char *child_name)
+{
+ struct file *np;
+
+ np = (struct file *)__archive_rb_tree_find_node(
+ &(parent->rbtree), child_name);
+ return (np);
+}
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+static void
+cleanup_backslash(char *utf8, size_t len)
+{
+
+ /* Convert a path-separator from '\' to '/' */
+ while (*utf8 != '\0' && len) {
+ if (*utf8 == '\\')
+ *utf8 = '/';
+ ++utf8;
+ --len;
+ }
+}
+#else
+#define cleanup_backslash(p, len) /* nop */
+#endif
+
+/*
+ * Generate a parent directory name and a base name from a pathname.
+ */
+static int
+file_gen_utility_names(struct archive_write *a, struct file *file)
+{
+ struct xar *xar;
+ const char *pp;
+ char *p, *dirname, *slash;
+ size_t len;
+ int r = ARCHIVE_OK;
+
+ xar = (struct xar *)a->format_data;
+ archive_string_empty(&(file->parentdir));
+ archive_string_empty(&(file->basename));
+ archive_string_empty(&(file->symlink));
+
+ if (file->parent == file)/* virtual root */
+ return (ARCHIVE_OK);
+
+ if (archive_entry_pathname_l(file->entry, &pp, &len, xar->sconv)
+ != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to UTF-8",
+ archive_entry_pathname(file->entry));
+ r = ARCHIVE_WARN;
+ }
+ archive_strncpy(&(file->parentdir), pp, len);
+ len = file->parentdir.length;
+ p = dirname = file->parentdir.s;
+ /*
+ * Convert a path-separator from '\' to '/'
+ */
+ cleanup_backslash(p, len);
+
+ /*
+ * Remove leading '/', '../' and './' elements
+ */
+ while (*p) {
+ if (p[0] == '/') {
+ p++;
+ len--;
+ } else if (p[0] != '.')
+ break;
+ else if (p[1] == '.' && p[2] == '/') {
+ p += 3;
+ len -= 3;
+ } else if (p[1] == '/' || (p[1] == '.' && p[2] == '\0')) {
+ p += 2;
+ len -= 2;
+ } else if (p[1] == '\0') {
+ p++;
+ len--;
+ } else
+ break;
+ }
+ if (p != dirname) {
+ memmove(dirname, p, len+1);
+ p = dirname;
+ }
+ /*
+ * Remove "/","/." and "/.." elements from tail.
+ */
+ while (len > 0) {
+ size_t ll = len;
+
+ if (p[len-1] == '/') {
+ p[len-1] = '\0';
+ len--;
+ }
+ if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {
+ p[len-2] = '\0';
+ len -= 2;
+ }
+ if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&
+ p[len-1] == '.') {
+ p[len-3] = '\0';
+ len -= 3;
+ }
+ if (ll == len)
+ break;
+ }
+ while (*p) {
+ if (p[0] == '/') {
+ if (p[1] == '/')
+ /* Convert '//' --> '/' */
+ memmove(p, p+1, strlen(p+1) + 1);
+ else if (p[1] == '.' && p[2] == '/')
+ /* Convert '/./' --> '/' */
+ memmove(p, p+2, strlen(p+2) + 1);
+ else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {
+ /* Convert 'dir/dir1/../dir2/'
+ * --> 'dir/dir2/'
+ */
+ char *rp = p -1;
+ while (rp >= dirname) {
+ if (*rp == '/')
+ break;
+ --rp;
+ }
+ if (rp > dirname) {
+ strcpy(rp, p+3);
+ p = rp;
+ } else {
+ strcpy(dirname, p+4);
+ p = dirname;
+ }
+ } else
+ p++;
+ } else
+ p++;
+ }
+ p = dirname;
+ len = strlen(p);
+
+ if (archive_entry_filetype(file->entry) == AE_IFLNK) {
+ size_t len2;
+ /* Convert symlink name too. */
+ if (archive_entry_symlink_l(file->entry, &pp, &len2,
+ xar->sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate symlink '%s' to UTF-8",
+ archive_entry_symlink(file->entry));
+ r = ARCHIVE_WARN;
+ }
+ archive_strncpy(&(file->symlink), pp, len2);
+ cleanup_backslash(file->symlink.s, file->symlink.length);
+ }
+ /*
+ * - Count up directory elements.
+ * - Find out the position which points the last position of
+ * path separator('/').
+ */
+ slash = NULL;
+ for (; *p != '\0'; p++)
+ if (*p == '/')
+ slash = p;
+ if (slash == NULL) {
+ /* The pathname doesn't have a parent directory. */
+ file->parentdir.length = len;
+ archive_string_copy(&(file->basename), &(file->parentdir));
+ archive_string_empty(&(file->parentdir));
+ *file->parentdir.s = '\0';
+ return (r);
+ }
+
+ /* Make a basename from dirname and slash */
+ *slash = '\0';
+ file->parentdir.length = slash - dirname;
+ archive_strcpy(&(file->basename), slash + 1);
+ return (r);
+}
+
+static int
+get_path_component(char *name, int n, const char *fn)
+{
+ char *p;
+ int l;
+
+ p = strchr(fn, '/');
+ if (p == NULL) {
+ if ((l = strlen(fn)) == 0)
+ return (0);
+ } else
+ l = p - fn;
+ if (l > n -1)
+ return (-1);
+ memcpy(name, fn, l);
+ name[l] = '\0';
+
+ return (l);
+}
+
+/*
+ * Add a new entry into the tree.
+ */
+static int
+file_tree(struct archive_write *a, struct file **filepp)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ char name[_MAX_FNAME];/* Included null terminator size. */
+#elif defined(NAME_MAX) && NAME_MAX >= 255
+ char name[NAME_MAX+1];
+#else
+ char name[256];
+#endif
+ struct xar *xar = (struct xar *)a->format_data;
+ struct file *dent, *file, *np;
+ struct archive_entry *ent;
+ const char *fn, *p;
+ int l;
+
+ file = *filepp;
+ dent = xar->root;
+ if (file->parentdir.length > 0)
+ fn = p = file->parentdir.s;
+ else
+ fn = p = "";
+
+ /*
+ * If the path of the parent directory of `file' entry is
+ * the same as the path of `cur_dirent', add isoent to
+ * `cur_dirent'.
+ */
+ if (archive_strlen(&(xar->cur_dirstr))
+ == archive_strlen(&(file->parentdir)) &&
+ strcmp(xar->cur_dirstr.s, fn) == 0) {
+ if (!file_add_child_tail(xar->cur_dirent, file)) {
+ np = (struct file *)__archive_rb_tree_find_node(
+ &(xar->cur_dirent->rbtree),
+ file->basename.s);
+ goto same_entry;
+ }
+ return (ARCHIVE_OK);
+ }
+
+ for (;;) {
+ l = get_path_component(name, sizeof(name), fn);
+ if (l == 0) {
+ np = NULL;
+ break;
+ }
+ if (l < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "A name buffer is too small");
+ file_free(file);
+ *filepp = NULL;
+ return (ARCHIVE_FATAL);
+ }
+
+ np = file_find_child(dent, name);
+ if (np == NULL || fn[0] == '\0')
+ break;
+
+ /* Find next subdirectory. */
+ if (!np->dir) {
+ /* NOT Directory! */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "`%s' is not directory, we cannot insert `%s' ",
+ archive_entry_pathname(np->entry),
+ archive_entry_pathname(file->entry));
+ file_free(file);
+ *filepp = NULL;
+ return (ARCHIVE_FAILED);
+ }
+ fn += l;
+ if (fn[0] == '/')
+ fn++;
+ dent = np;
+ }
+ if (np == NULL) {
+ /*
+ * Create virtual parent directories.
+ */
+ while (fn[0] != '\0') {
+ struct file *vp;
+ struct archive_string as;
+
+ archive_string_init(&as);
+ archive_strncat(&as, p, fn - p + l);
+ if (as.s[as.length-1] == '/') {
+ as.s[as.length-1] = '\0';
+ as.length--;
+ }
+ vp = file_create_virtual_dir(a, xar, as.s);
+ if (vp == NULL) {
+ archive_string_free(&as);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ file_free(file);
+ *filepp = NULL;
+ return (ARCHIVE_FATAL);
+ }
+ archive_string_free(&as);
+ if (file_gen_utility_names(a, vp) <= ARCHIVE_FAILED)
+ return (ARCHIVE_FATAL);
+ file_add_child_tail(dent, vp);
+ file_register(xar, vp);
+ np = vp;
+
+ fn += l;
+ if (fn[0] == '/')
+ fn++;
+ l = get_path_component(name, sizeof(name), fn);
+ if (l < 0) {
+ archive_string_free(&as);
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "A name buffer is too small");
+ file_free(file);
+ *filepp = NULL;
+ return (ARCHIVE_FATAL);
+ }
+ dent = np;
+ }
+
+ /* Found out the parent directory where isoent can be
+ * inserted. */
+ xar->cur_dirent = dent;
+ archive_string_empty(&(xar->cur_dirstr));
+ archive_string_ensure(&(xar->cur_dirstr),
+ archive_strlen(&(dent->parentdir)) +
+ archive_strlen(&(dent->basename)) + 2);
+ if (archive_strlen(&(dent->parentdir)) +
+ archive_strlen(&(dent->basename)) == 0)
+ xar->cur_dirstr.s[0] = 0;
+ else {
+ if (archive_strlen(&(dent->parentdir)) > 0) {
+ archive_string_copy(&(xar->cur_dirstr),
+ &(dent->parentdir));
+ archive_strappend_char(&(xar->cur_dirstr), '/');
+ }
+ archive_string_concat(&(xar->cur_dirstr),
+ &(dent->basename));
+ }
+
+ if (!file_add_child_tail(dent, file)) {
+ np = (struct file *)__archive_rb_tree_find_node(
+ &(dent->rbtree), file->basename.s);
+ goto same_entry;
+ }
+ return (ARCHIVE_OK);
+ }
+
+same_entry:
+ /*
+ * We have already has the entry the filename of which is
+ * the same.
+ */
+ if (archive_entry_filetype(np->entry) !=
+ archive_entry_filetype(file->entry)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Found duplicate entries `%s' and its file type is "
+ "different",
+ archive_entry_pathname(np->entry));
+ file_free(file);
+ *filepp = NULL;
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Swap files. */
+ ent = np->entry;
+ np->entry = file->entry;
+ file->entry = ent;
+ np->virtual = 0;
+
+ file_free(file);
+ *filepp = np;
+ return (ARCHIVE_OK);
+}
+
+static void
+file_register(struct xar *xar, struct file *file)
+{
+ file->id = xar->file_idx++;
+ file->next = NULL;
+ *xar->file_list.last = file;
+ xar->file_list.last = &(file->next);
+}
+
+static void
+file_init_register(struct xar *xar)
+{
+ xar->file_list.first = NULL;
+ xar->file_list.last = &(xar->file_list.first);
+}
+
+static void
+file_free_register(struct xar *xar)
+{
+ struct file *file, *file_next;
+
+ file = xar->file_list.first;
+ while (file != NULL) {
+ file_next = file->next;
+ file_free(file);
+ file = file_next;
+ }
+}
+
+/*
+ * Register entry to get a hardlink target.
+ */
+static int
+file_register_hardlink(struct archive_write *a, struct file *file)
+{
+ struct xar *xar = (struct xar *)a->format_data;
+ struct hardlink *hl;
+ const char *pathname;
+
+ archive_entry_set_nlink(file->entry, 1);
+ pathname = archive_entry_hardlink(file->entry);
+ if (pathname == NULL) {
+ /* This `file` is a hardlink target. */
+ hl = malloc(sizeof(*hl));
+ if (hl == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ hl->nlink = 1;
+ /* A hardlink target must be the first position. */
+ file->hlnext = NULL;
+ hl->file_list.first = file;
+ hl->file_list.last = &(file->hlnext);
+ __archive_rb_tree_insert_node(&(xar->hardlink_rbtree),
+ (struct archive_rb_node *)hl);
+ } else {
+ hl = (struct hardlink *)__archive_rb_tree_find_node(
+ &(xar->hardlink_rbtree), pathname);
+ if (hl != NULL) {
+ /* Insert `file` entry into the tail. */
+ file->hlnext = NULL;
+ *hl->file_list.last = file;
+ hl->file_list.last = &(file->hlnext);
+ hl->nlink++;
+ }
+ archive_entry_unset_size(file->entry);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Hardlinked files have to have the same location of extent.
+ * We have to find out hardlink target entries for entries which
+ * have a hardlink target name.
+ */
+static void
+file_connect_hardlink_files(struct xar *xar)
+{
+ struct archive_rb_node *n;
+ struct hardlink *hl;
+ struct file *target, *nf;
+
+ ARCHIVE_RB_TREE_FOREACH(n, &(xar->hardlink_rbtree)) {
+ hl = (struct hardlink *)n;
+
+ /* The first entry must be a hardlink target. */
+ target = hl->file_list.first;
+ archive_entry_set_nlink(target->entry, hl->nlink);
+ if (hl->nlink > 1)
+ /* It means this file is a hardlink
+ * target itself. */
+ target->hardlink_target = target;
+ for (nf = target->hlnext;
+ nf != NULL; nf = nf->hlnext) {
+ nf->hardlink_target = target;
+ archive_entry_set_nlink(nf->entry, hl->nlink);
+ }
+ }
+}
+
+static int
+file_hd_cmp_node(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct hardlink *h1 = (const struct hardlink *)n1;
+ const struct hardlink *h2 = (const struct hardlink *)n2;
+
+ return (strcmp(archive_entry_pathname(h1->file_list.first->entry),
+ archive_entry_pathname(h2->file_list.first->entry)));
+}
+
+static int
+file_hd_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ const struct hardlink *h = (const struct hardlink *)n;
+
+ return (strcmp(archive_entry_pathname(h->file_list.first->entry),
+ (const char *)key));
+}
+
+
+static void
+file_init_hardlinks(struct xar *xar)
+{
+ static const struct archive_rb_tree_ops rb_ops = {
+ file_hd_cmp_node, file_hd_cmp_key,
+ };
+
+ __archive_rb_tree_init(&(xar->hardlink_rbtree), &rb_ops);
+}
+
+static void
+file_free_hardlinks(struct xar *xar)
+{
+ struct archive_rb_node *n, *tmp;
+
+ ARCHIVE_RB_TREE_FOREACH_SAFE(n, &(xar->hardlink_rbtree), tmp) {
+ __archive_rb_tree_remove_node(&(xar->hardlink_rbtree), n);
+ free(n);
+ }
+}
+
+static void
+checksum_init(struct chksumwork *sumwrk, enum sumalg sum_alg)
+{
+ sumwrk->alg = sum_alg;
+ switch (sum_alg) {
+ case CKSUM_NONE:
+ break;
+ case CKSUM_SHA1:
+ archive_sha1_init(&(sumwrk->sha1ctx));
+ break;
+ case CKSUM_MD5:
+ archive_md5_init(&(sumwrk->md5ctx));
+ break;
+ }
+}
+
+static void
+checksum_update(struct chksumwork *sumwrk, const void *buff, size_t size)
+{
+
+ switch (sumwrk->alg) {
+ case CKSUM_NONE:
+ break;
+ case CKSUM_SHA1:
+ archive_sha1_update(&(sumwrk->sha1ctx), buff, size);
+ break;
+ case CKSUM_MD5:
+ archive_md5_update(&(sumwrk->md5ctx), buff, size);
+ break;
+ }
+}
+
+static void
+checksum_final(struct chksumwork *sumwrk, struct chksumval *sumval)
+{
+
+ switch (sumwrk->alg) {
+ case CKSUM_NONE:
+ sumval->len = 0;
+ break;
+ case CKSUM_SHA1:
+ archive_sha1_final(&(sumwrk->sha1ctx), sumval->val);
+ sumval->len = SHA1_SIZE;
+ break;
+ case CKSUM_MD5:
+ archive_md5_final(&(sumwrk->md5ctx), sumval->val);
+ sumval->len = MD5_SIZE;
+ break;
+ }
+ sumval->alg = sumwrk->alg;
+}
+
+#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR) || !defined(HAVE_LZMA_H)
+static int
+compression_unsupported_encoder(struct archive *a,
+ struct la_zstream *lastrm, const char *name)
+{
+
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "%s compression not supported on this platform", name);
+ lastrm->valid = 0;
+ lastrm->real_stream = NULL;
+ return (ARCHIVE_FAILED);
+}
+#endif
+
+static int
+compression_init_encoder_gzip(struct archive *a,
+ struct la_zstream *lastrm, int level, int withheader)
+{
+ z_stream *strm;
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ strm = calloc(1, sizeof(*strm));
+ if (strm == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for gzip stream");
+ return (ARCHIVE_FATAL);
+ }
+ /* zlib.h is not const-correct, so we need this one bit
+ * of ugly hackery to convert a const * pointer to
+ * a non-const pointer. */
+ strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;
+ strm->avail_in = lastrm->avail_in;
+ strm->total_in = (uLong)lastrm->total_in;
+ strm->next_out = lastrm->next_out;
+ strm->avail_out = lastrm->avail_out;
+ strm->total_out = (uLong)lastrm->total_out;
+ if (deflateInit2(strm, level, Z_DEFLATED,
+ (withheader)?15:-15,
+ 8, Z_DEFAULT_STRATEGY) != Z_OK) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library");
+ return (ARCHIVE_FATAL);
+ }
+ lastrm->real_stream = strm;
+ lastrm->valid = 1;
+ lastrm->code = compression_code_gzip;
+ lastrm->end = compression_end_gzip;
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_code_gzip(struct archive *a,
+ struct la_zstream *lastrm, enum la_zaction action)
+{
+ z_stream *strm;
+ int r;
+
+ strm = (z_stream *)lastrm->real_stream;
+ /* zlib.h is not const-correct, so we need this one bit
+ * of ugly hackery to convert a const * pointer to
+ * a non-const pointer. */
+ strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;
+ strm->avail_in = lastrm->avail_in;
+ strm->total_in = (uLong)lastrm->total_in;
+ strm->next_out = lastrm->next_out;
+ strm->avail_out = lastrm->avail_out;
+ strm->total_out = (uLong)lastrm->total_out;
+ r = deflate(strm,
+ (action == ARCHIVE_Z_FINISH)? Z_FINISH: Z_NO_FLUSH);
+ lastrm->next_in = strm->next_in;
+ lastrm->avail_in = strm->avail_in;
+ lastrm->total_in = strm->total_in;
+ lastrm->next_out = strm->next_out;
+ lastrm->avail_out = strm->avail_out;
+ lastrm->total_out = strm->total_out;
+ switch (r) {
+ case Z_OK:
+ return (ARCHIVE_OK);
+ case Z_STREAM_END:
+ return (ARCHIVE_EOF);
+ default:
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "GZip compression failed:"
+ " deflate() call returned status %d", r);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+static int
+compression_end_gzip(struct archive *a, struct la_zstream *lastrm)
+{
+ z_stream *strm;
+ int r;
+
+ strm = (z_stream *)lastrm->real_stream;
+ r = deflateEnd(strm);
+ free(strm);
+ lastrm->real_stream = NULL;
+ lastrm->valid = 0;
+ if (r != Z_OK) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up compressor");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+static int
+compression_init_encoder_bzip2(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+ bz_stream *strm;
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ strm = calloc(1, sizeof(*strm));
+ if (strm == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for bzip2 stream");
+ return (ARCHIVE_FATAL);
+ }
+ /* bzlib.h is not const-correct, so we need this one bit
+ * of ugly hackery to convert a const * pointer to
+ * a non-const pointer. */
+ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
+ strm->avail_in = lastrm->avail_in;
+ strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
+ strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
+ strm->next_out = (char *)lastrm->next_out;
+ strm->avail_out = lastrm->avail_out;
+ strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
+ strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
+ if (BZ2_bzCompressInit(strm, level, 0, 30) != BZ_OK) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library");
+ return (ARCHIVE_FATAL);
+ }
+ lastrm->real_stream = strm;
+ lastrm->valid = 1;
+ lastrm->code = compression_code_bzip2;
+ lastrm->end = compression_end_bzip2;
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_code_bzip2(struct archive *a,
+ struct la_zstream *lastrm, enum la_zaction action)
+{
+ bz_stream *strm;
+ int r;
+
+ strm = (bz_stream *)lastrm->real_stream;
+ /* bzlib.h is not const-correct, so we need this one bit
+ * of ugly hackery to convert a const * pointer to
+ * a non-const pointer. */
+ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
+ strm->avail_in = lastrm->avail_in;
+ strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
+ strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
+ strm->next_out = (char *)lastrm->next_out;
+ strm->avail_out = lastrm->avail_out;
+ strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
+ strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
+ r = BZ2_bzCompress(strm,
+ (action == ARCHIVE_Z_FINISH)? BZ_FINISH: BZ_RUN);
+ lastrm->next_in = (const unsigned char *)strm->next_in;
+ lastrm->avail_in = strm->avail_in;
+ lastrm->total_in =
+ (((uint64_t)(uint32_t)strm->total_in_hi32) << 32)
+ + (uint64_t)(uint32_t)strm->total_in_lo32;
+ lastrm->next_out = (unsigned char *)strm->next_out;
+ lastrm->avail_out = strm->avail_out;
+ lastrm->total_out =
+ (((uint64_t)(uint32_t)strm->total_out_hi32) << 32)
+ + (uint64_t)(uint32_t)strm->total_out_lo32;
+ switch (r) {
+ case BZ_RUN_OK: /* Non-finishing */
+ case BZ_FINISH_OK: /* Finishing: There's more work to do */
+ return (ARCHIVE_OK);
+ case BZ_STREAM_END: /* Finishing: all done */
+ /* Only occurs in finishing case */
+ return (ARCHIVE_EOF);
+ default:
+ /* Any other return value indicates an error */
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Bzip2 compression failed:"
+ " BZ2_bzCompress() call returned status %d", r);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+static int
+compression_end_bzip2(struct archive *a, struct la_zstream *lastrm)
+{
+ bz_stream *strm;
+ int r;
+
+ strm = (bz_stream *)lastrm->real_stream;
+ r = BZ2_bzCompressEnd(strm);
+ free(strm);
+ lastrm->real_stream = NULL;
+ lastrm->valid = 0;
+ if (r != BZ_OK) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up compressor");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+#else
+static int
+compression_init_encoder_bzip2(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+
+ (void) level; /* UNUSED */
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ return (compression_unsupported_encoder(a, lastrm, "bzip2"));
+}
+#endif
+
+#if defined(HAVE_LZMA_H)
+static int
+compression_init_encoder_lzma(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+ static const lzma_stream lzma_init_data = LZMA_STREAM_INIT;
+ lzma_stream *strm;
+ lzma_options_lzma lzma_opt;
+ int r;
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ if (lzma_lzma_preset(&lzma_opt, level)) {
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ENOMEM,
+ "Internal error initializing compression library");
+ return (ARCHIVE_FATAL);
+ }
+ strm = calloc(1, sizeof(*strm));
+ if (strm == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for lzma stream");
+ return (ARCHIVE_FATAL);
+ }
+ *strm = lzma_init_data;
+ r = lzma_alone_encoder(strm, &lzma_opt);
+ switch (r) {
+ case LZMA_OK:
+ lastrm->real_stream = strm;
+ lastrm->valid = 1;
+ lastrm->code = compression_code_lzma;
+ lastrm->end = compression_end_lzma;
+ r = ARCHIVE_OK;
+ break;
+ case LZMA_MEM_ERROR:
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ENOMEM,
+ "Internal error initializing compression library: "
+ "Cannot allocate memory");
+ r = ARCHIVE_FATAL;
+ break;
+ default:
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ "It's a bug in liblzma");
+ r = ARCHIVE_FATAL;
+ break;
+ }
+ return (r);
+}
+
+static int
+compression_init_encoder_xz(struct archive *a,
+ struct la_zstream *lastrm, int level, int threads)
+{
+ static const lzma_stream lzma_init_data = LZMA_STREAM_INIT;
+ lzma_stream *strm;
+ lzma_filter *lzmafilters;
+ lzma_options_lzma lzma_opt;
+ int r;
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+ lzma_mt mt_options;
+#endif
+
+ (void)threads; /* UNUSED (if multi-threaded LZMA library not avail) */
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ strm = calloc(1, sizeof(*strm) + sizeof(*lzmafilters) * 2);
+ if (strm == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for xz stream");
+ return (ARCHIVE_FATAL);
+ }
+ lzmafilters = (lzma_filter *)(strm+1);
+ if (level > 9)
+ level = 9;
+ if (lzma_lzma_preset(&lzma_opt, level)) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ENOMEM,
+ "Internal error initializing compression library");
+ return (ARCHIVE_FATAL);
+ }
+ lzmafilters[0].id = LZMA_FILTER_LZMA2;
+ lzmafilters[0].options = &lzma_opt;
+ lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
+
+ *strm = lzma_init_data;
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+ if (threads > 1) {
+ memset(&mt_options, 0, sizeof(mt_options));
+ mt_options.threads = threads;
+ mt_options.timeout = 300;
+ mt_options.filters = lzmafilters;
+ mt_options.check = LZMA_CHECK_CRC64;
+ r = lzma_stream_encoder_mt(strm, &mt_options);
+ } else
+#endif
+ r = lzma_stream_encoder(strm, lzmafilters, LZMA_CHECK_CRC64);
+ switch (r) {
+ case LZMA_OK:
+ lastrm->real_stream = strm;
+ lastrm->valid = 1;
+ lastrm->code = compression_code_lzma;
+ lastrm->end = compression_end_lzma;
+ r = ARCHIVE_OK;
+ break;
+ case LZMA_MEM_ERROR:
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ENOMEM,
+ "Internal error initializing compression library: "
+ "Cannot allocate memory");
+ r = ARCHIVE_FATAL;
+ break;
+ default:
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ "It's a bug in liblzma");
+ r = ARCHIVE_FATAL;
+ break;
+ }
+ return (r);
+}
+
+static int
+compression_code_lzma(struct archive *a,
+ struct la_zstream *lastrm, enum la_zaction action)
+{
+ lzma_stream *strm;
+ int r;
+
+ strm = (lzma_stream *)lastrm->real_stream;
+ strm->next_in = lastrm->next_in;
+ strm->avail_in = lastrm->avail_in;
+ strm->total_in = lastrm->total_in;
+ strm->next_out = lastrm->next_out;
+ strm->avail_out = lastrm->avail_out;
+ strm->total_out = lastrm->total_out;
+ r = lzma_code(strm,
+ (action == ARCHIVE_Z_FINISH)? LZMA_FINISH: LZMA_RUN);
+ lastrm->next_in = strm->next_in;
+ lastrm->avail_in = strm->avail_in;
+ lastrm->total_in = strm->total_in;
+ lastrm->next_out = strm->next_out;
+ lastrm->avail_out = strm->avail_out;
+ lastrm->total_out = strm->total_out;
+ switch (r) {
+ case LZMA_OK:
+ /* Non-finishing case */
+ return (ARCHIVE_OK);
+ case LZMA_STREAM_END:
+ /* This return can only occur in finishing case. */
+ return (ARCHIVE_EOF);
+ case LZMA_MEMLIMIT_ERROR:
+ archive_set_error(a, ENOMEM,
+ "lzma compression error:"
+ " %ju MiB would have been needed",
+ (uintmax_t)((lzma_memusage(strm) + 1024 * 1024 -1)
+ / (1024 * 1024)));
+ return (ARCHIVE_FATAL);
+ default:
+ /* Any other return value indicates an error */
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "lzma compression failed:"
+ " lzma_code() call returned status %d", r);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+static int
+compression_end_lzma(struct archive *a, struct la_zstream *lastrm)
+{
+ lzma_stream *strm;
+
+ (void)a; /* UNUSED */
+ strm = (lzma_stream *)lastrm->real_stream;
+ lzma_end(strm);
+ free(strm);
+ lastrm->valid = 0;
+ lastrm->real_stream = NULL;
+ return (ARCHIVE_OK);
+}
+#else
+static int
+compression_init_encoder_lzma(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+
+ (void) level; /* UNUSED */
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ return (compression_unsupported_encoder(a, lastrm, "lzma"));
+}
+static int
+compression_init_encoder_xz(struct archive *a,
+ struct la_zstream *lastrm, int level, int threads)
+{
+
+ (void) level; /* UNUSED */
+ (void) threads; /* UNUSED */
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ return (compression_unsupported_encoder(a, lastrm, "xz"));
+}
+#endif
+
+static int
+xar_compression_init_encoder(struct archive_write *a)
+{
+ struct xar *xar;
+ int r;
+
+ xar = (struct xar *)a->format_data;
+ switch (xar->opt_compression) {
+ case GZIP:
+ r = compression_init_encoder_gzip(
+ &(a->archive), &(xar->stream),
+ xar->opt_compression_level, 1);
+ break;
+ case BZIP2:
+ r = compression_init_encoder_bzip2(
+ &(a->archive), &(xar->stream),
+ xar->opt_compression_level);
+ break;
+ case LZMA:
+ r = compression_init_encoder_lzma(
+ &(a->archive), &(xar->stream),
+ xar->opt_compression_level);
+ break;
+ case XZ:
+ r = compression_init_encoder_xz(
+ &(a->archive), &(xar->stream),
+ xar->opt_compression_level, xar->opt_threads);
+ break;
+ default:
+ r = ARCHIVE_OK;
+ break;
+ }
+ if (r == ARCHIVE_OK) {
+ xar->stream.total_in = 0;
+ xar->stream.next_out = xar->wbuff;
+ xar->stream.avail_out = sizeof(xar->wbuff);
+ xar->stream.total_out = 0;
+ }
+
+ return (r);
+}
+
+static int
+compression_code(struct archive *a, struct la_zstream *lastrm,
+ enum la_zaction action)
+{
+ if (lastrm->valid)
+ return (lastrm->code(a, lastrm, action));
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_end(struct archive *a, struct la_zstream *lastrm)
+{
+ if (lastrm->valid)
+ return (lastrm->end(a, lastrm));
+ return (ARCHIVE_OK);
+}
+
+
+static int
+save_xattrs(struct archive_write *a, struct file *file)
+{
+ struct xar *xar;
+ const char *name;
+ const void *value;
+ struct heap_data *heap;
+ size_t size;
+ int count, r;
+
+ xar = (struct xar *)a->format_data;
+ count = archive_entry_xattr_reset(file->entry);
+ if (count == 0)
+ return (ARCHIVE_OK);
+ while (count--) {
+ archive_entry_xattr_next(file->entry,
+ &name, &value, &size);
+ checksum_init(&(xar->a_sumwrk), xar->opt_sumalg);
+ checksum_init(&(xar->e_sumwrk), xar->opt_sumalg);
+
+ heap = calloc(1, sizeof(*heap));
+ if (heap == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for xattr");
+ return (ARCHIVE_FATAL);
+ }
+ heap->id = file->ea_idx++;
+ heap->temp_offset = xar->temp_offset;
+ heap->size = size;/* save a extracted size */
+ heap->compression = xar->opt_compression;
+ /* Get a extracted sumcheck value. */
+ checksum_update(&(xar->e_sumwrk), value, size);
+ checksum_final(&(xar->e_sumwrk), &(heap->e_sum));
+
+ /*
+ * Not compression to xattr is simple way.
+ */
+ if (heap->compression == NONE) {
+ checksum_update(&(xar->a_sumwrk), value, size);
+ checksum_final(&(xar->a_sumwrk), &(heap->a_sum));
+ if (write_to_temp(a, value, size)
+ != ARCHIVE_OK) {
+ free(heap);
+ return (ARCHIVE_FATAL);
+ }
+ heap->length = size;
+ /* Add heap to the tail of file->xattr. */
+ heap->next = NULL;
+ *file->xattr.last = heap;
+ file->xattr.last = &(heap->next);
+ /* Next xattr */
+ continue;
+ }
+
+ /*
+ * Init compression library.
+ */
+ r = xar_compression_init_encoder(a);
+ if (r != ARCHIVE_OK) {
+ free(heap);
+ return (ARCHIVE_FATAL);
+ }
+
+ xar->stream.next_in = (const unsigned char *)value;
+ xar->stream.avail_in = size;
+ for (;;) {
+ r = compression_code(&(a->archive),
+ &(xar->stream), ARCHIVE_Z_FINISH);
+ if (r != ARCHIVE_OK && r != ARCHIVE_EOF) {
+ free(heap);
+ return (ARCHIVE_FATAL);
+ }
+ size = sizeof(xar->wbuff) - xar->stream.avail_out;
+ checksum_update(&(xar->a_sumwrk),
+ xar->wbuff, size);
+ if (write_to_temp(a, xar->wbuff, size)
+ != ARCHIVE_OK) {
+ free(heap);
+ return (ARCHIVE_FATAL);
+ }
+ if (r == ARCHIVE_OK) {
+ xar->stream.next_out = xar->wbuff;
+ xar->stream.avail_out = sizeof(xar->wbuff);
+ } else {
+ checksum_final(&(xar->a_sumwrk),
+ &(heap->a_sum));
+ heap->length = xar->stream.total_out;
+ /* Add heap to the tail of file->xattr. */
+ heap->next = NULL;
+ *file->xattr.last = heap;
+ file->xattr.last = &(heap->next);
+ break;
+ }
+ }
+ /* Clean up compression library. */
+ r = compression_end(&(a->archive), &(xar->stream));
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+getalgsize(enum sumalg sumalg)
+{
+ switch (sumalg) {
+ default:
+ case CKSUM_NONE:
+ return (0);
+ case CKSUM_SHA1:
+ return (SHA1_SIZE);
+ case CKSUM_MD5:
+ return (MD5_SIZE);
+ }
+}
+
+static const char *
+getalgname(enum sumalg sumalg)
+{
+ switch (sumalg) {
+ default:
+ case CKSUM_NONE:
+ return (NULL);
+ case CKSUM_SHA1:
+ return (SHA1_NAME);
+ case CKSUM_MD5:
+ return (MD5_NAME);
+ }
+}
+
+#endif /* Support xar format */
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_zip.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_zip.c
new file mode 100644
index 00000000000..5ac7e03d384
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_zip.c
@@ -0,0 +1,1693 @@
+/*-
+ * Copyright (c) 2008 Anselm Strauss
+ * Copyright (c) 2009 Joerg Sonnenberger
+ * Copyright (c) 2011-2012,2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Development supported by Google Summer of Code 2008.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_zip.c 201168 2009-12-29 06:15:32Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_cryptor_private.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_hmac_private.h"
+#include "archive_private.h"
+#include "archive_random_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+#ifndef HAVE_ZLIB_H
+#error #include "archive_crc32.h"
+#endif
+
+#define ZIP_ENTRY_FLAG_ENCRYPTED (1<<0)
+#define ZIP_ENTRY_FLAG_LENGTH_AT_END (1<<3)
+#define ZIP_ENTRY_FLAG_UTF8_NAME (1 << 11)
+
+#define ZIP_4GB_MAX ARCHIVE_LITERAL_LL(0xffffffff)
+#define ZIP_4GB_MAX_UNCOMPRESSED ARCHIVE_LITERAL_LL(0xff000000)
+
+enum compression {
+ COMPRESSION_UNSPECIFIED = -1,
+ COMPRESSION_STORE = 0,
+ COMPRESSION_DEFLATE = 8
+};
+
+#ifdef HAVE_ZLIB_H
+#define COMPRESSION_DEFAULT COMPRESSION_DEFLATE
+#else
+#define COMPRESSION_DEFAULT COMPRESSION_STORE
+#endif
+
+enum encryption {
+ ENCRYPTION_NONE = 0,
+ ENCRYPTION_TRADITIONAL, /* Traditional PKWARE encryption. */
+ ENCRYPTION_WINZIP_AES128, /* WinZIP AES-128 encryption. */
+ ENCRYPTION_WINZIP_AES256, /* WinZIP AES-256 encryption. */
+};
+
+#define TRAD_HEADER_SIZE 12
+/*
+ * See "WinZip - AES Encryption Information"
+ * http://www.winzip.com/aes_info.htm
+ */
+/* Value used in compression method. */
+#define WINZIP_AES_ENCRYPTION 99
+/* A WinZip AES header size which is stored at the beginning of
+ * file contents. */
+#define WINZIP_AES128_HEADER_SIZE (8 + 2)
+#define WINZIP_AES256_HEADER_SIZE (16 + 2)
+/* AES vendor version. */
+#define AES_VENDOR_AE_1 0x0001
+#define AES_VENDOR_AE_2 0x0002
+/* Authentication code size. */
+#define AUTH_CODE_SIZE 10
+/**/
+#define MAX_DERIVED_KEY_BUF_SIZE (AES_MAX_KEY_SIZE * 2 + 2)
+
+struct cd_segment {
+ struct cd_segment *next;
+ size_t buff_size;
+ unsigned char *buff;
+ unsigned char *p;
+};
+
+struct trad_enc_ctx {
+ uint32_t keys[3];
+};
+
+struct zip {
+
+ int64_t entry_offset;
+ int64_t entry_compressed_size;
+ int64_t entry_uncompressed_size;
+ int64_t entry_compressed_written;
+ int64_t entry_uncompressed_written;
+ int64_t entry_uncompressed_limit;
+ struct archive_entry *entry;
+ uint32_t entry_crc32;
+ enum compression entry_compression;
+ enum encryption entry_encryption;
+ int entry_flags;
+ int entry_uses_zip64;
+ int experiments;
+ struct trad_enc_ctx tctx;
+ char tctx_valid;
+ unsigned char trad_chkdat;
+ unsigned aes_vendor;
+ archive_crypto_ctx cctx;
+ char cctx_valid;
+ archive_hmac_sha1_ctx hctx;
+ char hctx_valid;
+
+ unsigned char *file_header;
+ size_t file_header_extra_offset;
+ unsigned long (*crc32func)(unsigned long crc, const void *buff, size_t len);
+
+ struct cd_segment *central_directory;
+ struct cd_segment *central_directory_last;
+ size_t central_directory_bytes;
+ size_t central_directory_entries;
+
+ int64_t written_bytes; /* Overall position in file. */
+
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_default;
+ enum compression requested_compression;
+ int deflate_compression_level;
+ int init_default_conversion;
+ enum encryption encryption_type;
+
+#define ZIP_FLAG_AVOID_ZIP64 1
+#define ZIP_FLAG_FORCE_ZIP64 2
+#define ZIP_FLAG_EXPERIMENT_xl 4
+ int flags;
+
+#ifdef HAVE_ZLIB_H
+ z_stream stream;
+#endif
+ size_t len_buf;
+ unsigned char *buf;
+};
+
+/* Don't call this min or MIN, since those are already defined
+ on lots of platforms (but not all). */
+#define zipmin(a, b) ((a) > (b) ? (b) : (a))
+
+static ssize_t archive_write_zip_data(struct archive_write *,
+ const void *buff, size_t s);
+static int archive_write_zip_close(struct archive_write *);
+static int archive_write_zip_free(struct archive_write *);
+static int archive_write_zip_finish_entry(struct archive_write *);
+static int archive_write_zip_header(struct archive_write *,
+ struct archive_entry *);
+static int archive_write_zip_options(struct archive_write *,
+ const char *, const char *);
+static unsigned int dos_time(const time_t);
+static size_t path_length(struct archive_entry *);
+static int write_path(struct archive_entry *, struct archive_write *);
+static void copy_path(struct archive_entry *, unsigned char *);
+static struct archive_string_conv *get_sconv(struct archive_write *, struct zip *);
+static int trad_enc_init(struct trad_enc_ctx *, const char *, size_t);
+static unsigned trad_enc_encrypt_update(struct trad_enc_ctx *, const uint8_t *,
+ size_t, uint8_t *, size_t);
+static int init_traditional_pkware_encryption(struct archive_write *);
+static int is_traditional_pkware_encryption_supported(void);
+static int init_winzip_aes_encryption(struct archive_write *);
+static int is_winzip_aes_encryption_supported(int encryption);
+
+static unsigned char *
+cd_alloc(struct zip *zip, size_t length)
+{
+ unsigned char *p;
+
+ if (zip->central_directory == NULL
+ || (zip->central_directory_last->p + length
+ > zip->central_directory_last->buff + zip->central_directory_last->buff_size)) {
+ struct cd_segment *segment = calloc(1, sizeof(*segment));
+ if (segment == NULL)
+ return NULL;
+ segment->buff_size = 64 * 1024;
+ segment->buff = malloc(segment->buff_size);
+ if (segment->buff == NULL) {
+ free(segment);
+ return NULL;
+ }
+ segment->p = segment->buff;
+
+ if (zip->central_directory == NULL) {
+ zip->central_directory
+ = zip->central_directory_last
+ = segment;
+ } else {
+ zip->central_directory_last->next = segment;
+ zip->central_directory_last = segment;
+ }
+ }
+
+ p = zip->central_directory_last->p;
+ zip->central_directory_last->p += length;
+ zip->central_directory_bytes += length;
+ return (p);
+}
+
+static unsigned long
+real_crc32(unsigned long crc, const void *buff, size_t len)
+{
+ return crc32(crc, buff, (unsigned int)len);
+}
+
+static unsigned long
+fake_crc32(unsigned long crc, const void *buff, size_t len)
+{
+ (void)crc; /* UNUSED */
+ (void)buff; /* UNUSED */
+ (void)len; /* UNUSED */
+ return 0;
+}
+
+static int
+archive_write_zip_options(struct archive_write *a, const char *key,
+ const char *val)
+{
+ struct zip *zip = a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ if (strcmp(key, "compression") == 0) {
+ /*
+ * Set compression to use on all future entries.
+ * This only affects regular files.
+ */
+ if (val == NULL || val[0] == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: compression option needs a compression name",
+ a->format_name);
+ } else if (strcmp(val, "deflate") == 0) {
+#ifdef HAVE_ZLIB_H
+ zip->requested_compression = COMPRESSION_DEFLATE;
+ ret = ARCHIVE_OK;
+#else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "deflate compression not supported");
+#endif
+ } else if (strcmp(val, "store") == 0) {
+ zip->requested_compression = COMPRESSION_STORE;
+ ret = ARCHIVE_OK;
+ }
+ return (ret);
+ } else if (strcmp(key, "compression-level") == 0) {
+ if (val == NULL || !(val[0] >= '0' && val[0] <= '9') || val[1] != '\0') {
+ return ARCHIVE_WARN;
+ }
+
+ if (val[0] == '0') {
+ zip->requested_compression = COMPRESSION_STORE;
+ return ARCHIVE_OK;
+ } else {
+#ifdef HAVE_ZLIB_H
+ zip->requested_compression = COMPRESSION_DEFLATE;
+ zip->deflate_compression_level = val[0] - '0';
+ return ARCHIVE_OK;
+#else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "deflate compression not supported");
+#endif
+ }
+ } else if (strcmp(key, "encryption") == 0) {
+ if (val == NULL) {
+ zip->encryption_type = ENCRYPTION_NONE;
+ ret = ARCHIVE_OK;
+ } else if (val[0] == '1' || strcmp(val, "traditional") == 0
+ || strcmp(val, "zipcrypt") == 0
+ || strcmp(val, "ZipCrypt") == 0) {
+ if (is_traditional_pkware_encryption_supported()) {
+ zip->encryption_type = ENCRYPTION_TRADITIONAL;
+ ret = ARCHIVE_OK;
+ } else {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "encryption not supported");
+ }
+ } else if (strcmp(val, "aes128") == 0) {
+ if (is_winzip_aes_encryption_supported(
+ ENCRYPTION_WINZIP_AES128)) {
+ zip->encryption_type = ENCRYPTION_WINZIP_AES128;
+ ret = ARCHIVE_OK;
+ } else {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "encryption not supported");
+ }
+ } else if (strcmp(val, "aes256") == 0) {
+ if (is_winzip_aes_encryption_supported(
+ ENCRYPTION_WINZIP_AES256)) {
+ zip->encryption_type = ENCRYPTION_WINZIP_AES256;
+ ret = ARCHIVE_OK;
+ } else {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "encryption not supported");
+ }
+ } else {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: unknown encryption '%s'",
+ a->format_name, val);
+ }
+ return (ret);
+ } else if (strcmp(key, "experimental") == 0) {
+ if (val == NULL || val[0] == 0) {
+ zip->flags &= ~ ZIP_FLAG_EXPERIMENT_xl;
+ } else {
+ zip->flags |= ZIP_FLAG_EXPERIMENT_xl;
+ }
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "fakecrc32") == 0) {
+ /*
+ * FOR TESTING ONLY: disable CRC calculation to speed up
+ * certain complex tests.
+ */
+ if (val == NULL || val[0] == 0) {
+ zip->crc32func = real_crc32;
+ } else {
+ zip->crc32func = fake_crc32;
+ }
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "hdrcharset") == 0) {
+ /*
+ * Set the character set used in translating filenames.
+ */
+ if (val == NULL || val[0] == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: hdrcharset option needs a character-set name",
+ a->format_name);
+ } else {
+ zip->opt_sconv = archive_string_conversion_to_charset(
+ &a->archive, val, 0);
+ if (zip->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ } else if (strcmp(key, "zip64") == 0) {
+ /*
+ * Bias decisions about Zip64: force them to be
+ * generated in certain cases where they are not
+ * forbidden or avoid them in certain cases where they
+ * are not strictly required.
+ */
+ if (val != NULL && *val != '\0') {
+ zip->flags |= ZIP_FLAG_FORCE_ZIP64;
+ zip->flags &= ~ZIP_FLAG_AVOID_ZIP64;
+ } else {
+ zip->flags &= ~ZIP_FLAG_FORCE_ZIP64;
+ zip->flags |= ZIP_FLAG_AVOID_ZIP64;
+ }
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+int
+archive_write_zip_set_compression_deflate(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int ret = ARCHIVE_FAILED;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW | ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_zip_set_compression_deflate");
+ if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can only use archive_write_zip_set_compression_deflate"
+ " with zip format");
+ ret = ARCHIVE_FATAL;
+ } else {
+#ifdef HAVE_ZLIB_H
+ struct zip *zip = a->format_data;
+ zip->requested_compression = COMPRESSION_DEFLATE;
+ ret = ARCHIVE_OK;
+#else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "deflate compression not supported");
+ ret = ARCHIVE_FAILED;
+#endif
+ }
+ return (ret);
+}
+
+int
+archive_write_zip_set_compression_store(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct zip *zip = a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW | ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_zip_set_compression_deflate");
+ if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can only use archive_write_zip_set_compression_store"
+ " with zip format");
+ ret = ARCHIVE_FATAL;
+ } else {
+ zip->requested_compression = COMPRESSION_STORE;
+ ret = ARCHIVE_OK;
+ }
+ return (ret);
+}
+
+int
+archive_write_set_format_zip(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct zip *zip;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_zip");
+
+ /* If another format was already registered, unregister it. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ zip = (struct zip *) calloc(1, sizeof(*zip));
+ if (zip == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate zip data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* "Unspecified" lets us choose the appropriate compression. */
+ zip->requested_compression = COMPRESSION_UNSPECIFIED;
+#ifdef HAVE_ZLIB_H
+ zip->deflate_compression_level = Z_DEFAULT_COMPRESSION;
+#endif
+ zip->crc32func = real_crc32;
+
+ /* A buffer used for both compression and encryption. */
+ zip->len_buf = 65536;
+ zip->buf = malloc(zip->len_buf);
+ if (zip->buf == NULL) {
+ free(zip);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+
+ a->format_data = zip;
+ a->format_name = "zip";
+ a->format_options = archive_write_zip_options;
+ a->format_write_header = archive_write_zip_header;
+ a->format_write_data = archive_write_zip_data;
+ a->format_finish_entry = archive_write_zip_finish_entry;
+ a->format_close = archive_write_zip_close;
+ a->format_free = archive_write_zip_free;
+ a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
+ a->archive.archive_format_name = "ZIP";
+
+ return (ARCHIVE_OK);
+}
+
+static int
+is_all_ascii(const char *p)
+{
+ const unsigned char *pp = (const unsigned char *)p;
+
+ while (*pp) {
+ if (*pp++ > 127)
+ return (0);
+ }
+ return (1);
+}
+
+static int
+archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
+{
+ unsigned char local_header[32];
+ unsigned char local_extra[144];
+ struct zip *zip = a->format_data;
+ unsigned char *e;
+ unsigned char *cd_extra;
+ size_t filename_length;
+ const char *slink = NULL;
+ size_t slink_size = 0;
+ struct archive_string_conv *sconv = get_sconv(a, zip);
+ int ret, ret2 = ARCHIVE_OK;
+ mode_t type;
+ int version_needed = 10;
+
+ /* Ignore types of entries that we don't support. */
+ type = archive_entry_filetype(entry);
+ if (type != AE_IFREG && type != AE_IFDIR && type != AE_IFLNK) {
+ __archive_write_entry_filetype_unsupported(
+ &a->archive, entry, "zip");
+ return ARCHIVE_FAILED;
+ };
+
+ /* If we're not using Zip64, reject large files. */
+ if (zip->flags & ZIP_FLAG_AVOID_ZIP64) {
+ /* Reject entries over 4GB. */
+ if (archive_entry_size_is_set(entry)
+ && (archive_entry_size(entry) > ZIP_4GB_MAX)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Files > 4GB require Zip64 extensions");
+ return ARCHIVE_FAILED;
+ }
+ /* Reject entries if archive is > 4GB. */
+ if (zip->written_bytes > ZIP_4GB_MAX) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Archives > 4GB require Zip64 extensions");
+ return ARCHIVE_FAILED;
+ }
+ }
+
+ /* Only regular files can have size > 0. */
+ if (type != AE_IFREG)
+ archive_entry_set_size(entry, 0);
+
+
+ /* Reset information from last entry. */
+ zip->entry_offset = zip->written_bytes;
+ zip->entry_uncompressed_limit = INT64_MAX;
+ zip->entry_compressed_size = 0;
+ zip->entry_uncompressed_size = 0;
+ zip->entry_compressed_written = 0;
+ zip->entry_uncompressed_written = 0;
+ zip->entry_flags = 0;
+ zip->entry_uses_zip64 = 0;
+ zip->entry_crc32 = zip->crc32func(0, NULL, 0);
+ zip->entry_encryption = 0;
+ archive_entry_free(zip->entry);
+ zip->entry = NULL;
+
+ if (zip->cctx_valid)
+ archive_encrypto_aes_ctr_release(&zip->cctx);
+ if (zip->hctx_valid)
+ archive_hmac_sha1_cleanup(&zip->hctx);
+ zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0;
+
+ if (type == AE_IFREG
+ &&(!archive_entry_size_is_set(entry)
+ || archive_entry_size(entry) > 0)) {
+ switch (zip->encryption_type) {
+ case ENCRYPTION_TRADITIONAL:
+ case ENCRYPTION_WINZIP_AES128:
+ case ENCRYPTION_WINZIP_AES256:
+ zip->entry_flags |= ZIP_ENTRY_FLAG_ENCRYPTED;
+ zip->entry_encryption = zip->encryption_type;
+ break;
+ case ENCRYPTION_NONE:
+ default:
+ break;
+ }
+ }
+
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Make sure the path separators in pathname, hardlink and symlink
+ * are all slash '/', not the Windows path separator '\'. */
+ zip->entry = __la_win_entry_in_posix_pathseparator(entry);
+ if (zip->entry == entry)
+ zip->entry = archive_entry_clone(entry);
+#else
+ zip->entry = archive_entry_clone(entry);
+#endif
+ if (zip->entry == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate zip header data");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (sconv != NULL) {
+ const char *p;
+ size_t len;
+
+ if (archive_entry_pathname_l(entry, &p, &len, sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate Pathname '%s' to %s",
+ archive_entry_pathname(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret2 = ARCHIVE_WARN;
+ }
+ if (len > 0)
+ archive_entry_set_pathname(zip->entry, p);
+
+ /*
+ * There is no standard for symlink handling; we convert
+ * it using the same character-set translation that we use
+ * for filename.
+ */
+ if (type == AE_IFLNK) {
+ if (archive_entry_symlink_l(entry, &p, &len, sconv)) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory "
+ " for Symlink");
+ return (ARCHIVE_FATAL);
+ }
+ /* No error if we can't convert. */
+ } else if (len > 0)
+ archive_entry_set_symlink(zip->entry, p);
+ }
+ }
+
+ /* If filename isn't ASCII and we can use UTF-8, set the UTF-8 flag. */
+ if (!is_all_ascii(archive_entry_pathname(zip->entry))) {
+ if (zip->opt_sconv != NULL) {
+ if (strcmp(archive_string_conversion_charset_name(
+ zip->opt_sconv), "UTF-8") == 0)
+ zip->entry_flags |= ZIP_ENTRY_FLAG_UTF8_NAME;
+#if HAVE_NL_LANGINFO
+ } else if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0) {
+ zip->entry_flags |= ZIP_ENTRY_FLAG_UTF8_NAME;
+#endif
+ }
+ }
+ filename_length = path_length(zip->entry);
+
+ /* Determine appropriate compression and size for this entry. */
+ if (type == AE_IFLNK) {
+ slink = archive_entry_symlink(zip->entry);
+ if (slink != NULL)
+ slink_size = strlen(slink);
+ else
+ slink_size = 0;
+ zip->entry_uncompressed_limit = slink_size;
+ zip->entry_compressed_size = slink_size;
+ zip->entry_uncompressed_size = slink_size;
+ zip->entry_crc32 = zip->crc32func(zip->entry_crc32,
+ (const unsigned char *)slink, slink_size);
+ zip->entry_compression = COMPRESSION_STORE;
+ version_needed = 20;
+ } else if (type != AE_IFREG) {
+ zip->entry_compression = COMPRESSION_STORE;
+ zip->entry_uncompressed_limit = 0;
+ version_needed = 20;
+ } else if (archive_entry_size_is_set(zip->entry)) {
+ int64_t size = archive_entry_size(zip->entry);
+ int64_t additional_size = 0;
+
+ zip->entry_uncompressed_limit = size;
+ zip->entry_compression = zip->requested_compression;
+ if (zip->entry_compression == COMPRESSION_UNSPECIFIED) {
+ zip->entry_compression = COMPRESSION_DEFAULT;
+ }
+ if (zip->entry_compression == COMPRESSION_STORE) {
+ zip->entry_compressed_size = size;
+ zip->entry_uncompressed_size = size;
+ version_needed = 10;
+ } else {
+ zip->entry_uncompressed_size = size;
+ version_needed = 20;
+ }
+
+ if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) {
+ switch (zip->entry_encryption) {
+ case ENCRYPTION_TRADITIONAL:
+ additional_size = TRAD_HEADER_SIZE;
+ version_needed = 20;
+ break;
+ case ENCRYPTION_WINZIP_AES128:
+ additional_size = WINZIP_AES128_HEADER_SIZE
+ + AUTH_CODE_SIZE;
+ version_needed = 20;
+ break;
+ case ENCRYPTION_WINZIP_AES256:
+ additional_size = WINZIP_AES256_HEADER_SIZE
+ + AUTH_CODE_SIZE;
+ version_needed = 20;
+ break;
+ case ENCRYPTION_NONE:
+ default:
+ break;
+ }
+ if (zip->entry_compression == COMPRESSION_STORE)
+ zip->entry_compressed_size += additional_size;
+ }
+
+ /*
+ * Set Zip64 extension in any of the following cases
+ * (this was suggested by discussion on info-zip-dev
+ * mailing list):
+ * = Zip64 is being forced by user
+ * = File is over 4GiB uncompressed
+ * (including encryption header, if any)
+ * = File is close to 4GiB and is being compressed
+ * (compression might make file larger)
+ */
+ if ((zip->flags & ZIP_FLAG_FORCE_ZIP64)
+ || (zip->entry_uncompressed_size + additional_size > ZIP_4GB_MAX)
+ || (zip->entry_uncompressed_size > ZIP_4GB_MAX_UNCOMPRESSED
+ && zip->entry_compression != COMPRESSION_STORE)) {
+ zip->entry_uses_zip64 = 1;
+ version_needed = 45;
+ }
+
+ /* We may know the size, but never the CRC. */
+ zip->entry_flags |= ZIP_ENTRY_FLAG_LENGTH_AT_END;
+ } else {
+ /* We don't know the size. Use the default
+ * compression unless specified otherwise.
+ * We enable Zip64 extensions unless we're told not to.
+ */
+
+ zip->entry_compression = zip->requested_compression;
+ if(zip->entry_compression == COMPRESSION_UNSPECIFIED){
+ zip->entry_compression = COMPRESSION_DEFAULT;
+ }
+
+ zip->entry_flags |= ZIP_ENTRY_FLAG_LENGTH_AT_END;
+ if ((zip->flags & ZIP_FLAG_AVOID_ZIP64) == 0) {
+ zip->entry_uses_zip64 = 1;
+ version_needed = 45;
+ } else if (zip->entry_compression == COMPRESSION_STORE) {
+ version_needed = 10;
+ } else {
+ version_needed = 20;
+ }
+
+ if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) {
+ switch (zip->entry_encryption) {
+ case ENCRYPTION_TRADITIONAL:
+ case ENCRYPTION_WINZIP_AES128:
+ case ENCRYPTION_WINZIP_AES256:
+ if (version_needed < 20)
+ version_needed = 20;
+ break;
+ case ENCRYPTION_NONE:
+ default:
+ break;
+ }
+ }
+ }
+
+ /* Format the local header. */
+ memset(local_header, 0, sizeof(local_header));
+ memcpy(local_header, "PK\003\004", 4);
+ archive_le16enc(local_header + 4, version_needed);
+ archive_le16enc(local_header + 6, zip->entry_flags);
+ if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128
+ || zip->entry_encryption == ENCRYPTION_WINZIP_AES256)
+ archive_le16enc(local_header + 8, WINZIP_AES_ENCRYPTION);
+ else
+ archive_le16enc(local_header + 8, zip->entry_compression);
+ archive_le32enc(local_header + 10,
+ dos_time(archive_entry_mtime(zip->entry)));
+ archive_le32enc(local_header + 14, zip->entry_crc32);
+ if (zip->entry_uses_zip64) {
+ /* Zip64 data in the local header "must" include both
+ * compressed and uncompressed sizes AND those fields
+ * are included only if these are 0xffffffff;
+ * THEREFORE these must be set this way, even if we
+ * know one of them is smaller. */
+ archive_le32enc(local_header + 18, ZIP_4GB_MAX);
+ archive_le32enc(local_header + 22, ZIP_4GB_MAX);
+ } else {
+ archive_le32enc(local_header + 18, (uint32_t)zip->entry_compressed_size);
+ archive_le32enc(local_header + 22, (uint32_t)zip->entry_uncompressed_size);
+ }
+ archive_le16enc(local_header + 26, (uint16_t)filename_length);
+
+ if (zip->entry_encryption == ENCRYPTION_TRADITIONAL) {
+ if (zip->entry_flags & ZIP_ENTRY_FLAG_LENGTH_AT_END)
+ zip->trad_chkdat = local_header[11];
+ else
+ zip->trad_chkdat = local_header[17];
+ }
+
+ /* Format as much of central directory file header as we can: */
+ zip->file_header = cd_alloc(zip, 46);
+ /* If (zip->file_header == NULL) XXXX */
+ ++zip->central_directory_entries;
+ memset(zip->file_header, 0, 46);
+ memcpy(zip->file_header, "PK\001\002", 4);
+ /* "Made by PKZip 2.0 on Unix." */
+ archive_le16enc(zip->file_header + 4, 3 * 256 + version_needed);
+ archive_le16enc(zip->file_header + 6, version_needed);
+ archive_le16enc(zip->file_header + 8, zip->entry_flags);
+ if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128
+ || zip->entry_encryption == ENCRYPTION_WINZIP_AES256)
+ archive_le16enc(zip->file_header + 10, WINZIP_AES_ENCRYPTION);
+ else
+ archive_le16enc(zip->file_header + 10, zip->entry_compression);
+ archive_le32enc(zip->file_header + 12,
+ dos_time(archive_entry_mtime(zip->entry)));
+ archive_le16enc(zip->file_header + 28, (uint16_t)filename_length);
+ /* Following Info-Zip, store mode in the "external attributes" field. */
+ archive_le32enc(zip->file_header + 38,
+ ((uint32_t)archive_entry_mode(zip->entry)) << 16);
+ e = cd_alloc(zip, filename_length);
+ /* If (e == NULL) XXXX */
+ copy_path(zip->entry, e);
+
+ /* Format extra data. */
+ memset(local_extra, 0, sizeof(local_extra));
+ e = local_extra;
+
+ /* First, extra blocks that are the same between
+ * the local file header and the central directory.
+ * We format them once and then duplicate them. */
+
+ /* UT timestamp, length depends on what timestamps are set. */
+ memcpy(e, "UT", 2);
+ archive_le16enc(e + 2,
+ 1
+ + (archive_entry_mtime_is_set(entry) ? 4 : 0)
+ + (archive_entry_atime_is_set(entry) ? 4 : 0)
+ + (archive_entry_ctime_is_set(entry) ? 4 : 0));
+ e += 4;
+ *e++ =
+ (archive_entry_mtime_is_set(entry) ? 1 : 0)
+ | (archive_entry_atime_is_set(entry) ? 2 : 0)
+ | (archive_entry_ctime_is_set(entry) ? 4 : 0);
+ if (archive_entry_mtime_is_set(entry)) {
+ archive_le32enc(e, (uint32_t)archive_entry_mtime(entry));
+ e += 4;
+ }
+ if (archive_entry_atime_is_set(entry)) {
+ archive_le32enc(e, (uint32_t)archive_entry_atime(entry));
+ e += 4;
+ }
+ if (archive_entry_ctime_is_set(entry)) {
+ archive_le32enc(e, (uint32_t)archive_entry_ctime(entry));
+ e += 4;
+ }
+
+ /* ux Unix extra data, length 11, version 1 */
+ /* TODO: If uid < 64k, use 2 bytes, ditto for gid. */
+ memcpy(e, "ux\013\000\001", 5);
+ e += 5;
+ *e++ = 4; /* Length of following UID */
+ archive_le32enc(e, (uint32_t)archive_entry_uid(entry));
+ e += 4;
+ *e++ = 4; /* Length of following GID */
+ archive_le32enc(e, (uint32_t)archive_entry_gid(entry));
+ e += 4;
+
+ /* AES extra data field: WinZIP AES information, ID=0x9901 */
+ if ((zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED)
+ && (zip->entry_encryption == ENCRYPTION_WINZIP_AES128
+ || zip->entry_encryption == ENCRYPTION_WINZIP_AES256)) {
+
+ memcpy(e, "\001\231\007\000\001\000AE", 8);
+ /* AES vendor version AE-2 does not store a CRC.
+ * WinZip 11 uses AE-1, which does store the CRC,
+ * but it does not store the CRC when the file size
+ * is less than 20 bytes. So we simulate what
+ * WinZip 11 does.
+ * NOTE: WinZip 9.0 and 10.0 uses AE-2 by default. */
+ if (archive_entry_size_is_set(zip->entry)
+ && archive_entry_size(zip->entry) < 20) {
+ archive_le16enc(e+4, AES_VENDOR_AE_2);
+ zip->aes_vendor = AES_VENDOR_AE_2;/* no CRC. */
+ } else
+ zip->aes_vendor = AES_VENDOR_AE_1;
+ e += 8;
+ /* AES encryption strength. */
+ *e++ = (zip->entry_encryption == ENCRYPTION_WINZIP_AES128)?1:3;
+ /* Actual compression method. */
+ archive_le16enc(e, zip->entry_compression);
+ e += 2;
+ }
+
+ /* Copy UT ,ux, and AES-extra into central directory as well. */
+ zip->file_header_extra_offset = zip->central_directory_bytes;
+ cd_extra = cd_alloc(zip, e - local_extra);
+ memcpy(cd_extra, local_extra, e - local_extra);
+
+ /*
+ * Following extra blocks vary between local header and
+ * central directory. These are the local header versions.
+ * Central directory versions get formatted in
+ * archive_write_zip_finish_entry() below.
+ */
+
+ /* "[Zip64 entry] in the local header MUST include BOTH
+ * original [uncompressed] and compressed size fields." */
+ if (zip->entry_uses_zip64) {
+ unsigned char *zip64_start = e;
+ memcpy(e, "\001\000\020\000", 4);
+ e += 4;
+ archive_le64enc(e, zip->entry_uncompressed_size);
+ e += 8;
+ archive_le64enc(e, zip->entry_compressed_size);
+ e += 8;
+ archive_le16enc(zip64_start + 2, (uint16_t)(e - (zip64_start + 4)));
+ }
+
+ if (zip->flags & ZIP_FLAG_EXPERIMENT_xl) {
+ /* Experimental 'xl' extension to improve streaming. */
+ unsigned char *external_info = e;
+ int included = 7;
+ memcpy(e, "xl\000\000", 4); // 0x6c65 + 2-byte length
+ e += 4;
+ e[0] = included; /* bitmap of included fields */
+ e += 1;
+ if (included & 1) {
+ archive_le16enc(e, /* "Version created by" */
+ 3 * 256 + version_needed);
+ e += 2;
+ }
+ if (included & 2) {
+ archive_le16enc(e, 0); /* internal file attributes */
+ e += 2;
+ }
+ if (included & 4) {
+ archive_le32enc(e, /* external file attributes */
+ ((uint32_t)archive_entry_mode(zip->entry)) << 16);
+ e += 4;
+ }
+ if (included & 8) {
+ // Libarchive does not currently support file comments.
+ }
+ archive_le16enc(external_info + 2, (uint16_t)(e - (external_info + 4)));
+ }
+
+ /* Update local header with size of extra data and write it all out: */
+ archive_le16enc(local_header + 28, (uint16_t)(e - local_extra));
+
+ ret = __archive_write_output(a, local_header, 30);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->written_bytes += 30;
+
+ ret = write_path(zip->entry, a);
+ if (ret <= ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->written_bytes += ret;
+
+ ret = __archive_write_output(a, local_extra, e - local_extra);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->written_bytes += e - local_extra;
+
+ /* For symlinks, write the body now. */
+ if (slink != NULL) {
+ ret = __archive_write_output(a, slink, slink_size);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->entry_compressed_written += slink_size;
+ zip->entry_uncompressed_written += slink_size;
+ zip->written_bytes += slink_size;
+ }
+
+#ifdef HAVE_ZLIB_H
+ if (zip->entry_compression == COMPRESSION_DEFLATE) {
+ zip->stream.zalloc = Z_NULL;
+ zip->stream.zfree = Z_NULL;
+ zip->stream.opaque = Z_NULL;
+ zip->stream.next_out = zip->buf;
+ zip->stream.avail_out = (uInt)zip->len_buf;
+ if (deflateInit2(&zip->stream, zip->deflate_compression_level,
+ Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't init deflate compressor");
+ return (ARCHIVE_FATAL);
+ }
+ }
+#endif
+
+ return (ret2);
+}
+
+static ssize_t
+archive_write_zip_data(struct archive_write *a, const void *buff, size_t s)
+{
+ int ret;
+ struct zip *zip = a->format_data;
+
+ if ((int64_t)s > zip->entry_uncompressed_limit)
+ s = (size_t)zip->entry_uncompressed_limit;
+ zip->entry_uncompressed_written += s;
+
+ if (s == 0) return 0;
+
+ if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) {
+ switch (zip->entry_encryption) {
+ case ENCRYPTION_TRADITIONAL:
+ /* Initialize traditional PKWARE encryption context. */
+ if (!zip->tctx_valid) {
+ ret = init_traditional_pkware_encryption(a);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->tctx_valid = 1;
+ }
+ break;
+ case ENCRYPTION_WINZIP_AES128:
+ case ENCRYPTION_WINZIP_AES256:
+ if (!zip->cctx_valid) {
+ ret = init_winzip_aes_encryption(a);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->cctx_valid = zip->hctx_valid = 1;
+ }
+ break;
+ case ENCRYPTION_NONE:
+ default:
+ break;
+ }
+ }
+
+ switch (zip->entry_compression) {
+ case COMPRESSION_STORE:
+ if (zip->tctx_valid || zip->cctx_valid) {
+ const uint8_t *rb = (const uint8_t *)buff;
+ const uint8_t * const re = rb + s;
+
+ while (rb < re) {
+ size_t l;
+
+ if (zip->tctx_valid) {
+ l = trad_enc_encrypt_update(&zip->tctx,
+ rb, re - rb,
+ zip->buf, zip->len_buf);
+ } else {
+ l = zip->len_buf;
+ ret = archive_encrypto_aes_ctr_update(
+ &zip->cctx,
+ rb, re - rb, zip->buf, &l);
+ if (ret < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to encrypt file");
+ return (ARCHIVE_FAILED);
+ }
+ archive_hmac_sha1_update(&zip->hctx,
+ zip->buf, l);
+ }
+ ret = __archive_write_output(a, zip->buf, l);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->entry_compressed_written += l;
+ zip->written_bytes += l;
+ rb += l;
+ }
+ } else {
+ ret = __archive_write_output(a, buff, s);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->written_bytes += s;
+ zip->entry_compressed_written += s;
+ }
+ break;
+#if HAVE_ZLIB_H
+ case COMPRESSION_DEFLATE:
+ zip->stream.next_in = (unsigned char*)(uintptr_t)buff;
+ zip->stream.avail_in = (uInt)s;
+ do {
+ ret = deflate(&zip->stream, Z_NO_FLUSH);
+ if (ret == Z_STREAM_ERROR)
+ return (ARCHIVE_FATAL);
+ if (zip->stream.avail_out == 0) {
+ if (zip->tctx_valid) {
+ trad_enc_encrypt_update(&zip->tctx,
+ zip->buf, zip->len_buf,
+ zip->buf, zip->len_buf);
+ } else if (zip->cctx_valid) {
+ size_t outl = zip->len_buf;
+ ret = archive_encrypto_aes_ctr_update(
+ &zip->cctx,
+ zip->buf, zip->len_buf,
+ zip->buf, &outl);
+ if (ret < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to encrypt file");
+ return (ARCHIVE_FAILED);
+ }
+ archive_hmac_sha1_update(&zip->hctx,
+ zip->buf, zip->len_buf);
+ }
+ ret = __archive_write_output(a, zip->buf,
+ zip->len_buf);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->entry_compressed_written += zip->len_buf;
+ zip->written_bytes += zip->len_buf;
+ zip->stream.next_out = zip->buf;
+ zip->stream.avail_out = (uInt)zip->len_buf;
+ }
+ } while (zip->stream.avail_in != 0);
+ break;
+#endif
+
+ case COMPRESSION_UNSPECIFIED:
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid ZIP compression type");
+ return ARCHIVE_FATAL;
+ }
+
+ zip->entry_uncompressed_limit -= s;
+ if (!zip->cctx_valid || zip->aes_vendor != AES_VENDOR_AE_2)
+ zip->entry_crc32 =
+ zip->crc32func(zip->entry_crc32, buff, (unsigned)s);
+ return (s);
+
+}
+
+static int
+archive_write_zip_finish_entry(struct archive_write *a)
+{
+ struct zip *zip = a->format_data;
+ int ret;
+
+#if HAVE_ZLIB_H
+ if (zip->entry_compression == COMPRESSION_DEFLATE) {
+ for (;;) {
+ size_t remainder;
+
+ ret = deflate(&zip->stream, Z_FINISH);
+ if (ret == Z_STREAM_ERROR)
+ return (ARCHIVE_FATAL);
+ remainder = zip->len_buf - zip->stream.avail_out;
+ if (zip->tctx_valid) {
+ trad_enc_encrypt_update(&zip->tctx,
+ zip->buf, remainder, zip->buf, remainder);
+ } else if (zip->cctx_valid) {
+ size_t outl = remainder;
+ ret = archive_encrypto_aes_ctr_update(
+ &zip->cctx, zip->buf, remainder,
+ zip->buf, &outl);
+ if (ret < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to encrypt file");
+ return (ARCHIVE_FAILED);
+ }
+ archive_hmac_sha1_update(&zip->hctx,
+ zip->buf, remainder);
+ }
+ ret = __archive_write_output(a, zip->buf, remainder);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->entry_compressed_written += remainder;
+ zip->written_bytes += remainder;
+ zip->stream.next_out = zip->buf;
+ if (zip->stream.avail_out != 0)
+ break;
+ zip->stream.avail_out = (uInt)zip->len_buf;
+ }
+ deflateEnd(&zip->stream);
+ }
+#endif
+ if (zip->hctx_valid) {
+ uint8_t hmac[20];
+ size_t hmac_len = 20;
+
+ archive_hmac_sha1_final(&zip->hctx, hmac, &hmac_len);
+ ret = __archive_write_output(a, hmac, AUTH_CODE_SIZE);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->entry_compressed_written += AUTH_CODE_SIZE;
+ zip->written_bytes += AUTH_CODE_SIZE;
+ }
+
+ /* Write trailing data descriptor. */
+ if ((zip->entry_flags & ZIP_ENTRY_FLAG_LENGTH_AT_END) != 0) {
+ char d[24];
+ memcpy(d, "PK\007\010", 4);
+ if (zip->cctx_valid && zip->aes_vendor == AES_VENDOR_AE_2)
+ archive_le32enc(d + 4, 0);/* no CRC.*/
+ else
+ archive_le32enc(d + 4, zip->entry_crc32);
+ if (zip->entry_uses_zip64) {
+ archive_le64enc(d + 8,
+ (uint64_t)zip->entry_compressed_written);
+ archive_le64enc(d + 16,
+ (uint64_t)zip->entry_uncompressed_written);
+ ret = __archive_write_output(a, d, 24);
+ zip->written_bytes += 24;
+ } else {
+ archive_le32enc(d + 8,
+ (uint32_t)zip->entry_compressed_written);
+ archive_le32enc(d + 12,
+ (uint32_t)zip->entry_uncompressed_written);
+ ret = __archive_write_output(a, d, 16);
+ zip->written_bytes += 16;
+ }
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Append Zip64 extra data to central directory information. */
+ if (zip->entry_compressed_written > ZIP_4GB_MAX
+ || zip->entry_uncompressed_written > ZIP_4GB_MAX
+ || zip->entry_offset > ZIP_4GB_MAX) {
+ unsigned char zip64[32];
+ unsigned char *z = zip64, *zd;
+ memcpy(z, "\001\000\000\000", 4);
+ z += 4;
+ if (zip->entry_uncompressed_written >= ZIP_4GB_MAX) {
+ archive_le64enc(z, zip->entry_uncompressed_written);
+ z += 8;
+ }
+ if (zip->entry_compressed_written >= ZIP_4GB_MAX) {
+ archive_le64enc(z, zip->entry_compressed_written);
+ z += 8;
+ }
+ if (zip->entry_offset >= ZIP_4GB_MAX) {
+ archive_le64enc(z, zip->entry_offset);
+ z += 8;
+ }
+ archive_le16enc(zip64 + 2, (uint16_t)(z - (zip64 + 4)));
+ zd = cd_alloc(zip, z - zip64);
+ if (zd == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate zip data");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(zd, zip64, z - zip64);
+ /* Zip64 means version needs to be set to at least 4.5 */
+ if (archive_le16dec(zip->file_header + 6) < 45)
+ archive_le16enc(zip->file_header + 6, 45);
+ }
+
+ /* Fix up central directory file header. */
+ if (zip->cctx_valid && zip->aes_vendor == AES_VENDOR_AE_2)
+ archive_le32enc(zip->file_header + 16, 0);/* no CRC.*/
+ else
+ archive_le32enc(zip->file_header + 16, zip->entry_crc32);
+ archive_le32enc(zip->file_header + 20,
+ (uint32_t)zipmin(zip->entry_compressed_written,
+ ZIP_4GB_MAX));
+ archive_le32enc(zip->file_header + 24,
+ (uint32_t)zipmin(zip->entry_uncompressed_written,
+ ZIP_4GB_MAX));
+ archive_le16enc(zip->file_header + 30,
+ (uint16_t)(zip->central_directory_bytes - zip->file_header_extra_offset));
+ archive_le32enc(zip->file_header + 42,
+ (uint32_t)zipmin(zip->entry_offset,
+ ZIP_4GB_MAX));
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_zip_close(struct archive_write *a)
+{
+ uint8_t buff[64];
+ int64_t offset_start, offset_end;
+ struct zip *zip = a->format_data;
+ struct cd_segment *segment;
+ int ret;
+
+ offset_start = zip->written_bytes;
+ segment = zip->central_directory;
+ while (segment != NULL) {
+ ret = __archive_write_output(a,
+ segment->buff, segment->p - segment->buff);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->written_bytes += segment->p - segment->buff;
+ segment = segment->next;
+ }
+ offset_end = zip->written_bytes;
+
+ /* If central dir info is too large, write Zip64 end-of-cd */
+ if (offset_end - offset_start > ZIP_4GB_MAX
+ || offset_start > ZIP_4GB_MAX
+ || zip->central_directory_entries > 0xffffUL
+ || (zip->flags & ZIP_FLAG_FORCE_ZIP64)) {
+ /* Zip64 end-of-cd record */
+ memset(buff, 0, 56);
+ memcpy(buff, "PK\006\006", 4);
+ archive_le64enc(buff + 4, 44);
+ archive_le16enc(buff + 12, 45);
+ archive_le16enc(buff + 14, 45);
+ /* This is disk 0 of 0. */
+ archive_le64enc(buff + 24, zip->central_directory_entries);
+ archive_le64enc(buff + 32, zip->central_directory_entries);
+ archive_le64enc(buff + 40, offset_end - offset_start);
+ archive_le64enc(buff + 48, offset_start);
+ ret = __archive_write_output(a, buff, 56);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->written_bytes += 56;
+
+ /* Zip64 end-of-cd locator record. */
+ memset(buff, 0, 20);
+ memcpy(buff, "PK\006\007", 4);
+ archive_le32enc(buff + 4, 0);
+ archive_le64enc(buff + 8, offset_end);
+ archive_le32enc(buff + 16, 1);
+ ret = __archive_write_output(a, buff, 20);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->written_bytes += 20;
+
+ }
+
+ /* Format and write end of central directory. */
+ memset(buff, 0, sizeof(buff));
+ memcpy(buff, "PK\005\006", 4);
+ archive_le16enc(buff + 8, (uint16_t)zipmin(0xffffU,
+ zip->central_directory_entries));
+ archive_le16enc(buff + 10, (uint16_t)zipmin(0xffffU,
+ zip->central_directory_entries));
+ archive_le32enc(buff + 12,
+ (uint32_t)zipmin(ZIP_4GB_MAX, (offset_end - offset_start)));
+ archive_le32enc(buff + 16,
+ (uint32_t)zipmin(ZIP_4GB_MAX, offset_start));
+ ret = __archive_write_output(a, buff, 22);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->written_bytes += 22;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_zip_free(struct archive_write *a)
+{
+ struct zip *zip;
+ struct cd_segment *segment;
+
+ zip = a->format_data;
+ while (zip->central_directory != NULL) {
+ segment = zip->central_directory;
+ zip->central_directory = segment->next;
+ free(segment->buff);
+ free(segment);
+ }
+ free(zip->buf);
+ archive_entry_free(zip->entry);
+ if (zip->cctx_valid)
+ archive_encrypto_aes_ctr_release(&zip->cctx);
+ if (zip->hctx_valid)
+ archive_hmac_sha1_cleanup(&zip->hctx);
+ /* TODO: Free opt_sconv, sconv_default */
+
+ free(zip);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+/* Convert into MSDOS-style date/time. */
+static unsigned int
+dos_time(const time_t unix_time)
+{
+ struct tm *t;
+ unsigned int dt;
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
+ struct tm tmbuf;
+#endif
+
+#if defined(HAVE_LOCALTIME_S)
+ t = localtime_s(&tmbuf, &unix_time) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+ t = localtime_r(&unix_time, &tmbuf);
+#else
+ t = localtime(&unix_time);
+#endif
+
+ /* MSDOS-style date/time is only between 1980-01-01 and 2107-12-31 */
+ if (t->tm_year < 1980 - 1900)
+ /* Set minimum date/time '1980-01-01 00:00:00'. */
+ dt = 0x00210000U;
+ else if (t->tm_year > 2107 - 1900)
+ /* Set maximum date/time '2107-12-31 23:59:58'. */
+ dt = 0xff9fbf7dU;
+ else {
+ dt = 0;
+ dt += ((t->tm_year - 80) & 0x7f) << 9;
+ dt += ((t->tm_mon + 1) & 0x0f) << 5;
+ dt += (t->tm_mday & 0x1f);
+ dt <<= 16;
+ dt += (t->tm_hour & 0x1f) << 11;
+ dt += (t->tm_min & 0x3f) << 5;
+ dt += (t->tm_sec & 0x3e) >> 1; /* Only counting every 2 seconds. */
+ }
+ return dt;
+}
+
+static size_t
+path_length(struct archive_entry *entry)
+{
+ mode_t type;
+ const char *path;
+ size_t len;
+
+ type = archive_entry_filetype(entry);
+ path = archive_entry_pathname(entry);
+
+ if (path == NULL)
+ return (0);
+ len = strlen(path);
+ if (type == AE_IFDIR && (path[0] == '\0' || path[len - 1] != '/'))
+ ++len; /* Space for the trailing / */
+ return len;
+}
+
+static int
+write_path(struct archive_entry *entry, struct archive_write *archive)
+{
+ int ret;
+ const char *path;
+ mode_t type;
+ size_t written_bytes;
+
+ path = archive_entry_pathname(entry);
+ type = archive_entry_filetype(entry);
+ written_bytes = 0;
+
+ if (path == NULL)
+ return (ARCHIVE_FATAL);
+
+ ret = __archive_write_output(archive, path, strlen(path));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ written_bytes += strlen(path);
+
+ /* Folders are recognized by a trailing slash. */
+ if ((type == AE_IFDIR) & (path[strlen(path) - 1] != '/')) {
+ ret = __archive_write_output(archive, "/", 1);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ written_bytes += 1;
+ }
+
+ return ((int)written_bytes);
+}
+
+static void
+copy_path(struct archive_entry *entry, unsigned char *p)
+{
+ const char *path;
+ size_t pathlen;
+ mode_t type;
+
+ path = archive_entry_pathname(entry);
+ pathlen = strlen(path);
+ type = archive_entry_filetype(entry);
+
+ memcpy(p, path, pathlen);
+
+ /* Folders are recognized by a trailing slash. */
+ if ((type == AE_IFDIR) && (path[pathlen - 1] != '/'))
+ p[pathlen] = '/';
+}
+
+
+static struct archive_string_conv *
+get_sconv(struct archive_write *a, struct zip *zip)
+{
+ if (zip->opt_sconv != NULL)
+ return (zip->opt_sconv);
+
+ if (!zip->init_default_conversion) {
+ zip->sconv_default =
+ archive_string_default_conversion_for_write(&(a->archive));
+ zip->init_default_conversion = 1;
+ }
+ return (zip->sconv_default);
+}
+
+/*
+ Traditional PKWARE Decryption functions.
+ */
+
+static void
+trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c)
+{
+ uint8_t t;
+#define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL)
+
+ ctx->keys[0] = CRC32(ctx->keys[0], c);
+ ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1;
+ t = (ctx->keys[1] >> 24) & 0xff;
+ ctx->keys[2] = CRC32(ctx->keys[2], t);
+#undef CRC32
+}
+
+static uint8_t
+trad_enc_decrypt_byte(struct trad_enc_ctx *ctx)
+{
+ unsigned temp = ctx->keys[2] | 2;
+ return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff;
+}
+
+static unsigned
+trad_enc_encrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out, size_t out_len)
+{
+ unsigned i, max;
+
+ max = (unsigned)((in_len < out_len)? in_len: out_len);
+
+ for (i = 0; i < max; i++) {
+ uint8_t t = in[i];
+ out[i] = t ^ trad_enc_decrypt_byte(ctx);
+ trad_enc_update_keys(ctx, t);
+ }
+ return i;
+}
+
+static int
+trad_enc_init(struct trad_enc_ctx *ctx, const char *pw, size_t pw_len)
+{
+
+ ctx->keys[0] = 305419896L;
+ ctx->keys[1] = 591751049L;
+ ctx->keys[2] = 878082192L;
+
+ for (;pw_len; --pw_len)
+ trad_enc_update_keys(ctx, *pw++);
+ return 0;
+}
+
+static int
+is_traditional_pkware_encryption_supported(void)
+{
+ uint8_t key[TRAD_HEADER_SIZE];
+
+ if (archive_random(key, sizeof(key)-1) != ARCHIVE_OK)
+ return (0);
+ return (1);
+}
+
+static int
+init_traditional_pkware_encryption(struct archive_write *a)
+{
+ struct zip *zip = a->format_data;
+ const char *passphrase;
+ uint8_t key[TRAD_HEADER_SIZE];
+ uint8_t key_encrypted[TRAD_HEADER_SIZE];
+ int ret;
+
+ passphrase = __archive_write_get_passphrase(a);
+ if (passphrase == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Encryption needs passphrase");
+ return ARCHIVE_FAILED;
+ }
+ if (archive_random(key, sizeof(key)-1) != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't generate random number for encryption");
+ return ARCHIVE_FATAL;
+ }
+ trad_enc_init(&zip->tctx, passphrase, strlen(passphrase));
+ /* Set the last key code which will be used as a check code
+ * for verifying passphrase in decryption. */
+ key[TRAD_HEADER_SIZE-1] = zip->trad_chkdat;
+ trad_enc_encrypt_update(&zip->tctx, key, TRAD_HEADER_SIZE,
+ key_encrypted, TRAD_HEADER_SIZE);
+ /* Write encrypted keys in the top of the file content. */
+ ret = __archive_write_output(a, key_encrypted, TRAD_HEADER_SIZE);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->written_bytes += TRAD_HEADER_SIZE;
+ zip->entry_compressed_written += TRAD_HEADER_SIZE;
+ return (ret);
+}
+
+static int
+init_winzip_aes_encryption(struct archive_write *a)
+{
+ struct zip *zip = a->format_data;
+ const char *passphrase;
+ size_t key_len, salt_len;
+ uint8_t salt[16 + 2];
+ uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE];
+ int ret;
+
+ passphrase = __archive_write_get_passphrase(a);
+ if (passphrase == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Encryption needs passphrase");
+ return (ARCHIVE_FAILED);
+ }
+ if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128) {
+ salt_len = 8;
+ key_len = 16;
+ } else {
+ /* AES 256 */
+ salt_len = 16;
+ key_len = 32;
+ }
+ if (archive_random(salt, salt_len) != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't generate random number for encryption");
+ return (ARCHIVE_FATAL);
+ }
+ archive_pbkdf2_sha1(passphrase, strlen(passphrase),
+ salt, salt_len, 1000, derived_key, key_len * 2 + 2);
+
+ ret = archive_encrypto_aes_ctr_init(&zip->cctx, derived_key, key_len);
+ if (ret != 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Decryption is unsupported due to lack of crypto library");
+ return (ARCHIVE_FAILED);
+ }
+ ret = archive_hmac_sha1_init(&zip->hctx, derived_key + key_len,
+ key_len);
+ if (ret != 0) {
+ archive_encrypto_aes_ctr_release(&zip->cctx);
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to initialize HMAC-SHA1");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Set a password verification value after the 'salt'. */
+ salt[salt_len] = derived_key[key_len * 2];
+ salt[salt_len + 1] = derived_key[key_len * 2 + 1];
+
+ /* Write encrypted keys in the top of the file content. */
+ ret = __archive_write_output(a, salt, salt_len + 2);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->written_bytes += salt_len + 2;
+ zip->entry_compressed_written += salt_len + 2;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+is_winzip_aes_encryption_supported(int encryption)
+{
+ size_t key_len, salt_len;
+ uint8_t salt[16 + 2];
+ uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE];
+ archive_crypto_ctx cctx;
+ archive_hmac_sha1_ctx hctx;
+ int ret;
+
+ if (encryption == ENCRYPTION_WINZIP_AES128) {
+ salt_len = 8;
+ key_len = 16;
+ } else {
+ /* AES 256 */
+ salt_len = 16;
+ key_len = 32;
+ }
+ if (archive_random(salt, salt_len) != ARCHIVE_OK)
+ return (0);
+ ret = archive_pbkdf2_sha1("p", 1, salt, salt_len, 1000,
+ derived_key, key_len * 2 + 2);
+ if (ret != 0)
+ return (0);
+
+ ret = archive_encrypto_aes_ctr_init(&cctx, derived_key, key_len);
+ if (ret != 0)
+ return (0);
+ ret = archive_hmac_sha1_init(&hctx, derived_key + key_len,
+ key_len);
+ archive_encrypto_aes_ctr_release(&cctx);
+ if (ret != 0)
+ return (0);
+ archive_hmac_sha1_cleanup(&hctx);
+ return (1);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_options.c b/contrib/libs/libarchive/libarchive/archive_write_set_options.c
new file mode 100644
index 00000000000..962309ada5c
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_options.c
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive_write_private.h"
+#include "archive_options_private.h"
+
+static int archive_set_format_option(struct archive *a,
+ const char *m, const char *o, const char *v);
+static int archive_set_filter_option(struct archive *a,
+ const char *m, const char *o, const char *v);
+static int archive_set_option(struct archive *a,
+ const char *m, const char *o, const char *v);
+
+int
+archive_write_set_format_option(struct archive *a, const char *m, const char *o,
+ const char *v)
+{
+ return _archive_set_option(a, m, o, v,
+ ARCHIVE_WRITE_MAGIC, "archive_write_set_format_option",
+ archive_set_format_option);
+}
+
+int
+archive_write_set_filter_option(struct archive *a, const char *m, const char *o,
+ const char *v)
+{
+ return _archive_set_option(a, m, o, v,
+ ARCHIVE_WRITE_MAGIC, "archive_write_set_filter_option",
+ archive_set_filter_option);
+}
+
+int
+archive_write_set_option(struct archive *a, const char *m, const char *o,
+ const char *v)
+{
+ return _archive_set_option(a, m, o, v,
+ ARCHIVE_WRITE_MAGIC, "archive_write_set_option",
+ archive_set_option);
+}
+
+int
+archive_write_set_options(struct archive *a, const char *options)
+{
+ return _archive_set_options(a, options,
+ ARCHIVE_WRITE_MAGIC, "archive_write_set_options",
+ archive_set_option);
+}
+
+static int
+archive_set_format_option(struct archive *_a, const char *m, const char *o,
+ const char *v)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+
+ if (a->format_name == NULL)
+ return (m == NULL)?ARCHIVE_FAILED:ARCHIVE_WARN - 1;
+ /* If the format name didn't match, return a special code for
+ * _archive_set_option[s]. */
+ if (m != NULL && strcmp(m, a->format_name) != 0)
+ return (ARCHIVE_WARN - 1);
+ if (a->format_options == NULL)
+ return (ARCHIVE_WARN);
+ return a->format_options(a, o, v);
+}
+
+static int
+archive_set_filter_option(struct archive *_a, const char *m, const char *o,
+ const char *v)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *filter;
+ int r, rv = ARCHIVE_WARN;
+
+ for (filter = a->filter_first; filter != NULL; filter = filter->next_filter) {
+ if (filter->options == NULL)
+ continue;
+ if (m != NULL && strcmp(filter->name, m) != 0)
+ continue;
+
+ r = filter->options(filter, o, v);
+
+ if (r == ARCHIVE_FATAL)
+ return (ARCHIVE_FATAL);
+
+ if (m != NULL)
+ return (r);
+
+ if (r == ARCHIVE_OK)
+ rv = ARCHIVE_OK;
+ }
+ /* If the filter name didn't match, return a special code for
+ * _archive_set_option[s]. */
+ if (rv == ARCHIVE_WARN && m != NULL)
+ rv = ARCHIVE_WARN - 1;
+ return (rv);
+}
+
+static int
+archive_set_option(struct archive *a, const char *m, const char *o,
+ const char *v)
+{
+ return _archive_set_either_option(a, m, o, v,
+ archive_set_format_option,
+ archive_set_filter_option);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_passphrase.c b/contrib/libs/libarchive/libarchive/archive_write_set_passphrase.c
new file mode 100644
index 00000000000..710ecba52c3
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_passphrase.c
@@ -0,0 +1,95 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "archive_write_private.h"
+
+int
+archive_write_set_passphrase(struct archive *_a, const char *p)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_write_set_passphrase");
+
+ if (p == NULL || p[0] == '\0') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Empty passphrase is unacceptable");
+ return (ARCHIVE_FAILED);
+ }
+ free(a->passphrase);
+ a->passphrase = strdup(p);
+ if (a->passphrase == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate data for passphrase");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+
+int
+archive_write_set_passphrase_callback(struct archive *_a, void *client_data,
+ archive_passphrase_callback *cb)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_write_set_passphrase_callback");
+
+ a->passphrase_callback = cb;
+ a->passphrase_client_data = client_data;
+ return (ARCHIVE_OK);
+}
+
+
+const char *
+__archive_write_get_passphrase(struct archive_write *a)
+{
+
+ if (a->passphrase != NULL)
+ return (a->passphrase);
+
+ if (a->passphrase_callback != NULL) {
+ const char *p;
+ p = a->passphrase_callback(&a->archive,
+ a->passphrase_client_data);
+ if (p != NULL) {
+ a->passphrase = strdup(p);
+ if (a->passphrase == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate data for passphrase");
+ return (NULL);
+ }
+ return (a->passphrase);
+ }
+ }
+ return (NULL);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_xxhash.h b/contrib/libs/libarchive/libarchive/archive_xxhash.h
new file mode 100644
index 00000000000..1c7131ca1e7
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_xxhash.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef ARCHIVE_XXHASH_H_INCLUDED
+#define ARCHIVE_XXHASH_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+
+typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;
+
+struct archive_xxhash {
+ unsigned int (*XXH32)(const void* input, unsigned int len,
+ unsigned int seed);
+ void* (*XXH32_init)(unsigned int seed);
+ XXH_errorcode (*XXH32_update)(void* state, const void* input,
+ unsigned int len);
+ unsigned int (*XXH32_digest)(void* state);
+};
+
+extern const struct archive_xxhash __archive_xxhash;
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/filter_fork.h b/contrib/libs/libarchive/libarchive/filter_fork.h
new file mode 100644
index 00000000000..2bf290c4d9e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/filter_fork.h
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: head/lib/libarchive/filter_fork.h 201087 2009-12-28 02:18:26Z kientzle $
+ */
+
+#ifndef FILTER_FORK_H
+#define FILTER_FORK_H
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+int
+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ HANDLE *out_child);
+#else
+ pid_t *out_child);
+#endif
+
+void
+__archive_check_child(int in, int out);
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/filter_fork_posix.c b/contrib/libs/libarchive/libarchive/filter_fork_posix.c
new file mode 100644
index 00000000000..62085a7099b
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/filter_fork_posix.c
@@ -0,0 +1,240 @@
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+/* This capability is only available on POSIX systems. */
+#if defined(HAVE_PIPE) && defined(HAVE_FCNTL) && \
+ (defined(HAVE_FORK) || defined(HAVE_VFORK) || defined(HAVE_POSIX_SPAWNP))
+
+__FBSDID("$FreeBSD: head/lib/libarchive/filter_fork.c 182958 2008-09-12 05:33:00Z kientzle $");
+
+#if defined(HAVE_SYS_TYPES_H)
+# include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+# include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+#if defined(HAVE_POLL) && (defined(HAVE_POLL_H) || defined(HAVE_SYS_POLL_H))
+# if defined(HAVE_POLL_H)
+# include <poll.h>
+# elif defined(HAVE_SYS_POLL_H)
+# include <sys/poll.h>
+# endif
+#elif defined(HAVE_SELECT)
+# if defined(HAVE_SYS_SELECT_H)
+# include <sys/select.h>
+# elif defined(HAVE_UNISTD_H)
+# include <unistd.h>
+# endif
+#endif
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#ifdef HAVE_SPAWN_H
+# include <spawn.h>
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_cmdline_private.h"
+
+#include "filter_fork.h"
+
+int
+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,
+ pid_t *out_child)
+{
+ pid_t child = -1;
+ int stdin_pipe[2], stdout_pipe[2], tmp;
+#if HAVE_POSIX_SPAWNP
+ posix_spawn_file_actions_t actions;
+ int r;
+#endif
+ struct archive_cmdline *cmdline;
+
+ cmdline = __archive_cmdline_allocate();
+ if (cmdline == NULL)
+ goto state_allocated;
+ if (__archive_cmdline_parse(cmdline, cmd) != ARCHIVE_OK)
+ goto state_allocated;
+
+ if (pipe(stdin_pipe) == -1)
+ goto state_allocated;
+ if (stdin_pipe[0] == 1 /* stdout */) {
+ if ((tmp = dup(stdin_pipe[0])) == -1)
+ goto stdin_opened;
+ close(stdin_pipe[0]);
+ stdin_pipe[0] = tmp;
+ }
+ if (pipe(stdout_pipe) == -1)
+ goto stdin_opened;
+ if (stdout_pipe[1] == 0 /* stdin */) {
+ if ((tmp = dup(stdout_pipe[1])) == -1)
+ goto stdout_opened;
+ close(stdout_pipe[1]);
+ stdout_pipe[1] = tmp;
+ }
+
+#if HAVE_POSIX_SPAWNP
+
+ r = posix_spawn_file_actions_init(&actions);
+ if (r != 0) {
+ errno = r;
+ goto stdout_opened;
+ }
+ r = posix_spawn_file_actions_addclose(&actions, stdin_pipe[1]);
+ if (r != 0)
+ goto actions_inited;
+ r = posix_spawn_file_actions_addclose(&actions, stdout_pipe[0]);
+ if (r != 0)
+ goto actions_inited;
+ /* Setup for stdin. */
+ r = posix_spawn_file_actions_adddup2(&actions, stdin_pipe[0], 0);
+ if (r != 0)
+ goto actions_inited;
+ if (stdin_pipe[0] != 0 /* stdin */) {
+ r = posix_spawn_file_actions_addclose(&actions, stdin_pipe[0]);
+ if (r != 0)
+ goto actions_inited;
+ }
+ /* Setup for stdout. */
+ r = posix_spawn_file_actions_adddup2(&actions, stdout_pipe[1], 1);
+ if (r != 0)
+ goto actions_inited;
+ if (stdout_pipe[1] != 1 /* stdout */) {
+ r = posix_spawn_file_actions_addclose(&actions, stdout_pipe[1]);
+ if (r != 0)
+ goto actions_inited;
+ }
+ r = posix_spawnp(&child, cmdline->path, &actions, NULL,
+ cmdline->argv, NULL);
+ if (r != 0)
+ goto actions_inited;
+ posix_spawn_file_actions_destroy(&actions);
+
+#else /* HAVE_POSIX_SPAWNP */
+
+#if HAVE_VFORK
+ child = vfork();
+#else
+ child = fork();
+#endif
+ if (child == -1)
+ goto stdout_opened;
+ if (child == 0) {
+ close(stdin_pipe[1]);
+ close(stdout_pipe[0]);
+ if (dup2(stdin_pipe[0], 0 /* stdin */) == -1)
+ _exit(254);
+ if (stdin_pipe[0] != 0 /* stdin */)
+ close(stdin_pipe[0]);
+ if (dup2(stdout_pipe[1], 1 /* stdout */) == -1)
+ _exit(254);
+ if (stdout_pipe[1] != 1 /* stdout */)
+ close(stdout_pipe[1]);
+ execvp(cmdline->path, cmdline->argv);
+ _exit(254);
+ }
+#endif /* HAVE_POSIX_SPAWNP */
+
+ close(stdin_pipe[0]);
+ close(stdout_pipe[1]);
+
+ *child_stdin = stdin_pipe[1];
+ fcntl(*child_stdin, F_SETFL, O_NONBLOCK);
+ *child_stdout = stdout_pipe[0];
+ fcntl(*child_stdout, F_SETFL, O_NONBLOCK);
+ __archive_cmdline_free(cmdline);
+
+ *out_child = child;
+ return ARCHIVE_OK;
+
+#if HAVE_POSIX_SPAWNP
+actions_inited:
+ errno = r;
+ posix_spawn_file_actions_destroy(&actions);
+#endif
+stdout_opened:
+ close(stdout_pipe[0]);
+ close(stdout_pipe[1]);
+stdin_opened:
+ close(stdin_pipe[0]);
+ close(stdin_pipe[1]);
+state_allocated:
+ __archive_cmdline_free(cmdline);
+ return ARCHIVE_FAILED;
+}
+
+void
+__archive_check_child(int in, int out)
+{
+#if defined(HAVE_POLL) && (defined(HAVE_POLL_H) || defined(HAVE_SYS_POLL_H))
+ struct pollfd fds[2];
+ int idx;
+
+ idx = 0;
+ if (in != -1) {
+ fds[idx].fd = in;
+ fds[idx].events = POLLOUT;
+ ++idx;
+ }
+ if (out != -1) {
+ fds[idx].fd = out;
+ fds[idx].events = POLLIN;
+ ++idx;
+ }
+
+ poll(fds, idx, -1); /* -1 == INFTIM, wait forever */
+#elif defined(HAVE_SELECT)
+ fd_set fds_in, fds_out, fds_error;
+
+ FD_ZERO(&fds_in);
+ FD_ZERO(&fds_out);
+ FD_ZERO(&fds_error);
+ if (out != -1) {
+ FD_SET(out, &fds_in);
+ FD_SET(out, &fds_error);
+ }
+ if (in != -1) {
+ FD_SET(in, &fds_out);
+ FD_SET(in, &fds_error);
+ }
+ select(in < out ? out + 1 : in + 1, &fds_in, &fds_out, &fds_error, NULL);
+#else
+ sleep(1);
+#endif
+}
+
+#endif /* defined(HAVE_PIPE) && defined(HAVE_VFORK) && defined(HAVE_FCNTL) */
diff --git a/contrib/libs/libarchive/libarchive/filter_fork_windows.c b/contrib/libs/libarchive/libarchive/filter_fork_windows.c
new file mode 100644
index 00000000000..9e49c5655f1
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/filter_fork_windows.c
@@ -0,0 +1,245 @@
+/*-
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include "archive_cmdline_private.h"
+#include "archive_string.h"
+
+#include "filter_fork.h"
+
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+/* There are some editions of Windows ("nano server," for example) that
+ * do not host user32.dll. If we want to keep running on those editions,
+ * we need to delay-load WaitForInputIdle. */
+static void *
+la_GetFunctionUser32(const char *name)
+{
+ static HINSTANCE lib;
+ static int set;
+ if (!set) {
+ set = 1;
+ lib = LoadLibrary(TEXT("user32.dll"));
+ }
+ if (lib == NULL) {
+ return NULL;
+ }
+ return (void *)GetProcAddress(lib, name);
+}
+
+static int
+la_WaitForInputIdle(HANDLE hProcess, DWORD dwMilliseconds)
+{
+ static DWORD (WINAPI *f)(HANDLE, DWORD);
+ static int set;
+
+ if (!set) {
+ set = 1;
+ f = la_GetFunctionUser32("WaitForInputIdle");
+ }
+
+ if (!f) {
+ /* An inability to wait for input idle is
+ * not _good_, but it is not catastrophic. */
+ return WAIT_FAILED;
+ }
+ return (*f)(hProcess, dwMilliseconds);
+}
+
+int
+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,
+ HANDLE *out_child)
+{
+ HANDLE childStdout[2], childStdin[2],childStderr;
+ SECURITY_ATTRIBUTES secAtts;
+ STARTUPINFOA staInfo;
+ PROCESS_INFORMATION childInfo;
+ struct archive_string cmdline;
+ struct archive_string fullpath;
+ struct archive_cmdline *acmd;
+ char *arg0, *ext;
+ int i, l;
+ DWORD fl, fl_old;
+ HANDLE child;
+
+ childStdout[0] = childStdout[1] = INVALID_HANDLE_VALUE;
+ childStdin[0] = childStdin[1] = INVALID_HANDLE_VALUE;
+ childStderr = INVALID_HANDLE_VALUE;
+ archive_string_init(&cmdline);
+ archive_string_init(&fullpath);
+
+ acmd = __archive_cmdline_allocate();
+ if (acmd == NULL)
+ goto fail;
+ if (__archive_cmdline_parse(acmd, cmd) != ARCHIVE_OK)
+ goto fail;
+
+ /*
+ * Search the full path of 'path'.
+ * NOTE: This does not need if we give CreateProcessA 'path' as
+ * a part of the cmdline and give CreateProcessA NULL as first
+ * parameter, but I do not like that way.
+ */
+ ext = strrchr(acmd->path, '.');
+ if (ext == NULL || strlen(ext) > 4)
+ /* 'path' does not have a proper extension, so we have to
+ * give SearchPath() ".exe" as the extension. */
+ ext = ".exe";
+ else
+ ext = NULL;/* 'path' has an extension. */
+
+ fl = MAX_PATH;
+ do {
+ if (archive_string_ensure(&fullpath, fl) == NULL)
+ goto fail;
+ fl_old = fl;
+ fl = SearchPathA(NULL, acmd->path, ext, fl, fullpath.s,
+ &arg0);
+ } while (fl != 0 && fl > fl_old);
+ if (fl == 0)
+ goto fail;
+
+ /*
+ * Make a command line.
+ */
+ for (l = 0, i = 0; acmd->argv[i] != NULL; i++) {
+ if (i == 0)
+ continue;
+ l += (int)strlen(acmd->argv[i]) + 1;
+ }
+ if (archive_string_ensure(&cmdline, l + 1) == NULL)
+ goto fail;
+ for (i = 0; acmd->argv[i] != NULL; i++) {
+ if (i == 0) {
+ const char *p, *sp;
+
+ if ((p = strchr(acmd->argv[i], '/')) != NULL ||
+ (p = strchr(acmd->argv[i], '\\')) != NULL)
+ p++;
+ else
+ p = acmd->argv[i];
+ if ((sp = strchr(p, ' ')) != NULL)
+ archive_strappend_char(&cmdline, '"');
+ archive_strcat(&cmdline, p);
+ if (sp != NULL)
+ archive_strappend_char(&cmdline, '"');
+ } else {
+ archive_strappend_char(&cmdline, ' ');
+ archive_strcat(&cmdline, acmd->argv[i]);
+ }
+ }
+ if (i <= 1) {
+ const char *sp;
+
+ if ((sp = strchr(arg0, ' ')) != NULL)
+ archive_strappend_char(&cmdline, '"');
+ archive_strcat(&cmdline, arg0);
+ if (sp != NULL)
+ archive_strappend_char(&cmdline, '"');
+ }
+
+ secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
+ secAtts.bInheritHandle = TRUE;
+ secAtts.lpSecurityDescriptor = NULL;
+ if (CreatePipe(&childStdout[0], &childStdout[1], &secAtts, 0) == 0)
+ goto fail;
+ if (!SetHandleInformation(childStdout[0], HANDLE_FLAG_INHERIT, 0))
+ goto fail;
+ if (CreatePipe(&childStdin[0], &childStdin[1], &secAtts, 0) == 0)
+ goto fail;
+ if (!SetHandleInformation(childStdin[1], HANDLE_FLAG_INHERIT, 0))
+ goto fail;
+ if (DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_ERROR_HANDLE),
+ GetCurrentProcess(), &childStderr, 0, TRUE,
+ DUPLICATE_SAME_ACCESS) == 0)
+ goto fail;
+
+ memset(&staInfo, 0, sizeof(staInfo));
+ staInfo.cb = sizeof(staInfo);
+ staInfo.hStdError = childStderr;
+ staInfo.hStdOutput = childStdout[1];
+ staInfo.hStdInput = childStdin[0];
+ staInfo.wShowWindow = SW_HIDE;
+ staInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ if (CreateProcessA(fullpath.s, cmdline.s, NULL, NULL, TRUE, 0,
+ NULL, NULL, &staInfo, &childInfo) == 0)
+ goto fail;
+ la_WaitForInputIdle(childInfo.hProcess, INFINITE);
+ CloseHandle(childInfo.hProcess);
+ CloseHandle(childInfo.hThread);
+
+ *child_stdout = _open_osfhandle((intptr_t)childStdout[0], _O_RDONLY);
+ *child_stdin = _open_osfhandle((intptr_t)childStdin[1], _O_WRONLY);
+
+ child = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,
+ childInfo.dwProcessId);
+ if (child == NULL) // INVALID_HANDLE_VALUE ?
+ goto fail;
+
+ *out_child = child;
+
+ CloseHandle(childStdout[1]);
+ CloseHandle(childStdin[0]);
+
+ archive_string_free(&cmdline);
+ archive_string_free(&fullpath);
+ __archive_cmdline_free(acmd);
+ return ARCHIVE_OK;
+
+fail:
+ if (childStdout[0] != INVALID_HANDLE_VALUE)
+ CloseHandle(childStdout[0]);
+ if (childStdout[1] != INVALID_HANDLE_VALUE)
+ CloseHandle(childStdout[1]);
+ if (childStdin[0] != INVALID_HANDLE_VALUE)
+ CloseHandle(childStdin[0]);
+ if (childStdin[1] != INVALID_HANDLE_VALUE)
+ CloseHandle(childStdin[1]);
+ if (childStderr != INVALID_HANDLE_VALUE)
+ CloseHandle(childStderr);
+ archive_string_free(&cmdline);
+ archive_string_free(&fullpath);
+ __archive_cmdline_free(acmd);
+ return ARCHIVE_FAILED;
+}
+#else /* !WINAPI_PARTITION_DESKTOP */
+int
+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout, HANDLE *out_child)
+{
+ (void)cmd; (void)child_stdin; (void) child_stdout; (void) out_child;
+ return ARCHIVE_FAILED;
+}
+#endif /* !WINAPI_PARTITION_DESKTOP */
+
+void
+__archive_check_child(int in, int out)
+{
+ (void)in; /* UNUSED */
+ (void)out; /* UNUSED */
+ Sleep(100);
+}
+
+#endif /* _WIN32 && !__CYGWIN__ */
diff --git a/contrib/libs/libarchive/libarchive/xxhash.c b/contrib/libs/libarchive/libarchive/xxhash.c
new file mode 100644
index 00000000000..beacd239122
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/xxhash.c
@@ -0,0 +1,529 @@
+/*
+xxHash - Fast Hash algorithm
+Copyright (C) 2012-2014, Yann Collet.
+BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+You can contact the author at :
+- xxHash source repository : http://code.google.com/p/xxhash/
+*/
+#include "archive_platform.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "archive_xxhash.h"
+
+#ifdef HAVE_LIBLZ4
+
+/***************************************
+** Tuning parameters
+****************************************/
+/* Unaligned memory access is automatically enabled for "common" CPU, such as x86.
+** For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected.
+** If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance.
+** You can also enable this parameter if you know your input data will always be aligned (boundaries of 4, for U32).
+*/
+#if defined(__ARM_FEATURE_UNALIGNED) || defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
+# define XXH_USE_UNALIGNED_ACCESS 1
+#endif
+
+/* XXH_ACCEPT_NULL_INPUT_POINTER :
+** If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer.
+** When this option is enabled, xxHash output for null input pointers will be the same as a null-length input.
+** This option has a very small performance cost (only measurable on small inputs).
+** By default, this option is disabled. To enable it, uncomment below define :
+** #define XXH_ACCEPT_NULL_INPUT_POINTER 1
+
+** XXH_FORCE_NATIVE_FORMAT :
+** By default, xxHash library provides endian-independent Hash values, based on little-endian convention.
+** Results are therefore identical for little-endian and big-endian CPU.
+** This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.
+** Should endian-independence be of no importance for your application, you may set the #define below to 1.
+** It will improve speed for Big-endian CPU.
+** This option has no impact on Little_Endian CPU.
+*/
+#define XXH_FORCE_NATIVE_FORMAT 0
+
+/***************************************
+** Compiler Specific Options
+****************************************/
+/* Disable some Visual warning messages */
+#ifdef _MSC_VER /* Visual Studio */
+# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
+#endif
+
+#ifdef _MSC_VER /* Visual Studio */
+# define FORCE_INLINE __forceinline
+#else
+# ifdef __GNUC__
+# define FORCE_INLINE inline __attribute__((always_inline))
+# else
+# define FORCE_INLINE inline
+# endif
+#endif
+
+/***************************************
+** Includes & Memory related functions
+****************************************/
+#define XXH_malloc malloc
+#define XXH_free free
+#define XXH_memcpy memcpy
+
+
+static unsigned int XXH32 (const void*, unsigned int, unsigned int);
+static void* XXH32_init (unsigned int);
+static XXH_errorcode XXH32_update (void*, const void*, unsigned int);
+static unsigned int XXH32_digest (void*);
+/*static int XXH32_sizeofState(void);*/
+static XXH_errorcode XXH32_resetState(void*, unsigned int);
+#define XXH32_SIZEOFSTATE 48
+typedef struct { long long ll[(XXH32_SIZEOFSTATE+(sizeof(long long)-1))/sizeof(long long)]; } XXH32_stateSpace_t;
+static unsigned int XXH32_intermediateDigest (void*);
+
+/***************************************
+** Basic Types
+****************************************/
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
+# include <stdint.h>
+ typedef uint8_t BYTE;
+ typedef uint16_t U16;
+ typedef uint32_t U32;
+ typedef int32_t S32;
+ typedef uint64_t U64;
+#else
+ typedef unsigned char BYTE;
+ typedef unsigned short U16;
+ typedef unsigned int U32;
+ typedef signed int S32;
+ typedef unsigned long long U64;
+#endif
+
+#if defined(__GNUC__) && !defined(XXH_USE_UNALIGNED_ACCESS)
+# define _PACKED __attribute__ ((packed))
+#else
+# define _PACKED
+#endif
+
+#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__)
+# ifdef __IBMC__
+# pragma pack(1)
+# else
+# pragma pack(push, 1)
+# endif
+#endif
+
+typedef struct _U32_S { U32 v; } _PACKED U32_S;
+
+#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__)
+# pragma pack(pop)
+#endif
+
+
+/****************************************
+** Compiler-specific Functions and Macros
+*****************************************/
+#define GCC_VERSION ((__GNUC__-0) * 100 + (__GNUC_MINOR__ - 0))
+
+#if GCC_VERSION >= 409
+__attribute__((__no_sanitize_undefined__))
+#else
+# if defined(__clang__)
+__attribute__((no_sanitize("undefined")))
+# endif
+#endif
+#if defined(_MSC_VER)
+static __inline U32 A32(const void * x)
+#else
+static inline U32 A32(const void* x)
+#endif
+{
+ return (((const U32_S *)(x))->v);
+}
+
+/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */
+#if defined(_MSC_VER)
+# define XXH_rotl32(x,r) _rotl(x,r)
+#else
+# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))
+#endif
+
+#if defined(_MSC_VER) /* Visual Studio */
+# define XXH_swap32 _byteswap_ulong
+#elif GCC_VERSION >= 403
+# define XXH_swap32 __builtin_bswap32
+#else
+static inline U32 XXH_swap32 (U32 x) {
+ return ((x << 24) & 0xff000000 ) |
+ ((x << 8) & 0x00ff0000 ) |
+ ((x >> 8) & 0x0000ff00 ) |
+ ((x >> 24) & 0x000000ff );}
+#endif
+
+
+/***************************************
+** Constants
+****************************************/
+#define PRIME32_1 2654435761U
+#define PRIME32_2 2246822519U
+#define PRIME32_3 3266489917U
+#define PRIME32_4 668265263U
+#define PRIME32_5 374761393U
+
+
+/***************************************
+** Architecture Macros
+****************************************/
+typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;
+#ifndef XXH_CPU_LITTLE_ENDIAN /* It is possible to define XXH_CPU_LITTLE_ENDIAN externally, for example using a compiler switch */
+ static const int one = 1;
+# define XXH_CPU_LITTLE_ENDIAN (*(const char*)(&one))
+#endif
+
+
+/***************************************
+** Macros
+****************************************/
+#define XXH_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/(!!(c)) }; } /* use only *after* variable declarations */
+
+
+/*****************************
+** Memory reads
+******************************/
+typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;
+
+static
+FORCE_INLINE U32 XXH_readLE32_align(const U32* ptr, XXH_endianess endian, XXH_alignment align)
+{
+ if (align==XXH_unaligned)
+ return endian==XXH_littleEndian ? A32(ptr) : XXH_swap32(A32(ptr));
+ else
+ return endian==XXH_littleEndian ? *ptr : XXH_swap32(*ptr);
+}
+
+static
+FORCE_INLINE U32 XXH_readLE32(const U32* ptr, XXH_endianess endian) { return XXH_readLE32_align(ptr, endian, XXH_unaligned); }
+
+
+/*****************************
+** Simple Hash Functions
+******************************/
+static
+FORCE_INLINE U32 XXH32_endian_align(const void* input, unsigned int len, U32 seed, XXH_endianess endian, XXH_alignment align)
+{
+ const BYTE* p = (const BYTE*)input;
+ const BYTE* bEnd = p + len;
+ U32 h32;
+#define XXH_get32bits(p) XXH_readLE32_align((const U32*)p, endian, align)
+
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
+ if (p==NULL) { len=0; bEnd=p=(const BYTE*)(size_t)16; }
+#endif
+
+ if (len>=16)
+ {
+ const BYTE* const limit = bEnd - 16;
+ U32 v1 = seed + PRIME32_1 + PRIME32_2;
+ U32 v2 = seed + PRIME32_2;
+ U32 v3 = seed + 0;
+ U32 v4 = seed - PRIME32_1;
+
+ do
+ {
+ v1 += XXH_get32bits(p) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4;
+ v2 += XXH_get32bits(p) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4;
+ v3 += XXH_get32bits(p) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4;
+ v4 += XXH_get32bits(p) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4;
+ } while (p<=limit);
+
+ h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);
+ }
+ else
+ {
+ h32 = seed + PRIME32_5;
+ }
+
+ h32 += (U32) len;
+
+ while (p<=bEnd-4)
+ {
+ h32 += XXH_get32bits(p) * PRIME32_3;
+ h32 = XXH_rotl32(h32, 17) * PRIME32_4 ;
+ p+=4;
+ }
+
+ while (p<bEnd)
+ {
+ h32 += (*p) * PRIME32_5;
+ h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;
+ p++;
+ }
+
+ h32 ^= h32 >> 15;
+ h32 *= PRIME32_2;
+ h32 ^= h32 >> 13;
+ h32 *= PRIME32_3;
+ h32 ^= h32 >> 16;
+
+ return h32;
+}
+
+
+U32 XXH32(const void* input, unsigned int len, U32 seed)
+{
+#if 0
+ // Simple version, good for code maintenance, but unfortunately slow for small inputs
+ void* state = XXH32_init(seed);
+ XXH32_update(state, input, len);
+ return XXH32_digest(state);
+#else
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+# if !defined(XXH_USE_UNALIGNED_ACCESS)
+ if ((((size_t)input) & 3) == 0) /* Input is aligned, let's leverage the speed advantage */
+ {
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
+ else
+ return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
+ }
+# endif
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
+ else
+ return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
+#endif
+}
+
+/*****************************
+** Advanced Hash Functions
+******************************/
+
+struct XXH_state32_t
+{
+ U64 total_len;
+ U32 seed;
+ U32 v1;
+ U32 v2;
+ U32 v3;
+ U32 v4;
+ int memsize;
+ char memory[16];
+};
+
+#if 0
+static
+int XXH32_sizeofState(void)
+{
+ XXH_STATIC_ASSERT(XXH32_SIZEOFSTATE >= sizeof(struct XXH_state32_t)); /* A compilation error here means XXH32_SIZEOFSTATE is not large enough */
+ return sizeof(struct XXH_state32_t);
+}
+#endif
+
+static
+XXH_errorcode XXH32_resetState(void* state_in, U32 seed)
+{
+ struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;
+ state->seed = seed;
+ state->v1 = seed + PRIME32_1 + PRIME32_2;
+ state->v2 = seed + PRIME32_2;
+ state->v3 = seed + 0;
+ state->v4 = seed - PRIME32_1;
+ state->total_len = 0;
+ state->memsize = 0;
+ return XXH_OK;
+}
+
+static
+void* XXH32_init (U32 seed)
+{
+ void* state = XXH_malloc (sizeof(struct XXH_state32_t));
+ XXH32_resetState(state, seed);
+ return state;
+}
+
+static
+FORCE_INLINE XXH_errorcode XXH32_update_endian (void* state_in, const void* input, int len, XXH_endianess endian)
+{
+ struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;
+ const BYTE* p = (const BYTE*)input;
+ const BYTE* const bEnd = p + len;
+
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
+ if (input==NULL) return XXH_ERROR;
+#endif
+
+ state->total_len += len;
+
+ if (state->memsize + len < 16) /* fill in tmp buffer */
+ {
+ XXH_memcpy(state->memory + state->memsize, input, len);
+ state->memsize += len;
+ return XXH_OK;
+ }
+
+ if (state->memsize) /* some data left from previous update */
+ {
+ XXH_memcpy(state->memory + state->memsize, input, 16-state->memsize);
+ {
+ const U32* p32 = (const U32*)state->memory;
+ state->v1 += XXH_readLE32(p32, endian) * PRIME32_2; state->v1 = XXH_rotl32(state->v1, 13); state->v1 *= PRIME32_1; p32++;
+ state->v2 += XXH_readLE32(p32, endian) * PRIME32_2; state->v2 = XXH_rotl32(state->v2, 13); state->v2 *= PRIME32_1; p32++;
+ state->v3 += XXH_readLE32(p32, endian) * PRIME32_2; state->v3 = XXH_rotl32(state->v3, 13); state->v3 *= PRIME32_1; p32++;
+ state->v4 += XXH_readLE32(p32, endian) * PRIME32_2; state->v4 = XXH_rotl32(state->v4, 13); state->v4 *= PRIME32_1; p32++;
+ }
+ p += 16-state->memsize;
+ state->memsize = 0;
+ }
+
+ if (p <= bEnd-16)
+ {
+ const BYTE* const limit = bEnd - 16;
+ U32 v1 = state->v1;
+ U32 v2 = state->v2;
+ U32 v3 = state->v3;
+ U32 v4 = state->v4;
+
+ do
+ {
+ v1 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4;
+ v2 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4;
+ v3 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4;
+ v4 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4;
+ } while (p<=limit);
+
+ state->v1 = v1;
+ state->v2 = v2;
+ state->v3 = v3;
+ state->v4 = v4;
+ }
+
+ if (p < bEnd)
+ {
+ XXH_memcpy(state->memory, p, bEnd-p);
+ state->memsize = (int)(bEnd-p);
+ }
+
+ return XXH_OK;
+}
+
+static
+XXH_errorcode XXH32_update (void* state_in, const void* input, unsigned int len)
+{
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH32_update_endian(state_in, input, len, XXH_littleEndian);
+ else
+ return XXH32_update_endian(state_in, input, len, XXH_bigEndian);
+}
+
+
+
+static
+FORCE_INLINE U32 XXH32_intermediateDigest_endian (void* state_in, XXH_endianess endian)
+{
+ struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;
+ const BYTE * p = (const BYTE*)state->memory;
+ BYTE* bEnd = (BYTE*)state->memory + state->memsize;
+ U32 h32;
+
+ if (state->total_len >= 16)
+ {
+ h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18);
+ }
+ else
+ {
+ h32 = state->seed + PRIME32_5;
+ }
+
+ h32 += (U32) state->total_len;
+
+ while (p<=bEnd-4)
+ {
+ h32 += XXH_readLE32((const U32*)p, endian) * PRIME32_3;
+ h32 = XXH_rotl32(h32, 17) * PRIME32_4;
+ p+=4;
+ }
+
+ while (p<bEnd)
+ {
+ h32 += (*p) * PRIME32_5;
+ h32 = XXH_rotl32(h32, 11) * PRIME32_1;
+ p++;
+ }
+
+ h32 ^= h32 >> 15;
+ h32 *= PRIME32_2;
+ h32 ^= h32 >> 13;
+ h32 *= PRIME32_3;
+ h32 ^= h32 >> 16;
+
+ return h32;
+}
+
+static
+U32 XXH32_intermediateDigest (void* state_in)
+{
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH32_intermediateDigest_endian(state_in, XXH_littleEndian);
+ else
+ return XXH32_intermediateDigest_endian(state_in, XXH_bigEndian);
+}
+
+static
+U32 XXH32_digest (void* state_in)
+{
+ U32 h32 = XXH32_intermediateDigest(state_in);
+
+ XXH_free(state_in);
+
+ return h32;
+}
+
+const
+struct archive_xxhash __archive_xxhash = {
+ XXH32,
+ XXH32_init,
+ XXH32_update,
+ XXH32_digest
+};
+#else
+
+/*
+ * Define an empty version of the struct if we aren't using the LZ4 library.
+ */
+const
+struct archive_xxhash __archive_xxhash = {
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+#endif /* HAVE_LIBLZ4 */
diff --git a/contrib/libs/libarchive/ya.make b/contrib/libs/libarchive/ya.make
new file mode 100644
index 00000000000..1b89dda0b92
--- /dev/null
+++ b/contrib/libs/libarchive/ya.make
@@ -0,0 +1,183 @@
+# Generated by devtools/yamaker from nixpkgs 22.11.
+
+LIBRARY()
+
+VERSION(3.7.1)
+
+ORIGINAL_SOURCE(https://github.com/libarchive/libarchive/archive/v3.7.1.tar.gz)
+
+LICENSE(
+ "(CC0-1.0 OR OpenSSL OR Apache-2.0)" AND
+ BSD-2-Clause AND
+ BSD-3-Clause AND
+ Bsd-Unchanged AND
+ ISC AND
+ PostgreSQL AND
+ Public-Domain
+)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/blake2
+ contrib/libs/libbz2
+ contrib/libs/lz4
+ contrib/libs/openssl
+ contrib/libs/zlib
+ contrib/libs/zstd
+)
+
+ADDINCL(
+ contrib/libs/blake2/include
+ contrib/libs/libarchive
+ contrib/libs/libarchive/libarchive
+ contrib/libs/libbz2
+ contrib/libs/lz4
+ contrib/libs/zstd/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DHAVE_CONFIG_H
+)
+
+SRCS(
+ libarchive/archive_acl.c
+ libarchive/archive_check_magic.c
+ libarchive/archive_cmdline.c
+ libarchive/archive_cryptor.c
+ libarchive/archive_digest.c
+ libarchive/archive_entry.c
+ libarchive/archive_entry_copy_stat.c
+ libarchive/archive_entry_link_resolver.c
+ libarchive/archive_entry_sparse.c
+ libarchive/archive_entry_stat.c
+ libarchive/archive_entry_strmode.c
+ libarchive/archive_entry_xattr.c
+ libarchive/archive_getdate.c
+ libarchive/archive_hmac.c
+ libarchive/archive_match.c
+ libarchive/archive_options.c
+ libarchive/archive_pack_dev.c
+ libarchive/archive_pathmatch.c
+ libarchive/archive_ppmd7.c
+ libarchive/archive_ppmd8.c
+ libarchive/archive_random.c
+ libarchive/archive_rb.c
+ libarchive/archive_read.c
+ libarchive/archive_read_add_passphrase.c
+ libarchive/archive_read_append_filter.c
+ libarchive/archive_read_data_into_fd.c
+ libarchive/archive_read_disk_entry_from_file.c
+ libarchive/archive_read_disk_posix.c
+ libarchive/archive_read_disk_set_standard_lookup.c
+ libarchive/archive_read_extract.c
+ libarchive/archive_read_extract2.c
+ libarchive/archive_read_open_fd.c
+ libarchive/archive_read_open_file.c
+ libarchive/archive_read_open_filename.c
+ libarchive/archive_read_open_memory.c
+ libarchive/archive_read_set_format.c
+ libarchive/archive_read_set_options.c
+ libarchive/archive_read_support_filter_all.c
+ libarchive/archive_read_support_filter_by_code.c
+ libarchive/archive_read_support_filter_bzip2.c
+ libarchive/archive_read_support_filter_compress.c
+ libarchive/archive_read_support_filter_grzip.c
+ libarchive/archive_read_support_filter_gzip.c
+ libarchive/archive_read_support_filter_lrzip.c
+ libarchive/archive_read_support_filter_lz4.c
+ libarchive/archive_read_support_filter_lzop.c
+ libarchive/archive_read_support_filter_none.c
+ libarchive/archive_read_support_filter_program.c
+ libarchive/archive_read_support_filter_rpm.c
+ libarchive/archive_read_support_filter_uu.c
+ libarchive/archive_read_support_filter_xz.c
+ libarchive/archive_read_support_filter_zstd.c
+ libarchive/archive_read_support_format_7zip.c
+ libarchive/archive_read_support_format_all.c
+ libarchive/archive_read_support_format_ar.c
+ libarchive/archive_read_support_format_by_code.c
+ libarchive/archive_read_support_format_cab.c
+ libarchive/archive_read_support_format_cpio.c
+ libarchive/archive_read_support_format_empty.c
+ libarchive/archive_read_support_format_iso9660.c
+ libarchive/archive_read_support_format_lha.c
+ libarchive/archive_read_support_format_mtree.c
+ libarchive/archive_read_support_format_rar.c
+ libarchive/archive_read_support_format_rar5.c
+ libarchive/archive_read_support_format_raw.c
+ libarchive/archive_read_support_format_tar.c
+ libarchive/archive_read_support_format_warc.c
+ libarchive/archive_read_support_format_xar.c
+ libarchive/archive_read_support_format_zip.c
+ libarchive/archive_string.c
+ libarchive/archive_string_sprintf.c
+ libarchive/archive_util.c
+ libarchive/archive_version_details.c
+ libarchive/archive_virtual.c
+ libarchive/archive_write.c
+ libarchive/archive_write_add_filter.c
+ libarchive/archive_write_add_filter_b64encode.c
+ libarchive/archive_write_add_filter_by_name.c
+ libarchive/archive_write_add_filter_bzip2.c
+ libarchive/archive_write_add_filter_compress.c
+ libarchive/archive_write_add_filter_grzip.c
+ libarchive/archive_write_add_filter_gzip.c
+ libarchive/archive_write_add_filter_lrzip.c
+ libarchive/archive_write_add_filter_lz4.c
+ libarchive/archive_write_add_filter_lzop.c
+ libarchive/archive_write_add_filter_none.c
+ libarchive/archive_write_add_filter_program.c
+ libarchive/archive_write_add_filter_uuencode.c
+ libarchive/archive_write_add_filter_xz.c
+ libarchive/archive_write_add_filter_zstd.c
+ libarchive/archive_write_disk_posix.c
+ libarchive/archive_write_disk_set_standard_lookup.c
+ libarchive/archive_write_open_fd.c
+ libarchive/archive_write_open_file.c
+ libarchive/archive_write_open_filename.c
+ libarchive/archive_write_open_memory.c
+ libarchive/archive_write_set_format.c
+ libarchive/archive_write_set_format_7zip.c
+ libarchive/archive_write_set_format_ar.c
+ libarchive/archive_write_set_format_by_name.c
+ libarchive/archive_write_set_format_cpio.c
+ libarchive/archive_write_set_format_cpio_binary.c
+ libarchive/archive_write_set_format_cpio_newc.c
+ libarchive/archive_write_set_format_cpio_odc.c
+ libarchive/archive_write_set_format_filter_by_ext.c
+ libarchive/archive_write_set_format_gnutar.c
+ libarchive/archive_write_set_format_iso9660.c
+ libarchive/archive_write_set_format_mtree.c
+ libarchive/archive_write_set_format_pax.c
+ libarchive/archive_write_set_format_raw.c
+ libarchive/archive_write_set_format_shar.c
+ libarchive/archive_write_set_format_ustar.c
+ libarchive/archive_write_set_format_v7tar.c
+ libarchive/archive_write_set_format_warc.c
+ libarchive/archive_write_set_format_xar.c
+ libarchive/archive_write_set_format_zip.c
+ libarchive/archive_write_set_options.c
+ libarchive/archive_write_set_passphrase.c
+ libarchive/filter_fork_posix.c
+ libarchive/xxhash.c
+)
+
+IF (OS_WINDOWS)
+ CFLAGS(
+ GLOBAL -DLIBARCHIVE_STATIC
+ )
+ SRCS(
+ libarchive/archive_entry_copy_bhfi.c
+ libarchive/archive_read_disk_windows.c
+ libarchive/archive_windows.c
+ libarchive/archive_write_disk_windows.c
+ libarchive/filter_fork_windows.c
+ )
+ENDIF()
+
+END()
diff --git a/contrib/libs/linux-headers/linux/fiemap.h b/contrib/libs/linux-headers/linux/fiemap.h
new file mode 100644
index 00000000000..7a900b2377b
--- /dev/null
+++ b/contrib/libs/linux-headers/linux/fiemap.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * FS_IOC_FIEMAP ioctl infrastructure.
+ *
+ * Some portions copyright (C) 2007 Cluster File Systems, Inc
+ *
+ * Authors: Mark Fasheh <mfasheh@suse.com>
+ * Kalpak Shah <kalpak.shah@sun.com>
+ * Andreas Dilger <adilger@sun.com>
+ */
+
+#ifndef _LINUX_FIEMAP_H
+#define _LINUX_FIEMAP_H
+
+#include <linux/types.h>
+
+struct fiemap_extent {
+ __u64 fe_logical; /* logical offset in bytes for the start of
+ * the extent from the beginning of the file */
+ __u64 fe_physical; /* physical offset in bytes for the start
+ * of the extent from the beginning of the disk */
+ __u64 fe_length; /* length in bytes for this extent */
+ __u64 fe_reserved64[2];
+ __u32 fe_flags; /* FIEMAP_EXTENT_* flags for this extent */
+ __u32 fe_reserved[3];
+};
+
+struct fiemap {
+ __u64 fm_start; /* logical offset (inclusive) at
+ * which to start mapping (in) */
+ __u64 fm_length; /* logical length of mapping which
+ * userspace wants (in) */
+ __u32 fm_flags; /* FIEMAP_FLAG_* flags for request (in/out) */
+ __u32 fm_mapped_extents;/* number of extents that were mapped (out) */
+ __u32 fm_extent_count; /* size of fm_extents array (in) */
+ __u32 fm_reserved;
+ struct fiemap_extent fm_extents[]; /* array of mapped extents (out) */
+};
+
+#define FIEMAP_MAX_OFFSET (~0ULL)
+
+#define FIEMAP_FLAG_SYNC 0x00000001 /* sync file data before map */
+#define FIEMAP_FLAG_XATTR 0x00000002 /* map extended attribute tree */
+#define FIEMAP_FLAG_CACHE 0x00000004 /* request caching of the extents */
+
+#define FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR)
+
+#define FIEMAP_EXTENT_LAST 0x00000001 /* Last extent in file. */
+#define FIEMAP_EXTENT_UNKNOWN 0x00000002 /* Data location unknown. */
+#define FIEMAP_EXTENT_DELALLOC 0x00000004 /* Location still pending.
+ * Sets EXTENT_UNKNOWN. */
+#define FIEMAP_EXTENT_ENCODED 0x00000008 /* Data can not be read
+ * while fs is unmounted */
+#define FIEMAP_EXTENT_DATA_ENCRYPTED 0x00000080 /* Data is encrypted by fs.
+ * Sets EXTENT_NO_BYPASS. */
+#define FIEMAP_EXTENT_NOT_ALIGNED 0x00000100 /* Extent offsets may not be
+ * block aligned. */
+#define FIEMAP_EXTENT_DATA_INLINE 0x00000200 /* Data mixed with metadata.
+ * Sets EXTENT_NOT_ALIGNED.*/
+#define FIEMAP_EXTENT_DATA_TAIL 0x00000400 /* Multiple files in block.
+ * Sets EXTENT_NOT_ALIGNED.*/
+#define FIEMAP_EXTENT_UNWRITTEN 0x00000800 /* Space allocated, but
+ * no data (i.e. zero). */
+#define FIEMAP_EXTENT_MERGED 0x00001000 /* File does not natively
+ * support extents. Result
+ * merged for efficiency. */
+#define FIEMAP_EXTENT_SHARED 0x00002000 /* Space shared with other
+ * files. */
+
+#endif /* _LINUX_FIEMAP_H */
diff --git a/contrib/libs/pcre2/AUTHORS b/contrib/libs/pcre2/AUTHORS
new file mode 100644
index 00000000000..11ef898b250
--- /dev/null
+++ b/contrib/libs/pcre2/AUTHORS
@@ -0,0 +1,36 @@
+THE MAIN PCRE2 LIBRARY CODE
+---------------------------
+
+Written by: Philip Hazel
+Email local part: Philip.Hazel
+Email domain: gmail.com
+
+Retired from University of Cambridge Computing Service,
+Cambridge, England.
+
+Copyright (c) 1997-2022 University of Cambridge
+All rights reserved
+
+
+PCRE2 JUST-IN-TIME COMPILATION SUPPORT
+--------------------------------------
+
+Written by: Zoltan Herczeg
+Email local part: hzmester
+Emain domain: freemail.hu
+
+Copyright(c) 2010-2022 Zoltan Herczeg
+All rights reserved.
+
+
+STACK-LESS JUST-IN-TIME COMPILER
+--------------------------------
+
+Written by: Zoltan Herczeg
+Email local part: hzmester
+Emain domain: freemail.hu
+
+Copyright(c) 2009-2022 Zoltan Herczeg
+All rights reserved.
+
+####
diff --git a/contrib/libs/pcre2/COPYING b/contrib/libs/pcre2/COPYING
new file mode 100644
index 00000000000..c233950f6f1
--- /dev/null
+++ b/contrib/libs/pcre2/COPYING
@@ -0,0 +1,5 @@
+PCRE2 LICENCE
+
+Please see the file LICENCE in the PCRE2 distribution for licensing details.
+
+End
diff --git a/contrib/libs/pcre2/ChangeLog b/contrib/libs/pcre2/ChangeLog
new file mode 100644
index 00000000000..2b100361e86
--- /dev/null
+++ b/contrib/libs/pcre2/ChangeLog
@@ -0,0 +1,2827 @@
+Change Log for PCRE2 - see also the Git log
+-------------------------------------------
+
+
+Version 10.42 11-December-2022
+------------------------------
+
+1. Change 19 of 10.41 wasn't quite right; it put the definition of a default,
+empty value for PCRE2_CALL_CONVENTION in src/pcre2posix.c instead of
+src/pcre2posix.h, which meant that programs that included pcre2posix.h but not
+pcre2.h failed to compile.
+
+2. To catch similar issues to the above in future, a new small test program
+that includes pcre2posix.h but not pcre2.h has been added to the test suite.
+
+3. When the -S option of pcre2test was used to set a stack size greater than
+the allowed maximum, the error message displayed the hard limit incorrectly.
+This was pointed out on GitHub pull request #171, but the suggested patch
+didn't cope with all cases. Some further modification was required.
+
+4. Supplying an ovector count of more than 65535 to pcre2_match_data_create()
+caused a crash because the field in the match data block is only 16 bits. A
+maximum of 65535 is now silently applied.
+
+5. Merged @carenas patch #175 which fixes #86 - segfault on aarch64 (ARM),
+
+
+Version 10.41 06-December-2022
+------------------------------
+
+1. Add fflush() before and after a fork callout in pcre2grep to get its output
+to be the same on all systems. (There were previously ordering differences in
+Alpine Linux).
+
+2. Merged patch from @carenas (GitHub #110) for pthreads support in CMake.
+
+3. SSF scorecards grumbled about possible overflow in an expression in
+pcre2test. It never would have overflowed in practice, but some casts have been
+added and at the some time there's been some tidying of fprints that output
+size_t values.
+
+4. PR #94 showed up an unused enum in pcre2_convert.c, which is now removed.
+
+5. Minor code re-arrangement to remove gcc warning about realloc() in
+pcre2test.
+
+6. Change a number of int variables that hold buffer and line lengths in
+pcre2grep to PCRE2_SIZE (aka size_t).
+
+7. Added an #ifdef to cut out a call to PRIV(jit_free) when JIT is not
+supported (even though that function would do nothing in that case) at the
+request of a user who doesn't even want to link with pcre_jit_compile.o. Also
+tidied up an untidy #ifdef arrangement in pcre2test.
+
+8. Fixed an issue in the backtracking optimization of character repeats in
+JIT. Furthermore optimize star repetitions, not just plus repetitions.
+
+9. Removed the use of an initial backtracking frames vector on the system stack
+in pcre2_match() so that it now always uses the heap. (In a multi-thread
+environment with very small stacks there had been an issue.) This also is
+tidier for JIT matching, which didn't need that vector. The heap vector is now
+remembered in the match data block and re-used if that block itself is re-used.
+It is freed with the match data block.
+
+10. Adjusted the find_limits code in pcre2test to work with change 9 above.
+
+11. Added find_limits_noheap to pcre2test, because the heap limits are now
+different in different environments and so cannot be included in the standard
+tests.
+
+12. Created a test for pcre2_match() heap processing that is not part of the
+tests run by 'make check', but can be run manually. The current output is from
+a 64-bit system.
+
+13. Implemented -Z aka --null in pcre2grep.
+
+14. A minor change to pcre2test and the addition of several new pcre2grep tests
+have improved LCOV coverage statistics. At the same time, code in pcre2grep and
+elsewhere that can never be obeyed in normal testing has been excluded from
+coverage.
+
+15. Fixed a bug in pcre2grep that could cause an extra newline to be written
+after output generaed by --output.
+
+16. If a file has a .bz2 extension but is not in fact compressed, pcre2grep
+should process it as a plain text file. A bug stopped this happening; now fixed
+and added to the tests.
+
+17. When pcre2grep was running not in UTF mode, if a string specified by
+--output or obtained from a callout in a pattern contained a character (byte)
+greater than 127, it was incorrectly output in UTF-8 format.
+
+18. Added some casts after warnings from Clang sanitize.
+
+19. Merged patch from cbouc (GitHub #139): 4 function prototypes were missing
+PCRE2_CALL_CONVENTION in src/pcre2posix.h. All function prototypes returning
+pointers had out of place PCRE2_CALL_CONVENTION in src/pcre2.h.*. These
+produced errors when building for Windows with #define PCRE2_CALL_CONVENTION
+__stdcall.
+
+20. A negative repeat value in a pcre2test subject line was not being
+diagnosed, leading to infinite looping.
+
+21. Updated RunGrepTest to discard the warning that Bash now gives when setting
+LC_CTYPE to a bad value (because older versions didn't).
+
+22. Updated pcre2grep so that it behaves like GNU grep when matching more than
+one pattern and a later pattern matches at an earlier point in the subject when
+the matched substrings are being identified by colour or by offsets.
+
+23. Updated the PrepareRelease script so that the man page that it makes for
+the pcre2demo demonstration program is more standard and does not cause errors
+when processed by lexgrog or mandb -c (GitHub issue #160).
+
+24. The JIT compiler was updated.
+
+
+Version 10.40 15-April-2022
+---------------------------
+
+1. Merged patch from @carenas (GitHub #35, 7db87842) to fix pcre2grep incorrect
+handling of multiple passes.
+
+2. Merged patch from @carenas (GitHub #36, dae47509) to fix portability issue
+in pcre2grep with buffered fseek(stdin).
+
+3. Merged patch from @carenas (GitHub #37, acc520924) to fix tests when -S is
+not supported.
+
+4. Revert an unintended change in JIT repeat detection.
+
+5. Merged patch from @carenas (GitHub #52, b037bfa1) to fix build on GNU Hurd.
+
+6. Merged documentation and comments patches from @carenas (GitHub #47).
+
+7. Merged patch from @carenas (GitHub #49) to remove obsolete JFriedl test code
+from pcre2grep.
+
+8. Merged patch from @carenas (GitHub #48) to fix CMake install issue #46.
+
+9. Merged patch from @carenas (GitHub #53) fixing NULL checks in matching and
+substituting.
+
+10. Add null_subject and null_replacement modifiers to pcre2test.
+
+11. Add check for NULL subject to POSIX regexec() function.
+
+12. Add check for NULL replacement to pcre2_substitute().
+
+13. For the subject arguments of pcre2_match(), pcre2_dfa_match(), and
+pcre2_substitute(), and the replacement argument of the latter, if the pointer
+is NULL and the length is zero, treat as an empty string. Apparently a number
+of applications treat NULL/0 in this way.
+
+14. Added support for Bidi_Class and a number of binary Unicode properties,
+including Bidi_Control.
+
+15. Fix some minor issues raised by clang sanitize.
+
+16. Very minor code speed up for maximizing character property matches.
+
+17. A number of changes to script matching for \p and \P:
+
+ (a) Script extensions for a character are now coded as a bitmap instead of
+ a list of script numbers, which should be faster and does not need a
+ loop.
+
+ (b) Added the syntax \p{script:xxx} and \p{script_extensions:xxx} (synonyms
+ sc and scx).
+
+ (c) Changed \p{scriptname} from being the same as \p{sc:scriptname} to being
+ the same as \p{scx:scriptname} because this change happened in Perl at
+ release 5.26.
+
+ (d) The standard Unicode 4-letter abbreviations for script names are now
+ recognized.
+
+ (e) In accordance with Unicode and Perl's "loose matching" rules, spaces,
+ hyphens, and underscores are ignored in property names, which are then
+ matched independent of case.
+
+18. The Python scripts in the maint directory have been refactored. There are
+now three scripts that generate pcre2_ucd.c, pcre2_ucp.h, and pcre2_ucptables.c
+(which is #included by pcre2_tables.c). The data lists that used to be
+duplicated are now held in a single common Python module.
+
+19. On CHERI, and thus Arm's Morello prototype, pointers are represented as
+hardware capabilities, which consist of both an integer address and additional
+metadata, meaning they are twice the size of the platform's size_t type, i.e.
+16 bytes on a 64-bit system. The ovector member of heapframe happens to only be
+8 byte aligned, and so computing frame_size ended up with a multiple of 8 but
+not 16. Whilst the first frame was always suitably aligned, this then
+misaligned the frame that follows, resulting in an alignment fault when storing
+a pointer to Fecode at the start of match. Patch to fix this issue by Jessica
+Clarke PR#72.
+
+20. Added -LP and -LS listing options to pcre2test.
+
+21. A user discovered that the library names in CMakeLists.txt for MSVC
+debugger (PDB) files were incorrect - perhaps never tried for PCRE2?
+
+22. An item such as [Aa] is optimized into a caseless single character match.
+When this was quantified (e.g. [Aa]{2}) and was also the last literal item in a
+pattern, the optimizing "must be present for a match" character check was not
+being flagged as caseless, causing some matches that should have succeeded to
+fail.
+
+23. Fixed a unicode property matching issue in JIT. The character was not
+fully read in caseless matching.
+
+24. Fixed an issue affecting recursions in JIT caused by duplicated data
+transfers.
+
+25. Merged patch from @carenas (GitHub #96) which fixes some problems with
+pcre2test and readline/readedit:
+
+ * Use the right header for libedit in FreeBSD with autoconf
+ * Really allow libedit with cmake
+ * Avoid using readline headers with libedit
+
+
+Version 10.39 29-October-2021
+-----------------------------
+
+1. Fix incorrect detection of alternatives in first character search in JIT.
+
+2. Merged patch from @carenas (GitHub #28):
+
+ Visual Studio 2013 includes support for %zu and %td, so let newer
+ versions of it avoid the fallback, and while at it, make sure that
+ the first check is for DISABLE_PERCENT_ZT so it will be always
+ honoured if chosen.
+
+ prtdiff_t is signed, so use a signed type instead, and make sure
+ that an appropriate width is chosen if pointers are 64bit wide and
+ long is not (ex: Windows 64bit).
+
+ IMHO removing the cast (and therefore the possibilty of truncation)
+ make the code cleaner and the fallback is likely portable enough
+ with all 64-bit POSIX systems doing LP64 except for Windows.
+
+3. Merged patch from @carenas (GitHub #29) to update to Unicode 14.0.0.
+
+4. Merged patch from @carenas (GitHub #30):
+
+ * Cleanup: remove references to no longer used stdint.h
+
+ Since 19c50b9d (Unconditionally use inttypes.h instead of trying for stdint.h
+ (simplification) and remove the now unnecessary inclusion in
+ pcre2_internal.h., 2018-11-14), stdint.h is no longer used.
+
+ Remove checks for it in autotools and CMake and document better the expected
+ build failures for systems that might have stdint.h (C99) and not inttypes.h
+ (from POSIX), like old Windows.
+
+ * Cleanup: remove detection for inttypes.h which is a hard dependency
+
+ CMake checks for standard headers are not meant to be used for hard
+ dependencies, so will prevent a possible fallback to work.
+
+ Alternatively, the header could be checked to make the configuration fail
+ instead of breaking the build, but that was punted, as it was missing anyway
+ from autotools.
+
+5. Merged patch from @carenas (GitHub #32):
+
+ * jit: allow building with ancient MSVC versions
+
+ Visual Studio older than 2013 fails to build with JIT enabled, because it is
+ unable to parse non C89 compatible syntax, with mixed declarations and code.
+ While most recent compilers wouldn't even report this as a warning since it
+ is valid C99, it could be also made visible by adding to gcc/clang the
+ -Wdeclaration-after-statement flag at build time.
+
+ Move the code below the affected definitions.
+
+ * pcre2grep: avoid mixing declarations with code
+
+ Since d5a61ee8 (Patch to detect (and ignore) symlink loops in pcre2grep,
+ 2021-08-28), code will fail to build in a strict C89 compiler.
+
+ Reformat slightly to make it C89 compatible again.
+
+
+Version 10.38 01-October-2021
+-----------------------------
+
+1. Fix invalid single character repetition issues in JIT when the repetition
+is inside a capturing bracket and the bracket is preceded by character
+literals.
+
+2. Installed revised CMake configuration files provided by Jan-Willem Blokland.
+This extends the CMake build system to build both static and shared libraries
+in one go, builds the static library with PIC, and exposes PCRE2 libraries
+using the CMake config files. JWB provided these notes:
+
+- Introduced CMake variable BUILD_STATIC_LIBS to build the static library.
+
+- Make a small modification to config-cmake.h.in by removing the PCRE2_STATIC
+ variable. Added PCRE2_STATIC variable to the static build using the
+ target_compile_definitions() function.
+
+- Extended the CMake config files.
+
+ - Introduced CMake variable PCRE2_USE_STATIC_LIBS to easily switch between
+ the static and shared libraries.
+
+ - Added the PCRE_STATIC variable to the target compile definitions for the
+ import of the static library.
+
+Building static and shared libraries using MSVC results in a name clash of
+the libraries. Both static and shared library builds create, for example, the
+file pcre2-8.lib. Therefore, I decided to change the static library names by
+adding "-static". For example, pcre2-8.lib has become pcre2-8-static.lib.
+[Comment by PH: this is MSVC-specific. It doesn't happen on Linux.]
+
+3. Increased the minimum release number for CMake to 3.0.0 because older than
+2.8.12 is deprecated (it was set to 2.8.5) and causes warnings. Even 3.0.0 is
+quite old; it was released in 2014.
+
+4. Implemented a modified version of Thomas Tempelmann's pcre2grep patch for
+detecting symlink loops. This is dependent on the availability of realpath(),
+which is now tested for in ./configure and CMakeLists.txt.
+
+5. Implemented a modified version of Thomas Tempelmann's patch for faster
+case-independent "first code unit" searches for unanchored patterns in 8-bit
+mode in the interpreters. Instead of just remembering whether one case matched
+or not, it remembers the position of a previous match so as to avoid
+unnecessary repeated searching.
+
+6. Perl now locks out \K in lookarounds, so PCRE2 now does the same by default.
+However, just in case anybody was relying on the old behaviour, there is an
+option called PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK that enables the old behaviour.
+An option has also been added to pcre2grep to enable this.
+
+7. Re-enable a JIT optimization which was unintentionally disabled in 10.35.
+
+8. There is a loop counter to catch excessively crazy patterns when checking
+the lengths of lookbehinds at compile time. This was incorrectly getting reset
+whenever a lookahead was processed, leading to some fuzzer-generated patterns
+taking a very long time to compile when (?|) was present in the pattern,
+because (?|) disables caching of group lengths.
+
+
+Version 10.37 26-May-2021
+-------------------------
+
+1. Change RunGrepTest to use tr instead of sed when testing with binary
+zero bytes, because sed varies a lot from system to system and has problems
+with binary zeros. This is from Bugzilla #2681. Patch from Jeremie
+Courreges-Anglas via Nam Nguyen. This fixes RunGrepTest for OpenBSD. Later:
+it broke it for at least one version of Solaris, where tr can't handle binary
+zeros. However, that system had /usr/xpg4/bin/tr installed, which works OK, so
+RunGrepTest now checks for that command and uses it if found.
+
+2. Compiling with gcc 10.2's -fanalyzer option showed up a hypothetical problem
+with a NULL dereference. I don't think this case could ever occur in practice,
+but I have put in a check in order to get rid of the compiler error.
+
+3. An alternative patch for CMakeLists.txt because 10.36 #4 breaks CMake on
+Windows. Patch from email@cs-ware.de fixes bugzilla #2688.
+
+4. Two bugs related to over-large numbers have been fixed so the behaviour is
+now the same as Perl.
+
+ (a) A pattern such as /\214748364/ gave an overflow error instead of being
+ treated as the octal number \214 followed by literal digits.
+
+ (b) A sequence such as {65536 that has no terminating } so is not a
+ quantifier was nevertheless complaining that a quantifier number was too big.
+
+5. A run of autoconf suggested that configure.ac was out-of-date with respect
+to the lastest autoconf. Running autoupdate made some valid changes, some valid
+suggestions, and also some invalid changes, which were fixed by hand. Autoconf
+now runs clean and the resulting "configure" seems to work, so I hope nothing
+is broken. Later: the requirement for autoconf 2.70 broke some automatic test
+robots. It doesn't seem to be necessary: trying a reduction to 2.60.
+
+6. The pattern /a\K.(?0)*/ when matched against "abac" by the interpreter gave
+the answer "bac", whereas Perl and JIT both yield "c". This was because the
+effect of \K was not propagating back from the full pattern recursion. Other
+recursions such as /(a\K.(?1)*)/ did not have this problem.
+
+7. Restore single character repetition optimization in JIT. Currently fewer
+character repetitions are optimized than in 10.34.
+
+8. When the names of the functions in the POSIX wrapper were changed to
+pcre2_regcomp() etc. (see change 10.33 #4 below), functions with the original
+names were left in the library so that pre-compiled programs would still work.
+However, this has proved troublesome when programs link with several libraries,
+some of which use PCRE2 via the POSIX interface while others use a native POSIX
+library. For this reason, the POSIX function names are removed in this release.
+The macros in pcre2posix.h should ensure that re-compiling fixes any programs
+that haven't been compiled since before 10.33.
+
+
+Version 10.36 04-December-2020
+------------------------------
+
+1. Add CET_CFLAGS so that when Intel CET is enabled, pass -mshstk to
+compiler. This fixes https://bugs.exim.org/show_bug.cgi?id=2578. Patch for
+Makefile.am and configure.ac by H.J. Lu. Equivalent patch for CMakeLists.txt
+invented by PH.
+
+2. Fix inifinite loop when a single byte newline is searched in JIT when
+invalid utf8 mode is enabled.
+
+3. Updated CMakeLists.txt with patch from Wolfgang Stöggl (Bugzilla #2584):
+
+ - Include GNUInstallDirs and use ${CMAKE_INSTALL_LIBDIR} instead of hardcoded
+ lib. This allows differentiation between lib and lib64.
+ CMAKE_INSTALL_LIBDIR is used for installation of libraries and also for
+ pkgconfig file generation.
+
+ - Add the version of PCRE2 to the configuration summary like ./configure
+ does.
+
+ - Fix typo: MACTHED_STRING->MATCHED_STRING
+
+4. Updated CMakeLists.txt with another patch from Wolfgang Stöggl (Bugzilla
+#2588):
+
+ - Add escaped double quotes around include directory in CMakeLists.txt to
+ allow spaces in directory names.
+
+ - This fixes a cmake error, if the path of the pcre2 source contains a space.
+
+5. Updated CMakeLists.txt with a patch from B. Scott Michel: CMake's
+documentation suggests using CHECK_SYMBOL_EXISTS over CHECK_FUNCTION_EXIST.
+Moreover, these functions come from specific header files, which need to be
+specified (and, thankfully, are the same on both the Linux and WinXX
+platforms.)
+
+6. Added a (uint32_t) cast to prevent a compiler warning in pcre2_compile.c.
+
+7. Applied a patch from Wolfgang Stöggl (Bugzilla #2600) to fix postfix for
+debug Windows builds using CMake. This also updated configure so that it
+generates *.pc files and pcre2-config with the same content, as in the past.
+
+8. If a pattern ended with (?(VERSION=n.d where n is any number but d is just a
+single digit, the code unit beyond d was being read (i.e. there was a read
+buffer overflow). Fixes ClusterFuzz 23779.
+
+9. After the rework in r1235, certain character ranges were incorrectly
+handled by an optimization in JIT. Furthermore a wrong offset was used to
+read a value from a buffer which could lead to memory overread.
+
+10. Unnoticed for many years was the fact that delimiters other than / in the
+testinput1 and testinput4 files could cause incorrect behaviour when these
+files were processed by perltest.sh. There were several tests that used quotes
+as delimiters, and it was just luck that they didn't go wrong with perltest.sh.
+All the patterns in testinput1 and testinput4 now use / as their delimiter.
+This fixes Bugzilla #2641.
+
+11. Perl has started to give an error for \K within lookarounds (though there
+are cases where it doesn't). PCRE2 still allows this, so the tests that include
+this case have been moved from test 1 to test 2.
+
+12. Further to 10 above, pcre2test has been updated to detect and grumble if a
+delimiter other than / is used after #perltest.
+
+13. Fixed a bug with PCRE2_MATCH_INVALID_UTF in 8-bit mode when PCRE2_CASELESS
+was set and PCRE2_NO_START_OPTIMIZE was not set. The optimization for finding
+the start of a match was not resetting correctly after a failed match on the
+first valid fragment of the subject, possibly causing incorrect "no match"
+returns on subsequent fragments. For example, the pattern /A/ failed to match
+the subject \xe5A. Fixes Bugzilla #2642.
+
+14. Fixed a bug in character set matching when JIT is enabled and both unicode
+scripts and unicode classes are present at the same time.
+
+15. Added GNU grep's -m (aka --max-count) option to pcre2grep.
+
+16. Refactored substitution processing in pcre2grep strings, both for the -O
+option and when dealing with callouts. There is now a single function that
+handles $ expansion in all cases (instead of multiple copies of almost
+identical code). This means that the same escape sequences are available
+everywhere, which was not previously the case. At the same time, the escape
+sequences $x{...} and $o{...} have been introduced, to allow for characters
+whose code points are greater than 255 in Unicode mode.
+
+17. Applied the patch from Bugzilla #2628 to RunGrepTest. This does an explicit
+test for a version of sed that can handle binary zero, instead of assuming that
+any Linux version will work. Later: replaced $(...) by `...` because not all
+shells recognize the former.
+
+18. Fixed a word boundary check bug in JIT when partial matching is enabled.
+
+19. Fix ARM64 compilation warning in JIT. Patch by Carlo.
+
+20. A bug in the RunTest script meant that if the first part of test 2 failed,
+the failure was not reported.
+
+21. Test 2 was failing when run from a directory other than the source
+directory. This failure was previously missed in RunTest because of 20 above.
+Fixes added to both RunTest and RunTest.bat.
+
+22. Patch to CMakeLists.txt from Daniel to fix problem with testing under
+Windows.
+
+
+Version 10.35 09-May-2020
+---------------------------
+
+1. Use PCRE2_MATCH_EMPTY flag to detect empty matches in JIT.
+
+2. Fix ARMv5 JIT improper handling of labels right after a constant pool.
+
+3. A JIT bug is fixed which allowed to read the fields of the compiled
+pattern before its existence is checked.
+
+4. Back in the PCRE1 day, capturing groups that contained recursive back
+references to themselves were made atomic (version 8.01, change 18) because
+after the end a repeated group, the captured substrings had their values from
+the final repetition, not from an earlier repetition that might be the
+destination of a backtrack. This feature was documented, and was carried over
+into PCRE2. However, it has now been realized that the major refactoring that
+was done for 10.30 has made this atomicizing unnecessary, and it is confusing
+when users are unaware of it, making some patterns appear not to be working as
+expected. Capture values of recursive back references in repeated groups are
+now correctly backtracked, so this unnecessary restriction has been removed.
+
+5. Added PCRE2_SUBSTITUTE_LITERAL.
+
+6. Avoid some VS compiler warnings.
+
+7. Added PCRE2_SUBSTITUTE_MATCHED.
+
+8. Added (?* and (?<* as synonyms for (*napla: and (*naplb: to match another
+regex engine. The Perl regex folks are aware of this usage and have made a note
+about it.
+
+9. When an assertion is repeated, PCRE2 used to limit the maximum repetition to
+1, believing that repeating an assertion is pointless. However, if a positive
+assertion contains capturing groups, repetition can be useful. In any case, an
+assertion could always be wrapped in a repeated group. The only restriction
+that is now imposed is that an unlimited maximum is changed to one more than
+the minimum.
+
+10. Fix *THEN verbs in lookahead assertions in JIT.
+
+11. Added PCRE2_SUBSTITUTE_REPLACEMENT_ONLY.
+
+12. The JIT stack should be freed when the low-level stack allocation fails.
+
+13. In pcre2grep, if the final line in a scanned file is output but does not
+end with a newline sequence, add a newline according to the --newline setting.
+
+14. (?(DEFINE)...) groups were not being handled correctly when checking for
+the fixed length of a lookbehind assertion. Such a group within a lookbehind
+should be skipped, as it does not contribute to the length of the group.
+Instead, the (DEFINE) group was being processed, and if at the end of the
+lookbehind, that end was not correctly recognized. Errors such as "lookbehind
+assertion is not fixed length" and also "internal error: bad code value in
+parsed_skip()" could result.
+
+15. Put a limit of 1000 on recursive calls in pcre2_study() when searching
+nested groups for starting code units, in order to avoid stack overflow issues.
+If the limit is reached, it just gives up trying for this optimization.
+
+16. The control verb chain list must always be restored when exiting from a
+recurse function in JIT.
+
+17. Fix a crash which occurs when the character type of an invalid UTF
+character is decoded in JIT.
+
+18. Changes in many areas of the code so that when Unicode is supported and
+PCRE2_UCP is set without PCRE2_UTF, Unicode character properties are used for
+upper/lower case computations on characters whose code points are greater than
+127.
+
+19. The function for checking UTF-16 validity was returning an incorrect offset
+for the start of the error when a high surrogate was not followed by a valid
+low surrogate. This caused incorrect behaviour, for example when
+PCRE2_MATCH_INVALID_UTF was set and a match started immediately following the
+invalid high surrogate, such as /aa/ matching "\x{d800}aa".
+
+20. If a DEFINE group immediately preceded a lookbehind assertion, the pattern
+could be mis-compiled and therefore not match correctly. This is the example
+that found this: /(?(DEFINE)(?<foo>bar))(?<![-a-z0-9])word/ which failed to
+match "word" because the "move back" value was set to zero.
+
+21. Following a request from a user, some extensions and tidies to the
+character tables handling have been done:
+
+ (a) The dftables auxiliary program is renamed pcre2_dftables, but it is still
+ not installed for public use.
+
+ (b) There is now a -b option for pcre2_dftables, which causes the tables to
+ be written in binary. There is also a -help option.
+
+ (c) PCRE2_CONFIG_TABLES_LENGTH is added to pcre2_config() so that an
+ application that wants to save tables in binary knows how long they are.
+
+22. Changed setting of CMAKE_MODULE_PATH in CMakeLists.txt from SET to
+LIST(APPEND...) to allow a setting from the command line to be included.
+
+23. Updated to Unicode 13.0.0.
+
+24. CMake build now checks for secure_getenv() and strerror(). Patch by Carlo.
+
+25. Avoid using [-1] as a suffix in pcre2test because it can provoke a compiler
+warning.
+
+26. Added tests for __attribute__((uninitialized)) to both the configure and
+CMake build files, and then applied this attribute to the variable called
+stack_frames_vector[] in pcre2_match(). When implemented, this disables
+automatic initialization (a facility in clang), which can take time on big
+variables.
+
+27. Updated CMakeLists.txt (patches by Uwe Korn) to add support for
+pcre2-config, the libpcre*.pc files, SOVERSION, VERSION and the
+MACHO_*_VERSIONS settings for CMake builds.
+
+28. Another patch to CMakeLists.txt to check for mkostemp (configure already
+does). Patch by Carlo Marcelo Arenas Belon.
+
+29. Check for the existence of memfd_create in both CMake and configure
+configurations. Patch by Carlo Marcelo Arenas Belon.
+
+30. Restrict the configuration setting for the SELinux compatible execmem
+allocator (change 10.30/44) to Linux and NetBSD.
+
+
+Version 10.34 21-November-2019
+------------------------------
+
+1. The maximum number of capturing subpatterns is 65535 (documented), but no
+check on this was ever implemented. This omission has been rectified; it fixes
+ClusterFuzz 14376.
+
+2. Improved the invalid utf32 support of the JIT compiler. Now it correctly
+detects invalid characters in the 0xd800-0xdfff range.
+
+3. Fix minor typo bug in JIT compile when \X is used in a non-UTF string.
+
+4. Add support for matching in invalid UTF strings to the pcre2_match()
+interpreter, and integrate with the existing JIT support via the new
+PCRE2_MATCH_INVALID_UTF compile-time option.
+
+5. Give more error detail for invalid UTF-8 when detected in pcre2grep.
+
+6. Add support for invalid UTF-8 to pcre2grep.
+
+7. Adjust the limit for "must have" code unit searching, in particular,
+increase it substantially for non-anchored patterns.
+
+8. Allow (*ACCEPT) to be quantified, because an ungreedy quantifier with a zero
+minimum is potentially useful.
+
+9. Some changes to the way the minimum subject length is handled:
+
+ * When PCRE2_NO_START_OPTIMIZE is set, no minimum length is computed;
+ pcre2test now omits this item instead of showing a value of zero.
+
+ * An incorrect minimum length could be calculated for a pattern that
+ contained (*ACCEPT) inside a qualified group whose minimum repetition was
+ zero, for example /A(?:(*ACCEPT))?B/, which incorrectly computed a minimum
+ of 2. The minimum length scan no longer happens for a pattern that
+ contains (*ACCEPT).
+
+ * When no minimum length is set by the normal scan, but a first and/or last
+ code unit is recorded, set the minimum to 1 or 2 as appropriate.
+
+ * When a pattern contains multiple groups with the same number, a back
+ reference cannot know which one to scan for a minimum length. This used to
+ cause the minimum length finder to give up with no result. Now it treats
+ such references as not adding to the minimum length (which it should have
+ done all along).
+
+ * Furthermore, the above action now happens only if the back reference is to
+ a group that exists more than once in a pattern instead of any back
+ reference in a pattern with duplicate numbers.
+
+10. A (*MARK) value inside a successful condition was not being returned by the
+interpretive matcher (it was returned by JIT). This bug has been mended.
+
+11. A bug in pcre2grep meant that -o without an argument (or -o0) didn't work
+if the pattern had more than 32 capturing parentheses. This is fixed. In
+addition (a) the default limit for groups requested by -o<n> has been raised to
+50, (b) the new --om-capture option changes the limit, (c) an error is raised
+if -o asks for a group that is above the limit.
+
+12. The quantifier {1} was always being ignored, but this is incorrect when it
+is made possessive and applied to an item in parentheses, because a
+parenthesized item may contain multiple branches or other backtracking points,
+for example /(a|ab){1}+c/ or /(a+){1}+a/.
+
+13. For partial matches, pcre2test was always showing the maximum lookbehind
+characters, flagged with "<", which is misleading when the lookbehind didn't
+actually look behind the start (because it was later in the pattern). Showing
+all consulted preceding characters for partial matches is now controlled by the
+existing "allusedtext" modifier and, as for complete matches, this facility is
+available only for non-JIT matching, because JIT does not maintain the first
+and last consulted characters.
+
+14. DFA matching (using pcre2_dfa_match()) was not recognising a partial match
+if the end of the subject was encountered in a lookahead (conditional or
+otherwise), an atomic group, or a recursion.
+
+15. Give error if pcre2test -t, -T, -tm or -TM is given an argument of zero.
+
+16. Check for integer overflow when computing lookbehind lengths. Fixes
+Clusterfuzz issue 15636.
+
+17. Implemented non-atomic positive lookaround assertions.
+
+18. If a lookbehind contained a lookahead that contained another lookbehind
+within it, the nested lookbehind was not correctly processed. For example, if
+/(?<=(?=(?<=a)))b/ was matched to "ab" it gave no match instead of matching
+"b".
+
+19. Implemented pcre2_get_match_data_size().
+
+20. Two alterations to partial matching:
+
+ (a) The definition of a partial match is slightly changed: if a pattern
+ contains any lookbehinds, an empty partial match may be given, because this
+ is another situation where adding characters to the current subject can
+ lead to a full match. Example: /c*+(?<=[bc])/ with subject "ab".
+
+ (b) Similarly, if a pattern could match an empty string, an empty partial
+ match may be given. Example: /(?![ab]).*/ with subject "ab". This case
+ applies only to PCRE2_PARTIAL_HARD.
+
+ (c) An empty string partial hard match can be returned for \z and \Z as it
+ is documented that they shouldn't match.
+
+21. A branch that started with (*ACCEPT) was not being recognized as one that
+could match an empty string.
+
+22. Corrected pcre2_set_character_tables() tables data type: was const unsigned
+char * instead of const uint8_t *, as generated by pcre2_maketables().
+
+23. Upgraded to Unicode 12.1.0.
+
+24. Add -jitfast command line option to pcre2test (to make all the jit options
+available directly).
+
+25. Make pcre2test -C show if libreadline or libedit is supported.
+
+26. If the length of one branch of a group exceeded 65535 (the maximum value
+that is remembered as a minimum length), the whole group's length was
+incorrectly recorded as 65535, leading to incorrect "no match" when start-up
+optimizations were in force.
+
+27. The "rightmost consulted character" value was not always correct; in
+particular, if a pattern ended with a negative lookahead, characters that were
+inspected in that lookahead were not included.
+
+28. Add the pcre2_maketables_free() function.
+
+29. The start-up optimization that looks for a unique initial matching
+code unit in the interpretive engines uses memchr() in 8-bit mode. When the
+search is caseless, it was doing so inefficiently, which ended up slowing down
+the match drastically when the subject was very long. The revised code (a)
+remembers if one case is not found, so it never repeats the search for that
+case after a bumpalong and (b) when one case has been found, it searches only
+up to that position for an earlier occurrence of the other case. This fix
+applies to both interpretive pcre2_match() and to pcre2_dfa_match().
+
+30. While scanning to find the minimum length of a group, if any branch has
+minimum length zero, there is no need to scan any subsequent branches (a small
+compile-time performance improvement).
+
+31. Installed a .gitignore file on a user's suggestion. When using the svn
+repository with git (through git svn) this helps keep it tidy.
+
+32. Add underflow check in JIT which may occur when the value of subject
+string pointer is close to 0.
+
+33. Arrange for classes such as [Aa] which contain just the two cases of the
+same character, to be treated as a single caseless character. This causes the
+first and required code unit optimizations to kick in where relevant.
+
+34. Improve the bitmap of starting bytes for positive classes that include wide
+characters, but no property types, in UTF-8 mode. Previously, on encountering
+such a class, the bits for all bytes greater than \xc4 were set, thus
+specifying any character with codepoint >= 0x100. Now the only bits that are
+set are for the relevant bytes that start the wide characters. This can give a
+noticeable performance improvement.
+
+35. If the bitmap of starting code units contains only 1 or 2 bits, replace it
+with a single starting code unit (1 bit) or a caseless single starting code
+unit if the two relevant characters are case-partners. This is particularly
+relevant to the 8-bit library, though it applies to all. It can give a
+performance boost for patterns such as [Ww]ord and (word|WORD). However, this
+optimization doesn't happen if there is a "required" code unit of the same
+value (because the search for a "required" code unit starts at the match start
+for non-unique first code unit patterns, but after a unique first code unit,
+and patterns such as a*a need the former action).
+
+36. Small patch to pcre2posix.c to set the erroroffset field to -1 immediately
+after a successful compile, instead of at the start of matching to avoid a
+sanitizer complaint (regexec is supposed to be thread safe).
+
+37. Add NEON vectorization to JIT to speed up matching of first character and
+pairs of characters on ARM64 CPUs.
+
+38. If a non-ASCII character was the first in a starting assertion in a
+caseless match, the "first code unit" optimization did not get the casing
+right, and the assertion failed to match a character in the other case if it
+did not start with the same code unit.
+
+39. Fixed the incorrect computation of jump sizes on x86 CPUs in JIT. A masking
+operation was incorrectly removed in r1136. Reported by Ralf Junker.
+
+
+Version 10.33 16-April-2019
+---------------------------
+
+1. Added "allvector" to pcre2test to make it easy to check the part of the
+ovector that shouldn't be changed, in particular after substitute and failed or
+partial matches.
+
+2. Fix subject buffer overread in JIT when UTF is disabled and \X or \R has
+a greater than 1 fixed quantifier. This issue was found by Yunho Kim.
+
+3. Added support for callouts from pcre2_substitute(). After 10.33-RC1, but
+prior to release, fixed a bug that caused a crash if pcre2_substitute() was
+called with a NULL match context.
+
+4. The POSIX functions are now all called pcre2_regcomp() etc., with wrapper
+functions that use the standard POSIX names. However, in pcre2posix.h the POSIX
+names are defined as macros. This should help avoid linking with the wrong
+library in some environments while still exporting the POSIX names for
+pre-existing programs that use them. (The Debian alternative names are also
+defined as macros, but not documented.)
+
+5. Fix an xclass matching issue in JIT.
+
+6. Implement PCRE2_EXTRA_ESCAPED_CR_IS_LF (see Bugzilla 2315).
+
+7. Implement the Perl 5.28 experimental alphabetic names for atomic groups and
+lookaround assertions, for example, (*pla:...) and (*atomic:...). These are
+characterized by a lower case letter following (* and to simplify coding for
+this, the character tables created by pcre2_maketables() were updated to add a
+new "is lower case letter" bit. At the same time, the now unused "is
+hexadecimal digit" bit was removed. The default tables in
+src/pcre2_chartables.c.dist are updated.
+
+8. Implement the new Perl "script run" features (*script_run:...) and
+(*atomic_script_run:...) aka (*sr:...) and (*asr:...).
+
+9. Fixed two typos in change 22 for 10.21, which added special handling for
+ranges such as a-z in EBCDIC environments. The original code probably never
+worked, though there were no bug reports.
+
+10. Implement PCRE2_COPY_MATCHED_SUBJECT for pcre2_match() (including JIT via
+pcre2_match()) and pcre2_dfa_match(), but *not* the pcre2_jit_match() fast
+path. Also, when a match fails, set the subject field in the match data to NULL
+for tidiness - none of the substring extractors should reference this after
+match failure.
+
+11. If a pattern started with a subroutine call that had a quantifier with a
+minimum of zero, an incorrect "match must start with this character" could be
+recorded. Example: /(?&xxx)*ABC(?<xxx>XYZ)/ would (incorrectly) expect 'A' to
+be the first character of a match.
+
+12. The heap limit checking code in pcre2_dfa_match() could suffer from
+overflow if the heap limit was set very large. This could cause incorrect "heap
+limit exceeded" errors.
+
+13. Add "kibibytes" to the heap limit output from pcre2test -C to make the
+units clear.
+
+14. Add a call to pcre2_jit_free_unused_memory() in pcre2grep, for tidiness.
+
+15. Updated the VMS-specific code in pcre2test on the advice of a VMS user.
+
+16. Removed the unnecessary inclusion of stdint.h (or inttypes.h) from
+pcre2_internal.h as it is now included by pcre2.h. Also, change 17 for 10.32
+below was unnecessarily complicated, as inttypes.h is a Standard C header,
+which is defined to be a superset of stdint.h. Instead of conditionally
+including stdint.h or inttypes.h, pcre2.h now unconditionally includes
+inttypes.h. This supports environments that do not have stdint.h but do have
+inttypes.h, which are known to exist. A note in the autotools documentation
+says (November 2018) that there are none known that are the other way round.
+
+17. Added --disable-percent-zt to "configure" (and equivalent to CMake) to
+forcibly disable the use of %zu and %td in formatting strings because there is
+at least one version of VMS that claims to be C99 but does not support these
+modifiers.
+
+18. Added --disable-pcre2grep-callout-fork, which restricts the callout support
+in pcre2grep to the inbuilt echo facility. This may be useful in environments
+that do not support fork().
+
+19. Fix two instances of <= 0 being applied to unsigned integers (the VMS
+compiler complains).
+
+20. Added "fork" support for VMS to pcre2grep, for running an external program
+via a string callout.
+
+21. Improve MAP_JIT flag usage on MacOS. Patch by Rich Siegel.
+
+22. If a pattern started with (*MARK), (*COMMIT), (*PRUNE), (*SKIP), or (*THEN)
+followed by ^ it was not recognized as anchored.
+
+23. The RunGrepTest script used to cut out the test of NUL characters for
+Solaris and MacOS as printf and sed can't handle them. It seems that the *BSD
+systems can't either. I've inverted the test so that only those OS that are
+known to work (currently only Linux) try to run this test.
+
+24. Some tests in RunGrepTest appended to testtrygrep from two different file
+descriptors instead of redirecting stderr to stdout. This worked on Linux, but
+it was reported not to on other systems, causing the tests to fail.
+
+25. In the RunTest script, make the test for stack setting use the same value
+for the stack as it needs for -bigstack.
+
+26. Insert a cast in pcre2_dfa_match.c to suppress a compiler warning.
+
+26. With PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL set, escape sequences such as \s
+which are valid in character classes, but not as the end of ranges, were being
+treated as literals. An example is [_-\s] (but not [\s-_] because that gave an
+error at the *start* of a range). Now an "invalid range" error is given
+independently of PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL.
+
+27. Related to 26 above, PCRE2_BAD_ESCAPE_IS_LITERAL was affecting known escape
+sequences such as \eX when they appeared invalidly in a character class. Now
+the option applies only to unrecognized or malformed escape sequences.
+
+28. Fix word boundary in JIT compiler. Patch by Mike Munday.
+
+29. The pcre2_dfa_match() function was incorrectly handling conditional version
+tests such as (?(VERSION>=0)...) when the version test was true. Incorrect
+processing or a crash could result.
+
+30. When PCRE2_UTF is set, allow non-ASCII letters and decimal digits in group
+names, as Perl does. There was a small bug in this new code, found by
+ClusterFuzz 12950, fixed before release.
+
+31. Implemented PCRE2_EXTRA_ALT_BSUX to support ECMAScript 6's \u{hhh}
+construct.
+
+32. Compile \p{Any} to be the same as . in DOTALL mode, so that it benefits
+from auto-anchoring if \p{Any}* starts a pattern.
+
+33. Compile invalid UTF check in JIT test when only pcre32 is enabled.
+
+34. For some time now, CMake has been warning about the setting of policy
+CMP0026 to "OLD" in CmakeLists.txt, and hinting that the feature might be
+removed in a future version. A request for CMake expertise on the list produced
+no result, so I have now hacked CMakeLists.txt along the lines of some changes
+I found on the Internet. The new code no longer needs the policy setting, and
+it appears to work fine on Linux.
+
+35. Setting --enable-jit=auto for an out-of-tree build failed because the
+source directory wasn't in the search path for AC_TRY_COMPILE always. Patch
+from Ross Burton.
+
+36. Disable SSE2 JIT optimizations in x86 CPUs when SSE2 is not available.
+Patch by Guillem Jover.
+
+37. Changed expressions such as 1<<10 to 1u<<10 in many places because compiler
+warnings were reported.
+
+38. Using the clang compiler with sanitizing options causes runtime complaints
+about truncation for statements such as x = ~x when x is an 8-bit value; it
+seems to compute ~x as a 32-bit value. Changing such statements to x = 255 ^ x
+gets rid of the warnings. There were also two missing casts in pcre2test.
+
+
+Version 10.32 10-September-2018
+-------------------------------
+
+1. When matching using the the REG_STARTEND feature of the POSIX API with a
+non-zero starting offset, unset capturing groups with lower numbers than a
+group that did capture something were not being correctly returned as "unset"
+(that is, with offset values of -1).
+
+2. When matching using the POSIX API, pcre2test used to omit listing unset
+groups altogether. Now it shows those that come before any actual captures as
+"<unset>", as happens for non-POSIX matching.
+
+3. Running "pcre2test -C" always stated "\R matches CR, LF, or CRLF only",
+whatever the build configuration was. It now correctly says "\R matches all
+Unicode newlines" in the default case when --enable-bsr-anycrlf has not been
+specified. Similarly, running "pcre2test -C bsr" never produced the result
+ANY.
+
+4. Matching the pattern /(*UTF)\C[^\v]+\x80/ against an 8-bit string containing
+multi-code-unit characters caused bad behaviour and possibly a crash. This
+issue was fixed for other kinds of repeat in release 10.20 by change 19, but
+repeating character classes were overlooked.
+
+5. pcre2grep now supports the inclusion of binary zeros in patterns that are
+read from files via the -f option.
+
+6. A small fix to pcre2grep to avoid compiler warnings for -Wformat-overflow=2.
+
+7. Added --enable-jit=auto support to configure.ac.
+
+8. Added some dummy variables to the heapframe structure in 16-bit and 32-bit
+modes for the benefit of m68k, where pointers can be 16-bit aligned. The
+dummies force 32-bit alignment and this ensures that the structure is a
+multiple of PCRE2_SIZE, a requirement that is tested at compile time. In other
+architectures, alignment requirements take care of this automatically.
+
+9. When returning an error from pcre2_pattern_convert(), ensure the error
+offset is set zero for early errors.
+
+10. A number of patches for Windows support from Daniel Richard G:
+
+ (a) List of error numbers in Runtest.bat corrected (it was not the same as in
+ Runtest).
+
+ (b) pcre2grep snprintf() workaround as used elsewhere in the tree.
+
+ (c) Support for non-C99 snprintf() that returns -1 in the overflow case.
+
+11. Minor tidy of pcre2_dfa_match() code.
+
+12. Refactored pcre2_dfa_match() so that the internal recursive calls no longer
+use the stack for local workspace and local ovectors. Instead, an initial block
+of stack is reserved, but if this is insufficient, heap memory is used. The
+heap limit parameter now applies to pcre2_dfa_match().
+
+13. If a "find limits" test of DFA matching in pcre2test resulted in too many
+matches for the ovector, no matches were displayed.
+
+14. Removed an occurrence of ctrl/Z from test 6 because Windows treats it as
+EOF. The test looks to have come from a fuzzer.
+
+15. If PCRE2 was built with a default match limit a lot greater than the
+default default of 10 000 000, some JIT tests of the match limit no longer
+failed. All such tests now set 10 000 000 as the upper limit.
+
+16. Another Windows related patch for pcregrep to ensure that WIN32 is
+undefined under Cygwin.
+
+17. Test for the presence of stdint.h and inttypes.h in configure and CMake and
+include whichever exists (stdint preferred) instead of unconditionally
+including stdint. This makes life easier for old and non-standard systems.
+
+18. Further changes to improve portability, especially to old and or non-
+standard systems:
+
+ (a) Put all printf arguments in RunGrepTest into single, not double, quotes,
+ and use \0 not \x00 for binary zero.
+
+ (b) Avoid the use of C++ (i.e. BCPL) // comments.
+
+ (c) Parameterize the use of %zu in pcre2test to make it like %td. For both of
+ these now, if using MSVC or a standard C before C99, %lu is used with a
+ cast if necessary.
+
+19. Applied a contributed patch to CMakeLists.txt to increase the stack size
+when linking pcre2test with MSVC. This gets rid of a stack overflow error in
+the standard set of tests.
+
+20. Output a warning in pcre2test when ignoring the "altglobal" modifier when
+it is given with the "replace" modifier.
+
+21. In both pcre2test and pcre2_substitute(), with global matching, a pattern
+that matched an empty string, but never at the starting match offset, was not
+handled in a Perl-compatible way. The pattern /(<?=\G.)/ is an example of such
+a pattern. Because \G is in a lookbehind assertion, there has to be a
+"bumpalong" before there can be a match. The automatic "advance by one
+character after an empty string match" rule is therefore inappropriate. A more
+complicated algorithm has now been implemented.
+
+22. When checking to see if a lookbehind is of fixed length, lookaheads were
+correctly ignored, but qualifiers on lookaheads were not being ignored, leading
+to an incorrect "lookbehind assertion is not fixed length" error.
+
+23. The VERSION condition test was reading fractional PCRE2 version numbers
+such as the 04 in 10.04 incorrectly and hence giving wrong results.
+
+24. Updated to Unicode version 11.0.0. As well as the usual addition of new
+scripts and characters, this involved re-jigging the grapheme break property
+algorithm because Unicode has changed the way emojis are handled.
+
+25. Fixed an obscure bug that struck when there were two atomic groups not
+separated by something with a backtracking point. There could be an incorrect
+backtrack into the first of the atomic groups. A complicated example is
+/(?>a(*:1))(?>b)(*SKIP:1)x|.*/ matched against "abc", where the *SKIP
+shouldn't find a MARK (because is in an atomic group), but it did.
+
+26. Upgraded the perltest.sh script: (1) #pattern lines can now be used to set
+a list of modifiers for all subsequent patterns - only those that the script
+recognizes are meaningful; (2) #subject lines can be used to set or unset a
+default "mark" modifier; (3) Unsupported #command lines give a warning when
+they are ignored; (4) Mark data is output only if the "mark" modifier is
+present.
+
+27. (*ACCEPT:ARG), (*FAIL:ARG), and (*COMMIT:ARG) are now supported.
+
+28. A (*MARK) name was not being passed back for positive assertions that were
+terminated by (*ACCEPT).
+
+29. Add support for \N{U+dddd}, but only in Unicode mode.
+
+30. Add support for (?^) for unsetting all imnsx options.
+
+31. The PCRE2_EXTENDED (/x) option only ever discarded space characters whose
+code point was less than 256 and that were recognized by the lookup table
+generated by pcre2_maketables(), which uses isspace() to identify white space.
+Now, when Unicode support is compiled, PCRE2_EXTENDED also discards U+0085,
+U+200E, U+200F, U+2028, and U+2029, which are additional characters defined by
+Unicode as "Pattern White Space". This makes PCRE2 compatible with Perl.
+
+32. In certain circumstances, option settings within patterns were not being
+correctly processed. For example, the pattern /((?i)A)(?m)B/ incorrectly
+matched "ab". (The (?m) setting lost the fact that (?i) should be reset at the
+end of its group during the parse process, but without another setting such as
+(?m) the compile phase got it right.) This bug was introduced by the
+refactoring in release 10.23.
+
+33. PCRE2 uses bcopy() if available when memmove() is not, and it used just to
+define memmove() as function call to bcopy(). This hasn't been tested for a
+long time because in pcre2test the result of memmove() was being used, whereas
+bcopy() doesn't return a result. This feature is now refactored always to call
+an emulation function when there is no memmove(). The emulation makes use of
+bcopy() when available.
+
+34. When serializing a pattern, set the memctl, executable_jit, and tables
+fields (that is, all the fields that contain pointers) to zeros so that the
+result of serializing is always the same. These fields are re-set when the
+pattern is deserialized.
+
+35. In a pattern such as /[^\x{100}-\x{ffff}]*[\x80-\xff]/ which has a repeated
+negative class with no characters less than 0x100 followed by a positive class
+with only characters less than 0x100, the first class was incorrectly being
+auto-possessified, causing incorrect match failures.
+
+36. Removed the character type bit ctype_meta, which dates from PCRE1 and is
+not used in PCRE2.
+
+37. Tidied up unnecessarily complicated macros used in the escapes table.
+
+38. Since 10.21, the new testoutput8-16-4 file has accidentally been omitted
+from distribution tarballs, owing to a typo in Makefile.am which had
+testoutput8-16-3 twice. Now fixed.
+
+39. If the only branch in a conditional subpattern was anchored, the whole
+subpattern was treated as anchored, when it should not have been, since the
+assumed empty second branch cannot be anchored. Demonstrated by test patterns
+such as /(?(1)^())b/ or /(?(?=^))b/.
+
+40. A repeated conditional subpattern that could match an empty string was
+always assumed to be unanchored. Now it it checked just like any other
+repeated conditional subpattern, and can be found to be anchored if the minimum
+quantifier is one or more. I can't see much use for a repeated anchored
+pattern, but the behaviour is now consistent.
+
+41. Minor addition to pcre2_jit_compile.c to avoid static analyzer complaint
+(for an event that could never occur but you had to have external information
+to know that).
+
+42. If before the first match in a file that was being searched by pcre2grep
+there was a line that was sufficiently long to cause the input buffer to be
+expanded, the variable holding the location of the end of the previous match
+was being adjusted incorrectly, and could cause an overflow warning from a code
+sanitizer. However, as the value is used only to print pending "after" lines
+when the next match is reached (and there are no such lines in this case) this
+bug could do no damage.
+
+
+Version 10.31 12-February-2018
+------------------------------
+
+1. Fix typo (missing ]) in VMS code in pcre2test.c.
+
+2. Replace the replicated code for matching extended Unicode grapheme sequences
+(which got a lot more complicated by change 10.30/49) by a single subroutine
+that is called by both pcre2_match() and pcre2_dfa_match().
+
+3. Add idempotent guard to pcre2_internal.h.
+
+4. Add new pcre2_config() options: PCRE2_CONFIG_NEVER_BACKSLASH_C and
+PCRE2_CONFIG_COMPILED_WIDTHS.
+
+5. Cut out \C tests in the JIT regression tests when NEVER_BACKSLASH_C is
+defined (e.g. by --enable-never-backslash-C).
+
+6. Defined public names for all the pcre2_compile() error numbers, and used
+the public names in pcre2_convert.c.
+
+7. Fixed a small memory leak in pcre2test (convert contexts).
+
+8. Added two casts to compile.c and one to match.c to avoid compiler warnings.
+
+9. Added code to pcre2grep when compiled under VMS to set the symbol
+PCRE2GREP_RC to the exit status, because VMS does not distinguish between
+exit(0) and exit(1).
+
+10. Added the -LM (list modifiers) option to pcre2test. Also made -C complain
+about a bad option only if the following argument item does not start with a
+hyphen.
+
+11. pcre2grep was truncating components of file names to 128 characters when
+processing files with the -r option, and also (some very odd code) truncating
+path names to 512 characters. There is now a check on the absolute length of
+full path file names, which may be up to 2047 characters long.
+
+12. When an assertion contained (*ACCEPT) it caused all open capturing groups
+to be closed (as for a non-assertion ACCEPT), which was wrong and could lead to
+misbehaviour for subsequent references to groups that started outside the
+assertion. ACCEPT in an assertion now closes only those groups that were
+started within that assertion. Fixes oss-fuzz issues 3852 and 3891.
+
+13. Multiline matching in pcre2grep was misbehaving if the pattern matched
+within a line, and then matched again at the end of the line and over into
+subsequent lines. Behaviour was different with and without colouring, and
+sometimes context lines were incorrectly printed and/or line endings were lost.
+All these issues should now be fixed.
+
+14. If --line-buffered was specified for pcre2grep when input was from a
+compressed file (.gz or .bz2) a segfault occurred. (Line buffering should be
+ignored for compressed files.)
+
+15. Although pcre2_jit_match checks whether the pattern is compiled
+in a given mode, it was also expected that at least one mode is available.
+This is fixed and pcre2_jit_match returns with PCRE2_ERROR_JIT_BADOPTION
+when the pattern is not optimized by JIT at all.
+
+16. The line number and related variables such as match counts in pcre2grep
+were all int variables, causing overflow when files with more than 2147483647
+lines were processed (assuming 32-bit ints). They have all been changed to
+unsigned long ints.
+
+17. If a backreference with a minimum repeat count of zero was first in a
+pattern, apart from assertions, an incorrect first matching character could be
+recorded. For example, for the pattern /(?=(a))\1?b/, "b" was incorrectly set
+as the first character of a match.
+
+18. Characters in a leading positive assertion are considered for recording a
+first character of a match when the rest of the pattern does not provide one.
+However, a character in a non-assertive group within a leading assertion such
+as in the pattern /(?=(a))\1?b/ caused this process to fail. This was an
+infelicity rather than an outright bug, because it did not affect the result of
+a match, just its speed. (In fact, in this case, the starting 'a' was
+subsequently picked up in the study.)
+
+19. A minor tidy in pcre2_match(): making all PCRE2_ERROR_ returns use "return"
+instead of "RRETURN" saves unwinding the backtracks in these cases (only one
+didn't).
+
+20. Allocate a single callout block on the stack at the start of pcre2_match()
+and set its never-changing fields once only. Do the same for pcre2_dfa_match().
+
+21. Save the extra compile options (set in the compile context) with the
+compiled pattern (they were not previously saved), add PCRE2_INFO_EXTRAOPTIONS
+to retrieve them, and update pcre2test to show them.
+
+22. Added PCRE2_CALLOUT_STARTMATCH and PCRE2_CALLOUT_BACKTRACK bits to a new
+field callout_flags in callout blocks. The bits are set by pcre2_match(), but
+not by JIT or pcre2_dfa_match(). Their settings are shown in pcre2test callouts
+if the callout_extra subject modifier is set. These bits are provided to help
+with tracking how a backtracking match is proceeding.
+
+23. Updated the pcre2demo.c demonstration program, which was missing the extra
+code for -g that handles the case when \K in an assertion causes the match to
+end at the original start point. Also arranged for it to detect when \K causes
+the end of a match to be before its start.
+
+24. Similar to 23 above, strange things (including loops) could happen in
+pcre2grep when \K was used in an assertion when --colour was used or in
+multiline mode. The "end at original start point" bug is fixed, and if the end
+point is found to be before the start point, they are swapped.
+
+25. When PCRE2_FIRSTLINE without PCRE2_NO_START_OPTIMIZE was used in non-JIT
+matching (both pcre2_match() and pcre2_dfa_match()) and the matched string
+started with the first code unit of a newline sequence, matching failed because
+it was not tried at the newline.
+
+26. Code for giving up a non-partial match after failing to find a starting
+code unit anywhere in the subject was missing when searching for one of a
+number of code units (the bitmap case) in both pcre2_match() and
+pcre2_dfa_match(). This was a missing optimization rather than a bug.
+
+27. Tidied up the ACROSSCHAR macro to be like FORWARDCHAR and BACKCHAR, using a
+pointer argument rather than a code unit value. This should not have affected
+the generated code.
+
+28. The JIT compiler has been updated.
+
+29. Avoid pointer overflow for unset captures in pcre2_substring_list_get().
+This could not actually cause a crash because it was always used in a memcpy()
+call with zero length.
+
+30. Some internal structures have a variable-length ovector[] as their last
+element. Their actual memory is obtained dynamically, giving an ovector of
+appropriate length. However, they are defined in the structure as
+ovector[NUMBER], where NUMBER is large so that array bound checkers don't
+grumble. The value of NUMBER was 10000, but a fuzzer exceeded 5000 capturing
+groups, making the ovector larger than this. The number has been increased to
+131072, which allows for the maximum number of captures (65535) plus the
+overall match. This fixes oss-fuzz issue 5415.
+
+31. Auto-possessification at the end of a capturing group was dependent on what
+follows the group (e.g. /(a+)b/ would auto-possessify the a+) but this caused
+incorrect behaviour when the group was called recursively from elsewhere in the
+pattern where something different might follow. This bug is an unforseen
+consequence of change #1 for 10.30 - the implementation of backtracking into
+recursions. Iterators at the ends of capturing groups are no longer considered
+for auto-possessification if the pattern contains any recursions. Fixes
+Bugzilla #2232.
+
+
+Version 10.30 14-August-2017
+----------------------------
+
+1. The main interpreter, pcre2_match(), has been refactored into a new version
+that does not use recursive function calls (and therefore the stack) for
+remembering backtracking positions. This makes --disable-stack-for-recursion a
+NOOP. The new implementation allows backtracking into recursive group calls in
+patterns, making it more compatible with Perl, and also fixes some other
+hard-to-do issues such as #1887 in Bugzilla. The code is also cleaner because
+the old code had a number of fudges to try to reduce stack usage. It seems to
+run no slower than the old code.
+
+A number of bugs in the refactored code were subsequently fixed during testing
+before release, but after the code was made available in the repository. These
+bugs were never in fully released code, but are noted here for the record.
+
+ (a) If a pattern had fewer capturing parentheses than the ovector supplied in
+ the match data block, a memory error (detectable by ASAN) occurred after
+ a match, because the external block was being set from non-existent
+ internal ovector fields. Fixes oss-fuzz issue 781.
+
+ (b) A pattern with very many capturing parentheses (when the internal frame
+ size was greater than the initial frame vector on the stack) caused a
+ crash. A vector on the heap is now set up at the start of matching if the
+ vector on the stack is not big enough to handle at least 10 frames.
+ Fixes oss-fuzz issue 783.
+
+ (c) Handling of (*VERB)s in recursions was wrong in some cases.
+
+ (d) Captures in negative assertions that were used as conditions were not
+ happening if the assertion matched via (*ACCEPT).
+
+ (e) Mark values were not being passed out of recursions.
+
+ (f) Refactor some code in do_callout() to avoid picky compiler warnings about
+ negative indices. Fixes oss-fuzz issue 1454.
+
+ (g) Similarly refactor the way the variable length ovector is addressed for
+ similar reasons. Fixes oss-fuzz issue 1465.
+
+2. Now that pcre2_match() no longer uses recursive function calls (see above),
+the "match limit recursion" value seems misnamed. It still exists, and limits
+the depth of tree that is searched. To avoid future confusion, it has been
+renamed as "depth limit" in all relevant places (--with-depth-limit,
+(*LIMIT_DEPTH), pcre2_set_depth_limit(), etc) but the old names are still
+available for backwards compatibility.
+
+3. Hardened pcre2test so as to reduce the number of bugs reported by fuzzers:
+
+ (a) Check for malloc failures when getting memory for the ovector (POSIX) or
+ the match data block (non-POSIX).
+
+4. In the 32-bit library in non-UTF mode, an attempt to find a Unicode property
+for a character with a code point greater than 0x10ffff (the Unicode maximum)
+caused a crash.
+
+5. If a lookbehind assertion that contained a back reference to a group
+appearing later in the pattern was compiled with the PCRE2_ANCHORED option,
+undefined actions (often a segmentation fault) could occur, depending on what
+other options were set. An example assertion is (?<!\1(abc)) where the
+reference \1 precedes the group (abc). This fixes oss-fuzz issue 865.
+
+6. Added the PCRE2_INFO_FRAMESIZE item to pcre2_pattern_info() and arranged for
+pcre2test to use it to output the frame size when the "framesize" modifier is
+given.
+
+7. Reworked the recursive pattern matching in the JIT compiler to follow the
+interpreter changes.
+
+8. When the zero_terminate modifier was specified on a pcre2test subject line
+for global matching, unpredictable things could happen. For example, in UTF-8
+mode, the pattern //g,zero_terminate read random memory when matched against an
+empty string with zero_terminate. This was a bug in pcre2test, not the library.
+
+9. Moved some Windows-specific code in pcre2grep (introduced in 10.23/13) out
+of the section that is compiled when Unix-style directory scanning is
+available, and into a new section that is always compiled for Windows.
+
+10. In pcre2test, explicitly close the file after an error during serialization
+or deserialization (the "load" or "save" commands).
+
+11. Fix memory leak in pcre2_serialize_decode() when the input is invalid.
+
+12. Fix potential NULL dereference in pcre2_callout_enumerate() if called with
+a NULL pattern pointer when Unicode support is available.
+
+13. When the 32-bit library was being tested by pcre2test, error messages that
+were longer than 64 code units could cause a buffer overflow. This was a bug in
+pcre2test.
+
+14. The alternative matching function, pcre2_dfa_match() misbehaved if it
+encountered a character class with a possessive repeat, for example [a-f]{3}+.
+
+15. The depth (formerly recursion) limit now applies to DFA matching (as
+of 10.23/36); pcre2test has been upgraded so that \=find_limits works with DFA
+matching to find the minimum value for this limit.
+
+16. Since 10.21, if pcre2_match() was called with a null context, default
+memory allocation functions were used instead of whatever was used when the
+pattern was compiled.
+
+17. Changes to the pcre2test "memory" modifier on a subject line. These apply
+only to pcre2_match():
+
+ (a) Warn if null_context is set on both pattern and subject, because the
+ memory details cannot then be shown.
+
+ (b) Remember (up to a certain number of) memory allocations and their
+ lengths, and list only the lengths, so as to be system-independent.
+ (In practice, the new interpreter never has more than 2 blocks allocated
+ simultaneously.)
+
+18. Make pcre2test detect an error return from pcre2_get_error_message(), give
+a message, and abandon the run (this would have detected #13 above).
+
+19. Implemented PCRE2_ENDANCHORED.
+
+20. Applied Jason Hood's patches (slightly modified) to pcre2grep, to implement
+the --output=text (-O) option and the inbuilt callout echo.
+
+21. Extend auto-anchoring etc. to ignore groups with a zero qualifier and
+single-branch conditions with a false condition (e.g. DEFINE) at the start of a
+branch. For example, /(?(DEFINE)...)^A/ and /(...){0}^B/ are now flagged as
+anchored.
+
+22. Added an explicit limit on the amount of heap used by pcre2_match(), set by
+pcre2_set_heap_limit() or (*LIMIT_HEAP=xxx). Upgraded pcre2test to show the
+heap limit along with other pattern information, and to find the minimum when
+the find_limits modifier is set.
+
+23. Write to the last 8 bytes of the pcre2_real_code structure when a compiled
+pattern is set up so as to initialize any padding the compiler might have
+included. This avoids valgrind warnings when a compiled pattern is copied, in
+particular when it is serialized.
+
+24. Remove a redundant line of code left in accidentally a long time ago.
+
+25. Remove a duplication typo in pcre2_tables.c
+
+26. Correct an incorrect cast in pcre2_valid_utf.c
+
+27. Update pcre2test, remove some unused code in pcre2_match(), and upgrade the
+tests to improve coverage.
+
+28. Some fixes/tidies as a result of looking at Coverity Scan output:
+
+ (a) Typo: ">" should be ">=" in opcode check in pcre2_auto_possess.c.
+ (b) Added some casts to avoid "suspicious implicit sign extension".
+ (c) Resource leaks in pcre2test in rare error cases.
+ (d) Avoid warning for never-use case OP_TABLE_LENGTH which is just a fudge
+ for checking at compile time that tables are the right size.
+ (e) Add missing "fall through" comment.
+
+29. Implemented PCRE2_EXTENDED_MORE and related /xx and (?xx) features.
+
+30. Implement (?n: for PCRE2_NO_AUTO_CAPTURE, because Perl now has this.
+
+31. If more than one of "push", "pushcopy", or "pushtablescopy" were set in
+pcre2test, a crash could occur.
+
+32. Make -bigstack in RunTest allocate a 64MiB stack (instead of 16MiB) so
+that all the tests can run with clang's sanitizing options.
+
+33. Implement extra compile options in the compile context and add the first
+one: PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES.
+
+34. Implement newline type PCRE2_NEWLINE_NUL.
+
+35. A lookbehind assertion that had a zero-length branch caused undefined
+behaviour when processed by pcre2_dfa_match(). This is oss-fuzz issue 1859.
+
+36. The match limit value now also applies to pcre2_dfa_match() as there are
+patterns that can use up a lot of resources without necessarily recursing very
+deeply. (Compare item 10.23/36.) This should fix oss-fuzz #1761.
+
+37. Implement PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL.
+
+38. Fix returned offsets from regexec() when REG_STARTEND is used with a
+starting offset greater than zero.
+
+39. Implement REG_PEND (GNU extension) for the POSIX wrapper.
+
+40. Implement the subject_literal modifier in pcre2test, and allow jitstack on
+pattern lines.
+
+41. Implement PCRE2_LITERAL and use it to support REG_NOSPEC.
+
+42. Implement PCRE2_EXTRA_MATCH_LINE and PCRE2_EXTRA_MATCH_WORD for the benefit
+of pcre2grep.
+
+43. Re-implement pcre2grep's -F, -w, and -x options using PCRE2_LITERAL,
+PCRE2_EXTRA_MATCH_WORD, and PCRE2_EXTRA_MATCH_LINE. This fixes two bugs:
+
+ (a) The -F option did not work for fixed strings containing \E.
+ (b) The -w option did not work for patterns with multiple branches.
+
+44. Added configuration options for the SELinux compatible execmem allocator in
+JIT.
+
+45. Increased the limit for searching for a "must be present" code unit in
+subjects from 1000 to 2000 for 8-bit searches, since they use memchr() and are
+much faster.
+
+46. Arrange for anchored patterns to record and use "first code unit" data,
+because this can give a fast "no match" without searching for a "required code
+unit". Previously only non-anchored patterns did this.
+
+47. Upgraded the Unicode tables from Unicode 8.0.0 to Unicode 10.0.0.
+
+48. Add the callout_no_where modifier to pcre2test.
+
+49. Update extended grapheme breaking rules to the latest set that are in
+Unicode Standard Annex #29.
+
+50. Added experimental foreign pattern conversion facilities
+(pcre2_pattern_convert() and friends).
+
+51. Change the macro FWRITE, used in pcre2grep, to FWRITE_IGNORE because FWRITE
+is defined in a system header in cygwin. Also modified some of the #ifdefs in
+pcre2grep related to Windows and Cygwin support.
+
+52. Change 3(g) for 10.23 was a bit too zealous. If a hyphen that follows a
+character class is the last character in the class, Perl does not give a
+warning. PCRE2 now also treats this as a literal.
+
+53. Related to 52, though PCRE2 was throwing an error for [[:digit:]-X] it was
+not doing so for [\d-X] (and similar escapes), as is documented.
+
+54. Fixed a MIPS issue in the JIT compiler reported by Joshua Kinard.
+
+55. Fixed a "maybe uninitialized" warning for class_uchardata in \p handling in
+pcre2_compile() which could never actually trigger (code should have been cut
+out when Unicode support is disabled).
+
+
+Version 10.23 14-February-2017
+------------------------------
+
+1. Extended pcre2test with the utf8_input modifier so that it is able to
+generate all possible 16-bit and 32-bit code unit values in non-UTF modes.
+
+2. In any wide-character mode (8-bit UTF or any 16-bit or 32-bit mode), without
+PCRE2_UCP set, a negative character type such as \D in a positive class should
+cause all characters greater than 255 to match, whatever else is in the class.
+There was a bug that caused this not to happen if a Unicode property item was
+added to such a class, for example [\D\P{Nd}] or [\W\pL].
+
+3. There has been a major re-factoring of the pcre2_compile.c file. Most syntax
+checking is now done in the pre-pass that identifies capturing groups. This has
+reduced the amount of duplication and made the code tidier. While doing this,
+some minor bugs and Perl incompatibilities were fixed, including:
+
+ (a) \Q\E in the middle of a quantifier such as A+\Q\E+ is now ignored instead
+ of giving an invalid quantifier error.
+
+ (b) {0} can now be used after a group in a lookbehind assertion; previously
+ this caused an "assertion is not fixed length" error.
+
+ (c) Perl always treats (?(DEFINE) as a "define" group, even if a group with
+ the name "DEFINE" exists. PCRE2 now does likewise.
+
+ (d) A recursion condition test such as (?(R2)...) must now refer to an
+ existing subpattern.
+
+ (e) A conditional recursion test such as (?(R)...) misbehaved if there was a
+ group whose name began with "R".
+
+ (f) When testing zero-terminated patterns under valgrind, the terminating
+ zero is now marked "no access". This catches bugs that would otherwise
+ show up only with non-zero-terminated patterns.
+
+ (g) A hyphen appearing immediately after a POSIX character class (for example
+ /[[:ascii:]-z]/) now generates an error. Perl does accept this as a
+ literal, but gives a warning, so it seems best to fail it in PCRE.
+
+ (h) An empty \Q\E sequence may appear after a callout that precedes an
+ assertion condition (it is, of course, ignored).
+
+One effect of the refactoring is that some error numbers and messages have
+changed, and the pattern offset given for compiling errors is not always the
+right-most character that has been read. In particular, for a variable-length
+lookbehind assertion it now points to the start of the assertion. Another
+change is that when a callout appears before a group, the "length of next
+pattern item" that is passed now just gives the length of the opening
+parenthesis item, not the length of the whole group. A length of zero is now
+given only for a callout at the end of the pattern. Automatic callouts are no
+longer inserted before and after explicit callouts in the pattern.
+
+A number of bugs in the refactored code were subsequently fixed during testing
+before release, but after the code was made available in the repository. Many
+of the bugs were discovered by fuzzing testing. Several of them were related to
+the change from assuming a zero-terminated pattern (which previously had
+required non-zero terminated strings to be copied). These bugs were never in
+fully released code, but are noted here for the record.
+
+ (a) An overall recursion such as (?0) inside a lookbehind assertion was not
+ being diagnosed as an error.
+
+ (b) In utf mode, the length of a *MARK (or other verb) name was being checked
+ in characters instead of code units, which could lead to bad code being
+ compiled, leading to unpredictable behaviour.
+
+ (c) In extended /x mode, characters whose code was greater than 255 caused
+ a lookup outside one of the global tables. A similar bug existed for wide
+ characters in *VERB names.
+
+ (d) The amount of memory needed for a compiled pattern was miscalculated if a
+ lookbehind contained more than one toplevel branch and the first branch
+ was of length zero.
+
+ (e) In UTF-8 or UTF-16 modes with PCRE2_EXTENDED (/x) set and a non-zero-
+ terminated pattern, if a # comment ran on to the end of the pattern, one
+ or more code units past the end were being read.
+
+ (f) An unterminated repeat at the end of a non-zero-terminated pattern (e.g.
+ "{2,2") could cause reading beyond the pattern.
+
+ (g) When reading a callout string, if the end delimiter was at the end of the
+ pattern one further code unit was read.
+
+ (h) An unterminated number after \g' could cause reading beyond the pattern.
+
+ (i) An insufficient memory size was being computed for compiling with
+ PCRE2_AUTO_CALLOUT.
+
+ (j) A conditional group with an assertion condition used more memory than was
+ allowed for it during parsing, so too many of them could therefore
+ overrun a buffer.
+
+ (k) If parsing a pattern exactly filled the buffer, the internal test for
+ overrun did not check when the final META_END item was added.
+
+ (l) If a lookbehind contained a subroutine call, and the called group
+ contained an option setting such as (?s), and the PCRE2_ANCHORED option
+ was set, unpredictable behaviour could occur. The underlying bug was
+ incorrect code and insufficient checking while searching for the end of
+ the called subroutine in the parsed pattern.
+
+ (m) Quantifiers following (*VERB)s were not being diagnosed as errors.
+
+ (n) The use of \Q...\E in a (*VERB) name when PCRE2_ALT_VERBNAMES and
+ PCRE2_AUTO_CALLOUT were both specified caused undetermined behaviour.
+
+ (o) If \Q was preceded by a quantified item, and the following \E was
+ followed by '?' or '+', and there was at least one literal character
+ between them, an internal error "unexpected repeat" occurred (example:
+ /.+\QX\E+/).
+
+ (p) A buffer overflow could occur while sorting the names in the group name
+ list (depending on the order in which the names were seen).
+
+ (q) A conditional group that started with a callout was not doing the right
+ check for a following assertion, leading to compiling bad code. Example:
+ /(?(C'XX))?!XX/
+
+ (r) If a character whose code point was greater than 0xffff appeared within
+ a lookbehind that was within another lookbehind, the calculation of the
+ lookbehind length went wrong and could provoke an internal error.
+
+ (t) The sequence \E- or \Q\E- after a POSIX class in a character class caused
+ an internal error. Now the hyphen is treated as a literal.
+
+4. Back references are now permitted in lookbehind assertions when there are
+no duplicated group numbers (that is, (?| has not been used), and, if the
+reference is by name, there is only one group of that name. The referenced
+group must, of course be of fixed length.
+
+5. pcre2test has been upgraded so that, when run under valgrind with valgrind
+support enabled, reading past the end of the pattern is detected, both when
+compiling and during callout processing.
+
+6. \g{+<number>} (e.g. \g{+2} ) is now supported. It is a "forward back
+reference" and can be useful in repetitions (compare \g{-<number>} ). Perl does
+not recognize this syntax.
+
+7. Automatic callouts are no longer generated before and after callouts in the
+pattern.
+
+8. When pcre2test was outputing information from a callout, the caret indicator
+for the current position in the subject line was incorrect if it was after an
+escape sequence for a character whose code point was greater than \x{ff}.
+
+9. Change 19 for 10.22 had a typo (PCRE_STATIC_RUNTIME should be
+PCRE2_STATIC_RUNTIME). Fix from David Gaussmann.
+
+10. Added --max-buffer-size to pcre2grep, to allow for automatic buffer
+expansion when long lines are encountered. Original patch by Dmitry
+Cherniachenko.
+
+11. If pcre2grep was compiled with JIT support, but the library was compiled
+without it (something that neither ./configure nor CMake allow, but it can be
+done by editing config.h), pcre2grep was giving a JIT error. Now it detects
+this situation and does not try to use JIT.
+
+12. Added some "const" qualifiers to variables in pcre2grep.
+
+13. Added Dmitry Cherniachenko's patch for colouring output in Windows
+(untested by me). Also, look for GREP_COLOUR or GREP_COLOR if the environment
+variables PCRE2GREP_COLOUR and PCRE2GREP_COLOR are not found.
+
+14. Add the -t (grand total) option to pcre2grep.
+
+15. A number of bugs have been mended relating to match start-up optimizations
+when the first thing in a pattern is a positive lookahead. These all applied
+only when PCRE2_NO_START_OPTIMIZE was *not* set:
+
+ (a) A pattern such as (?=.*X)X$ was incorrectly optimized as if it needed
+ both an initial 'X' and a following 'X'.
+ (b) Some patterns starting with an assertion that started with .* were
+ incorrectly optimized as having to match at the start of the subject or
+ after a newline. There are cases where this is not true, for example,
+ (?=.*[A-Z])(?=.{8,16})(?!.*[\s]) matches after the start in lines that
+ start with spaces. Starting .* in an assertion is no longer taken as an
+ indication of matching at the start (or after a newline).
+
+16. The "offset" modifier in pcre2test was not being ignored (as documented)
+when the POSIX API was in use.
+
+17. Added --enable-fuzz-support to "configure", causing an non-installed
+library containing a test function that can be called by fuzzers to be
+compiled. A non-installed binary to run the test function locally, called
+pcre2fuzzcheck is also compiled.
+
+18. A pattern with PCRE2_DOTALL (/s) set but not PCRE2_NO_DOTSTAR_ANCHOR, and
+which started with .* inside a positive lookahead was incorrectly being
+compiled as implicitly anchored.
+
+19. Removed all instances of "register" declarations, as they are considered
+obsolete these days and in any case had become very haphazard.
+
+20. Add strerror() to pcre2test for failed file opening.
+
+21. Make pcre2test -C list valgrind support when it is enabled.
+
+22. Add the use_length modifier to pcre2test.
+
+23. Fix an off-by-one bug in pcre2test for the list of names for 'get' and
+'copy' modifiers.
+
+24. Add PCRE2_CALL_CONVENTION into the prototype declarations in pcre2.h as it
+is apparently needed there as well as in the function definitions. (Why did
+nobody ask for this in PCRE1?)
+
+25. Change the _PCRE2_H and _PCRE2_UCP_H guard macros in the header files to
+PCRE2_H_IDEMPOTENT_GUARD and PCRE2_UCP_H_IDEMPOTENT_GUARD to be more standard
+compliant and unique.
+
+26. pcre2-config --libs-posix was listing -lpcre2posix instead of
+-lpcre2-posix. Also, the CMake build process was building the library with the
+wrong name.
+
+27. In pcre2test, give some offset information for errors in hex patterns.
+This uses the C99 formatting sequence %td, except for MSVC which doesn't
+support it - %lu is used instead.
+
+28. Implemented pcre2_code_copy_with_tables(), and added pushtablescopy to
+pcre2test for testing it.
+
+29. Fix small memory leak in pcre2test.
+
+30. Fix out-of-bounds read for partial matching of /./ against an empty string
+when the newline type is CRLF.
+
+31. Fix a bug in pcre2test that caused a crash when a locale was set either in
+the current pattern or a previous one and a wide character was matched.
+
+32. The appearance of \p, \P, or \X in a substitution string when
+PCRE2_SUBSTITUTE_EXTENDED was set caused a segmentation fault (NULL
+dereference).
+
+33. If the starting offset was specified as greater than the subject length in
+a call to pcre2_substitute() an out-of-bounds memory reference could occur.
+
+34. When PCRE2 was compiled to use the heap instead of the stack for recursive
+calls to match(), a repeated minimizing caseless back reference, or a
+maximizing one where the two cases had different numbers of code units,
+followed by a caseful back reference, could lose the caselessness of the first
+repeated back reference (example: /(Z)(a)\2{1,2}?(?-i)\1X/i should match ZaAAZX
+but didn't).
+
+35. When a pattern is too complicated, PCRE2 gives up trying to find a minimum
+matching length and just records zero. Typically this happens when there are
+too many nested or recursive back references. If the limit was reached in
+certain recursive cases it failed to be triggered and an internal error could
+be the result.
+
+36. The pcre2_dfa_match() function now takes note of the recursion limit for
+the internal recursive calls that are used for lookrounds and recursions within
+the pattern.
+
+37. More refactoring has got rid of the internal could_be_empty_branch()
+function (around 400 lines of code, including comments) by keeping track of
+could-be-emptiness as the pattern is compiled instead of scanning compiled
+groups. (This would have been much harder before the refactoring of #3 above.)
+This lifts a restriction on the number of branches in a group (more than about
+1100 would give "pattern is too complicated").
+
+38. Add the "-ac" command line option to pcre2test as a synonym for "-pattern
+auto_callout".
+
+39. In a library with Unicode support, incorrect data was compiled for a
+pattern with PCRE2_UCP set without PCRE2_UTF if a class required all wide
+characters to match (for example, /[\s[:^ascii:]]/).
+
+40. The callout_error modifier has been added to pcre2test to make it possible
+to return PCRE2_ERROR_CALLOUT from a callout.
+
+41. A minor change to pcre2grep: colour reset is now "<esc>[0m" instead of
+"<esc>[00m".
+
+42. The limit in the auto-possessification code that was intended to catch
+overly-complicated patterns and not spend too much time auto-possessifying was
+being reset too often, resulting in very long compile times for some patterns.
+Now such patterns are no longer completely auto-possessified.
+
+43. Applied Jason Hood's revised patch for RunTest.bat.
+
+44. Added a new Windows script RunGrepTest.bat, courtesy of Jason Hood.
+
+45. Minor cosmetic fix to pcre2test: move a variable that is not used under
+Windows into the "not Windows" code.
+
+46. Applied Jason Hood's patches to upgrade pcre2grep under Windows and tidy
+some of the code:
+
+ * normalised the Windows condition by ensuring WIN32 is defined;
+ * enables the callout feature under Windows;
+ * adds globbing (Microsoft's implementation expands quoted args),
+ using a tweaked opendirectory;
+ * implements the is_*_tty functions for Windows;
+ * --color=always will write the ANSI sequences to file;
+ * add sequences 4 (underline works on Win10) and 5 (blink as bright
+ background, relatively standard on DOS/Win);
+ * remove the (char *) casts for the now-const strings;
+ * remove GREP_COLOUR (grep's command line allowed the 'u', but not
+ the environment), parsing GREP_COLORS instead;
+ * uses the current colour if not set, rather than black;
+ * add print_match for the undefined case;
+ * fixes a typo.
+
+In addition, colour settings containing anything other than digits and
+semicolon are ignored, and the colour controls are no longer output for empty
+strings.
+
+47. Detecting patterns that are too large inside the length-measuring loop
+saves processing ridiculously long patterns to their end.
+
+48. Ignore PCRE2_CASELESS when processing \h, \H, \v, and \V in classes as it
+just wastes time. In the UTF case it can also produce redundant entries in
+XCLASS lists caused by characters with multiple other cases and pairs of
+characters in the same "not-x" sublists.
+
+49. A pattern such as /(?=(a\K))/ can report the end of the match being before
+its start; pcre2test was not handling this correctly when using the POSIX
+interface (it was OK with the native interface).
+
+50. In pcre2grep, ignore all JIT compile errors. This means that pcre2grep will
+continue to work, falling back to interpretation if anything goes wrong with
+JIT.
+
+51. Applied patches from Christian Persch to configure.ac to make use of the
+AC_USE_SYSTEM_EXTENSIONS macro and to test for functions used by the JIT
+modules.
+
+52. Minor fixes to pcre2grep from Jason Hood:
+ * fixed some spacing;
+ * Windows doesn't usually use single quotes, so I've added a define
+ to use appropriate quotes [in an example];
+ * LC_ALL was displayed as "LCC_ALL";
+ * numbers 11, 12 & 13 should end in "th";
+ * use double quotes in usage message.
+
+53. When autopossessifying, skip empty branches without recursion, to reduce
+stack usage for the benefit of clang with -fsanitize-address, which uses huge
+stack frames. Example pattern: /X?(R||){3335}/. Fixes oss-fuzz issue 553.
+
+54. A pattern with very many explicit back references to a group that is a long
+way from the start of the pattern could take a long time to compile because
+searching for the referenced group in order to find the minimum length was
+being done repeatedly. Now up to 128 group minimum lengths are cached and the
+attempt to find a minimum length is abandoned if there is a back reference to a
+group whose number is greater than 128. (In that case, the pattern is so
+complicated that this optimization probably isn't worth it.) This fixes
+oss-fuzz issue 557.
+
+55. Issue 32 for 10.22 below was not correctly fixed. If pcre2grep in multiline
+mode with --only-matching matched several lines, it restarted scanning at the
+next line instead of moving on to the end of the matched string, which can be
+several lines after the start.
+
+56. Applied Jason Hood's new patch for RunGrepTest.bat that updates it in line
+with updates to the non-Windows version.
+
+
+
+Version 10.22 29-July-2016
+--------------------------
+
+1. Applied Jason Hood's patches to RunTest.bat and testdata/wintestoutput3
+to fix problems with running the tests under Windows.
+
+2. Implemented a facility for quoting literal characters within hexadecimal
+patterns in pcre2test, to make it easier to create patterns with just a few
+non-printing characters.
+
+3. Binary zeros are not supported in pcre2test input files. It now detects them
+and gives an error.
+
+4. Updated the valgrind parameters in RunTest: (a) changed smc-check=all to
+smc-check=all-non-file; (b) changed obj:* in the suppression file to obj:??? so
+that it matches only unknown objects.
+
+5. Updated the maintenance script maint/ManyConfigTests to make it easier to
+select individual groups of tests.
+
+6. When the POSIX wrapper function regcomp() is called, the REG_NOSUB option
+used to set PCRE2_NO_AUTO_CAPTURE when calling pcre2_compile(). However, this
+disables the use of back references (and subroutine calls), which are supported
+by other implementations of regcomp() with RE_NOSUB. Therefore, REG_NOSUB no
+longer causes PCRE2_NO_AUTO_CAPTURE to be set, though it still ignores nmatch
+and pmatch when regexec() is called.
+
+7. Because of 6 above, pcre2test has been modified with a new modifier called
+posix_nosub, to call regcomp() with REG_NOSUB. Previously the no_auto_capture
+modifier had this effect. That option is now ignored when the POSIX API is in
+use.
+
+8. Minor tidies to the pcre2demo.c sample program, including more comments
+about its 8-bit-ness.
+
+9. Detect unmatched closing parentheses and give the error in the pre-scan
+instead of later. Previously the pre-scan carried on and could give a
+misleading incorrect error message. For example, /(?J)(?'a'))(?'a')/ gave a
+message about invalid duplicate group names.
+
+10. It has happened that pcre2test was accidentally linked with another POSIX
+regex library instead of libpcre2-posix. In this situation, a call to regcomp()
+(in the other library) may succeed, returning zero, but of course putting its
+own data into the regex_t block. In one example the re_pcre2_code field was
+left as NULL, which made pcre2test think it had not got a compiled POSIX regex,
+so it treated the next line as another pattern line, resulting in a confusing
+error message. A check has been added to pcre2test to see if the data returned
+from a successful call of regcomp() are valid for PCRE2's regcomp(). If they
+are not, an error message is output and the pcre2test run is abandoned. The
+message points out the possibility of a mis-linking. Hopefully this will avoid
+some head-scratching the next time this happens.
+
+11. A pattern such as /(?<=((?C)0))/, which has a callout inside a lookbehind
+assertion, caused pcre2test to output a very large number of spaces when the
+callout was taken, making the program appearing to loop.
+
+12. A pattern that included (*ACCEPT) in the middle of a sufficiently deeply
+nested set of parentheses of sufficient size caused an overflow of the
+compiling workspace (which was diagnosed, but of course is not desirable).
+
+13. Detect missing closing parentheses during the pre-pass for group
+identification.
+
+14. Changed some integer variable types and put in a number of casts, following
+a report of compiler warnings from Visual Studio 2013 and a few tests with
+gcc's -Wconversion (which still throws up a lot).
+
+15. Implemented pcre2_code_copy(), and added pushcopy and #popcopy to pcre2test
+for testing it.
+
+16. Change 66 for 10.21 introduced the use of snprintf() in PCRE2's version of
+regerror(). When the error buffer is too small, my version of snprintf() puts a
+binary zero in the final byte. Bug #1801 seems to show that other versions do
+not do this, leading to bad output from pcre2test when it was checking for
+buffer overflow. It no longer assumes a binary zero at the end of a too-small
+regerror() buffer.
+
+17. Fixed typo ("&&" for "&") in pcre2_study(). Fortunately, this could not
+actually affect anything, by sheer luck.
+
+18. Two minor fixes for MSVC compilation: (a) removal of apparently incorrect
+"const" qualifiers in pcre2test and (b) defining snprintf as _snprintf for
+older MSVC compilers. This has been done both in src/pcre2_internal.h for most
+of the library, and also in src/pcre2posix.c, which no longer includes
+pcre2_internal.h (see 24 below).
+
+19. Applied Chris Wilson's patch (Bugzilla #1681) to CMakeLists.txt for MSVC
+static compilation. Subsequently applied Chris Wilson's second patch, putting
+the first patch under a new option instead of being unconditional when
+PCRE_STATIC is set.
+
+20. Updated pcre2grep to set stdout as binary when run under Windows, so as not
+to convert \r\n at the ends of reflected lines into \r\r\n. This required
+ensuring that other output that is written to stdout (e.g. file names) uses the
+appropriate line terminator: \r\n for Windows, \n otherwise.
+
+21. When a line is too long for pcre2grep's internal buffer, show the maximum
+length in the error message.
+
+22. Added support for string callouts to pcre2grep (Zoltan's patch with PH
+additions).
+
+23. RunTest.bat was missing a "set type" line for test 22.
+
+24. The pcre2posix.c file was including pcre2_internal.h, and using some
+"private" knowledge of the data structures. This is unnecessary; the code has
+been re-factored and no longer includes pcre2_internal.h.
+
+25. A racing condition is fixed in JIT reported by Mozilla.
+
+26. Minor code refactor to avoid "array subscript is below array bounds"
+compiler warning.
+
+27. Minor code refactor to avoid "left shift of negative number" warning.
+
+28. Add a bit more sanity checking to pcre2_serialize_decode() and document
+that it expects trusted data.
+
+29. Fix typo in pcre2_jit_test.c
+
+30. Due to an oversight, pcre2grep was not making use of JIT when available.
+This is now fixed.
+
+31. The RunGrepTest script is updated to use the valgrind suppressions file
+when testing with JIT under valgrind (compare 10.21/51 below). The suppressions
+file is updated so that is now the same as for PCRE1: it suppresses the
+Memcheck warnings Addr16 and Cond in unknown objects (that is, JIT-compiled
+code). Also changed smc-check=all to smc-check=all-non-file as was done for
+RunTest (see 4 above).
+
+32. Implemented the PCRE2_NO_JIT option for pcre2_match().
+
+33. Fix typo that gave a compiler error when JIT not supported.
+
+34. Fix comment describing the returns from find_fixedlength().
+
+35. Fix potential negative index in pcre2test.
+
+36. Calls to pcre2_get_error_message() with error numbers that are never
+returned by PCRE2 functions were returning empty strings. Now the error code
+PCRE2_ERROR_BADDATA is returned. A facility has been added to pcre2test to
+show the texts for given error numbers (i.e. to call pcre2_get_error_message()
+and display what it returns) and a few representative error codes are now
+checked in RunTest.
+
+37. Added "&& !defined(__INTEL_COMPILER)" to the test for __GNUC__ in
+pcre2_match.c, in anticipation that this is needed for the same reason it was
+recently added to pcrecpp.cc in PCRE1.
+
+38. Using -o with -M in pcre2grep could cause unnecessary repeated output when
+the match extended over a line boundary, as it tried to find more matches "on
+the same line" - but it was already over the end.
+
+39. Allow \C in lookbehinds and DFA matching in UTF-32 mode (by converting it
+to the same code as '.' when PCRE2_DOTALL is set).
+
+40. Fix two clang compiler warnings in pcre2test when only one code unit width
+is supported.
+
+41. Upgrade RunTest to automatically re-run test 2 with a large (64MiB) stack
+if it fails when running the interpreter with a 16MiB stack (and if changing
+the stack size via pcre2test is possible). This avoids having to manually set a
+large stack size when testing with clang.
+
+42. Fix register overwite in JIT when SSE2 acceleration is enabled.
+
+43. Detect integer overflow in pcre2test pattern and data repetition counts.
+
+44. In pcre2test, ignore "allcaptures" after DFA matching.
+
+45. Fix unaligned accesses on x86. Patch by Marc Mutz.
+
+46. Fix some more clang compiler warnings.
+
+
+Version 10.21 12-January-2016
+-----------------------------
+
+1. Improve matching speed of patterns starting with + or * in JIT.
+
+2. Use memchr() to find the first character in an unanchored match in 8-bit
+mode in the interpreter. This gives a significant speed improvement.
+
+3. Removed a redundant copy of the opcode_possessify table in the
+pcre2_auto_possessify.c source.
+
+4. Fix typos in dftables.c for z/OS.
+
+5. Change 36 for 10.20 broke the handling of [[:>:]] and [[:<:]] in that
+processing them could involve a buffer overflow if the following character was
+an opening parenthesis.
+
+6. Change 36 for 10.20 also introduced a bug in processing this pattern:
+/((?x)(*:0))#(?'/. Specifically: if a setting of (?x) was followed by a (*MARK)
+setting (which (*:0) is), then (?x) did not get unset at the end of its group
+during the scan for named groups, and hence the external # was incorrectly
+treated as a comment and the invalid (?' at the end of the pattern was not
+diagnosed. This caused a buffer overflow during the real compile. This bug was
+discovered by Karl Skomski with the LLVM fuzzer.
+
+7. Moved the pcre2_find_bracket() function from src/pcre2_compile.c into its
+own source module to avoid a circular dependency between src/pcre2_compile.c
+and src/pcre2_study.c
+
+8. A callout with a string argument containing an opening square bracket, for
+example /(?C$[$)(?<]/, was incorrectly processed and could provoke a buffer
+overflow. This bug was discovered by Karl Skomski with the LLVM fuzzer.
+
+9. The handling of callouts during the pre-pass for named group identification
+has been tightened up.
+
+10. The quantifier {1} can be ignored, whether greedy, non-greedy, or
+possessive. This is a very minor optimization.
+
+11. A possessively repeated conditional group that could match an empty string,
+for example, /(?(R))*+/, was incorrectly compiled.
+
+12. The Unicode tables have been updated to Unicode 8.0.0 (thanks to Christian
+Persch).
+
+13. An empty comment (?#) in a pattern was incorrectly processed and could
+provoke a buffer overflow. This bug was discovered by Karl Skomski with the
+LLVM fuzzer.
+
+14. Fix infinite recursion in the JIT compiler when certain patterns such as
+/(?:|a|){100}x/ are analysed.
+
+15. Some patterns with character classes involving [: and \\ were incorrectly
+compiled and could cause reading from uninitialized memory or an incorrect
+error diagnosis. Examples are: /[[:\\](?<[::]/ and /[[:\\](?'abc')[a:]. The
+first of these bugs was discovered by Karl Skomski with the LLVM fuzzer.
+
+16. Pathological patterns containing many nested occurrences of [: caused
+pcre2_compile() to run for a very long time. This bug was found by the LLVM
+fuzzer.
+
+17. A missing closing parenthesis for a callout with a string argument was not
+being diagnosed, possibly leading to a buffer overflow. This bug was found by
+the LLVM fuzzer.
+
+18. A conditional group with only one branch has an implicit empty alternative
+branch and must therefore be treated as potentially matching an empty string.
+
+19. If (?R was followed by - or + incorrect behaviour happened instead of a
+diagnostic. This bug was discovered by Karl Skomski with the LLVM fuzzer.
+
+20. Another bug that was introduced by change 36 for 10.20: conditional groups
+whose condition was an assertion preceded by an explicit callout with a string
+argument might be incorrectly processed, especially if the string contained \Q.
+This bug was discovered by Karl Skomski with the LLVM fuzzer.
+
+21. Compiling PCRE2 with the sanitize options of clang showed up a number of
+very pedantic coding infelicities and a buffer overflow while checking a UTF-8
+string if the final multi-byte UTF-8 character was truncated.
+
+22. For Perl compatibility in EBCDIC environments, ranges such as a-z in a
+class, where both values are literal letters in the same case, omit the
+non-letter EBCDIC code points within the range.
+
+23. Finding the minimum matching length of complex patterns with back
+references and/or recursions can take a long time. There is now a cut-off that
+gives up trying to find a minimum length when things get too complex.
+
+24. An optimization has been added that speeds up finding the minimum matching
+length for patterns containing repeated capturing groups or recursions.
+
+25. If a pattern contained a back reference to a group whose number was
+duplicated as a result of appearing in a (?|...) group, the computation of the
+minimum matching length gave a wrong result, which could cause incorrect "no
+match" errors. For such patterns, a minimum matching length cannot at present
+be computed.
+
+26. Added a check for integer overflow in conditions (?(<digits>) and
+(?(R<digits>). This omission was discovered by Karl Skomski with the LLVM
+fuzzer.
+
+27. Fixed an issue when \p{Any} inside an xclass did not read the current
+character.
+
+28. If pcre2grep was given the -q option with -c or -l, or when handling a
+binary file, it incorrectly wrote output to stdout.
+
+29. The JIT compiler did not restore the control verb head in case of *THEN
+control verbs. This issue was found by Karl Skomski with a custom LLVM fuzzer.
+
+30. The way recursive references such as (?3) are compiled has been re-written
+because the old way was the cause of many issues. Now, conversion of the group
+number into a pattern offset does not happen until the pattern has been
+completely compiled. This does mean that detection of all infinitely looping
+recursions is postponed till match time. In the past, some easy ones were
+detected at compile time. This re-writing was done in response to yet another
+bug found by the LLVM fuzzer.
+
+31. A test for a back reference to a non-existent group was missing for items
+such as \987. This caused incorrect code to be compiled. This issue was found
+by Karl Skomski with a custom LLVM fuzzer.
+
+32. Error messages for syntax errors following \g and \k were giving inaccurate
+offsets in the pattern.
+
+33. Improve the performance of starting single character repetitions in JIT.
+
+34. (*LIMIT_MATCH=) now gives an error instead of setting the value to 0.
+
+35. Error messages for syntax errors in *LIMIT_MATCH and *LIMIT_RECURSION now
+give the right offset instead of zero.
+
+36. The JIT compiler should not check repeats after a {0,1} repeat byte code.
+This issue was found by Karl Skomski with a custom LLVM fuzzer.
+
+37. The JIT compiler should restore the control chain for empty possessive
+repeats. This issue was found by Karl Skomski with a custom LLVM fuzzer.
+
+38. A bug which was introduced by the single character repetition optimization
+was fixed.
+
+39. Match limit check added to recursion. This issue was found by Karl Skomski
+with a custom LLVM fuzzer.
+
+40. Arrange for the UTF check in pcre2_match() and pcre2_dfa_match() to look
+only at the part of the subject that is relevant when the starting offset is
+non-zero.
+
+41. Improve first character match in JIT with SSE2 on x86.
+
+42. Fix two assertion fails in JIT. These issues were found by Karl Skomski
+with a custom LLVM fuzzer.
+
+43. Correct the setting of CMAKE_C_FLAGS in CMakeLists.txt (patch from Roy Ivy
+III).
+
+44. Fix bug in RunTest.bat for new test 14, and adjust the script for the added
+test (there are now 20 in total).
+
+45. Fixed a corner case of range optimization in JIT.
+
+46. Add the ${*MARK} facility to pcre2_substitute().
+
+47. Modifier lists in pcre2test were splitting at spaces without the required
+commas.
+
+48. Implemented PCRE2_ALT_VERBNAMES.
+
+49. Fixed two issues in JIT. These were found by Karl Skomski with a custom
+LLVM fuzzer.
+
+50. The pcre2test program has been extended by adding the #newline_default
+command. This has made it possible to run the standard tests when PCRE2 is
+compiled with either CR or CRLF as the default newline convention. As part of
+this work, the new command was added to several test files and the testing
+scripts were modified. The pcre2grep tests can now also be run when there is no
+LF in the default newline convention.
+
+51. The RunTest script has been modified so that, when JIT is used and valgrind
+is specified, a valgrind suppressions file is set up to ignore "Invalid read of
+size 16" errors because these are false positives when the hardware supports
+the SSE2 instruction set.
+
+52. It is now possible to have comment lines amid the subject strings in
+pcre2test (and perltest.sh) input.
+
+53. Implemented PCRE2_USE_OFFSET_LIMIT and pcre2_set_offset_limit().
+
+54. Add the null_context modifier to pcre2test so that calling pcre2_compile()
+and the matching functions with NULL contexts can be tested.
+
+55. Implemented PCRE2_SUBSTITUTE_EXTENDED.
+
+56. In a character class such as [\W\p{Any}] where both a negative-type escape
+("not a word character") and a property escape were present, the property
+escape was being ignored.
+
+57. Fixed integer overflow for patterns whose minimum matching length is very,
+very large.
+
+58. Implemented --never-backslash-C.
+
+59. Change 55 above introduced a bug by which certain patterns provoked the
+erroneous error "\ at end of pattern".
+
+60. The special sequences [[:<:]] and [[:>:]] gave rise to incorrect compiling
+errors or other strange effects if compiled in UCP mode. Found with libFuzzer
+and AddressSanitizer.
+
+61. Whitespace at the end of a pcre2test pattern line caused a spurious error
+message if there were only single-character modifiers. It should be ignored.
+
+62. The use of PCRE2_NO_AUTO_CAPTURE could cause incorrect compilation results
+or segmentation errors for some patterns. Found with libFuzzer and
+AddressSanitizer.
+
+63. Very long names in (*MARK) or (*THEN) etc. items could provoke a buffer
+overflow.
+
+64. Improve error message for overly-complicated patterns.
+
+65. Implemented an optional replication feature for patterns in pcre2test, to
+make it easier to test long repetitive patterns. The tests for 63 above are
+converted to use the new feature.
+
+66. In the POSIX wrapper, if regerror() was given too small a buffer, it could
+misbehave.
+
+67. In pcre2_substitute() in UTF mode, the UTF validity check on the
+replacement string was happening before the length setting when the replacement
+string was zero-terminated.
+
+68. In pcre2_substitute() in UTF mode, PCRE2_NO_UTF_CHECK can be set for the
+second and subsequent calls to pcre2_match().
+
+69. There was no check for integer overflow for a replacement group number in
+pcre2_substitute(). An added check for a number greater than the largest group
+number in the pattern means this is not now needed.
+
+70. The PCRE2-specific VERSION condition didn't work correctly if only one
+digit was given after the decimal point, or if more than two digits were given.
+It now works with one or two digits, and gives a compile time error if more are
+given.
+
+71. In pcre2_substitute() there was the possibility of reading one code unit
+beyond the end of the replacement string.
+
+72. The code for checking a subject's UTF-32 validity for a pattern with a
+lookbehind involved an out-of-bounds pointer, which could potentially cause
+trouble in some environments.
+
+73. The maximum lookbehind length was incorrectly calculated for patterns such
+as /(?<=(a)(?-1))x/ which have a recursion within a backreference.
+
+74. Give an error if a lookbehind assertion is longer than 65535 code units.
+
+75. Give an error in pcre2_substitute() if a match ends before it starts (as a
+result of the use of \K).
+
+76. Check the length of subpattern names and the names in (*MARK:xx) etc.
+dynamically to avoid the possibility of integer overflow.
+
+77. Implement pcre2_set_max_pattern_length() so that programs can restrict the
+size of patterns that they are prepared to handle.
+
+78. (*NO_AUTO_POSSESS) was not working.
+
+79. Adding group information caching improves the speed of compiling when
+checking whether a group has a fixed length and/or could match an empty string,
+especially when recursion or subroutine calls are involved. However, this
+cannot be used when (?| is present in the pattern because the same number may
+be used for groups of different sizes. To catch runaway patterns in this
+situation, counts have been introduced to the functions that scan for empty
+branches or compute fixed lengths.
+
+80. Allow for the possibility of the size of the nest_save structure not being
+a factor of the size of the compiling workspace (it currently is).
+
+81. Check for integer overflow in minimum length calculation and cap it at
+65535.
+
+82. Small optimizations in code for finding the minimum matching length.
+
+83. Lock out configuring for EBCDIC with non-8-bit libraries.
+
+84. Test for error code <= 0 in regerror().
+
+85. Check for too many replacements (more than INT_MAX) in pcre2_substitute().
+
+86. Avoid the possibility of computing with an out-of-bounds pointer (though
+not dereferencing it) while handling lookbehind assertions.
+
+87. Failure to get memory for the match data in regcomp() is now given as a
+regcomp() error instead of waiting for regexec() to pick it up.
+
+88. In pcre2_substitute(), ensure that CRLF is not split when it is a valid
+newline sequence.
+
+89. Paranoid check in regcomp() for bad error code from pcre2_compile().
+
+90. Run test 8 (internal offsets and code sizes) for link sizes 3 and 4 as well
+as for link size 2.
+
+91. Document that JIT has a limit on pattern size, and give more information
+about JIT compile failures in pcre2test.
+
+92. Implement PCRE2_INFO_HASBACKSLASHC.
+
+93. Re-arrange valgrind support code in pcre2test to avoid spurious reports
+with JIT (possibly caused by SSE2?).
+
+94. Support offset_limit in JIT.
+
+95. A sequence such as [[:punct:]b] that is, a POSIX character class followed
+by a single ASCII character in a class item, was incorrectly compiled in UCP
+mode. The POSIX class got lost, but only if the single character followed it.
+
+96. [:punct:] in UCP mode was matching some characters in the range 128-255
+that should not have been matched.
+
+97. If [:^ascii:] or [:^xdigit:] are present in a non-negated class, all
+characters with code points greater than 255 are in the class. When a Unicode
+property was also in the class (if PCRE2_UCP is set, escapes such as \w are
+turned into Unicode properties), wide characters were not correctly handled,
+and could fail to match.
+
+98. In pcre2test, make the "startoffset" modifier a synonym of "offset",
+because it sets the "startoffset" parameter for pcre2_match().
+
+99. If PCRE2_AUTO_CALLOUT was set on a pattern that had a (?# comment between
+an item and its qualifier (for example, A(?#comment)?B) pcre2_compile()
+misbehaved. This bug was found by the LLVM fuzzer.
+
+100. The error for an invalid UTF pattern string always gave the code unit
+offset as zero instead of where the invalidity was found.
+
+101. Further to 97 above, negated classes such as [^[:^ascii:]\d] were also not
+working correctly in UCP mode.
+
+102. Similar to 99 above, if an isolated \E was present between an item and its
+qualifier when PCRE2_AUTO_CALLOUT was set, pcre2_compile() misbehaved. This bug
+was found by the LLVM fuzzer.
+
+103. The POSIX wrapper function regexec() crashed if the option REG_STARTEND
+was set when the pmatch argument was NULL. It now returns REG_INVARG.
+
+104. Allow for up to 32-bit numbers in the ordin() function in pcre2grep.
+
+105. An empty \Q\E sequence between an item and its qualifier caused
+pcre2_compile() to misbehave when auto callouts were enabled. This bug
+was found by the LLVM fuzzer.
+
+106. If both PCRE2_ALT_VERBNAMES and PCRE2_EXTENDED were set, and a (*MARK) or
+other verb "name" ended with whitespace immediately before the closing
+parenthesis, pcre2_compile() misbehaved. Example: /(*:abc )/, but only when
+both those options were set.
+
+107. In a number of places pcre2_compile() was not handling NULL characters
+correctly, and pcre2test with the "bincode" modifier was not always correctly
+displaying fields containing NULLS:
+
+ (a) Within /x extended #-comments
+ (b) Within the "name" part of (*MARK) and other *verbs
+ (c) Within the text argument of a callout
+
+108. If a pattern that was compiled with PCRE2_EXTENDED started with white
+space or a #-type comment that was followed by (?-x), which turns off
+PCRE2_EXTENDED, and there was no subsequent (?x) to turn it on again,
+pcre2_compile() assumed that (?-x) applied to the whole pattern and
+consequently mis-compiled it. This bug was found by the LLVM fuzzer. The fix
+for this bug means that a setting of any of the (?imsxJU) options at the start
+of a pattern is no longer transferred to the options that are returned by
+PCRE2_INFO_ALLOPTIONS. In fact, this was an anachronism that should have
+changed when the effects of those options were all moved to compile time.
+
+109. An escaped closing parenthesis in the "name" part of a (*verb) when
+PCRE2_ALT_VERBNAMES was set caused pcre2_compile() to malfunction. This bug
+was found by the LLVM fuzzer.
+
+110. Implemented PCRE2_SUBSTITUTE_UNSET_EMPTY, and updated pcre2test to make it
+possible to test it.
+
+111. "Harden" pcre2test against ridiculously large values in modifiers and
+command line arguments.
+
+112. Implemented PCRE2_SUBSTITUTE_UNKNOWN_UNSET and PCRE2_SUBSTITUTE_OVERFLOW_
+LENGTH.
+
+113. Fix printing of *MARK names that contain binary zeroes in pcre2test.
+
+
+Version 10.20 30-June-2015
+--------------------------
+
+1. Callouts with string arguments have been added.
+
+2. Assertion code generator in JIT has been optimized.
+
+3. The invalid pattern (?(?C) has a missing assertion condition at the end. The
+pcre2_compile() function read past the end of the input before diagnosing an
+error. This bug was discovered by the LLVM fuzzer.
+
+4. Implemented pcre2_callout_enumerate().
+
+5. Fix JIT compilation of conditional blocks whose assertion is converted to
+(*FAIL). E.g: /(?(?!))/.
+
+6. The pattern /(?(?!)^)/ caused references to random memory. This bug was
+discovered by the LLVM fuzzer.
+
+7. The assertion (?!) is optimized to (*FAIL). This was not handled correctly
+when this assertion was used as a condition, for example (?(?!)a|b). In
+pcre2_match() it worked by luck; in pcre2_dfa_match() it gave an incorrect
+error about an unsupported item.
+
+8. For some types of pattern, for example /Z*(|d*){216}/, the auto-
+possessification code could take exponential time to complete. A recursion
+depth limit of 1000 has been imposed to limit the resources used by this
+optimization. This infelicity was discovered by the LLVM fuzzer.
+
+9. A pattern such as /(*UTF)[\S\V\H]/, which contains a negated special class
+such as \S in non-UCP mode, explicit wide characters (> 255) can be ignored
+because \S ensures they are all in the class. The code for doing this was
+interacting badly with the code for computing the amount of space needed to
+compile the pattern, leading to a buffer overflow. This bug was discovered by
+the LLVM fuzzer.
+
+10. A pattern such as /((?2)+)((?1))/ which has mutual recursion nested inside
+other kinds of group caused stack overflow at compile time. This bug was
+discovered by the LLVM fuzzer.
+
+11. A pattern such as /(?1)(?#?'){8}(a)/ which had a parenthesized comment
+between a subroutine call and its quantifier was incorrectly compiled, leading
+to buffer overflow or other errors. This bug was discovered by the LLVM fuzzer.
+
+12. The illegal pattern /(?(?<E>.*!.*)?)/ was not being diagnosed as missing an
+assertion after (?(. The code was failing to check the character after (?(?<
+for the ! or = that would indicate a lookbehind assertion. This bug was
+discovered by the LLVM fuzzer.
+
+13. A pattern such as /X((?2)()*+){2}+/ which has a possessive quantifier with
+a fixed maximum following a group that contains a subroutine reference was
+incorrectly compiled and could trigger buffer overflow. This bug was discovered
+by the LLVM fuzzer.
+
+14. Negative relative recursive references such as (?-7) to non-existent
+subpatterns were not being diagnosed and could lead to unpredictable behaviour.
+This bug was discovered by the LLVM fuzzer.
+
+15. The bug fixed in 14 was due to an integer variable that was unsigned when
+it should have been signed. Some other "int" variables, having been checked,
+have either been changed to uint32_t or commented as "must be signed".
+
+16. A mutual recursion within a lookbehind assertion such as (?<=((?2))((?1)))
+caused a stack overflow instead of the diagnosis of a non-fixed length
+lookbehind assertion. This bug was discovered by the LLVM fuzzer.
+
+17. The use of \K in a positive lookbehind assertion in a non-anchored pattern
+(e.g. /(?<=\Ka)/) could make pcre2grep loop.
+
+18. There was a similar problem to 17 in pcre2test for global matches, though
+the code there did catch the loop.
+
+19. If a greedy quantified \X was preceded by \C in UTF mode (e.g. \C\X*),
+and a subsequent item in the pattern caused a non-match, backtracking over the
+repeated \X did not stop, but carried on past the start of the subject, causing
+reference to random memory and/or a segfault. There were also some other cases
+where backtracking after \C could crash. This set of bugs was discovered by the
+LLVM fuzzer.
+
+20. The function for finding the minimum length of a matching string could take
+a very long time if mutual recursion was present many times in a pattern, for
+example, /((?2){73}(?2))((?1))/. A better mutual recursion detection method has
+been implemented. This infelicity was discovered by the LLVM fuzzer.
+
+21. Implemented PCRE2_NEVER_BACKSLASH_C.
+
+22. The feature for string replication in pcre2test could read from freed
+memory if the replication required a buffer to be extended, and it was not
+working properly in 16-bit and 32-bit modes. This issue was discovered by a
+fuzzer: see http://lcamtuf.coredump.cx/afl/.
+
+23. Added the PCRE2_ALT_CIRCUMFLEX option.
+
+24. Adjust the treatment of \8 and \9 to be the same as the current Perl
+behaviour.
+
+25. Static linking against the PCRE2 library using the pkg-config module was
+failing on missing pthread symbols.
+
+26. If a group that contained a recursive back reference also contained a
+forward reference subroutine call followed by a non-forward-reference
+subroutine call, for example /.((?2)(?R)\1)()/, pcre2_compile() failed to
+compile correct code, leading to undefined behaviour or an internally detected
+error. This bug was discovered by the LLVM fuzzer.
+
+27. Quantification of certain items (e.g. atomic back references) could cause
+incorrect code to be compiled when recursive forward references were involved.
+For example, in this pattern: /(?1)()((((((\1++))\x85)+)|))/. This bug was
+discovered by the LLVM fuzzer.
+
+28. A repeated conditional group whose condition was a reference by name caused
+a buffer overflow if there was more than one group with the given name. This
+bug was discovered by the LLVM fuzzer.
+
+29. A recursive back reference by name within a group that had the same name as
+another group caused a buffer overflow. For example: /(?J)(?'d'(?'d'\g{d}))/.
+This bug was discovered by the LLVM fuzzer.
+
+30. A forward reference by name to a group whose number is the same as the
+current group, for example in this pattern: /(?|(\k'Pm')|(?'Pm'))/, caused a
+buffer overflow at compile time. This bug was discovered by the LLVM fuzzer.
+
+31. Fix -fsanitize=undefined warnings for left shifts of 1 by 31 (it treats 1
+as an int; fixed by writing it as 1u).
+
+32. Fix pcre2grep compile when -std=c99 is used with gcc, though it still gives
+a warning for "fileno" unless -std=gnu99 us used.
+
+33. A lookbehind assertion within a set of mutually recursive subpatterns could
+provoke a buffer overflow. This bug was discovered by the LLVM fuzzer.
+
+34. Give an error for an empty subpattern name such as (?'').
+
+35. Make pcre2test give an error if a pattern that follows #forbud_utf contains
+\P, \p, or \X.
+
+36. The way named subpatterns are handled has been refactored. There is now a
+pre-pass over the regex which does nothing other than identify named
+subpatterns and count the total captures. This means that information about
+named patterns is known before the rest of the compile. In particular, it means
+that forward references can be checked as they are encountered. Previously, the
+code for handling forward references was contorted and led to several errors in
+computing the memory requirements for some patterns, leading to buffer
+overflows.
+
+37. There was no check for integer overflow in subroutine calls such as (?123).
+
+38. The table entry for \l in EBCDIC environments was incorrect, leading to its
+being treated as a literal 'l' instead of causing an error.
+
+39. If a non-capturing group containing a conditional group that could match
+an empty string was repeated, it was not identified as matching an empty string
+itself. For example: /^(?:(?(1)x|)+)+$()/.
+
+40. In an EBCDIC environment, pcretest was mishandling the escape sequences
+\a and \e in test subject lines.
+
+41. In an EBCDIC environment, \a in a pattern was converted to the ASCII
+instead of the EBCDIC value.
+
+42. The handling of \c in an EBCDIC environment has been revised so that it is
+now compatible with the specification in Perl's perlebcdic page.
+
+43. Single character repetition in JIT has been improved. 20-30% speedup
+was achieved on certain patterns.
+
+44. The EBCDIC character 0x41 is a non-breaking space, equivalent to 0xa0 in
+ASCII/Unicode. This has now been added to the list of characters that are
+recognized as white space in EBCDIC.
+
+45. When PCRE2 was compiled without Unicode support, the use of \p and \P gave
+an error (correctly) when used outside a class, but did not give an error
+within a class.
+
+46. \h within a class was incorrectly compiled in EBCDIC environments.
+
+47. JIT should return with error when the compiled pattern requires
+more stack space than the maximum.
+
+48. Fixed a memory leak in pcre2grep when a locale is set.
+
+
+Version 10.10 06-March-2015
+---------------------------
+
+1. When a pattern is compiled, it remembers the highest back reference so that
+when matching, if the ovector is too small, extra memory can be obtained to
+use instead. A conditional subpattern whose condition is a check on a capture
+having happened, such as, for example in the pattern /^(?:(a)|b)(?(1)A|B)/, is
+another kind of back reference, but it was not setting the highest
+backreference number. This mattered only if pcre2_match() was called with an
+ovector that was too small to hold the capture, and there was no other kind of
+back reference (a situation which is probably quite rare). The effect of the
+bug was that the condition was always treated as FALSE when the capture could
+not be consulted, leading to a incorrect behaviour by pcre2_match(). This bug
+has been fixed.
+
+2. Functions for serialization and deserialization of sets of compiled patterns
+have been added.
+
+3. The value that is returned by PCRE2_INFO_SIZE has been corrected to remove
+excess code units at the end of the data block that may occasionally occur if
+the code for calculating the size over-estimates. This change stops the
+serialization code copying uninitialized data, to which valgrind objects. The
+documentation of PCRE2_INFO_SIZE was incorrect in stating that the size did not
+include the general overhead. This has been corrected.
+
+4. All code units in every slot in the table of group names are now set, again
+in order to avoid accessing uninitialized data when serializing.
+
+5. The (*NO_JIT) feature is implemented.
+
+6. If a bug that caused pcre2_compile() to use more memory than allocated was
+triggered when using valgrind, the code in (3) above passed a stupidly large
+value to valgrind. This caused a crash instead of an "internal error" return.
+
+7. A reference to a duplicated named group (either a back reference or a test
+for being set in a conditional) that occurred in a part of the pattern where
+PCRE2_DUPNAMES was not set caused the amount of memory needed for the pattern
+to be incorrectly calculated, leading to overwriting.
+
+8. A mutually recursive set of back references such as (\2)(\1) caused a
+segfault at compile time (while trying to find the minimum matching length).
+The infinite loop is now broken (with the minimum length unset, that is, zero).
+
+9. If an assertion that was used as a condition was quantified with a minimum
+of zero, matching went wrong. In particular, if the whole group had unlimited
+repetition and could match an empty string, a segfault was likely. The pattern
+(?(?=0)?)+ is an example that caused this. Perl allows assertions to be
+quantified, but not if they are being used as conditions, so the above pattern
+is faulted by Perl. PCRE2 has now been changed so that it also rejects such
+patterns.
+
+10. The error message for an invalid quantifier has been changed from "nothing
+to repeat" to "quantifier does not follow a repeatable item".
+
+11. If a bad UTF string is compiled with NO_UTF_CHECK, it may succeed, but
+scanning the compiled pattern in subsequent auto-possessification can get out
+of step and lead to an unknown opcode. Previously this could have caused an
+infinite loop. Now it generates an "internal error" error. This is a tidyup,
+not a bug fix; passing bad UTF with NO_UTF_CHECK is documented as having an
+undefined outcome.
+
+12. A UTF pattern containing a "not" match of a non-ASCII character and a
+subroutine reference could loop at compile time. Example: /[^\xff]((?1))/.
+
+13. The locale test (RunTest 3) has been upgraded. It now checks that a locale
+that is found in the output of "locale -a" can actually be set by pcre2test
+before it is accepted. Previously, in an environment where a locale was listed
+but would not set (an example does exist), the test would "pass" without
+actually doing anything. Also the fr_CA locale has been added to the list of
+locales that can be used.
+
+14. Fixed a bug in pcre2_substitute(). If a replacement string ended in a
+capturing group number without parentheses, the last character was incorrectly
+literally included at the end of the replacement string.
+
+15. A possessive capturing group such as (a)*+ with a minimum repeat of zero
+failed to allow the zero-repeat case if pcre2_match() was called with an
+ovector too small to capture the group.
+
+16. Improved error message in pcre2test when setting the stack size (-S) fails.
+
+17. Fixed two bugs in CMakeLists.txt: (1) Some lines had got lost in the
+transfer from PCRE1, meaning that CMake configuration failed if "build tests"
+was selected. (2) The file src/pcre2_serialize.c had not been added to the list
+of PCRE2 sources, which caused a failure to build pcre2test.
+
+18. Fixed typo in pcre2_serialize.c (DECL instead of DEFN) that causes problems
+only on Windows.
+
+19. Use binary input when reading back saved serialized patterns in pcre2test.
+
+20. Added RunTest.bat for running the tests under Windows.
+
+21. "make distclean" was not removing config.h, a file that may be created for
+use with CMake.
+
+22. A pattern such as "((?2){0,1999}())?", which has a group containing a
+forward reference repeated a large (but limited) number of times within a
+repeated outer group that has a zero minimum quantifier, caused incorrect code
+to be compiled, leading to the error "internal error: previously-checked
+referenced subpattern not found" when an incorrect memory address was read.
+This bug was reported as "heap overflow", discovered by Kai Lu of Fortinet's
+FortiGuard Labs. (Added 24-March-2015: CVE-2015-2325 was given to this.)
+
+23. A pattern such as "((?+1)(\1))/" containing a forward reference subroutine
+call within a group that also contained a recursive back reference caused
+incorrect code to be compiled. This bug was reported as "heap overflow",
+discovered by Kai Lu of Fortinet's FortiGuard Labs. (Added 24-March-2015:
+CVE-2015-2326 was given to this.)
+
+24. Computing the size of the JIT read-only data in advance has been a source
+of various issues, and new ones are still appear unfortunately. To fix
+existing and future issues, size computation is eliminated from the code,
+and replaced by on-demand memory allocation.
+
+25. A pattern such as /(?i)[A-`]/, where characters in the other case are
+adjacent to the end of the range, and the range contained characters with more
+than one other case, caused incorrect behaviour when compiled in UTF mode. In
+that example, the range a-j was left out of the class.
+
+
+Version 10.00 05-January-2015
+-----------------------------
+
+Version 10.00 is the first release of PCRE2, a revised API for the PCRE
+library. Changes prior to 10.00 are logged in the ChangeLog file for the old
+API, up to item 20 for release 8.36.
+
+The code of the library was heavily revised as part of the new API
+implementation. Details of each and every modification were not individually
+logged. In addition to the API changes, the following changes were made. They
+are either new functionality, or bug fixes and other noticeable changes of
+behaviour that were implemented after the code had been forked.
+
+1. Including Unicode support at build time is now enabled by default, but it
+can optionally be disabled. It is not enabled by default at run time (no
+change).
+
+2. The test program, now called pcre2test, was re-specified and almost
+completely re-written. Its input is not compatible with input for pcretest.
+
+3. Patterns may start with (*NOTEMPTY) or (*NOTEMPTY_ATSTART) to set the
+PCRE2_NOTEMPTY or PCRE2_NOTEMPTY_ATSTART options for every subject line that is
+matched by that pattern.
+
+4. For the benefit of those who use PCRE2 via some other application, that is,
+not writing the function calls themselves, it is possible to check the PCRE2
+version by matching a pattern such as /(?(VERSION>=10)yes|no)/ against a
+string such as "yesno".
+
+5. There are case-equivalent Unicode characters whose encodings use different
+numbers of code units in UTF-8. U+023A and U+2C65 are one example. (It is
+theoretically possible for this to happen in UTF-16 too.) If a backreference to
+a group containing one of these characters was greedily repeated, and during
+the match a backtrack occurred, the subject might be backtracked by the wrong
+number of code units. For example, if /^(\x{23a})\1*(.)/ is matched caselessly
+(and in UTF-8 mode) against "\x{23a}\x{2c65}\x{2c65}\x{2c65}", group 2 should
+capture the final character, which is the three bytes E2, B1, and A5 in UTF-8.
+Incorrect backtracking meant that group 2 captured only the last two bytes.
+This bug has been fixed; the new code is slower, but it is used only when the
+strings matched by the repetition are not all the same length.
+
+6. A pattern such as /()a/ was not setting the "first character must be 'a'"
+information. This applied to any pattern with a group that matched no
+characters, for example: /(?:(?=.)|(?<!x))a/.
+
+7. When an (*ACCEPT) is triggered inside capturing parentheses, it arranges for
+those parentheses to be closed with whatever has been captured so far. However,
+it was failing to mark any other groups between the highest capture so far and
+the currrent group as "unset". Thus, the ovector for those groups contained
+whatever was previously there. An example is the pattern /(x)|((*ACCEPT))/ when
+matched against "abcd".
+
+8. The pcre2_substitute() function has been implemented.
+
+9. If an assertion used as a condition was quantified with a minimum of zero
+(an odd thing to do, but it happened), SIGSEGV or other misbehaviour could
+occur.
+
+10. The PCRE2_NO_DOTSTAR_ANCHOR option has been implemented.
+
+****
diff --git a/contrib/libs/pcre2/INSTALL b/contrib/libs/pcre2/INSTALL
new file mode 100644
index 00000000000..e82fd21de2e
--- /dev/null
+++ b/contrib/libs/pcre2/INSTALL
@@ -0,0 +1,368 @@
+Installation Instructions
+*************************
+
+ Copyright (C) 1994-1996, 1999-2002, 2004-2017, 2020-2021 Free
+Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved. This file is offered as-is,
+without warranty of any kind.
+
+Basic Installation
+==================
+
+ Briefly, the shell command './configure && make && make install'
+should configure, build, and install this package. The following
+more-detailed instructions are generic; see the 'README' file for
+instructions specific to this package. Some packages provide this
+'INSTALL' file but do not implement all of the features documented
+below. The lack of an optional feature in a given package is not
+necessarily a bug. More recommendations for GNU packages can be found
+in *note Makefile Conventions: (standards)Makefile Conventions.
+
+ The 'configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a 'Makefile' in each directory of the package.
+It may also create one or more '.h' files containing system-dependent
+definitions. Finally, it creates a shell script 'config.status' that
+you can run in the future to recreate the current configuration, and a
+file 'config.log' containing compiler output (useful mainly for
+debugging 'configure').
+
+ It can also use an optional file (typically called 'config.cache' and
+enabled with '--cache-file=config.cache' or simply '-C') that saves the
+results of its tests to speed up reconfiguring. Caching is disabled by
+default to prevent problems with accidental use of stale cache files.
+
+ If you need to do unusual things to compile the package, please try
+to figure out how 'configure' could check whether to do them, and mail
+diffs or instructions to the address given in the 'README' so they can
+be considered for the next release. If you are using the cache, and at
+some point 'config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file 'configure.ac' (or 'configure.in') is used to create
+'configure' by a program called 'autoconf'. You need 'configure.ac' if
+you want to change it or regenerate 'configure' using a newer version of
+'autoconf'.
+
+ The simplest way to compile this package is:
+
+ 1. 'cd' to the directory containing the package's source code and type
+ './configure' to configure the package for your system.
+
+ Running 'configure' might take a while. While running, it prints
+ some messages telling which features it is checking for.
+
+ 2. Type 'make' to compile the package.
+
+ 3. Optionally, type 'make check' to run any self-tests that come with
+ the package, generally using the just-built uninstalled binaries.
+
+ 4. Type 'make install' to install the programs and any data files and
+ documentation. When installing into a prefix owned by root, it is
+ recommended that the package be configured and built as a regular
+ user, and only the 'make install' phase executed with root
+ privileges.
+
+ 5. Optionally, type 'make installcheck' to repeat any self-tests, but
+ this time using the binaries in their final installed location.
+ This target does not install anything. Running this target as a
+ regular user, particularly if the prior 'make install' required
+ root privileges, verifies that the installation completed
+ correctly.
+
+ 6. You can remove the program binaries and object files from the
+ source code directory by typing 'make clean'. To also remove the
+ files that 'configure' created (so you can compile the package for
+ a different kind of computer), type 'make distclean'. There is
+ also a 'make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+ 7. Often, you can also type 'make uninstall' to remove the installed
+ files again. In practice, not all packages have tested that
+ uninstallation works correctly, even though it is required by the
+ GNU Coding Standards.
+
+ 8. Some packages, particularly those that use Automake, provide 'make
+ distcheck', which can by used by developers to test that all other
+ targets like 'make install' and 'make uninstall' work correctly.
+ This target is generally not run by end users.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the 'configure' script does not know about. Run './configure --help'
+for details on some of the pertinent environment variables.
+
+ You can give 'configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here is
+an example:
+
+ ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you can use GNU 'make'. 'cd' to the
+directory where you want the object files and executables to go and run
+the 'configure' script. 'configure' automatically checks for the source
+code in the directory that 'configure' is in and in '..'. This is known
+as a "VPATH" build.
+
+ With a non-GNU 'make', it is safer to compile the package for one
+architecture at a time in the source code directory. After you have
+installed the package for one architecture, use 'make distclean' before
+reconfiguring for another architecture.
+
+ On MacOS X 10.5 and later systems, you can create libraries and
+executables that work on multiple system types--known as "fat" or
+"universal" binaries--by specifying multiple '-arch' options to the
+compiler but only a single '-arch' option to the preprocessor. Like
+this:
+
+ ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+ CPP="gcc -E" CXXCPP="g++ -E"
+
+ This is not guaranteed to produce working output in all cases, you
+may have to build one architecture at a time and combine the results
+using the 'lipo' tool if you have problems.
+
+Installation Names
+==================
+
+ By default, 'make install' installs the package's commands under
+'/usr/local/bin', include files under '/usr/local/include', etc. You
+can specify an installation prefix other than '/usr/local' by giving
+'configure' the option '--prefix=PREFIX', where PREFIX must be an
+absolute file name.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option '--exec-prefix=PREFIX' to 'configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like '--bindir=DIR' to specify different values for particular
+kinds of files. Run 'configure --help' for a list of the directories
+you can set and what kinds of files go in them. In general, the default
+for these options is expressed in terms of '${prefix}', so that
+specifying just '--prefix' will affect all of the other directory
+specifications that were not explicitly provided.
+
+ The most portable way to affect installation locations is to pass the
+correct locations to 'configure'; however, many packages provide one or
+both of the following shortcuts of passing variable assignments to the
+'make install' command line to change installation locations without
+having to reconfigure or recompile.
+
+ The first method involves providing an override variable for each
+affected directory. For example, 'make install
+prefix=/alternate/directory' will choose an alternate location for all
+directory configuration variables that were expressed in terms of
+'${prefix}'. Any directories that were specified during 'configure',
+but not in terms of '${prefix}', must each be overridden at install time
+for the entire installation to be relocated. The approach of makefile
+variable overrides for each directory variable is required by the GNU
+Coding Standards, and ideally causes no recompilation. However, some
+platforms have known limitations with the semantics of shared libraries
+that end up requiring recompilation when using this method, particularly
+noticeable in packages that use GNU Libtool.
+
+ The second method involves providing the 'DESTDIR' variable. For
+example, 'make install DESTDIR=/alternate/directory' will prepend
+'/alternate/directory' before all installation names. The approach of
+'DESTDIR' overrides is not required by the GNU Coding Standards, and
+does not work on platforms that have drive letters. On the other hand,
+it does better at avoiding recompilation issues, and works well even
+when some directory options were not specified in terms of '${prefix}'
+at 'configure' time.
+
+Optional Features
+=================
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving 'configure' the
+option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'.
+
+ Some packages pay attention to '--enable-FEATURE' options to
+'configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to '--with-PACKAGE' options, where PACKAGE
+is something like 'gnu-as' or 'x' (for the X Window System). The
+'README' should mention any '--enable-' and '--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, 'configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the 'configure' options '--x-includes=DIR' and
+'--x-libraries=DIR' to specify their locations.
+
+ Some packages offer the ability to configure how verbose the
+execution of 'make' will be. For these packages, running './configure
+--enable-silent-rules' sets the default to minimal output, which can be
+overridden with 'make V=1'; while running './configure
+--disable-silent-rules' sets the default to verbose, which can be
+overridden with 'make V=0'.
+
+Particular systems
+==================
+
+ On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC
+is not installed, it is recommended to use the following options in
+order to use an ANSI C compiler:
+
+ ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
+
+and if that doesn't work, install pre-built binaries of GCC for HP-UX.
+
+ HP-UX 'make' updates targets which have the same timestamps as their
+prerequisites, which makes it generally unusable when shipped generated
+files such as 'configure' are involved. Use GNU 'make' instead.
+
+ On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
+parse its '<wchar.h>' header file. The option '-nodtk' can be used as a
+workaround. If GNU CC is not installed, it is therefore recommended to
+try
+
+ ./configure CC="cc"
+
+and if that doesn't work, try
+
+ ./configure CC="cc -nodtk"
+
+ On Solaris, don't put '/usr/ucb' early in your 'PATH'. This
+directory contains several dysfunctional programs; working variants of
+these programs are available in '/usr/bin'. So, if you need '/usr/ucb'
+in your 'PATH', put it _after_ '/usr/bin'.
+
+ On Haiku, software installed for all users goes in '/boot/common',
+not '/usr/local'. It is recommended to use the following options:
+
+ ./configure --prefix=/boot/common
+
+Specifying the System Type
+==========================
+
+ There may be some features 'configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on. Usually, assuming the package is built to be run on the
+_same_ architectures, 'configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+'--build=TYPE' option. TYPE can either be a short name for the system
+type, such as 'sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS
+ KERNEL-OS
+
+ See the file 'config.sub' for the possible values of each field. If
+'config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option '--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with '--host=TYPE'.
+
+Sharing Defaults
+================
+
+ If you want to set default values for 'configure' scripts to share,
+you can create a site shell script called 'config.site' that gives
+default values for variables like 'CC', 'cache_file', and 'prefix'.
+'configure' looks for 'PREFIX/share/config.site' if it exists, then
+'PREFIX/etc/config.site' if it exists. Or, you can set the
+'CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all 'configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+ Variables not defined in a site shell script can be set in the
+environment passed to 'configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the 'configure' command line, using 'VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified 'gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an
+Autoconf limitation. Until the limitation is lifted, you can use this
+workaround:
+
+ CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+'configure' Invocation
+======================
+
+ 'configure' recognizes the following options to control how it
+operates.
+
+'--help'
+'-h'
+ Print a summary of all of the options to 'configure', and exit.
+
+'--help=short'
+'--help=recursive'
+ Print a summary of the options unique to this package's
+ 'configure', and exit. The 'short' variant lists options used only
+ in the top level, while the 'recursive' variant lists options also
+ present in any nested packages.
+
+'--version'
+'-V'
+ Print the version of Autoconf used to generate the 'configure'
+ script, and exit.
+
+'--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally 'config.cache'. FILE defaults to '/dev/null' to
+ disable caching.
+
+'--config-cache'
+'-C'
+ Alias for '--cache-file=config.cache'.
+
+'--quiet'
+'--silent'
+'-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to '/dev/null' (any error
+ messages will still be shown).
+
+'--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ 'configure' can determine that directory automatically.
+
+'--prefix=DIR'
+ Use DIR as the installation prefix. *note Installation Names:: for
+ more details, including other options available for fine-tuning the
+ installation locations.
+
+'--no-create'
+'-n'
+ Run the configure checks, but stop before creating any output
+ files.
+
+'configure' also accepts some other, not widely useful, options. Run
+'configure --help' for more details.
diff --git a/contrib/libs/pcre2/NEWS b/contrib/libs/pcre2/NEWS
new file mode 100644
index 00000000000..97da19e0715
--- /dev/null
+++ b/contrib/libs/pcre2/NEWS
@@ -0,0 +1,436 @@
+News about PCRE2 releases
+-------------------------
+
+
+Version 10.42 11-December-2022
+------------------------------
+
+This is an unexpectedly early release to fix a problem that was introduced in
+10.41. ChangeLog number 19 (GitHub #139) added the default definition of
+PCRE2_CALL_CONVENTION to pcre2posix.c instead of pcre2posix.h, which meant that
+programs including pcre2posix.h but not pcre2.h couldn't compile. A new test
+that checks this case has been added.
+
+A couple of other minor issues are also fixed, and a patch for an intermittent
+JIT fault is also included. See ChangeLog and the Git log.
+
+
+Version 10.41 06-December-2022
+------------------------------
+
+This is another mainly bug-fixing and code-tidying release. There is one
+significant upgrade to pcre2grep: it now behaves like GNU grep when matching
+more than one pattern and a later pattern matches at an earlier point in the
+subject when the matched substrings are being identified by colour or by
+offsets.
+
+
+Version 10.40 15-April-2022
+---------------------------
+
+This is mostly a bug-fixing and code-tidying release. However, there are some
+extensions to Unicode property handling:
+
+* Added support for Bidi_Class and a number of binary Unicode properties,
+including Bidi_Control.
+
+* A number of changes to script matching for \p and \P:
+
+ (a) Script extensions for a character are now coded as a bitmap instead of
+ a list of script numbers, which should be faster and does not need a
+ loop.
+
+ (b) Added the syntax \p{script:xxx} and \p{script_extensions:xxx} (synonyms
+ sc and scx).
+
+ (c) Changed \p{scriptname} from being the same as \p{sc:scriptname} to being
+ the same as \p{scx:scriptname} because this change happened in Perl at
+ release 5.26.
+
+ (d) The standard Unicode 4-letter abbreviations for script names are now
+ recognized.
+
+ (e) In accordance with Unicode and Perl's "loose matching" rules, spaces,
+ hyphens, and underscores are ignored in property names, which are then
+ matched independent of case.
+
+As always, see ChangeLog for a list of all changes (also the Git log).
+
+
+Version 10.39 29-October-2021
+-----------------------------
+
+This release is happening soon after 10.38 because the bug fix is important.
+
+1. Fix incorrect detection of alternatives in first character search in JIT.
+
+2. Update to Unicode 14.0.0.
+
+3. Some code cleanups (see ChangeLog).
+
+
+Version 10.38 01-October-2021
+-----------------------------
+
+As well as some bug fixes and tidies (as always, see ChangeLog for details),
+the documentation is updated to list the new URLs, following the move of the
+source repository to GitHub and the mailing list to Google Groups.
+
+* The CMake build system can now build both static and shared libraries in one
+go.
+
+* Following Perl's lead, \K is now locked out in lookaround assertions by
+default, but an option is provided to re-enable the previous behaviour.
+
+
+Version 10.37 26-May-2021
+-------------------------
+
+A few more bug fixes and tidies. The only change of real note is the removal of
+the actual POSIX names regcomp etc. from the POSIX wrapper library because
+these have caused issues for some applications (see 10.33 #2 below).
+
+
+Version 10.36 04-December-2020
+------------------------------
+
+Again, mainly bug fixes and tidies. The only enhancements are the addition of
+GNU grep's -m (aka --max-count) option to pcre2grep, and also unifying the
+handling of substitution strings for both -O and callouts in pcre2grep, with
+the addition of $x{...} and $o{...} to allow for characters whose code points
+are greater than 255 in Unicode mode.
+
+NOTE: there is an outstanding issue with JIT support for MacOS on arm64
+hardware. For details, please see Bugzilla issue #2618.
+
+
+Version 10.35 15-April-2020
+---------------------------
+
+Bugfixes, tidies, and a few new enhancements.
+
+1. Capturing groups that contain recursive backreferences to themselves are no
+longer automatically atomic, because the restriction is no longer necessary
+as a result of the 10.30 restructuring.
+
+2. Several new options for pcre2_substitute().
+
+3. When Unicode is supported and PCRE2_UCP is set without PCRE2_UTF, Unicode
+character properties are used for upper/lower case computations on characters
+whose code points are greater than 127.
+
+4. The character tables (for low-valued characters) can now more easily be
+saved and restored in binary.
+
+5. Updated to Unicode 13.0.0.
+
+
+Version 10.34 21-November-2019
+------------------------------
+
+Another release with a few enhancements as well as bugfixes and tidies. The
+main new features are:
+
+1. There is now some support for matching in invalid UTF strings.
+
+2. Non-atomic positive lookarounds are implemented in the pcre2_match()
+interpreter, but not in JIT.
+
+3. Added two new functions: pcre2_get_match_data_size() and
+pcre2_maketables_free().
+
+4. Upgraded to Unicode 12.1.0.
+
+
+Version 10.33 16-April-2019
+---------------------------
+
+Yet more bugfixes, tidies, and a few enhancements, summarized here (see
+ChangeLog for the full list):
+
+1. Callouts from pcre2_substitute() are now available.
+
+2. The POSIX functions are now all called pcre2_regcomp() etc., with wrapper
+functions that use the standard POSIX names. However, in pcre2posix.h the POSIX
+names are defined as macros. This should help avoid linking with the wrong
+library in some environments, while still exporting the POSIX names for
+pre-existing programs that use them.
+
+3. Some new options:
+
+ (a) PCRE2_EXTRA_ESCAPED_CR_IS_LF makes \r behave as \n.
+
+ (b) PCRE2_EXTRA_ALT_BSUX enables support for ECMAScript 6's \u{hh...}
+ construct.
+
+ (c) PCRE2_COPY_MATCHED_SUBJECT causes a copy of a matched subject to be
+ made, instead of just remembering a pointer.
+
+4. Some new Perl features:
+
+ (a) Perl 5.28's experimental alphabetic names for atomic groups and
+ lookaround assertions, for example, (*pla:...) and (*atomic:...).
+
+ (b) The new Perl "script run" features (*script_run:...) and
+ (*atomic_script_run:...) aka (*sr:...) and (*asr:...).
+
+ (c) When PCRE2_UTF is set, allow non-ASCII letters and decimal digits in
+ capture group names.
+
+5. --disable-percent-zt disables the use of %zu and %td in formatting strings
+in pcre2test. They were already automatically disabled for VC and older C
+compilers.
+
+6. Some changes related to callouts in pcre2grep:
+
+ (a) Support for running an external program under VMS has been added, in
+ addition to Windows and fork() support.
+
+ (b) --disable-pcre2grep-callout-fork restricts the callout support in
+ to the inbuilt echo facility.
+
+
+Version 10.32 10-September-2018
+-------------------------------
+
+This is another mainly bugfix and tidying release with a few minor
+enhancements. These are the main ones:
+
+1. pcre2grep now supports the inclusion of binary zeros in patterns that are
+read from files via the -f option.
+
+2. ./configure now supports --enable-jit=auto, which automatically enables JIT
+if the hardware supports it.
+
+3. In pcre2_dfa_match(), internal recursive calls no longer use the stack for
+local workspace and local ovectors. Instead, an initial block of stack is
+reserved, but if this is insufficient, heap memory is used. The heap limit
+parameter now applies to pcre2_dfa_match().
+
+4. Updated to Unicode version 11.0.0.
+
+5. (*ACCEPT:ARG), (*FAIL:ARG), and (*COMMIT:ARG) are now supported.
+
+6. Added support for \N{U+dddd}, but only in Unicode mode.
+
+7. Added support for (?^) to unset all imnsx options.
+
+
+Version 10.31 12-February-2018
+------------------------------
+
+This is mainly a bugfix and tidying release (see ChangeLog for full details).
+However, there are some minor enhancements.
+
+1. New pcre2_config() options: PCRE2_CONFIG_NEVER_BACKSLASH_C and
+PCRE2_CONFIG_COMPILED_WIDTHS.
+
+2. New pcre2_pattern_info() option PCRE2_INFO_EXTRAOPTIONS to retrieve the
+extra compile time options.
+
+3. There are now public names for all the pcre2_compile() error numbers.
+
+4. Added PCRE2_CALLOUT_STARTMATCH and PCRE2_CALLOUT_BACKTRACK bits to a new
+field callout_flags in callout blocks.
+
+
+Version 10.30 14-August-2017
+----------------------------
+
+The full list of changes that includes bugfixes and tidies is, as always, in
+ChangeLog. These are the most important new features:
+
+1. The main interpreter, pcre2_match(), has been refactored into a new version
+that does not use recursive function calls (and therefore the system stack) for
+remembering backtracking positions. This makes --disable-stack-for-recursion a
+NOOP. The new implementation allows backtracking into recursive group calls in
+patterns, making it more compatible with Perl, and also fixes some other
+previously hard-to-do issues. For patterns that have a lot of backtracking, the
+heap is now used, and there is an explicit limit on the amount, settable by
+pcre2_set_heap_limit() or (*LIMIT_HEAP=xxx). The "recursion limit" is retained,
+but is renamed as "depth limit" (though the old names remain for
+compatibility).
+
+There is also a change in the way callouts from pcre2_match() are handled. The
+offset_vector field in the callout block is no longer a pointer to the
+actual ovector that was passed to the matching function in the match data
+block. Instead it points to an internal ovector of a size large enough to hold
+all possible captured substrings in the pattern.
+
+2. The new option PCRE2_ENDANCHORED insists that a pattern match must end at
+the end of the subject.
+
+3. The new option PCRE2_EXTENDED_MORE implements Perl's /xx feature, and
+pcre2test is upgraded to support it. Setting within the pattern by (?xx) is
+also supported.
+
+4. (?n) can be used to set PCRE2_NO_AUTO_CAPTURE, because Perl now has this.
+
+5. Additional compile options in the compile context are now available, and the
+first two are: PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES and
+PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL.
+
+6. The newline type PCRE2_NEWLINE_NUL is now available.
+
+7. The match limit value now also applies to pcre2_dfa_match() as there are
+patterns that can use up a lot of resources without necessarily recursing very
+deeply.
+
+8. The option REG_PEND (a GNU extension) is now available for the POSIX
+wrapper. Also there is a new option PCRE2_LITERAL which is used to support
+REG_NOSPEC.
+
+9. PCRE2_EXTRA_MATCH_LINE and PCRE2_EXTRA_MATCH_WORD are implemented for the
+benefit of pcre2grep, and pcre2grep's -F, -w, and -x options are re-implemented
+using PCRE2_LITERAL, PCRE2_EXTRA_MATCH_WORD, and PCRE2_EXTRA_MATCH_LINE. This
+is tidier and also fixes some bugs.
+
+10. The Unicode tables are upgraded from Unicode 8.0.0 to Unicode 10.0.0.
+
+11. There are some experimental functions for converting foreign patterns
+(globs and POSIX patterns) into PCRE2 patterns.
+
+
+Version 10.23 14-February-2017
+------------------------------
+
+1. ChangeLog has the details of a lot of bug fixes and tidies.
+
+2. There has been a major re-factoring of the pcre2_compile.c file. Most syntax
+checking is now done in the pre-pass that identifies capturing groups. This has
+reduced the amount of duplication and made the code tidier. While doing this,
+some minor bugs and Perl incompatibilities were fixed (see ChangeLog for
+details.)
+
+3. Back references are now permitted in lookbehind assertions when there are
+no duplicated group numbers (that is, (?| has not been used), and, if the
+reference is by name, there is only one group of that name. The referenced
+group must, of course be of fixed length.
+
+4. \g{+<number>} (e.g. \g{+2} ) is now supported. It is a "forward back
+reference" and can be useful in repetitions (compare \g{-<number>} ). Perl does
+not recognize this syntax.
+
+5. pcre2grep now automatically expands its buffer up to a maximum set by
+--max-buffer-size.
+
+6. The -t option (grand total) has been added to pcre2grep.
+
+7. A new function called pcre2_code_copy_with_tables() exists to copy a
+compiled pattern along with a private copy of the character tables that is
+uses.
+
+8. A user supplied a number of patches to upgrade pcre2grep under Windows and
+tidy the code.
+
+9. Several updates have been made to pcre2test and test scripts (see
+ChangeLog).
+
+
+Version 10.22 29-July-2016
+--------------------------
+
+1. ChangeLog has the details of a number of bug fixes.
+
+2. The POSIX wrapper function regcomp() did not used to support back references
+and subroutine calls if called with the REG_NOSUB option. It now does.
+
+3. A new function, pcre2_code_copy(), is added, to make a copy of a compiled
+pattern.
+
+4. Support for string callouts is added to pcre2grep.
+
+5. Added the PCRE2_NO_JIT option to pcre2_match().
+
+6. The pcre2_get_error_message() function now returns with a negative error
+code if the error number it is given is unknown.
+
+7. Several updates have been made to pcre2test and test scripts (see
+ChangeLog).
+
+
+Version 10.21 12-January-2016
+-----------------------------
+
+1. Many bugs have been fixed. A large number of them were provoked only by very
+strange pattern input, and were discovered by fuzzers. Some others were
+discovered by code auditing. See ChangeLog for details.
+
+2. The Unicode tables have been updated to Unicode version 8.0.0.
+
+3. For Perl compatibility in EBCDIC environments, ranges such as a-z in a
+class, where both values are literal letters in the same case, omit the
+non-letter EBCDIC code points within the range.
+
+4. There have been a number of enhancements to the pcre2_substitute() function,
+giving more flexibility to replacement facilities. It is now also possible to
+cause the function to return the needed buffer size if the one given is too
+small.
+
+5. The PCRE2_ALT_VERBNAMES option causes the "name" parts of special verbs such
+as (*THEN:name) to be processed for backslashes and to take note of
+PCRE2_EXTENDED.
+
+6. PCRE2_INFO_HASBACKSLASHC makes it possible for a client to find out if a
+pattern uses \C, and --never-backslash-C makes it possible to compile a version
+PCRE2 in which the use of \C is always forbidden.
+
+7. A limit to the length of pattern that can be handled can now be set by
+calling pcre2_set_max_pattern_length().
+
+8. When matching an unanchored pattern, a match can be required to begin within
+a given number of code units after the start of the subject by calling
+pcre2_set_offset_limit().
+
+9. The pcre2test program has been extended to test new facilities, and it can
+now run the tests when LF on its own is not a valid newline sequence.
+
+10. The RunTest script has also been updated to enable more tests to be run.
+
+11. There have been some minor performance enhancements.
+
+
+Version 10.20 30-June-2015
+--------------------------
+
+1. Callouts with string arguments and the pcre2_callout_enumerate() function
+have been implemented.
+
+2. The PCRE2_NEVER_BACKSLASH_C option, which locks out the use of \C, is added.
+
+3. The PCRE2_ALT_CIRCUMFLEX option lets ^ match after a newline at the end of a
+subject in multiline mode.
+
+4. The way named subpatterns are handled has been refactored. The previous
+approach had several bugs.
+
+5. The handling of \c in EBCDIC environments has been changed to conform to the
+perlebcdic document. This is an incompatible change.
+
+6. Bugs have been mended, many of them discovered by fuzzers.
+
+
+Version 10.10 06-March-2015
+---------------------------
+
+1. Serialization and de-serialization functions have been added to the API,
+making it possible to save and restore sets of compiled patterns, though
+restoration must be done in the same environment that was used for compilation.
+
+2. The (*NO_JIT) feature has been added; this makes it possible for a pattern
+creator to specify that JIT is not to be used.
+
+3. A number of bugs have been fixed. In particular, bugs that caused building
+on Windows using CMake to fail have been mended.
+
+
+Version 10.00 05-January-2015
+-----------------------------
+
+Version 10.00 is the first release of PCRE2, a revised API for the PCRE
+library. Changes prior to 10.00 are logged in the ChangeLog file for the old
+API, up to item 20 for release 8.36. New programs are recommended to use the
+new library. Programs that use the original (PCRE1) API will need changing
+before linking with the new library.
+
+****
diff --git a/contrib/libs/pcre2/README b/contrib/libs/pcre2/README
new file mode 100644
index 00000000000..c88acff8ba4
--- /dev/null
+++ b/contrib/libs/pcre2/README
@@ -0,0 +1,924 @@
+README file for PCRE2 (Perl-compatible regular expression library)
+------------------------------------------------------------------
+
+PCRE2 is a re-working of the original PCRE1 library to provide an entirely new
+API. Since its initial release in 2015, there has been further development of
+the code and it now differs from PCRE1 in more than just the API. There are new
+features, and the internals have been improved. The original PCRE1 library is
+now obsolete and no longer maintained. The latest release of PCRE2 is available
+in .tar.gz, tar.bz2, or .zip form from this GitHub repository:
+
+https://github.com/PCRE2Project/pcre2/releases
+
+There is a mailing list for discussion about the development of PCRE2 at
+pcre2-dev@googlegroups.com. You can subscribe by sending an email to
+pcre2-dev+subscribe@googlegroups.com.
+
+You can access the archives and also subscribe or manage your subscription
+here:
+
+https://groups.google.com/g/pcre2-dev
+
+Please read the NEWS file if you are upgrading from a previous release. The
+contents of this README file are:
+
+ The PCRE2 APIs
+ Documentation for PCRE2
+ Contributions by users of PCRE2
+ Building PCRE2 on non-Unix-like systems
+ Building PCRE2 without using autotools
+ Building PCRE2 using autotools
+ Retrieving configuration information
+ Shared libraries
+ Cross-compiling using autotools
+ Making new tarballs
+ Testing PCRE2
+ Character tables
+ File manifest
+
+
+The PCRE2 APIs
+--------------
+
+PCRE2 is written in C, and it has its own API. There are three sets of
+functions, one for the 8-bit library, which processes strings of bytes, one for
+the 16-bit library, which processes strings of 16-bit values, and one for the
+32-bit library, which processes strings of 32-bit values. Unlike PCRE1, there
+are no C++ wrappers.
+
+The distribution does contain a set of C wrapper functions for the 8-bit
+library that are based on the POSIX regular expression API (see the pcre2posix
+man page). These are built into a library called libpcre2-posix. Note that this
+just provides a POSIX calling interface to PCRE2; the regular expressions
+themselves still follow Perl syntax and semantics. The POSIX API is restricted,
+and does not give full access to all of PCRE2's facilities.
+
+The header file for the POSIX-style functions is called pcre2posix.h. The
+official POSIX name is regex.h, but I did not want to risk possible problems
+with existing files of that name by distributing it that way. To use PCRE2 with
+an existing program that uses the POSIX API, pcre2posix.h will have to be
+renamed or pointed at by a link (or the program modified, of course). See the
+pcre2posix documentation for more details.
+
+
+Documentation for PCRE2
+-----------------------
+
+If you install PCRE2 in the normal way on a Unix-like system, you will end up
+with a set of man pages whose names all start with "pcre2". The one that is
+just called "pcre2" lists all the others. In addition to these man pages, the
+PCRE2 documentation is supplied in two other forms:
+
+ 1. There are files called doc/pcre2.txt, doc/pcre2grep.txt, and
+ doc/pcre2test.txt in the source distribution. The first of these is a
+ concatenation of the text forms of all the section 3 man pages except the
+ listing of pcre2demo.c and those that summarize individual functions. The
+ other two are the text forms of the section 1 man pages for the pcre2grep
+ and pcre2test commands. These text forms are provided for ease of scanning
+ with text editors or similar tools. They are installed in
+ <prefix>/share/doc/pcre2, where <prefix> is the installation prefix
+ (defaulting to /usr/local).
+
+ 2. A set of files containing all the documentation in HTML form, hyperlinked
+ in various ways, and rooted in a file called index.html, is distributed in
+ doc/html and installed in <prefix>/share/doc/pcre2/html.
+
+
+Building PCRE2 on non-Unix-like systems
+---------------------------------------
+
+For a non-Unix-like system, please read the file NON-AUTOTOOLS-BUILD, though if
+your system supports the use of "configure" and "make" you may be able to build
+PCRE2 using autotools in the same way as for many Unix-like systems.
+
+PCRE2 can also be configured using CMake, which can be run in various ways
+(command line, GUI, etc). This creates Makefiles, solution files, etc. The file
+NON-AUTOTOOLS-BUILD has information about CMake.
+
+PCRE2 has been compiled on many different operating systems. It should be
+straightforward to build PCRE2 on any system that has a Standard C compiler and
+library, because it uses only Standard C functions.
+
+
+Building PCRE2 without using autotools
+--------------------------------------
+
+The use of autotools (in particular, libtool) is problematic in some
+environments, even some that are Unix or Unix-like. See the NON-AUTOTOOLS-BUILD
+file for ways of building PCRE2 without using autotools.
+
+
+Building PCRE2 using autotools
+------------------------------
+
+The following instructions assume the use of the widely used "configure; make;
+make install" (autotools) process.
+
+If you have downloaded and unpacked a PCRE2 release tarball, run the
+"configure" command from the PCRE2 directory, with your current directory set
+to the directory where you want the files to be created. This command is a
+standard GNU "autoconf" configuration script, for which generic instructions
+are supplied in the file INSTALL.
+
+The files in the GitHub repository do not contain "configure". If you have
+downloaded the PCRE2 source files from GitHub, before you can run "configure"
+you must run the shell script called autogen.sh. This runs a number of
+autotools to create a "configure" script (you must of course have the autotools
+commands installed in order to do this).
+
+Most commonly, people build PCRE2 within its own distribution directory, and in
+this case, on many systems, just running "./configure" is sufficient. However,
+the usual methods of changing standard defaults are available. For example:
+
+CFLAGS='-O2 -Wall' ./configure --prefix=/opt/local
+
+This command specifies that the C compiler should be run with the flags '-O2
+-Wall' instead of the default, and that "make install" should install PCRE2
+under /opt/local instead of the default /usr/local.
+
+If you want to build in a different directory, just run "configure" with that
+directory as current. For example, suppose you have unpacked the PCRE2 source
+into /source/pcre2/pcre2-xxx, but you want to build it in
+/build/pcre2/pcre2-xxx:
+
+cd /build/pcre2/pcre2-xxx
+/source/pcre2/pcre2-xxx/configure
+
+PCRE2 is written in C and is normally compiled as a C library. However, it is
+possible to build it as a C++ library, though the provided building apparatus
+does not have any features to support this.
+
+There are some optional features that can be included or omitted from the PCRE2
+library. They are also documented in the pcre2build man page.
+
+. By default, both shared and static libraries are built. You can change this
+ by adding one of these options to the "configure" command:
+
+ --disable-shared
+ --disable-static
+
+ (See also "Shared libraries on Unix-like systems" below.)
+
+. By default, only the 8-bit library is built. If you add --enable-pcre2-16 to
+ the "configure" command, the 16-bit library is also built. If you add
+ --enable-pcre2-32 to the "configure" command, the 32-bit library is also
+ built. If you want only the 16-bit or 32-bit library, use --disable-pcre2-8
+ to disable building the 8-bit library.
+
+. If you want to include support for just-in-time (JIT) compiling, which can
+ give large performance improvements on certain platforms, add --enable-jit to
+ the "configure" command. This support is available only for certain hardware
+ architectures. If you try to enable it on an unsupported architecture, there
+ will be a compile time error. If in doubt, use --enable-jit=auto, which
+ enables JIT only if the current hardware is supported.
+
+. If you are enabling JIT under SELinux environment you may also want to add
+ --enable-jit-sealloc, which enables the use of an executable memory allocator
+ that is compatible with SELinux. Warning: this allocator is experimental!
+ It does not support fork() operation and may crash when no disk space is
+ available. This option has no effect if JIT is disabled.
+
+. If you do not want to make use of the default support for UTF-8 Unicode
+ character strings in the 8-bit library, UTF-16 Unicode character strings in
+ the 16-bit library, or UTF-32 Unicode character strings in the 32-bit
+ library, you can add --disable-unicode to the "configure" command. This
+ reduces the size of the libraries. It is not possible to configure one
+ library with Unicode support, and another without, in the same configuration.
+ It is also not possible to use --enable-ebcdic (see below) with Unicode
+ support, so if this option is set, you must also use --disable-unicode.
+
+ When Unicode support is available, the use of a UTF encoding still has to be
+ enabled by setting the PCRE2_UTF option at run time or starting a pattern
+ with (*UTF). When PCRE2 is compiled with Unicode support, its input can only
+ either be ASCII or UTF-8/16/32, even when running on EBCDIC platforms.
+
+ As well as supporting UTF strings, Unicode support includes support for the
+ \P, \p, and \X sequences that recognize Unicode character properties.
+ However, only a subset of Unicode properties are supported; see the
+ pcre2pattern man page for details. Escape sequences such as \d and \w in
+ patterns do not by default make use of Unicode properties, but can be made to
+ do so by setting the PCRE2_UCP option or starting a pattern with (*UCP).
+
+. You can build PCRE2 to recognize either CR or LF or the sequence CRLF, or any
+ of the preceding, or any of the Unicode newline sequences, or the NUL (zero)
+ character as indicating the end of a line. Whatever you specify at build time
+ is the default; the caller of PCRE2 can change the selection at run time. The
+ default newline indicator is a single LF character (the Unix standard). You
+ can specify the default newline indicator by adding --enable-newline-is-cr,
+ --enable-newline-is-lf, --enable-newline-is-crlf,
+ --enable-newline-is-anycrlf, --enable-newline-is-any, or
+ --enable-newline-is-nul to the "configure" command, respectively.
+
+. By default, the sequence \R in a pattern matches any Unicode line ending
+ sequence. This is independent of the option specifying what PCRE2 considers
+ to be the end of a line (see above). However, the caller of PCRE2 can
+ restrict \R to match only CR, LF, or CRLF. You can make this the default by
+ adding --enable-bsr-anycrlf to the "configure" command (bsr = "backslash R").
+
+. In a pattern, the escape sequence \C matches a single code unit, even in a
+ UTF mode. This can be dangerous because it breaks up multi-code-unit
+ characters. You can build PCRE2 with the use of \C permanently locked out by
+ adding --enable-never-backslash-C (note the upper case C) to the "configure"
+ command. When \C is allowed by the library, individual applications can lock
+ it out by calling pcre2_compile() with the PCRE2_NEVER_BACKSLASH_C option.
+
+. PCRE2 has a counter that limits the depth of nesting of parentheses in a
+ pattern. This limits the amount of system stack that a pattern uses when it
+ is compiled. The default is 250, but you can change it by setting, for
+ example,
+
+ --with-parens-nest-limit=500
+
+. PCRE2 has a counter that can be set to limit the amount of computing resource
+ it uses when matching a pattern. If the limit is exceeded during a match, the
+ match fails. The default is ten million. You can change the default by
+ setting, for example,
+
+ --with-match-limit=500000
+
+ on the "configure" command. This is just the default; individual calls to
+ pcre2_match() or pcre2_dfa_match() can supply their own value. There is more
+ discussion in the pcre2api man page (search for pcre2_set_match_limit).
+
+. There is a separate counter that limits the depth of nested backtracking
+ (pcre2_match()) or nested function calls (pcre2_dfa_match()) during a
+ matching process, which indirectly limits the amount of heap memory that is
+ used, and in the case of pcre2_dfa_match() the amount of stack as well. This
+ counter also has a default of ten million, which is essentially "unlimited".
+ You can change the default by setting, for example,
+
+ --with-match-limit-depth=5000
+
+ There is more discussion in the pcre2api man page (search for
+ pcre2_set_depth_limit).
+
+. You can also set an explicit limit on the amount of heap memory used by
+ the pcre2_match() and pcre2_dfa_match() interpreters:
+
+ --with-heap-limit=500
+
+ The units are kibibytes (units of 1024 bytes). This limit does not apply when
+ the JIT optimization (which has its own memory control features) is used.
+ There is more discussion on the pcre2api man page (search for
+ pcre2_set_heap_limit).
+
+. In the 8-bit library, the default maximum compiled pattern size is around
+ 64 kibibytes. You can increase this by adding --with-link-size=3 to the
+ "configure" command. PCRE2 then uses three bytes instead of two for offsets
+ to different parts of the compiled pattern. In the 16-bit library,
+ --with-link-size=3 is the same as --with-link-size=4, which (in both
+ libraries) uses four-byte offsets. Increasing the internal link size reduces
+ performance in the 8-bit and 16-bit libraries. In the 32-bit library, the
+ link size setting is ignored, as 4-byte offsets are always used.
+
+. For speed, PCRE2 uses four tables for manipulating and identifying characters
+ whose code point values are less than 256. By default, it uses a set of
+ tables for ASCII encoding that is part of the distribution. If you specify
+
+ --enable-rebuild-chartables
+
+ a program called pcre2_dftables is compiled and run in the default C locale
+ when you obey "make". It builds a source file called pcre2_chartables.c. If
+ you do not specify this option, pcre2_chartables.c is created as a copy of
+ pcre2_chartables.c.dist. See "Character tables" below for further
+ information.
+
+. It is possible to compile PCRE2 for use on systems that use EBCDIC as their
+ character code (as opposed to ASCII/Unicode) by specifying
+
+ --enable-ebcdic --disable-unicode
+
+ This automatically implies --enable-rebuild-chartables (see above). However,
+ when PCRE2 is built this way, it always operates in EBCDIC. It cannot support
+ both EBCDIC and UTF-8/16/32. There is a second option, --enable-ebcdic-nl25,
+ which specifies that the code value for the EBCDIC NL character is 0x25
+ instead of the default 0x15.
+
+. If you specify --enable-debug, additional debugging code is included in the
+ build. This option is intended for use by the PCRE2 maintainers.
+
+. In environments where valgrind is installed, if you specify
+
+ --enable-valgrind
+
+ PCRE2 will use valgrind annotations to mark certain memory regions as
+ unaddressable. This allows it to detect invalid memory accesses, and is
+ mostly useful for debugging PCRE2 itself.
+
+. In environments where the gcc compiler is used and lcov is installed, if you
+ specify
+
+ --enable-coverage
+
+ the build process implements a code coverage report for the test suite. The
+ report is generated by running "make coverage". If ccache is installed on
+ your system, it must be disabled when building PCRE2 for coverage reporting.
+ You can do this by setting the environment variable CCACHE_DISABLE=1 before
+ running "make" to build PCRE2. There is more information about coverage
+ reporting in the "pcre2build" documentation.
+
+. When JIT support is enabled, pcre2grep automatically makes use of it, unless
+ you add --disable-pcre2grep-jit to the "configure" command.
+
+. There is support for calling external programs during matching in the
+ pcre2grep command, using PCRE2's callout facility with string arguments. This
+ support can be disabled by adding --disable-pcre2grep-callout to the
+ "configure" command. There are two kinds of callout: one that generates
+ output from inbuilt code, and another that calls an external program. The
+ latter has special support for Windows and VMS; otherwise it assumes the
+ existence of the fork() function. This facility can be disabled by adding
+ --disable-pcre2grep-callout-fork to the "configure" command.
+
+. The pcre2grep program currently supports only 8-bit data files, and so
+ requires the 8-bit PCRE2 library. It is possible to compile pcre2grep to use
+ libz and/or libbz2, in order to read .gz and .bz2 files (respectively), by
+ specifying one or both of
+
+ --enable-pcre2grep-libz
+ --enable-pcre2grep-libbz2
+
+ Of course, the relevant libraries must be installed on your system.
+
+. The default starting size (in bytes) of the internal buffer used by pcre2grep
+ can be set by, for example:
+
+ --with-pcre2grep-bufsize=51200
+
+ The value must be a plain integer. The default is 20480. The amount of memory
+ used by pcre2grep is actually three times this number, to allow for "before"
+ and "after" lines. If very long lines are encountered, the buffer is
+ automatically enlarged, up to a fixed maximum size.
+
+. The default maximum size of pcre2grep's internal buffer can be set by, for
+ example:
+
+ --with-pcre2grep-max-bufsize=2097152
+
+ The default is either 1048576 or the value of --with-pcre2grep-bufsize,
+ whichever is the larger.
+
+. It is possible to compile pcre2test so that it links with the libreadline
+ or libedit libraries, by specifying, respectively,
+
+ --enable-pcre2test-libreadline or --enable-pcre2test-libedit
+
+ If this is done, when pcre2test's input is from a terminal, it reads it using
+ the readline() function. This provides line-editing and history facilities.
+ Note that libreadline is GPL-licenced, so if you distribute a binary of
+ pcre2test linked in this way, there may be licensing issues. These can be
+ avoided by linking with libedit (which has a BSD licence) instead.
+
+ Enabling libreadline causes the -lreadline option to be added to the
+ pcre2test build. In many operating environments with a sytem-installed
+ readline library this is sufficient. However, in some environments (e.g. if
+ an unmodified distribution version of readline is in use), it may be
+ necessary to specify something like LIBS="-lncurses" as well. This is
+ because, to quote the readline INSTALL, "Readline uses the termcap functions,
+ but does not link with the termcap or curses library itself, allowing
+ applications which link with readline the option to choose an appropriate
+ library." If you get error messages about missing functions tgetstr, tgetent,
+ tputs, tgetflag, or tgoto, this is the problem, and linking with the ncurses
+ library should fix it.
+
+. The C99 standard defines formatting modifiers z and t for size_t and
+ ptrdiff_t values, respectively. By default, PCRE2 uses these modifiers in
+ environments other than Microsoft Visual Studio versions earlier than 2013
+ when __STDC_VERSION__ is defined and has a value greater than or equal to
+ 199901L (indicating C99). However, there is at least one environment that
+ claims to be C99 but does not support these modifiers. If
+ --disable-percent-zt is specified, no use is made of the z or t modifiers.
+ Instead of %td or %zu, %lu is used, with a cast for size_t values.
+
+. There is a special option called --enable-fuzz-support for use by people who
+ want to run fuzzing tests on PCRE2. At present this applies only to the 8-bit
+ library. If set, it causes an extra library called libpcre2-fuzzsupport.a to
+ be built, but not installed. This contains a single function called
+ LLVMFuzzerTestOneInput() whose arguments are a pointer to a string and the
+ length of the string. When called, this function tries to compile the string
+ as a pattern, and if that succeeds, to match it. This is done both with no
+ options and with some random options bits that are generated from the string.
+ Setting --enable-fuzz-support also causes a binary called pcre2fuzzcheck to
+ be created. This is normally run under valgrind or used when PCRE2 is
+ compiled with address sanitizing enabled. It calls the fuzzing function and
+ outputs information about what it is doing. The input strings are specified
+ by arguments: if an argument starts with "=" the rest of it is a literal
+ input string. Otherwise, it is assumed to be a file name, and the contents
+ of the file are the test string.
+
+. Releases before 10.30 could be compiled with --disable-stack-for-recursion,
+ which caused pcre2_match() to use individual blocks on the heap for
+ backtracking instead of recursive function calls (which use the stack). This
+ is now obsolete because pcre2_match() was refactored always to use the heap
+ (in a much more efficient way than before). This option is retained for
+ backwards compatibility, but has no effect other than to output a warning.
+
+The "configure" script builds the following files for the basic C library:
+
+. Makefile the makefile that builds the library
+. src/config.h build-time configuration options for the library
+. src/pcre2.h the public PCRE2 header file
+. pcre2-config script that shows the building settings such as CFLAGS
+ that were set for "configure"
+. libpcre2-8.pc )
+. libpcre2-16.pc ) data for the pkg-config command
+. libpcre2-32.pc )
+. libpcre2-posix.pc )
+. libtool script that builds shared and/or static libraries
+
+Versions of config.h and pcre2.h are distributed in the src directory of PCRE2
+tarballs under the names config.h.generic and pcre2.h.generic. These are
+provided for those who have to build PCRE2 without using "configure" or CMake.
+If you use "configure" or CMake, the .generic versions are not used.
+
+The "configure" script also creates config.status, which is an executable
+script that can be run to recreate the configuration, and config.log, which
+contains compiler output from tests that "configure" runs.
+
+Once "configure" has run, you can run "make". This builds whichever of the
+libraries libpcre2-8, libpcre2-16 and libpcre2-32 are configured, and a test
+program called pcre2test. If you enabled JIT support with --enable-jit, another
+test program called pcre2_jit_test is built as well. If the 8-bit library is
+built, libpcre2-posix, pcre2posix_test, and the pcre2grep command are also
+built. Running "make" with the -j option may speed up compilation on
+multiprocessor systems.
+
+The command "make check" runs all the appropriate tests. Details of the PCRE2
+tests are given below in a separate section of this document. The -j option of
+"make" can also be used when running the tests.
+
+You can use "make install" to install PCRE2 into live directories on your
+system. The following are installed (file names are all relative to the
+<prefix> that is set when "configure" is run):
+
+ Commands (bin):
+ pcre2test
+ pcre2grep (if 8-bit support is enabled)
+ pcre2-config
+
+ Libraries (lib):
+ libpcre2-8 (if 8-bit support is enabled)
+ libpcre2-16 (if 16-bit support is enabled)
+ libpcre2-32 (if 32-bit support is enabled)
+ libpcre2-posix (if 8-bit support is enabled)
+
+ Configuration information (lib/pkgconfig):
+ libpcre2-8.pc
+ libpcre2-16.pc
+ libpcre2-32.pc
+ libpcre2-posix.pc
+
+ Header files (include):
+ pcre2.h
+ pcre2posix.h
+
+ Man pages (share/man/man{1,3}):
+ pcre2grep.1
+ pcre2test.1
+ pcre2-config.1
+ pcre2.3
+ pcre2*.3 (lots more pages, all starting "pcre2")
+
+ HTML documentation (share/doc/pcre2/html):
+ index.html
+ *.html (lots more pages, hyperlinked from index.html)
+
+ Text file documentation (share/doc/pcre2):
+ AUTHORS
+ COPYING
+ ChangeLog
+ LICENCE
+ NEWS
+ README
+ pcre2.txt (a concatenation of the man(3) pages)
+ pcre2test.txt the pcre2test man page
+ pcre2grep.txt the pcre2grep man page
+ pcre2-config.txt the pcre2-config man page
+
+If you want to remove PCRE2 from your system, you can run "make uninstall".
+This removes all the files that "make install" installed. However, it does not
+remove any directories, because these are often shared with other programs.
+
+
+Retrieving configuration information
+------------------------------------
+
+Running "make install" installs the command pcre2-config, which can be used to
+recall information about the PCRE2 configuration and installation. For example:
+
+ pcre2-config --version
+
+prints the version number, and
+
+ pcre2-config --libs8
+
+outputs information about where the 8-bit library is installed. This command
+can be included in makefiles for programs that use PCRE2, saving the programmer
+from having to remember too many details. Run pcre2-config with no arguments to
+obtain a list of possible arguments.
+
+The pkg-config command is another system for saving and retrieving information
+about installed libraries. Instead of separate commands for each library, a
+single command is used. For example:
+
+ pkg-config --libs libpcre2-16
+
+The data is held in *.pc files that are installed in a directory called
+<prefix>/lib/pkgconfig.
+
+
+Shared libraries
+----------------
+
+The default distribution builds PCRE2 as shared libraries and static libraries,
+as long as the operating system supports shared libraries. Shared library
+support relies on the "libtool" script which is built as part of the
+"configure" process.
+
+The libtool script is used to compile and link both shared and static
+libraries. They are placed in a subdirectory called .libs when they are newly
+built. The programs pcre2test and pcre2grep are built to use these uninstalled
+libraries (by means of wrapper scripts in the case of shared libraries). When
+you use "make install" to install shared libraries, pcre2grep and pcre2test are
+automatically re-built to use the newly installed shared libraries before being
+installed themselves. However, the versions left in the build directory still
+use the uninstalled libraries.
+
+To build PCRE2 using static libraries only you must use --disable-shared when
+configuring it. For example:
+
+./configure --prefix=/usr/gnu --disable-shared
+
+Then run "make" in the usual way. Similarly, you can use --disable-static to
+build only shared libraries.
+
+
+Cross-compiling using autotools
+-------------------------------
+
+You can specify CC and CFLAGS in the normal way to the "configure" command, in
+order to cross-compile PCRE2 for some other host. However, you should NOT
+specify --enable-rebuild-chartables, because if you do, the pcre2_dftables.c
+source file is compiled and run on the local host, in order to generate the
+inbuilt character tables (the pcre2_chartables.c file). This will probably not
+work, because pcre2_dftables.c needs to be compiled with the local compiler,
+not the cross compiler.
+
+When --enable-rebuild-chartables is not specified, pcre2_chartables.c is
+created by making a copy of pcre2_chartables.c.dist, which is a default set of
+tables that assumes ASCII code. Cross-compiling with the default tables should
+not be a problem.
+
+If you need to modify the character tables when cross-compiling, you should
+move pcre2_chartables.c.dist out of the way, then compile pcre2_dftables.c by
+hand and run it on the local host to make a new version of
+pcre2_chartables.c.dist. See the pcre2build section "Creating character tables
+at build time" for more details.
+
+
+Making new tarballs
+-------------------
+
+The command "make dist" creates three PCRE2 tarballs, in tar.gz, tar.bz2, and
+zip formats. The command "make distcheck" does the same, but then does a trial
+build of the new distribution to ensure that it works.
+
+If you have modified any of the man page sources in the doc directory, you
+should first run the PrepareRelease script before making a distribution. This
+script creates the .txt and HTML forms of the documentation from the man pages.
+
+
+Testing PCRE2
+-------------
+
+To test the basic PCRE2 library on a Unix-like system, run the RunTest script.
+There is another script called RunGrepTest that tests the pcre2grep command.
+When the 8-bit library is built, a test program for the POSIX wrapper, called
+pcre2posix_test, is compiled, and when JIT support is enabled, a test program
+called pcre2_jit_test is built. The scripts and the program tests are all run
+when you obey "make check". For other environments, see the instructions in
+NON-AUTOTOOLS-BUILD.
+
+The RunTest script runs the pcre2test test program (which is documented in its
+own man page) on each of the relevant testinput files in the testdata
+directory, and compares the output with the contents of the corresponding
+testoutput files. RunTest uses a file called testtry to hold the main output
+from pcre2test. Other files whose names begin with "test" are used as working
+files in some tests.
+
+Some tests are relevant only when certain build-time options were selected. For
+example, the tests for UTF-8/16/32 features are run only when Unicode support
+is available. RunTest outputs a comment when it skips a test.
+
+Many (but not all) of the tests that are not skipped are run twice if JIT
+support is available. On the second run, JIT compilation is forced. This
+testing can be suppressed by putting "-nojit" on the RunTest command line.
+
+The entire set of tests is run once for each of the 8-bit, 16-bit and 32-bit
+libraries that are enabled. If you want to run just one set of tests, call
+RunTest with either the -8, -16 or -32 option.
+
+If valgrind is installed, you can run the tests under it by putting "-valgrind"
+on the RunTest command line. To run pcre2test on just one or more specific test
+files, give their numbers as arguments to RunTest, for example:
+
+ RunTest 2 7 11
+
+You can also specify ranges of tests such as 3-6 or 3- (meaning 3 to the
+end), or a number preceded by ~ to exclude a test. For example:
+
+ Runtest 3-15 ~10
+
+This runs tests 3 to 15, excluding test 10, and just ~13 runs all the tests
+except test 13. Whatever order the arguments are in, the tests are always run
+in numerical order.
+
+You can also call RunTest with the single argument "list" to cause it to output
+a list of tests.
+
+The test sequence starts with "test 0", which is a special test that has no
+input file, and whose output is not checked. This is because it will be
+different on different hardware and with different configurations. The test
+exists in order to exercise some of pcre2test's code that would not otherwise
+be run.
+
+Tests 1 and 2 can always be run, as they expect only plain text strings (not
+UTF) and make no use of Unicode properties. The first test file can be fed
+directly into the perltest.sh script to check that Perl gives the same results.
+The only difference you should see is in the first few lines, where the Perl
+version is given instead of the PCRE2 version. The second set of tests check
+auxiliary functions, error detection, and run-time flags that are specific to
+PCRE2. It also uses the debugging flags to check some of the internals of
+pcre2_compile().
+
+If you build PCRE2 with a locale setting that is not the standard C locale, the
+character tables may be different (see next paragraph). In some cases, this may
+cause failures in the second set of tests. For example, in a locale where the
+isprint() function yields TRUE for characters in the range 128-255, the use of
+[:isascii:] inside a character class defines a different set of characters, and
+this shows up in this test as a difference in the compiled code, which is being
+listed for checking. For example, where the comparison test output contains
+[\x00-\x7f] the test might contain [\x00-\xff], and similarly in some other
+cases. This is not a bug in PCRE2.
+
+Test 3 checks pcre2_maketables(), the facility for building a set of character
+tables for a specific locale and using them instead of the default tables. The
+script uses the "locale" command to check for the availability of the "fr_FR",
+"french", or "fr" locale, and uses the first one that it finds. If the "locale"
+command fails, or if its output doesn't include "fr_FR", "french", or "fr" in
+the list of available locales, the third test cannot be run, and a comment is
+output to say why. If running this test produces an error like this:
+
+ ** Failed to set locale "fr_FR"
+
+it means that the given locale is not available on your system, despite being
+listed by "locale". This does not mean that PCRE2 is broken. There are three
+alternative output files for the third test, because three different versions
+of the French locale have been encountered. The test passes if its output
+matches any one of them.
+
+Tests 4 and 5 check UTF and Unicode property support, test 4 being compatible
+with the perltest.sh script, and test 5 checking PCRE2-specific things.
+
+Tests 6 and 7 check the pcre2_dfa_match() alternative matching function, in
+non-UTF mode and UTF-mode with Unicode property support, respectively.
+
+Test 8 checks some internal offsets and code size features, but it is run only
+when Unicode support is enabled. The output is different in 8-bit, 16-bit, and
+32-bit modes and for different link sizes, so there are different output files
+for each mode and link size.
+
+Tests 9 and 10 are run only in 8-bit mode, and tests 11 and 12 are run only in
+16-bit and 32-bit modes. These are tests that generate different output in
+8-bit mode. Each pair are for general cases and Unicode support, respectively.
+
+Test 13 checks the handling of non-UTF characters greater than 255 by
+pcre2_dfa_match() in 16-bit and 32-bit modes.
+
+Test 14 contains some special UTF and UCP tests that give different output for
+different code unit widths.
+
+Test 15 contains a number of tests that must not be run with JIT. They check,
+among other non-JIT things, the match-limiting features of the interpretive
+matcher.
+
+Test 16 is run only when JIT support is not available. It checks that an
+attempt to use JIT has the expected behaviour.
+
+Test 17 is run only when JIT support is available. It checks JIT complete and
+partial modes, match-limiting under JIT, and other JIT-specific features.
+
+Tests 18 and 19 are run only in 8-bit mode. They check the POSIX interface to
+the 8-bit library, without and with Unicode support, respectively.
+
+Test 20 checks the serialization functions by writing a set of compiled
+patterns to a file, and then reloading and checking them.
+
+Tests 21 and 22 test \C support when the use of \C is not locked out, without
+and with UTF support, respectively. Test 23 tests \C when it is locked out.
+
+Tests 24 and 25 test the experimental pattern conversion functions, without and
+with UTF support, respectively.
+
+Test 26 checks Unicode property support using tests that are generated
+automatically from the Unicode data tables.
+
+
+Character tables
+----------------
+
+For speed, PCRE2 uses four tables for manipulating and identifying characters
+whose code point values are less than 256. By default, a set of tables that is
+built into the library is used. The pcre2_maketables() function can be called
+by an application to create a new set of tables in the current locale. This are
+passed to PCRE2 by calling pcre2_set_character_tables() to put a pointer into a
+compile context.
+
+The source file called pcre2_chartables.c contains the default set of tables.
+By default, this is created as a copy of pcre2_chartables.c.dist, which
+contains tables for ASCII coding. However, if --enable-rebuild-chartables is
+specified for ./configure, a new version of pcre2_chartables.c is built by the
+program pcre2_dftables (compiled from pcre2_dftables.c), which uses the ANSI C
+character handling functions such as isalnum(), isalpha(), isupper(),
+islower(), etc. to build the table sources. This means that the default C
+locale that is set for your system will control the contents of these default
+tables. You can change the default tables by editing pcre2_chartables.c and
+then re-building PCRE2. If you do this, you should take care to ensure that the
+file does not get automatically re-generated. The best way to do this is to
+move pcre2_chartables.c.dist out of the way and replace it with your customized
+tables.
+
+When the pcre2_dftables program is run as a result of specifying
+--enable-rebuild-chartables, it uses the default C locale that is set on your
+system. It does not pay attention to the LC_xxx environment variables. In other
+words, it uses the system's default locale rather than whatever the compiling
+user happens to have set. If you really do want to build a source set of
+character tables in a locale that is specified by the LC_xxx variables, you can
+run the pcre2_dftables program by hand with the -L option. For example:
+
+ ./pcre2_dftables -L pcre2_chartables.c.special
+
+The second argument names the file where the source code for the tables is
+written. The first two 256-byte tables provide lower casing and case flipping
+functions, respectively. The next table consists of a number of 32-byte bit
+maps which identify certain character classes such as digits, "word"
+characters, white space, etc. These are used when building 32-byte bit maps
+that represent character classes for code points less than 256. The final
+256-byte table has bits indicating various character types, as follows:
+
+ 1 white space character
+ 2 letter
+ 4 lower case letter
+ 8 decimal digit
+ 16 alphanumeric or '_'
+
+You can also specify -b (with or without -L) when running pcre2_dftables. This
+causes the tables to be written in binary instead of as source code. A set of
+binary tables can be loaded into memory by an application and passed to
+pcre2_compile() in the same way as tables created dynamically by calling
+pcre2_maketables(). The tables are just a string of bytes, independent of
+hardware characteristics such as endianness. This means they can be bundled
+with an application that runs in different environments, to ensure consistent
+behaviour.
+
+See also the pcre2build section "Creating character tables at build time".
+
+
+File manifest
+-------------
+
+The distribution should contain the files listed below.
+
+(A) Source files for the PCRE2 library functions and their headers are found in
+ the src directory:
+
+ src/pcre2_dftables.c auxiliary program for building pcre2_chartables.c
+ when --enable-rebuild-chartables is specified
+
+ src/pcre2_chartables.c.dist a default set of character tables that assume
+ ASCII coding; unless --enable-rebuild-chartables is
+ specified, used by copying to pcre2_chartables.c
+
+ src/pcre2posix.c )
+ src/pcre2_auto_possess.c )
+ src/pcre2_compile.c )
+ src/pcre2_config.c )
+ src/pcre2_context.c )
+ src/pcre2_convert.c )
+ src/pcre2_dfa_match.c )
+ src/pcre2_error.c )
+ src/pcre2_extuni.c )
+ src/pcre2_find_bracket.c )
+ src/pcre2_jit_compile.c )
+ src/pcre2_jit_match.c ) sources for the functions in the library,
+ src/pcre2_jit_misc.c ) and some internal functions that they use
+ src/pcre2_maketables.c )
+ src/pcre2_match.c )
+ src/pcre2_match_data.c )
+ src/pcre2_newline.c )
+ src/pcre2_ord2utf.c )
+ src/pcre2_pattern_info.c )
+ src/pcre2_script_run.c )
+ src/pcre2_serialize.c )
+ src/pcre2_string_utils.c )
+ src/pcre2_study.c )
+ src/pcre2_substitute.c )
+ src/pcre2_substring.c )
+ src/pcre2_tables.c )
+ src/pcre2_ucd.c )
+ src/pcre2_ucptables.c )
+ src/pcre2_valid_utf.c )
+ src/pcre2_xclass.c )
+
+ src/pcre2_printint.c debugging function that is used by pcre2test,
+ src/pcre2_fuzzsupport.c function for (optional) fuzzing support
+
+ src/config.h.in template for config.h, when built by "configure"
+ src/pcre2.h.in template for pcre2.h when built by "configure"
+ src/pcre2posix.h header for the external POSIX wrapper API
+ src/pcre2_internal.h header for internal use
+ src/pcre2_intmodedep.h a mode-specific internal header
+ src/pcre2_jit_neon_inc.h header used by JIT
+ src/pcre2_jit_simd_inc.h header used by JIT
+ src/pcre2_ucp.h header for Unicode property handling
+
+ sljit/* source files for the JIT compiler
+
+(B) Source files for programs that use PCRE2:
+
+ src/pcre2demo.c simple demonstration of coding calls to PCRE2
+ src/pcre2grep.c source of a grep utility that uses PCRE2
+ src/pcre2test.c comprehensive test program
+ src/pcre2_jit_test.c JIT test program
+ src/pcre2posix_test.c POSIX wrapper API test program
+
+(C) Auxiliary files:
+
+ 132html script to turn "man" pages into HTML
+ AUTHORS information about the author of PCRE2
+ ChangeLog log of changes to the code
+ CleanTxt script to clean nroff output for txt man pages
+ Detrail script to remove trailing spaces
+ HACKING some notes about the internals of PCRE2
+ INSTALL generic installation instructions
+ LICENCE conditions for the use of PCRE2
+ COPYING the same, using GNU's standard name
+ Makefile.in ) template for Unix Makefile, which is built by
+ ) "configure"
+ Makefile.am ) the automake input that was used to create
+ ) Makefile.in
+ NEWS important changes in this release
+ NON-AUTOTOOLS-BUILD notes on building PCRE2 without using autotools
+ PrepareRelease script to make preparations for "make dist"
+ README this file
+ RunTest a Unix shell script for running tests
+ RunGrepTest a Unix shell script for pcre2grep tests
+ aclocal.m4 m4 macros (generated by "aclocal")
+ config.guess ) files used by libtool,
+ config.sub ) used only when building a shared library
+ configure a configuring shell script (built by autoconf)
+ configure.ac ) the autoconf input that was used to build
+ ) "configure" and config.h
+ depcomp ) script to find program dependencies, generated by
+ ) automake
+ doc/*.3 man page sources for PCRE2
+ doc/*.1 man page sources for pcre2grep and pcre2test
+ doc/index.html.src the base HTML page
+ doc/html/* HTML documentation
+ doc/pcre2.txt plain text version of the man pages
+ doc/pcre2test.txt plain text documentation of test program
+ install-sh a shell script for installing files
+ libpcre2-8.pc.in template for libpcre2-8.pc for pkg-config
+ libpcre2-16.pc.in template for libpcre2-16.pc for pkg-config
+ libpcre2-32.pc.in template for libpcre2-32.pc for pkg-config
+ libpcre2-posix.pc.in template for libpcre2-posix.pc for pkg-config
+ ltmain.sh file used to build a libtool script
+ missing ) common stub for a few missing GNU programs while
+ ) installing, generated by automake
+ mkinstalldirs script for making install directories
+ perltest.sh Script for running a Perl test program
+ pcre2-config.in source of script which retains PCRE2 information
+ testdata/testinput* test data for main library tests
+ testdata/testoutput* expected test results
+ testdata/grep* input and output for pcre2grep tests
+ testdata/* other supporting test files
+
+(D) Auxiliary files for cmake support
+
+ cmake/COPYING-CMAKE-SCRIPTS
+ cmake/FindPackageHandleStandardArgs.cmake
+ cmake/FindEditline.cmake
+ cmake/FindReadline.cmake
+ CMakeLists.txt
+ config-cmake.h.in
+
+(E) Auxiliary files for building PCRE2 "by hand"
+
+ src/pcre2.h.generic ) a version of the public PCRE2 header file
+ ) for use in non-"configure" environments
+ src/config.h.generic ) a version of config.h for use in non-"configure"
+ ) environments
+
+Philip Hazel
+Email local part: Philip.Hazel
+Email domain: gmail.com
+Last updated: 10 December 2022
diff --git a/contrib/libs/pcre2/README.md b/contrib/libs/pcre2/README.md
new file mode 100644
index 00000000000..d3fff179e32
--- /dev/null
+++ b/contrib/libs/pcre2/README.md
@@ -0,0 +1,56 @@
+# PCRE2 - Perl-Compatible Regular Expressions
+
+The PCRE2 library is a set of C functions that implement regular expression
+pattern matching using the same syntax and semantics as Perl 5. PCRE2 has its
+own native API, as well as a set of wrapper functions that correspond to the
+POSIX regular expression API. The PCRE2 library is free, even for building
+proprietary software. It comes in three forms, for processing 8-bit, 16-bit,
+or 32-bit code units, in either literal or UTF encoding.
+
+PCRE2 was first released in 2015 to replace the API in the original PCRE
+library, which is now obsolete and no longer maintained. As well as a more
+flexible API, the code of PCRE2 has been much improved since the fork.
+
+## Download
+
+As well as downloading from the
+[GitHub site](https://github.com/PCRE2Project/pcre2), you can download PCRE2
+or the older, unmaintained PCRE1 library from an
+[*unofficial* mirror](https://sourceforge.net/projects/pcre/files/) at SourceForge.
+
+You can check out the PCRE2 source code via Git or Subversion:
+
+ git clone https://github.com/PCRE2Project/pcre2.git
+ svn co https://github.com/PCRE2Project/pcre2.git
+
+## Contributed Ports
+
+If you just need the command-line PCRE2 tools on Windows, precompiled binary
+versions are available at this
+[Rexegg page](http://www.rexegg.com/pcregrep-pcretest.html).
+
+A PCRE2 port for z/OS, a mainframe operating system which uses EBCDIC as its
+default character encoding, can be found at
+[http://www.cbttape.org](http://www.cbttape.org/) (File 939).
+
+## Documentation
+
+You can read the PCRE2 documentation
+[here](https://PCRE2Project.github.io/pcre2/doc/html/index.html).
+
+Comparisons to Perl's regular expression semantics can be found in the
+community authored Wikipedia entry for PCRE.
+
+There is a curated summary of changes for each PCRE release, copies of
+documentation from older releases, and other useful information from the third
+party authored
+[RexEgg PCRE Documentation and Change Log page](http://www.rexegg.com/pcre-documentation.html).
+
+## Contact
+
+To report a problem with the PCRE2 library, or to make a feature request, please
+use the PCRE2 GitHub issues tracker. There is a mailing list for discussion of
+ PCRE2 issues and development at pcre2-dev@googlegroups.com, which is where any
+announcements will be made. You can browse the
+[list archives](https://groups.google.com/g/pcre2-dev).
+
diff --git a/contrib/libs/pcre2/pcre2.h b/contrib/libs/pcre2/pcre2.h
new file mode 100644
index 00000000000..1cbecd0e86e
--- /dev/null
+++ b/contrib/libs/pcre2/pcre2.h
@@ -0,0 +1,993 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* This is the public header file for the PCRE library, second API, to be
+#included by applications that call PCRE2 functions.
+
+ Copyright (c) 2016-2021 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#ifndef PCRE2_H_IDEMPOTENT_GUARD
+#define PCRE2_H_IDEMPOTENT_GUARD
+
+/* The current PCRE version information. */
+
+#define PCRE2_MAJOR 10
+#define PCRE2_MINOR 42
+#define PCRE2_PRERELEASE
+#define PCRE2_DATE 2022-12-11
+
+/* When an application links to a PCRE DLL in Windows, the symbols that are
+imported have to be identified as such. When building PCRE2, the appropriate
+export setting is defined in pcre2_internal.h, which includes this file. So we
+don't change existing definitions of PCRE2_EXP_DECL. */
+
+#if defined(_WIN32) && !defined(PCRE2_STATIC)
+# ifndef PCRE2_EXP_DECL
+# define PCRE2_EXP_DECL extern __declspec(dllimport)
+# endif
+#endif
+
+/* By default, we use the standard "extern" declarations. */
+
+#ifndef PCRE2_EXP_DECL
+# ifdef __cplusplus
+# define PCRE2_EXP_DECL extern "C"
+# else
+# define PCRE2_EXP_DECL extern
+# endif
+#endif
+
+/* When compiling with the MSVC compiler, it is sometimes necessary to include
+a "calling convention" before exported function names. (This is secondhand
+information; I know nothing about MSVC myself). For example, something like
+
+ void __cdecl function(....)
+
+might be needed. In order so make this easy, all the exported functions have
+PCRE2_CALL_CONVENTION just before their names. It is rarely needed; if not
+set, we ensure here that it has no effect. */
+
+#ifndef PCRE2_CALL_CONVENTION
+#define PCRE2_CALL_CONVENTION
+#endif
+
+/* Have to include limits.h, stdlib.h, and inttypes.h to ensure that size_t and
+uint8_t, UCHAR_MAX, etc are defined. Some systems that do have inttypes.h do
+not have stdint.h, which is why we use inttypes.h, which according to the C
+standard is a superset of stdint.h. If inttypes.h is not available the build
+will break and the relevant values must be provided by some other means. */
+
+#include <limits.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+/* Allow for C++ users compiling this directly. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The following option bits can be passed to pcre2_compile(), pcre2_match(),
+or pcre2_dfa_match(). PCRE2_NO_UTF_CHECK affects only the function to which it
+is passed. Put these bits at the most significant end of the options word so
+others can be added next to them */
+
+#define PCRE2_ANCHORED 0x80000000u
+#define PCRE2_NO_UTF_CHECK 0x40000000u
+#define PCRE2_ENDANCHORED 0x20000000u
+
+/* The following option bits can be passed only to pcre2_compile(). However,
+they may affect compilation, JIT compilation, and/or interpretive execution.
+The following tags indicate which:
+
+C alters what is compiled by pcre2_compile()
+J alters what is compiled by pcre2_jit_compile()
+M is inspected during pcre2_match() execution
+D is inspected during pcre2_dfa_match() execution
+*/
+
+#define PCRE2_ALLOW_EMPTY_CLASS 0x00000001u /* C */
+#define PCRE2_ALT_BSUX 0x00000002u /* C */
+#define PCRE2_AUTO_CALLOUT 0x00000004u /* C */
+#define PCRE2_CASELESS 0x00000008u /* C */
+#define PCRE2_DOLLAR_ENDONLY 0x00000010u /* J M D */
+#define PCRE2_DOTALL 0x00000020u /* C */
+#define PCRE2_DUPNAMES 0x00000040u /* C */
+#define PCRE2_EXTENDED 0x00000080u /* C */
+#define PCRE2_FIRSTLINE 0x00000100u /* J M D */
+#define PCRE2_MATCH_UNSET_BACKREF 0x00000200u /* C J M */
+#define PCRE2_MULTILINE 0x00000400u /* C */
+#define PCRE2_NEVER_UCP 0x00000800u /* C */
+#define PCRE2_NEVER_UTF 0x00001000u /* C */
+#define PCRE2_NO_AUTO_CAPTURE 0x00002000u /* C */
+#define PCRE2_NO_AUTO_POSSESS 0x00004000u /* C */
+#define PCRE2_NO_DOTSTAR_ANCHOR 0x00008000u /* C */
+#define PCRE2_NO_START_OPTIMIZE 0x00010000u /* J M D */
+#define PCRE2_UCP 0x00020000u /* C J M D */
+#define PCRE2_UNGREEDY 0x00040000u /* C */
+#define PCRE2_UTF 0x00080000u /* C J M D */
+#define PCRE2_NEVER_BACKSLASH_C 0x00100000u /* C */
+#define PCRE2_ALT_CIRCUMFLEX 0x00200000u /* J M D */
+#define PCRE2_ALT_VERBNAMES 0x00400000u /* C */
+#define PCRE2_USE_OFFSET_LIMIT 0x00800000u /* J M D */
+#define PCRE2_EXTENDED_MORE 0x01000000u /* C */
+#define PCRE2_LITERAL 0x02000000u /* C */
+#define PCRE2_MATCH_INVALID_UTF 0x04000000u /* J M D */
+
+/* An additional compile options word is available in the compile context. */
+
+#define PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES 0x00000001u /* C */
+#define PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL 0x00000002u /* C */
+#define PCRE2_EXTRA_MATCH_WORD 0x00000004u /* C */
+#define PCRE2_EXTRA_MATCH_LINE 0x00000008u /* C */
+#define PCRE2_EXTRA_ESCAPED_CR_IS_LF 0x00000010u /* C */
+#define PCRE2_EXTRA_ALT_BSUX 0x00000020u /* C */
+#define PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK 0x00000040u /* C */
+
+/* These are for pcre2_jit_compile(). */
+
+#define PCRE2_JIT_COMPLETE 0x00000001u /* For full matching */
+#define PCRE2_JIT_PARTIAL_SOFT 0x00000002u
+#define PCRE2_JIT_PARTIAL_HARD 0x00000004u
+#define PCRE2_JIT_INVALID_UTF 0x00000100u
+
+/* These are for pcre2_match(), pcre2_dfa_match(), pcre2_jit_match(), and
+pcre2_substitute(). Some are allowed only for one of the functions, and in
+these cases it is noted below. Note that PCRE2_ANCHORED, PCRE2_ENDANCHORED and
+PCRE2_NO_UTF_CHECK can also be passed to these functions (though
+pcre2_jit_match() ignores the latter since it bypasses all sanity checks). */
+
+#define PCRE2_NOTBOL 0x00000001u
+#define PCRE2_NOTEOL 0x00000002u
+#define PCRE2_NOTEMPTY 0x00000004u /* ) These two must be kept */
+#define PCRE2_NOTEMPTY_ATSTART 0x00000008u /* ) adjacent to each other. */
+#define PCRE2_PARTIAL_SOFT 0x00000010u
+#define PCRE2_PARTIAL_HARD 0x00000020u
+#define PCRE2_DFA_RESTART 0x00000040u /* pcre2_dfa_match() only */
+#define PCRE2_DFA_SHORTEST 0x00000080u /* pcre2_dfa_match() only */
+#define PCRE2_SUBSTITUTE_GLOBAL 0x00000100u /* pcre2_substitute() only */
+#define PCRE2_SUBSTITUTE_EXTENDED 0x00000200u /* pcre2_substitute() only */
+#define PCRE2_SUBSTITUTE_UNSET_EMPTY 0x00000400u /* pcre2_substitute() only */
+#define PCRE2_SUBSTITUTE_UNKNOWN_UNSET 0x00000800u /* pcre2_substitute() only */
+#define PCRE2_SUBSTITUTE_OVERFLOW_LENGTH 0x00001000u /* pcre2_substitute() only */
+#define PCRE2_NO_JIT 0x00002000u /* Not for pcre2_dfa_match() */
+#define PCRE2_COPY_MATCHED_SUBJECT 0x00004000u
+#define PCRE2_SUBSTITUTE_LITERAL 0x00008000u /* pcre2_substitute() only */
+#define PCRE2_SUBSTITUTE_MATCHED 0x00010000u /* pcre2_substitute() only */
+#define PCRE2_SUBSTITUTE_REPLACEMENT_ONLY 0x00020000u /* pcre2_substitute() only */
+
+/* Options for pcre2_pattern_convert(). */
+
+#define PCRE2_CONVERT_UTF 0x00000001u
+#define PCRE2_CONVERT_NO_UTF_CHECK 0x00000002u
+#define PCRE2_CONVERT_POSIX_BASIC 0x00000004u
+#define PCRE2_CONVERT_POSIX_EXTENDED 0x00000008u
+#define PCRE2_CONVERT_GLOB 0x00000010u
+#define PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR 0x00000030u
+#define PCRE2_CONVERT_GLOB_NO_STARSTAR 0x00000050u
+
+/* Newline and \R settings, for use in compile contexts. The newline values
+must be kept in step with values set in config.h and both sets must all be
+greater than zero. */
+
+#define PCRE2_NEWLINE_CR 1
+#define PCRE2_NEWLINE_LF 2
+#define PCRE2_NEWLINE_CRLF 3
+#define PCRE2_NEWLINE_ANY 4
+#define PCRE2_NEWLINE_ANYCRLF 5
+#define PCRE2_NEWLINE_NUL 6
+
+#define PCRE2_BSR_UNICODE 1
+#define PCRE2_BSR_ANYCRLF 2
+
+/* Error codes for pcre2_compile(). Some of these are also used by
+pcre2_pattern_convert(). */
+
+#define PCRE2_ERROR_END_BACKSLASH 101
+#define PCRE2_ERROR_END_BACKSLASH_C 102
+#define PCRE2_ERROR_UNKNOWN_ESCAPE 103
+#define PCRE2_ERROR_QUANTIFIER_OUT_OF_ORDER 104
+#define PCRE2_ERROR_QUANTIFIER_TOO_BIG 105
+#define PCRE2_ERROR_MISSING_SQUARE_BRACKET 106
+#define PCRE2_ERROR_ESCAPE_INVALID_IN_CLASS 107
+#define PCRE2_ERROR_CLASS_RANGE_ORDER 108
+#define PCRE2_ERROR_QUANTIFIER_INVALID 109
+#define PCRE2_ERROR_INTERNAL_UNEXPECTED_REPEAT 110
+#define PCRE2_ERROR_INVALID_AFTER_PARENS_QUERY 111
+#define PCRE2_ERROR_POSIX_CLASS_NOT_IN_CLASS 112
+#define PCRE2_ERROR_POSIX_NO_SUPPORT_COLLATING 113
+#define PCRE2_ERROR_MISSING_CLOSING_PARENTHESIS 114
+#define PCRE2_ERROR_BAD_SUBPATTERN_REFERENCE 115
+#define PCRE2_ERROR_NULL_PATTERN 116
+#define PCRE2_ERROR_BAD_OPTIONS 117
+#define PCRE2_ERROR_MISSING_COMMENT_CLOSING 118
+#define PCRE2_ERROR_PARENTHESES_NEST_TOO_DEEP 119
+#define PCRE2_ERROR_PATTERN_TOO_LARGE 120
+#define PCRE2_ERROR_HEAP_FAILED 121
+#define PCRE2_ERROR_UNMATCHED_CLOSING_PARENTHESIS 122
+#define PCRE2_ERROR_INTERNAL_CODE_OVERFLOW 123
+#define PCRE2_ERROR_MISSING_CONDITION_CLOSING 124
+#define PCRE2_ERROR_LOOKBEHIND_NOT_FIXED_LENGTH 125
+#define PCRE2_ERROR_ZERO_RELATIVE_REFERENCE 126
+#define PCRE2_ERROR_TOO_MANY_CONDITION_BRANCHES 127
+#define PCRE2_ERROR_CONDITION_ASSERTION_EXPECTED 128
+#define PCRE2_ERROR_BAD_RELATIVE_REFERENCE 129
+#define PCRE2_ERROR_UNKNOWN_POSIX_CLASS 130
+#define PCRE2_ERROR_INTERNAL_STUDY_ERROR 131
+#define PCRE2_ERROR_UNICODE_NOT_SUPPORTED 132
+#define PCRE2_ERROR_PARENTHESES_STACK_CHECK 133
+#define PCRE2_ERROR_CODE_POINT_TOO_BIG 134
+#define PCRE2_ERROR_LOOKBEHIND_TOO_COMPLICATED 135
+#define PCRE2_ERROR_LOOKBEHIND_INVALID_BACKSLASH_C 136
+#define PCRE2_ERROR_UNSUPPORTED_ESCAPE_SEQUENCE 137
+#define PCRE2_ERROR_CALLOUT_NUMBER_TOO_BIG 138
+#define PCRE2_ERROR_MISSING_CALLOUT_CLOSING 139
+#define PCRE2_ERROR_ESCAPE_INVALID_IN_VERB 140
+#define PCRE2_ERROR_UNRECOGNIZED_AFTER_QUERY_P 141
+#define PCRE2_ERROR_MISSING_NAME_TERMINATOR 142
+#define PCRE2_ERROR_DUPLICATE_SUBPATTERN_NAME 143
+#define PCRE2_ERROR_INVALID_SUBPATTERN_NAME 144
+#define PCRE2_ERROR_UNICODE_PROPERTIES_UNAVAILABLE 145
+#define PCRE2_ERROR_MALFORMED_UNICODE_PROPERTY 146
+#define PCRE2_ERROR_UNKNOWN_UNICODE_PROPERTY 147
+#define PCRE2_ERROR_SUBPATTERN_NAME_TOO_LONG 148
+#define PCRE2_ERROR_TOO_MANY_NAMED_SUBPATTERNS 149
+#define PCRE2_ERROR_CLASS_INVALID_RANGE 150
+#define PCRE2_ERROR_OCTAL_BYTE_TOO_BIG 151
+#define PCRE2_ERROR_INTERNAL_OVERRAN_WORKSPACE 152
+#define PCRE2_ERROR_INTERNAL_MISSING_SUBPATTERN 153
+#define PCRE2_ERROR_DEFINE_TOO_MANY_BRANCHES 154
+#define PCRE2_ERROR_BACKSLASH_O_MISSING_BRACE 155
+#define PCRE2_ERROR_INTERNAL_UNKNOWN_NEWLINE 156
+#define PCRE2_ERROR_BACKSLASH_G_SYNTAX 157
+#define PCRE2_ERROR_PARENS_QUERY_R_MISSING_CLOSING 158
+/* Error 159 is obsolete and should now never occur */
+#define PCRE2_ERROR_VERB_ARGUMENT_NOT_ALLOWED 159
+#define PCRE2_ERROR_VERB_UNKNOWN 160
+#define PCRE2_ERROR_SUBPATTERN_NUMBER_TOO_BIG 161
+#define PCRE2_ERROR_SUBPATTERN_NAME_EXPECTED 162
+#define PCRE2_ERROR_INTERNAL_PARSED_OVERFLOW 163
+#define PCRE2_ERROR_INVALID_OCTAL 164
+#define PCRE2_ERROR_SUBPATTERN_NAMES_MISMATCH 165
+#define PCRE2_ERROR_MARK_MISSING_ARGUMENT 166
+#define PCRE2_ERROR_INVALID_HEXADECIMAL 167
+#define PCRE2_ERROR_BACKSLASH_C_SYNTAX 168
+#define PCRE2_ERROR_BACKSLASH_K_SYNTAX 169
+#define PCRE2_ERROR_INTERNAL_BAD_CODE_LOOKBEHINDS 170
+#define PCRE2_ERROR_BACKSLASH_N_IN_CLASS 171
+#define PCRE2_ERROR_CALLOUT_STRING_TOO_LONG 172
+#define PCRE2_ERROR_UNICODE_DISALLOWED_CODE_POINT 173
+#define PCRE2_ERROR_UTF_IS_DISABLED 174
+#define PCRE2_ERROR_UCP_IS_DISABLED 175
+#define PCRE2_ERROR_VERB_NAME_TOO_LONG 176
+#define PCRE2_ERROR_BACKSLASH_U_CODE_POINT_TOO_BIG 177
+#define PCRE2_ERROR_MISSING_OCTAL_OR_HEX_DIGITS 178
+#define PCRE2_ERROR_VERSION_CONDITION_SYNTAX 179
+#define PCRE2_ERROR_INTERNAL_BAD_CODE_AUTO_POSSESS 180
+#define PCRE2_ERROR_CALLOUT_NO_STRING_DELIMITER 181
+#define PCRE2_ERROR_CALLOUT_BAD_STRING_DELIMITER 182
+#define PCRE2_ERROR_BACKSLASH_C_CALLER_DISABLED 183
+#define PCRE2_ERROR_QUERY_BARJX_NEST_TOO_DEEP 184
+#define PCRE2_ERROR_BACKSLASH_C_LIBRARY_DISABLED 185
+#define PCRE2_ERROR_PATTERN_TOO_COMPLICATED 186
+#define PCRE2_ERROR_LOOKBEHIND_TOO_LONG 187
+#define PCRE2_ERROR_PATTERN_STRING_TOO_LONG 188
+#define PCRE2_ERROR_INTERNAL_BAD_CODE 189
+#define PCRE2_ERROR_INTERNAL_BAD_CODE_IN_SKIP 190
+#define PCRE2_ERROR_NO_SURROGATES_IN_UTF16 191
+#define PCRE2_ERROR_BAD_LITERAL_OPTIONS 192
+#define PCRE2_ERROR_SUPPORTED_ONLY_IN_UNICODE 193
+#define PCRE2_ERROR_INVALID_HYPHEN_IN_OPTIONS 194
+#define PCRE2_ERROR_ALPHA_ASSERTION_UNKNOWN 195
+#define PCRE2_ERROR_SCRIPT_RUN_NOT_AVAILABLE 196
+#define PCRE2_ERROR_TOO_MANY_CAPTURES 197
+#define PCRE2_ERROR_CONDITION_ATOMIC_ASSERTION_EXPECTED 198
+#define PCRE2_ERROR_BACKSLASH_K_IN_LOOKAROUND 199
+
+
+/* "Expected" matching error codes: no match and partial match. */
+
+#define PCRE2_ERROR_NOMATCH (-1)
+#define PCRE2_ERROR_PARTIAL (-2)
+
+/* Error codes for UTF-8 validity checks */
+
+#define PCRE2_ERROR_UTF8_ERR1 (-3)
+#define PCRE2_ERROR_UTF8_ERR2 (-4)
+#define PCRE2_ERROR_UTF8_ERR3 (-5)
+#define PCRE2_ERROR_UTF8_ERR4 (-6)
+#define PCRE2_ERROR_UTF8_ERR5 (-7)
+#define PCRE2_ERROR_UTF8_ERR6 (-8)
+#define PCRE2_ERROR_UTF8_ERR7 (-9)
+#define PCRE2_ERROR_UTF8_ERR8 (-10)
+#define PCRE2_ERROR_UTF8_ERR9 (-11)
+#define PCRE2_ERROR_UTF8_ERR10 (-12)
+#define PCRE2_ERROR_UTF8_ERR11 (-13)
+#define PCRE2_ERROR_UTF8_ERR12 (-14)
+#define PCRE2_ERROR_UTF8_ERR13 (-15)
+#define PCRE2_ERROR_UTF8_ERR14 (-16)
+#define PCRE2_ERROR_UTF8_ERR15 (-17)
+#define PCRE2_ERROR_UTF8_ERR16 (-18)
+#define PCRE2_ERROR_UTF8_ERR17 (-19)
+#define PCRE2_ERROR_UTF8_ERR18 (-20)
+#define PCRE2_ERROR_UTF8_ERR19 (-21)
+#define PCRE2_ERROR_UTF8_ERR20 (-22)
+#define PCRE2_ERROR_UTF8_ERR21 (-23)
+
+/* Error codes for UTF-16 validity checks */
+
+#define PCRE2_ERROR_UTF16_ERR1 (-24)
+#define PCRE2_ERROR_UTF16_ERR2 (-25)
+#define PCRE2_ERROR_UTF16_ERR3 (-26)
+
+/* Error codes for UTF-32 validity checks */
+
+#define PCRE2_ERROR_UTF32_ERR1 (-27)
+#define PCRE2_ERROR_UTF32_ERR2 (-28)
+
+/* Miscellaneous error codes for pcre2[_dfa]_match(), substring extraction
+functions, context functions, and serializing functions. They are in numerical
+order. Originally they were in alphabetical order too, but now that PCRE2 is
+released, the numbers must not be changed. */
+
+#define PCRE2_ERROR_BADDATA (-29)
+#define PCRE2_ERROR_MIXEDTABLES (-30) /* Name was changed */
+#define PCRE2_ERROR_BADMAGIC (-31)
+#define PCRE2_ERROR_BADMODE (-32)
+#define PCRE2_ERROR_BADOFFSET (-33)
+#define PCRE2_ERROR_BADOPTION (-34)
+#define PCRE2_ERROR_BADREPLACEMENT (-35)
+#define PCRE2_ERROR_BADUTFOFFSET (-36)
+#define PCRE2_ERROR_CALLOUT (-37) /* Never used by PCRE2 itself */
+#define PCRE2_ERROR_DFA_BADRESTART (-38)
+#define PCRE2_ERROR_DFA_RECURSE (-39)
+#define PCRE2_ERROR_DFA_UCOND (-40)
+#define PCRE2_ERROR_DFA_UFUNC (-41)
+#define PCRE2_ERROR_DFA_UITEM (-42)
+#define PCRE2_ERROR_DFA_WSSIZE (-43)
+#define PCRE2_ERROR_INTERNAL (-44)
+#define PCRE2_ERROR_JIT_BADOPTION (-45)
+#define PCRE2_ERROR_JIT_STACKLIMIT (-46)
+#define PCRE2_ERROR_MATCHLIMIT (-47)
+#define PCRE2_ERROR_NOMEMORY (-48)
+#define PCRE2_ERROR_NOSUBSTRING (-49)
+#define PCRE2_ERROR_NOUNIQUESUBSTRING (-50)
+#define PCRE2_ERROR_NULL (-51)
+#define PCRE2_ERROR_RECURSELOOP (-52)
+#define PCRE2_ERROR_DEPTHLIMIT (-53)
+#define PCRE2_ERROR_RECURSIONLIMIT (-53) /* Obsolete synonym */
+#define PCRE2_ERROR_UNAVAILABLE (-54)
+#define PCRE2_ERROR_UNSET (-55)
+#define PCRE2_ERROR_BADOFFSETLIMIT (-56)
+#define PCRE2_ERROR_BADREPESCAPE (-57)
+#define PCRE2_ERROR_REPMISSINGBRACE (-58)
+#define PCRE2_ERROR_BADSUBSTITUTION (-59)
+#define PCRE2_ERROR_BADSUBSPATTERN (-60)
+#define PCRE2_ERROR_TOOMANYREPLACE (-61)
+#define PCRE2_ERROR_BADSERIALIZEDDATA (-62)
+#define PCRE2_ERROR_HEAPLIMIT (-63)
+#define PCRE2_ERROR_CONVERT_SYNTAX (-64)
+#define PCRE2_ERROR_INTERNAL_DUPMATCH (-65)
+#define PCRE2_ERROR_DFA_UINVALID_UTF (-66)
+
+
+/* Request types for pcre2_pattern_info() */
+
+#define PCRE2_INFO_ALLOPTIONS 0
+#define PCRE2_INFO_ARGOPTIONS 1
+#define PCRE2_INFO_BACKREFMAX 2
+#define PCRE2_INFO_BSR 3
+#define PCRE2_INFO_CAPTURECOUNT 4
+#define PCRE2_INFO_FIRSTCODEUNIT 5
+#define PCRE2_INFO_FIRSTCODETYPE 6
+#define PCRE2_INFO_FIRSTBITMAP 7
+#define PCRE2_INFO_HASCRORLF 8
+#define PCRE2_INFO_JCHANGED 9
+#define PCRE2_INFO_JITSIZE 10
+#define PCRE2_INFO_LASTCODEUNIT 11
+#define PCRE2_INFO_LASTCODETYPE 12
+#define PCRE2_INFO_MATCHEMPTY 13
+#define PCRE2_INFO_MATCHLIMIT 14
+#define PCRE2_INFO_MAXLOOKBEHIND 15
+#define PCRE2_INFO_MINLENGTH 16
+#define PCRE2_INFO_NAMECOUNT 17
+#define PCRE2_INFO_NAMEENTRYSIZE 18
+#define PCRE2_INFO_NAMETABLE 19
+#define PCRE2_INFO_NEWLINE 20
+#define PCRE2_INFO_DEPTHLIMIT 21
+#define PCRE2_INFO_RECURSIONLIMIT 21 /* Obsolete synonym */
+#define PCRE2_INFO_SIZE 22
+#define PCRE2_INFO_HASBACKSLASHC 23
+#define PCRE2_INFO_FRAMESIZE 24
+#define PCRE2_INFO_HEAPLIMIT 25
+#define PCRE2_INFO_EXTRAOPTIONS 26
+
+/* Request types for pcre2_config(). */
+
+#define PCRE2_CONFIG_BSR 0
+#define PCRE2_CONFIG_JIT 1
+#define PCRE2_CONFIG_JITTARGET 2
+#define PCRE2_CONFIG_LINKSIZE 3
+#define PCRE2_CONFIG_MATCHLIMIT 4
+#define PCRE2_CONFIG_NEWLINE 5
+#define PCRE2_CONFIG_PARENSLIMIT 6
+#define PCRE2_CONFIG_DEPTHLIMIT 7
+#define PCRE2_CONFIG_RECURSIONLIMIT 7 /* Obsolete synonym */
+#define PCRE2_CONFIG_STACKRECURSE 8 /* Obsolete */
+#define PCRE2_CONFIG_UNICODE 9
+#define PCRE2_CONFIG_UNICODE_VERSION 10
+#define PCRE2_CONFIG_VERSION 11
+#define PCRE2_CONFIG_HEAPLIMIT 12
+#define PCRE2_CONFIG_NEVER_BACKSLASH_C 13
+#define PCRE2_CONFIG_COMPILED_WIDTHS 14
+#define PCRE2_CONFIG_TABLES_LENGTH 15
+
+
+/* Types for code units in patterns and subject strings. */
+
+typedef uint8_t PCRE2_UCHAR8;
+typedef uint16_t PCRE2_UCHAR16;
+typedef uint32_t PCRE2_UCHAR32;
+
+typedef const PCRE2_UCHAR8 *PCRE2_SPTR8;
+typedef const PCRE2_UCHAR16 *PCRE2_SPTR16;
+typedef const PCRE2_UCHAR32 *PCRE2_SPTR32;
+
+/* The PCRE2_SIZE type is used for all string lengths and offsets in PCRE2,
+including pattern offsets for errors and subject offsets after a match. We
+define special values to indicate zero-terminated strings and unset offsets in
+the offset vector (ovector). */
+
+#define PCRE2_SIZE size_t
+#define PCRE2_SIZE_MAX SIZE_MAX
+#define PCRE2_ZERO_TERMINATED (~(PCRE2_SIZE)0)
+#define PCRE2_UNSET (~(PCRE2_SIZE)0)
+
+/* Generic types for opaque structures and JIT callback functions. These
+declarations are defined in a macro that is expanded for each width later. */
+
+#define PCRE2_TYPES_LIST \
+struct pcre2_real_general_context; \
+typedef struct pcre2_real_general_context pcre2_general_context; \
+\
+struct pcre2_real_compile_context; \
+typedef struct pcre2_real_compile_context pcre2_compile_context; \
+\
+struct pcre2_real_match_context; \
+typedef struct pcre2_real_match_context pcre2_match_context; \
+\
+struct pcre2_real_convert_context; \
+typedef struct pcre2_real_convert_context pcre2_convert_context; \
+\
+struct pcre2_real_code; \
+typedef struct pcre2_real_code pcre2_code; \
+\
+struct pcre2_real_match_data; \
+typedef struct pcre2_real_match_data pcre2_match_data; \
+\
+struct pcre2_real_jit_stack; \
+typedef struct pcre2_real_jit_stack pcre2_jit_stack; \
+\
+typedef pcre2_jit_stack *(*pcre2_jit_callback)(void *);
+
+
+/* The structures for passing out data via callout functions. We use structures
+so that new fields can be added on the end in future versions, without changing
+the API of the function, thereby allowing old clients to work without
+modification. Define the generic versions in a macro; the width-specific
+versions are generated from this macro below. */
+
+/* Flags for the callout_flags field. These are cleared after a callout. */
+
+#define PCRE2_CALLOUT_STARTMATCH 0x00000001u /* Set for each bumpalong */
+#define PCRE2_CALLOUT_BACKTRACK 0x00000002u /* Set after a backtrack */
+
+#define PCRE2_STRUCTURE_LIST \
+typedef struct pcre2_callout_block { \
+ uint32_t version; /* Identifies version of block */ \
+ /* ------------------------ Version 0 ------------------------------- */ \
+ uint32_t callout_number; /* Number compiled into pattern */ \
+ uint32_t capture_top; /* Max current capture */ \
+ uint32_t capture_last; /* Most recently closed capture */ \
+ PCRE2_SIZE *offset_vector; /* The offset vector */ \
+ PCRE2_SPTR mark; /* Pointer to current mark or NULL */ \
+ PCRE2_SPTR subject; /* The subject being matched */ \
+ PCRE2_SIZE subject_length; /* The length of the subject */ \
+ PCRE2_SIZE start_match; /* Offset to start of this match attempt */ \
+ PCRE2_SIZE current_position; /* Where we currently are in the subject */ \
+ PCRE2_SIZE pattern_position; /* Offset to next item in the pattern */ \
+ PCRE2_SIZE next_item_length; /* Length of next item in the pattern */ \
+ /* ------------------- Added for Version 1 -------------------------- */ \
+ PCRE2_SIZE callout_string_offset; /* Offset to string within pattern */ \
+ PCRE2_SIZE callout_string_length; /* Length of string compiled into pattern */ \
+ PCRE2_SPTR callout_string; /* String compiled into pattern */ \
+ /* ------------------- Added for Version 2 -------------------------- */ \
+ uint32_t callout_flags; /* See above for list */ \
+ /* ------------------------------------------------------------------ */ \
+} pcre2_callout_block; \
+\
+typedef struct pcre2_callout_enumerate_block { \
+ uint32_t version; /* Identifies version of block */ \
+ /* ------------------------ Version 0 ------------------------------- */ \
+ PCRE2_SIZE pattern_position; /* Offset to next item in the pattern */ \
+ PCRE2_SIZE next_item_length; /* Length of next item in the pattern */ \
+ uint32_t callout_number; /* Number compiled into pattern */ \
+ PCRE2_SIZE callout_string_offset; /* Offset to string within pattern */ \
+ PCRE2_SIZE callout_string_length; /* Length of string compiled into pattern */ \
+ PCRE2_SPTR callout_string; /* String compiled into pattern */ \
+ /* ------------------------------------------------------------------ */ \
+} pcre2_callout_enumerate_block; \
+\
+typedef struct pcre2_substitute_callout_block { \
+ uint32_t version; /* Identifies version of block */ \
+ /* ------------------------ Version 0 ------------------------------- */ \
+ PCRE2_SPTR input; /* Pointer to input subject string */ \
+ PCRE2_SPTR output; /* Pointer to output buffer */ \
+ PCRE2_SIZE output_offsets[2]; /* Changed portion of the output */ \
+ PCRE2_SIZE *ovector; /* Pointer to current ovector */ \
+ uint32_t oveccount; /* Count of pairs set in ovector */ \
+ uint32_t subscount; /* Substitution number */ \
+ /* ------------------------------------------------------------------ */ \
+} pcre2_substitute_callout_block;
+
+
+/* List the generic forms of all other functions in macros, which will be
+expanded for each width below. Start with functions that give general
+information. */
+
+#define PCRE2_GENERAL_INFO_FUNCTIONS \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION pcre2_config(uint32_t, void *);
+
+
+/* Functions for manipulating contexts. */
+
+#define PCRE2_GENERAL_CONTEXT_FUNCTIONS \
+PCRE2_EXP_DECL pcre2_general_context *PCRE2_CALL_CONVENTION \
+ pcre2_general_context_copy(pcre2_general_context *); \
+PCRE2_EXP_DECL pcre2_general_context *PCRE2_CALL_CONVENTION \
+ pcre2_general_context_create(void *(*)(PCRE2_SIZE, void *), \
+ void (*)(void *, void *), void *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_general_context_free(pcre2_general_context *);
+
+#define PCRE2_COMPILE_CONTEXT_FUNCTIONS \
+PCRE2_EXP_DECL pcre2_compile_context *PCRE2_CALL_CONVENTION \
+ pcre2_compile_context_copy(pcre2_compile_context *); \
+PCRE2_EXP_DECL pcre2_compile_context *PCRE2_CALL_CONVENTION \
+ pcre2_compile_context_create(pcre2_general_context *);\
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_compile_context_free(pcre2_compile_context *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_bsr(pcre2_compile_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_character_tables(pcre2_compile_context *, const uint8_t *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_compile_extra_options(pcre2_compile_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_max_pattern_length(pcre2_compile_context *, PCRE2_SIZE); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_newline(pcre2_compile_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_parens_nest_limit(pcre2_compile_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_compile_recursion_guard(pcre2_compile_context *, \
+ int (*)(uint32_t, void *), void *);
+
+#define PCRE2_MATCH_CONTEXT_FUNCTIONS \
+PCRE2_EXP_DECL pcre2_match_context *PCRE2_CALL_CONVENTION \
+ pcre2_match_context_copy(pcre2_match_context *); \
+PCRE2_EXP_DECL pcre2_match_context *PCRE2_CALL_CONVENTION \
+ pcre2_match_context_create(pcre2_general_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_match_context_free(pcre2_match_context *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_callout(pcre2_match_context *, \
+ int (*)(pcre2_callout_block *, void *), void *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_substitute_callout(pcre2_match_context *, \
+ int (*)(pcre2_substitute_callout_block *, void *), void *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_depth_limit(pcre2_match_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_heap_limit(pcre2_match_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_match_limit(pcre2_match_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_offset_limit(pcre2_match_context *, PCRE2_SIZE); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_recursion_limit(pcre2_match_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_recursion_memory_management(pcre2_match_context *, \
+ void *(*)(PCRE2_SIZE, void *), void (*)(void *, void *), void *);
+
+#define PCRE2_CONVERT_CONTEXT_FUNCTIONS \
+PCRE2_EXP_DECL pcre2_convert_context *PCRE2_CALL_CONVENTION \
+ pcre2_convert_context_copy(pcre2_convert_context *); \
+PCRE2_EXP_DECL pcre2_convert_context *PCRE2_CALL_CONVENTION \
+ pcre2_convert_context_create(pcre2_general_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_convert_context_free(pcre2_convert_context *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_glob_escape(pcre2_convert_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_glob_separator(pcre2_convert_context *, uint32_t);
+
+
+/* Functions concerned with compiling a pattern to PCRE internal code. */
+
+#define PCRE2_COMPILE_FUNCTIONS \
+PCRE2_EXP_DECL pcre2_code *PCRE2_CALL_CONVENTION \
+ pcre2_compile(PCRE2_SPTR, PCRE2_SIZE, uint32_t, int *, PCRE2_SIZE *, \
+ pcre2_compile_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_code_free(pcre2_code *); \
+PCRE2_EXP_DECL pcre2_code *PCRE2_CALL_CONVENTION \
+ pcre2_code_copy(const pcre2_code *); \
+PCRE2_EXP_DECL pcre2_code *PCRE2_CALL_CONVENTION \
+ pcre2_code_copy_with_tables(const pcre2_code *);
+
+
+/* Functions that give information about a compiled pattern. */
+
+#define PCRE2_PATTERN_INFO_FUNCTIONS \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_pattern_info(const pcre2_code *, uint32_t, void *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_callout_enumerate(const pcre2_code *, \
+ int (*)(pcre2_callout_enumerate_block *, void *), void *);
+
+
+/* Functions for running a match and inspecting the result. */
+
+#define PCRE2_MATCH_FUNCTIONS \
+PCRE2_EXP_DECL pcre2_match_data *PCRE2_CALL_CONVENTION \
+ pcre2_match_data_create(uint32_t, pcre2_general_context *); \
+PCRE2_EXP_DECL pcre2_match_data *PCRE2_CALL_CONVENTION \
+ pcre2_match_data_create_from_pattern(const pcre2_code *, \
+ pcre2_general_context *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_dfa_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \
+ uint32_t, pcre2_match_data *, pcre2_match_context *, int *, PCRE2_SIZE); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \
+ uint32_t, pcre2_match_data *, pcre2_match_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_match_data_free(pcre2_match_data *); \
+PCRE2_EXP_DECL PCRE2_SPTR PCRE2_CALL_CONVENTION \
+ pcre2_get_mark(pcre2_match_data *); \
+PCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \
+ pcre2_get_match_data_size(pcre2_match_data *); \
+PCRE2_EXP_DECL uint32_t PCRE2_CALL_CONVENTION \
+ pcre2_get_ovector_count(pcre2_match_data *); \
+PCRE2_EXP_DECL PCRE2_SIZE *PCRE2_CALL_CONVENTION \
+ pcre2_get_ovector_pointer(pcre2_match_data *); \
+PCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \
+ pcre2_get_startchar(pcre2_match_data *);
+
+
+/* Convenience functions for handling matched substrings. */
+
+#define PCRE2_SUBSTRING_FUNCTIONS \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_copy_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_UCHAR *, \
+ PCRE2_SIZE *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_copy_bynumber(pcre2_match_data *, uint32_t, PCRE2_UCHAR *, \
+ PCRE2_SIZE *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_substring_free(PCRE2_UCHAR *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_get_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_UCHAR **, \
+ PCRE2_SIZE *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_get_bynumber(pcre2_match_data *, uint32_t, PCRE2_UCHAR **, \
+ PCRE2_SIZE *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_length_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_SIZE *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_length_bynumber(pcre2_match_data *, uint32_t, PCRE2_SIZE *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_nametable_scan(const pcre2_code *, PCRE2_SPTR, PCRE2_SPTR *, \
+ PCRE2_SPTR *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_number_from_name(const pcre2_code *, PCRE2_SPTR); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_substring_list_free(PCRE2_SPTR *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_list_get(pcre2_match_data *, PCRE2_UCHAR ***, PCRE2_SIZE **);
+
+/* Functions for serializing / deserializing compiled patterns. */
+
+#define PCRE2_SERIALIZE_FUNCTIONS \
+PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \
+ pcre2_serialize_encode(const pcre2_code **, int32_t, uint8_t **, \
+ PCRE2_SIZE *, pcre2_general_context *); \
+PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \
+ pcre2_serialize_decode(pcre2_code **, int32_t, const uint8_t *, \
+ pcre2_general_context *); \
+PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \
+ pcre2_serialize_get_number_of_codes(const uint8_t *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_serialize_free(uint8_t *);
+
+
+/* Convenience function for match + substitute. */
+
+#define PCRE2_SUBSTITUTE_FUNCTION \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substitute(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \
+ uint32_t, pcre2_match_data *, pcre2_match_context *, PCRE2_SPTR, \
+ PCRE2_SIZE, PCRE2_UCHAR *, PCRE2_SIZE *);
+
+
+/* Functions for converting pattern source strings. */
+
+#define PCRE2_CONVERT_FUNCTIONS \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_pattern_convert(PCRE2_SPTR, PCRE2_SIZE, uint32_t, PCRE2_UCHAR **, \
+ PCRE2_SIZE *, pcre2_convert_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_converted_pattern_free(PCRE2_UCHAR *);
+
+
+/* Functions for JIT processing */
+
+#define PCRE2_JIT_FUNCTIONS \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_jit_compile(pcre2_code *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_jit_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \
+ uint32_t, pcre2_match_data *, pcre2_match_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_jit_free_unused_memory(pcre2_general_context *); \
+PCRE2_EXP_DECL pcre2_jit_stack *PCRE2_CALL_CONVENTION \
+ pcre2_jit_stack_create(PCRE2_SIZE, PCRE2_SIZE, pcre2_general_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_jit_stack_assign(pcre2_match_context *, pcre2_jit_callback, void *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_jit_stack_free(pcre2_jit_stack *);
+
+
+/* Other miscellaneous functions. */
+
+#define PCRE2_OTHER_FUNCTIONS \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_get_error_message(int, PCRE2_UCHAR *, PCRE2_SIZE); \
+PCRE2_EXP_DECL const uint8_t *PCRE2_CALL_CONVENTION \
+ pcre2_maketables(pcre2_general_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_maketables_free(pcre2_general_context *, const uint8_t *);
+
+/* Define macros that generate width-specific names from generic versions. The
+three-level macro scheme is necessary to get the macros expanded when we want
+them to be. First we get the width from PCRE2_LOCAL_WIDTH, which is used for
+generating three versions of everything below. After that, PCRE2_SUFFIX will be
+re-defined to use PCRE2_CODE_UNIT_WIDTH, for use when macros such as
+pcre2_compile are called by application code. */
+
+#define PCRE2_JOIN(a,b) a ## b
+#define PCRE2_GLUE(a,b) PCRE2_JOIN(a,b)
+#define PCRE2_SUFFIX(a) PCRE2_GLUE(a,PCRE2_LOCAL_WIDTH)
+
+
+/* Data types */
+
+#define PCRE2_UCHAR PCRE2_SUFFIX(PCRE2_UCHAR)
+#define PCRE2_SPTR PCRE2_SUFFIX(PCRE2_SPTR)
+
+#define pcre2_code PCRE2_SUFFIX(pcre2_code_)
+#define pcre2_jit_callback PCRE2_SUFFIX(pcre2_jit_callback_)
+#define pcre2_jit_stack PCRE2_SUFFIX(pcre2_jit_stack_)
+
+#define pcre2_real_code PCRE2_SUFFIX(pcre2_real_code_)
+#define pcre2_real_general_context PCRE2_SUFFIX(pcre2_real_general_context_)
+#define pcre2_real_compile_context PCRE2_SUFFIX(pcre2_real_compile_context_)
+#define pcre2_real_convert_context PCRE2_SUFFIX(pcre2_real_convert_context_)
+#define pcre2_real_match_context PCRE2_SUFFIX(pcre2_real_match_context_)
+#define pcre2_real_jit_stack PCRE2_SUFFIX(pcre2_real_jit_stack_)
+#define pcre2_real_match_data PCRE2_SUFFIX(pcre2_real_match_data_)
+
+
+/* Data blocks */
+
+#define pcre2_callout_block PCRE2_SUFFIX(pcre2_callout_block_)
+#define pcre2_callout_enumerate_block PCRE2_SUFFIX(pcre2_callout_enumerate_block_)
+#define pcre2_substitute_callout_block PCRE2_SUFFIX(pcre2_substitute_callout_block_)
+#define pcre2_general_context PCRE2_SUFFIX(pcre2_general_context_)
+#define pcre2_compile_context PCRE2_SUFFIX(pcre2_compile_context_)
+#define pcre2_convert_context PCRE2_SUFFIX(pcre2_convert_context_)
+#define pcre2_match_context PCRE2_SUFFIX(pcre2_match_context_)
+#define pcre2_match_data PCRE2_SUFFIX(pcre2_match_data_)
+
+
+/* Functions: the complete list in alphabetical order */
+
+#define pcre2_callout_enumerate PCRE2_SUFFIX(pcre2_callout_enumerate_)
+#define pcre2_code_copy PCRE2_SUFFIX(pcre2_code_copy_)
+#define pcre2_code_copy_with_tables PCRE2_SUFFIX(pcre2_code_copy_with_tables_)
+#define pcre2_code_free PCRE2_SUFFIX(pcre2_code_free_)
+#define pcre2_compile PCRE2_SUFFIX(pcre2_compile_)
+#define pcre2_compile_context_copy PCRE2_SUFFIX(pcre2_compile_context_copy_)
+#define pcre2_compile_context_create PCRE2_SUFFIX(pcre2_compile_context_create_)
+#define pcre2_compile_context_free PCRE2_SUFFIX(pcre2_compile_context_free_)
+#define pcre2_config PCRE2_SUFFIX(pcre2_config_)
+#define pcre2_convert_context_copy PCRE2_SUFFIX(pcre2_convert_context_copy_)
+#define pcre2_convert_context_create PCRE2_SUFFIX(pcre2_convert_context_create_)
+#define pcre2_convert_context_free PCRE2_SUFFIX(pcre2_convert_context_free_)
+#define pcre2_converted_pattern_free PCRE2_SUFFIX(pcre2_converted_pattern_free_)
+#define pcre2_dfa_match PCRE2_SUFFIX(pcre2_dfa_match_)
+#define pcre2_general_context_copy PCRE2_SUFFIX(pcre2_general_context_copy_)
+#define pcre2_general_context_create PCRE2_SUFFIX(pcre2_general_context_create_)
+#define pcre2_general_context_free PCRE2_SUFFIX(pcre2_general_context_free_)
+#define pcre2_get_error_message PCRE2_SUFFIX(pcre2_get_error_message_)
+#define pcre2_get_mark PCRE2_SUFFIX(pcre2_get_mark_)
+#define pcre2_get_match_data_size PCRE2_SUFFIX(pcre2_get_match_data_size_)
+#define pcre2_get_ovector_pointer PCRE2_SUFFIX(pcre2_get_ovector_pointer_)
+#define pcre2_get_ovector_count PCRE2_SUFFIX(pcre2_get_ovector_count_)
+#define pcre2_get_startchar PCRE2_SUFFIX(pcre2_get_startchar_)
+#define pcre2_jit_compile PCRE2_SUFFIX(pcre2_jit_compile_)
+#define pcre2_jit_match PCRE2_SUFFIX(pcre2_jit_match_)
+#define pcre2_jit_free_unused_memory PCRE2_SUFFIX(pcre2_jit_free_unused_memory_)
+#define pcre2_jit_stack_assign PCRE2_SUFFIX(pcre2_jit_stack_assign_)
+#define pcre2_jit_stack_create PCRE2_SUFFIX(pcre2_jit_stack_create_)
+#define pcre2_jit_stack_free PCRE2_SUFFIX(pcre2_jit_stack_free_)
+#define pcre2_maketables PCRE2_SUFFIX(pcre2_maketables_)
+#define pcre2_maketables_free PCRE2_SUFFIX(pcre2_maketables_free_)
+#define pcre2_match PCRE2_SUFFIX(pcre2_match_)
+#define pcre2_match_context_copy PCRE2_SUFFIX(pcre2_match_context_copy_)
+#define pcre2_match_context_create PCRE2_SUFFIX(pcre2_match_context_create_)
+#define pcre2_match_context_free PCRE2_SUFFIX(pcre2_match_context_free_)
+#define pcre2_match_data_create PCRE2_SUFFIX(pcre2_match_data_create_)
+#define pcre2_match_data_create_from_pattern PCRE2_SUFFIX(pcre2_match_data_create_from_pattern_)
+#define pcre2_match_data_free PCRE2_SUFFIX(pcre2_match_data_free_)
+#define pcre2_pattern_convert PCRE2_SUFFIX(pcre2_pattern_convert_)
+#define pcre2_pattern_info PCRE2_SUFFIX(pcre2_pattern_info_)
+#define pcre2_serialize_decode PCRE2_SUFFIX(pcre2_serialize_decode_)
+#define pcre2_serialize_encode PCRE2_SUFFIX(pcre2_serialize_encode_)
+#define pcre2_serialize_free PCRE2_SUFFIX(pcre2_serialize_free_)
+#define pcre2_serialize_get_number_of_codes PCRE2_SUFFIX(pcre2_serialize_get_number_of_codes_)
+#define pcre2_set_bsr PCRE2_SUFFIX(pcre2_set_bsr_)
+#define pcre2_set_callout PCRE2_SUFFIX(pcre2_set_callout_)
+#define pcre2_set_character_tables PCRE2_SUFFIX(pcre2_set_character_tables_)
+#define pcre2_set_compile_extra_options PCRE2_SUFFIX(pcre2_set_compile_extra_options_)
+#define pcre2_set_compile_recursion_guard PCRE2_SUFFIX(pcre2_set_compile_recursion_guard_)
+#define pcre2_set_depth_limit PCRE2_SUFFIX(pcre2_set_depth_limit_)
+#define pcre2_set_glob_escape PCRE2_SUFFIX(pcre2_set_glob_escape_)
+#define pcre2_set_glob_separator PCRE2_SUFFIX(pcre2_set_glob_separator_)
+#define pcre2_set_heap_limit PCRE2_SUFFIX(pcre2_set_heap_limit_)
+#define pcre2_set_match_limit PCRE2_SUFFIX(pcre2_set_match_limit_)
+#define pcre2_set_max_pattern_length PCRE2_SUFFIX(pcre2_set_max_pattern_length_)
+#define pcre2_set_newline PCRE2_SUFFIX(pcre2_set_newline_)
+#define pcre2_set_parens_nest_limit PCRE2_SUFFIX(pcre2_set_parens_nest_limit_)
+#define pcre2_set_offset_limit PCRE2_SUFFIX(pcre2_set_offset_limit_)
+#define pcre2_set_substitute_callout PCRE2_SUFFIX(pcre2_set_substitute_callout_)
+#define pcre2_substitute PCRE2_SUFFIX(pcre2_substitute_)
+#define pcre2_substring_copy_byname PCRE2_SUFFIX(pcre2_substring_copy_byname_)
+#define pcre2_substring_copy_bynumber PCRE2_SUFFIX(pcre2_substring_copy_bynumber_)
+#define pcre2_substring_free PCRE2_SUFFIX(pcre2_substring_free_)
+#define pcre2_substring_get_byname PCRE2_SUFFIX(pcre2_substring_get_byname_)
+#define pcre2_substring_get_bynumber PCRE2_SUFFIX(pcre2_substring_get_bynumber_)
+#define pcre2_substring_length_byname PCRE2_SUFFIX(pcre2_substring_length_byname_)
+#define pcre2_substring_length_bynumber PCRE2_SUFFIX(pcre2_substring_length_bynumber_)
+#define pcre2_substring_list_get PCRE2_SUFFIX(pcre2_substring_list_get_)
+#define pcre2_substring_list_free PCRE2_SUFFIX(pcre2_substring_list_free_)
+#define pcre2_substring_nametable_scan PCRE2_SUFFIX(pcre2_substring_nametable_scan_)
+#define pcre2_substring_number_from_name PCRE2_SUFFIX(pcre2_substring_number_from_name_)
+
+/* Keep this old function name for backwards compatibility */
+#define pcre2_set_recursion_limit PCRE2_SUFFIX(pcre2_set_recursion_limit_)
+
+/* Keep this obsolete function for backwards compatibility: it is now a noop. */
+#define pcre2_set_recursion_memory_management PCRE2_SUFFIX(pcre2_set_recursion_memory_management_)
+
+/* Now generate all three sets of width-specific structures and function
+prototypes. */
+
+#define PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS \
+PCRE2_TYPES_LIST \
+PCRE2_STRUCTURE_LIST \
+PCRE2_GENERAL_INFO_FUNCTIONS \
+PCRE2_GENERAL_CONTEXT_FUNCTIONS \
+PCRE2_COMPILE_CONTEXT_FUNCTIONS \
+PCRE2_CONVERT_CONTEXT_FUNCTIONS \
+PCRE2_CONVERT_FUNCTIONS \
+PCRE2_MATCH_CONTEXT_FUNCTIONS \
+PCRE2_COMPILE_FUNCTIONS \
+PCRE2_PATTERN_INFO_FUNCTIONS \
+PCRE2_MATCH_FUNCTIONS \
+PCRE2_SUBSTRING_FUNCTIONS \
+PCRE2_SERIALIZE_FUNCTIONS \
+PCRE2_SUBSTITUTE_FUNCTION \
+PCRE2_JIT_FUNCTIONS \
+PCRE2_OTHER_FUNCTIONS
+
+#define PCRE2_LOCAL_WIDTH 8
+PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS
+#undef PCRE2_LOCAL_WIDTH
+
+#define PCRE2_LOCAL_WIDTH 16
+PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS
+#undef PCRE2_LOCAL_WIDTH
+
+#define PCRE2_LOCAL_WIDTH 32
+PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS
+#undef PCRE2_LOCAL_WIDTH
+
+/* Undefine the list macros; they are no longer needed. */
+
+#undef PCRE2_TYPES_LIST
+#undef PCRE2_STRUCTURE_LIST
+#undef PCRE2_GENERAL_INFO_FUNCTIONS
+#undef PCRE2_GENERAL_CONTEXT_FUNCTIONS
+#undef PCRE2_COMPILE_CONTEXT_FUNCTIONS
+#undef PCRE2_CONVERT_CONTEXT_FUNCTIONS
+#undef PCRE2_MATCH_CONTEXT_FUNCTIONS
+#undef PCRE2_COMPILE_FUNCTIONS
+#undef PCRE2_PATTERN_INFO_FUNCTIONS
+#undef PCRE2_MATCH_FUNCTIONS
+#undef PCRE2_SUBSTRING_FUNCTIONS
+#undef PCRE2_SERIALIZE_FUNCTIONS
+#undef PCRE2_SUBSTITUTE_FUNCTION
+#undef PCRE2_JIT_FUNCTIONS
+#undef PCRE2_OTHER_FUNCTIONS
+#undef PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS
+
+/* PCRE2_CODE_UNIT_WIDTH must be defined. If it is 8, 16, or 32, redefine
+PCRE2_SUFFIX to use it. If it is 0, undefine the other macros and make
+PCRE2_SUFFIX a no-op. Otherwise, generate an error. */
+
+#undef PCRE2_SUFFIX
+#ifndef PCRE2_CODE_UNIT_WIDTH
+#error PCRE2_CODE_UNIT_WIDTH must be defined before including pcre2.h.
+#error Use 8, 16, or 32; or 0 for a multi-width application.
+#else /* PCRE2_CODE_UNIT_WIDTH is defined */
+#if PCRE2_CODE_UNIT_WIDTH == 8 || \
+ PCRE2_CODE_UNIT_WIDTH == 16 || \
+ PCRE2_CODE_UNIT_WIDTH == 32
+#define PCRE2_SUFFIX(a) PCRE2_GLUE(a, PCRE2_CODE_UNIT_WIDTH)
+#elif PCRE2_CODE_UNIT_WIDTH == 0
+#undef PCRE2_JOIN
+#undef PCRE2_GLUE
+#define PCRE2_SUFFIX(a) a
+#else
+#error PCRE2_CODE_UNIT_WIDTH must be 0, 8, 16, or 32.
+#endif
+#endif /* PCRE2_CODE_UNIT_WIDTH is defined */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* PCRE2_H_IDEMPOTENT_GUARD */
+
+/* End of pcre2.h */
diff --git a/contrib/libs/pcre2/src/pcre2_auto_possess.c b/contrib/libs/pcre2/src/pcre2_auto_possess.c
new file mode 100644
index 00000000000..e8338690cad
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_auto_possess.c
@@ -0,0 +1,1365 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains functions that scan a compiled pattern and change
+repeats into possessive repeats where possible. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+
+#include "pcre2_internal.h"
+
+
+/*************************************************
+* Tables for auto-possessification *
+*************************************************/
+
+/* This table is used to check whether auto-possessification is possible
+between adjacent character-type opcodes. The left-hand (repeated) opcode is
+used to select the row, and the right-hand opcode is use to select the column.
+A value of 1 means that auto-possessification is OK. For example, the second
+value in the first row means that \D+\d can be turned into \D++\d.
+
+The Unicode property types (\P and \p) have to be present to fill out the table
+because of what their opcode values are, but the table values should always be
+zero because property types are handled separately in the code. The last four
+columns apply to items that cannot be repeated, so there is no need to have
+rows for them. Note that OP_DIGIT etc. are generated only when PCRE_UCP is
+*not* set. When it is set, \d etc. are converted into OP_(NOT_)PROP codes. */
+
+#define APTROWS (LAST_AUTOTAB_LEFT_OP - FIRST_AUTOTAB_OP + 1)
+#define APTCOLS (LAST_AUTOTAB_RIGHT_OP - FIRST_AUTOTAB_OP + 1)
+
+static const uint8_t autoposstab[APTROWS][APTCOLS] = {
+/* \D \d \S \s \W \w . .+ \C \P \p \R \H \h \V \v \X \Z \z $ $M */
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, /* \D */
+ { 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1 }, /* \d */
+ { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1 }, /* \S */
+ { 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, /* \s */
+ { 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, /* \W */
+ { 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1 }, /* \w */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, /* . */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, /* .+ */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, /* \C */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* \P */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* \p */
+ { 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 }, /* \R */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 }, /* \H */
+ { 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0 }, /* \h */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0 }, /* \V */
+ { 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0 }, /* \v */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 } /* \X */
+};
+
+#ifdef SUPPORT_UNICODE
+/* This table is used to check whether auto-possessification is possible
+between adjacent Unicode property opcodes (OP_PROP and OP_NOTPROP). The
+left-hand (repeated) opcode is used to select the row, and the right-hand
+opcode is used to select the column. The values are as follows:
+
+ 0 Always return FALSE (never auto-possessify)
+ 1 Character groups are distinct (possessify if both are OP_PROP)
+ 2 Check character categories in the same group (general or particular)
+ 3 TRUE if the two opcodes are not the same (PROP vs NOTPROP)
+
+ 4 Check left general category vs right particular category
+ 5 Check right general category vs left particular category
+
+ 6 Left alphanum vs right general category
+ 7 Left space vs right general category
+ 8 Left word vs right general category
+
+ 9 Right alphanum vs left general category
+ 10 Right space vs left general category
+ 11 Right word vs left general category
+
+ 12 Left alphanum vs right particular category
+ 13 Left space vs right particular category
+ 14 Left word vs right particular category
+
+ 15 Right alphanum vs left particular category
+ 16 Right space vs left particular category
+ 17 Right word vs left particular category
+*/
+
+static const uint8_t propposstab[PT_TABSIZE][PT_TABSIZE] = {
+/* ANY LAMP GC PC SC SCX ALNUM SPACE PXSPACE WORD CLIST UCNC BIDICL BOOL */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* PT_ANY */
+ { 0, 3, 0, 0, 0, 0, 3, 1, 1, 0, 0, 0, 0, 0 }, /* PT_LAMP */
+ { 0, 0, 2, 4, 0, 0, 9, 10, 10, 11, 0, 0, 0, 0 }, /* PT_GC */
+ { 0, 0, 5, 2, 0, 0, 15, 16, 16, 17, 0, 0, 0, 0 }, /* PT_PC */
+ { 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, /* PT_SC */
+ { 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, /* PT_SCX */
+ { 0, 3, 6, 12, 0, 0, 3, 1, 1, 0, 0, 0, 0, 0 }, /* PT_ALNUM */
+ { 0, 1, 7, 13, 0, 0, 1, 3, 3, 1, 0, 0, 0, 0 }, /* PT_SPACE */
+ { 0, 1, 7, 13, 0, 0, 1, 3, 3, 1, 0, 0, 0, 0 }, /* PT_PXSPACE */
+ { 0, 0, 8, 14, 0, 0, 0, 1, 1, 3, 0, 0, 0, 0 }, /* PT_WORD */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* PT_CLIST */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0 }, /* PT_UCNC */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* PT_BIDICL */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } /* PT_BOOL */
+};
+
+/* This table is used to check whether auto-possessification is possible
+between adjacent Unicode property opcodes (OP_PROP and OP_NOTPROP) when one
+specifies a general category and the other specifies a particular category. The
+row is selected by the general category and the column by the particular
+category. The value is 1 if the particular category is not part of the general
+category. */
+
+static const uint8_t catposstab[7][30] = {
+/* Cc Cf Cn Co Cs Ll Lm Lo Lt Lu Mc Me Mn Nd Nl No Pc Pd Pe Pf Pi Po Ps Sc Sk Sm So Zl Zp Zs */
+ { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* C */
+ { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* L */
+ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* M */
+ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* N */
+ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1 }, /* P */
+ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1 }, /* S */
+ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 } /* Z */
+};
+
+/* This table is used when checking ALNUM, (PX)SPACE, SPACE, and WORD against
+a general or particular category. The properties in each row are those
+that apply to the character set in question. Duplication means that a little
+unnecessary work is done when checking, but this keeps things much simpler
+because they can all use the same code. For more details see the comment where
+this table is used.
+
+Note: SPACE and PXSPACE used to be different because Perl excluded VT from
+"space", but from Perl 5.18 it's included, so both categories are treated the
+same here. */
+
+static const uint8_t posspropstab[3][4] = {
+ { ucp_L, ucp_N, ucp_N, ucp_Nl }, /* ALNUM, 3rd and 4th values redundant */
+ { ucp_Z, ucp_Z, ucp_C, ucp_Cc }, /* SPACE and PXSPACE, 2nd value redundant */
+ { ucp_L, ucp_N, ucp_P, ucp_Po } /* WORD */
+};
+#endif /* SUPPORT_UNICODE */
+
+
+
+#ifdef SUPPORT_UNICODE
+/*************************************************
+* Check a character and a property *
+*************************************************/
+
+/* This function is called by compare_opcodes() when a property item is
+adjacent to a fixed character.
+
+Arguments:
+ c the character
+ ptype the property type
+ pdata the data for the type
+ negated TRUE if it's a negated property (\P or \p{^)
+
+Returns: TRUE if auto-possessifying is OK
+*/
+
+static BOOL
+check_char_prop(uint32_t c, unsigned int ptype, unsigned int pdata,
+ BOOL negated)
+{
+BOOL ok;
+const uint32_t *p;
+const ucd_record *prop = GET_UCD(c);
+
+switch(ptype)
+ {
+ case PT_LAMP:
+ return (prop->chartype == ucp_Lu ||
+ prop->chartype == ucp_Ll ||
+ prop->chartype == ucp_Lt) == negated;
+
+ case PT_GC:
+ return (pdata == PRIV(ucp_gentype)[prop->chartype]) == negated;
+
+ case PT_PC:
+ return (pdata == prop->chartype) == negated;
+
+ case PT_SC:
+ return (pdata == prop->script) == negated;
+
+ case PT_SCX:
+ ok = (pdata == prop->script
+ || MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), pdata) != 0);
+ return ok == negated;
+
+ /* These are specials */
+
+ case PT_ALNUM:
+ return (PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N) == negated;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included, which
+ means that Perl space and POSIX space are now identical. PCRE was changed
+ at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ switch(c)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ return negated;
+
+ default:
+ return (PRIV(ucp_gentype)[prop->chartype] == ucp_Z) == negated;
+ }
+ break; /* Control never reaches here */
+
+ case PT_WORD:
+ return (PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N ||
+ c == CHAR_UNDERSCORE) == negated;
+
+ case PT_CLIST:
+ p = PRIV(ucd_caseless_sets) + prop->caseset;
+ for (;;)
+ {
+ if (c < *p) return !negated;
+ if (c == *p++) return negated;
+ }
+ break; /* Control never reaches here */
+
+ /* Haven't yet thought these through. */
+
+ case PT_BIDICL:
+ return FALSE;
+
+ case PT_BOOL:
+ return FALSE;
+ }
+
+return FALSE;
+}
+#endif /* SUPPORT_UNICODE */
+
+
+
+/*************************************************
+* Base opcode of repeated opcodes *
+*************************************************/
+
+/* Returns the base opcode for repeated single character type opcodes. If the
+opcode is not a repeated character type, it returns with the original value.
+
+Arguments: c opcode
+Returns: base opcode for the type
+*/
+
+static PCRE2_UCHAR
+get_repeat_base(PCRE2_UCHAR c)
+{
+return (c > OP_TYPEPOSUPTO)? c :
+ (c >= OP_TYPESTAR)? OP_TYPESTAR :
+ (c >= OP_NOTSTARI)? OP_NOTSTARI :
+ (c >= OP_NOTSTAR)? OP_NOTSTAR :
+ (c >= OP_STARI)? OP_STARI :
+ OP_STAR;
+}
+
+
+/*************************************************
+* Fill the character property list *
+*************************************************/
+
+/* Checks whether the code points to an opcode that can take part in auto-
+possessification, and if so, fills a list with its properties.
+
+Arguments:
+ code points to start of expression
+ utf TRUE if in UTF mode
+ ucp TRUE if in UCP mode
+ fcc points to the case-flipping table
+ list points to output list
+ list[0] will be filled with the opcode
+ list[1] will be non-zero if this opcode
+ can match an empty character string
+ list[2..7] depends on the opcode
+
+Returns: points to the start of the next opcode if *code is accepted
+ NULL if *code is not accepted
+*/
+
+static PCRE2_SPTR
+get_chr_property_list(PCRE2_SPTR code, BOOL utf, BOOL ucp, const uint8_t *fcc,
+ uint32_t *list)
+{
+PCRE2_UCHAR c = *code;
+PCRE2_UCHAR base;
+PCRE2_SPTR end;
+uint32_t chr;
+
+#ifdef SUPPORT_UNICODE
+uint32_t *clist_dest;
+const uint32_t *clist_src;
+#else
+(void)utf; /* Suppress "unused parameter" compiler warnings */
+(void)ucp;
+#endif
+
+list[0] = c;
+list[1] = FALSE;
+code++;
+
+if (c >= OP_STAR && c <= OP_TYPEPOSUPTO)
+ {
+ base = get_repeat_base(c);
+ c -= (base - OP_STAR);
+
+ if (c == OP_UPTO || c == OP_MINUPTO || c == OP_EXACT || c == OP_POSUPTO)
+ code += IMM2_SIZE;
+
+ list[1] = (c != OP_PLUS && c != OP_MINPLUS && c != OP_EXACT &&
+ c != OP_POSPLUS);
+
+ switch(base)
+ {
+ case OP_STAR:
+ list[0] = OP_CHAR;
+ break;
+
+ case OP_STARI:
+ list[0] = OP_CHARI;
+ break;
+
+ case OP_NOTSTAR:
+ list[0] = OP_NOT;
+ break;
+
+ case OP_NOTSTARI:
+ list[0] = OP_NOTI;
+ break;
+
+ case OP_TYPESTAR:
+ list[0] = *code;
+ code++;
+ break;
+ }
+ c = list[0];
+ }
+
+switch(c)
+ {
+ case OP_NOT_DIGIT:
+ case OP_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ case OP_WORDCHAR:
+ case OP_ANY:
+ case OP_ALLANY:
+ case OP_ANYNL:
+ case OP_NOT_HSPACE:
+ case OP_HSPACE:
+ case OP_NOT_VSPACE:
+ case OP_VSPACE:
+ case OP_EXTUNI:
+ case OP_EODN:
+ case OP_EOD:
+ case OP_DOLL:
+ case OP_DOLLM:
+ return code;
+
+ case OP_CHAR:
+ case OP_NOT:
+ GETCHARINCTEST(chr, code);
+ list[2] = chr;
+ list[3] = NOTACHAR;
+ return code;
+
+ case OP_CHARI:
+ case OP_NOTI:
+ list[0] = (c == OP_CHARI) ? OP_CHAR : OP_NOT;
+ GETCHARINCTEST(chr, code);
+ list[2] = chr;
+
+#ifdef SUPPORT_UNICODE
+ if (chr < 128 || (chr < 256 && !utf && !ucp))
+ list[3] = fcc[chr];
+ else
+ list[3] = UCD_OTHERCASE(chr);
+#elif defined SUPPORT_WIDE_CHARS
+ list[3] = (chr < 256) ? fcc[chr] : chr;
+#else
+ list[3] = fcc[chr];
+#endif
+
+ /* The othercase might be the same value. */
+
+ if (chr == list[3])
+ list[3] = NOTACHAR;
+ else
+ list[4] = NOTACHAR;
+ return code;
+
+#ifdef SUPPORT_UNICODE
+ case OP_PROP:
+ case OP_NOTPROP:
+ if (code[0] != PT_CLIST)
+ {
+ list[2] = code[0];
+ list[3] = code[1];
+ return code + 2;
+ }
+
+ /* Convert only if we have enough space. */
+
+ clist_src = PRIV(ucd_caseless_sets) + code[1];
+ clist_dest = list + 2;
+ code += 2;
+
+ do {
+ if (clist_dest >= list + 8)
+ {
+ /* Early return if there is not enough space. This should never
+ happen, since all clists are shorter than 5 character now. */
+ list[2] = code[0];
+ list[3] = code[1];
+ return code;
+ }
+ *clist_dest++ = *clist_src;
+ }
+ while(*clist_src++ != NOTACHAR);
+
+ /* All characters are stored. The terminating NOTACHAR is copied from the
+ clist itself. */
+
+ list[0] = (c == OP_PROP) ? OP_CHAR : OP_NOT;
+ return code;
+#endif
+
+ case OP_NCLASS:
+ case OP_CLASS:
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+ if (c == OP_XCLASS)
+ end = code + GET(code, 0) - 1;
+ else
+#endif
+ end = code + 32 / sizeof(PCRE2_UCHAR);
+
+ switch(*end)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSSTAR:
+ case OP_CRPOSQUERY:
+ list[1] = TRUE;
+ end++;
+ break;
+
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRPOSPLUS:
+ end++;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ list[1] = (GET2(end, 1) == 0);
+ end += 1 + 2 * IMM2_SIZE;
+ break;
+ }
+ list[2] = (uint32_t)(end - code);
+ return end;
+ }
+
+return NULL; /* Opcode not accepted */
+}
+
+
+
+/*************************************************
+* Scan further character sets for match *
+*************************************************/
+
+/* Checks whether the base and the current opcode have a common character, in
+which case the base cannot be possessified.
+
+Arguments:
+ code points to the byte code
+ utf TRUE in UTF mode
+ ucp TRUE in UCP mode
+ cb compile data block
+ base_list the data list of the base opcode
+ base_end the end of the base opcode
+ rec_limit points to recursion depth counter
+
+Returns: TRUE if the auto-possessification is possible
+*/
+
+static BOOL
+compare_opcodes(PCRE2_SPTR code, BOOL utf, BOOL ucp, const compile_block *cb,
+ const uint32_t *base_list, PCRE2_SPTR base_end, int *rec_limit)
+{
+PCRE2_UCHAR c;
+uint32_t list[8];
+const uint32_t *chr_ptr;
+const uint32_t *ochr_ptr;
+const uint32_t *list_ptr;
+PCRE2_SPTR next_code;
+#ifdef SUPPORT_WIDE_CHARS
+PCRE2_SPTR xclass_flags;
+#endif
+const uint8_t *class_bitset;
+const uint8_t *set1, *set2, *set_end;
+uint32_t chr;
+BOOL accepted, invert_bits;
+BOOL entered_a_group = FALSE;
+
+if (--(*rec_limit) <= 0) return FALSE; /* Recursion has gone too deep */
+
+/* Note: the base_list[1] contains whether the current opcode has a greedy
+(represented by a non-zero value) quantifier. This is a different from
+other character type lists, which store here that the character iterator
+matches to an empty string (also represented by a non-zero value). */
+
+for(;;)
+ {
+ /* All operations move the code pointer forward.
+ Therefore infinite recursions are not possible. */
+
+ c = *code;
+
+ /* Skip over callouts */
+
+ if (c == OP_CALLOUT)
+ {
+ code += PRIV(OP_lengths)[c];
+ continue;
+ }
+
+ if (c == OP_CALLOUT_STR)
+ {
+ code += GET(code, 1 + 2*LINK_SIZE);
+ continue;
+ }
+
+ /* At the end of a branch, skip to the end of the group. */
+
+ if (c == OP_ALT)
+ {
+ do code += GET(code, 1); while (*code == OP_ALT);
+ c = *code;
+ }
+
+ /* Inspect the next opcode. */
+
+ switch(c)
+ {
+ /* We can always possessify a greedy iterator at the end of the pattern,
+ which is reached after skipping over the final OP_KET. A non-greedy
+ iterator must never be possessified. */
+
+ case OP_END:
+ return base_list[1] != 0;
+
+ /* When an iterator is at the end of certain kinds of group we can inspect
+ what follows the group by skipping over the closing ket. Note that this
+ does not apply to OP_KETRMAX or OP_KETRMIN because what follows any given
+ iteration is variable (could be another iteration or could be the next
+ item). As these two opcodes are not listed in the next switch, they will
+ end up as the next code to inspect, and return FALSE by virtue of being
+ unsupported. */
+
+ case OP_KET:
+ case OP_KETRPOS:
+ /* The non-greedy case cannot be converted to a possessive form. */
+
+ if (base_list[1] == 0) return FALSE;
+
+ /* If the bracket is capturing it might be referenced by an OP_RECURSE
+ so its last iterator can never be possessified if the pattern contains
+ recursions. (This could be improved by keeping a list of group numbers that
+ are called by recursion.) */
+
+ switch(*(code - GET(code, 1)))
+ {
+ case OP_CBRA:
+ case OP_SCBRA:
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ if (cb->had_recurse) return FALSE;
+ break;
+
+ /* A script run might have to backtrack if the iterated item can match
+ characters from more than one script. So give up unless repeating an
+ explicit character. */
+
+ case OP_SCRIPT_RUN:
+ if (base_list[0] != OP_CHAR && base_list[0] != OP_CHARI)
+ return FALSE;
+ break;
+
+ /* Atomic sub-patterns and assertions can always auto-possessify their
+ last iterator. However, if the group was entered as a result of checking
+ a previous iterator, this is not possible. */
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ONCE:
+ return !entered_a_group;
+
+ /* Non-atomic assertions - don't possessify last iterator. This needs
+ more thought. */
+
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ return FALSE;
+ }
+
+ /* Skip over the bracket and inspect what comes next. */
+
+ code += PRIV(OP_lengths)[c];
+ continue;
+
+ /* Handle cases where the next item is a group. */
+
+ case OP_ONCE:
+ case OP_BRA:
+ case OP_CBRA:
+ next_code = code + GET(code, 1);
+ code += PRIV(OP_lengths)[c];
+
+ /* Check each branch. We have to recurse a level for all but the last
+ branch. */
+
+ while (*next_code == OP_ALT)
+ {
+ if (!compare_opcodes(code, utf, ucp, cb, base_list, base_end, rec_limit))
+ return FALSE;
+ code = next_code + 1 + LINK_SIZE;
+ next_code += GET(next_code, 1);
+ }
+
+ entered_a_group = TRUE;
+ continue;
+
+ case OP_BRAZERO:
+ case OP_BRAMINZERO:
+
+ next_code = code + 1;
+ if (*next_code != OP_BRA && *next_code != OP_CBRA &&
+ *next_code != OP_ONCE) return FALSE;
+
+ do next_code += GET(next_code, 1); while (*next_code == OP_ALT);
+
+ /* The bracket content will be checked by the OP_BRA/OP_CBRA case above. */
+
+ next_code += 1 + LINK_SIZE;
+ if (!compare_opcodes(next_code, utf, ucp, cb, base_list, base_end,
+ rec_limit))
+ return FALSE;
+
+ code += PRIV(OP_lengths)[c];
+ continue;
+
+ /* The next opcode does not need special handling; fall through and use it
+ to see if the base can be possessified. */
+
+ default:
+ break;
+ }
+
+ /* We now have the next appropriate opcode to compare with the base. Check
+ for a supported opcode, and load its properties. */
+
+ code = get_chr_property_list(code, utf, ucp, cb->fcc, list);
+ if (code == NULL) return FALSE; /* Unsupported */
+
+ /* If either opcode is a small character list, set pointers for comparing
+ characters from that list with another list, or with a property. */
+
+ if (base_list[0] == OP_CHAR)
+ {
+ chr_ptr = base_list + 2;
+ list_ptr = list;
+ }
+ else if (list[0] == OP_CHAR)
+ {
+ chr_ptr = list + 2;
+ list_ptr = base_list;
+ }
+
+ /* Character bitsets can also be compared to certain opcodes. */
+
+ else if (base_list[0] == OP_CLASS || list[0] == OP_CLASS
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ /* In 8 bit, non-UTF mode, OP_CLASS and OP_NCLASS are the same. */
+ || (!utf && (base_list[0] == OP_NCLASS || list[0] == OP_NCLASS))
+#endif
+ )
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (base_list[0] == OP_CLASS || (!utf && base_list[0] == OP_NCLASS))
+#else
+ if (base_list[0] == OP_CLASS)
+#endif
+ {
+ set1 = (uint8_t *)(base_end - base_list[2]);
+ list_ptr = list;
+ }
+ else
+ {
+ set1 = (uint8_t *)(code - list[2]);
+ list_ptr = base_list;
+ }
+
+ invert_bits = FALSE;
+ switch(list_ptr[0])
+ {
+ case OP_CLASS:
+ case OP_NCLASS:
+ set2 = (uint8_t *)
+ ((list_ptr == list ? code : base_end) - list_ptr[2]);
+ break;
+
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+ xclass_flags = (list_ptr == list ? code : base_end) - list_ptr[2] + LINK_SIZE;
+ if ((*xclass_flags & XCL_HASPROP) != 0) return FALSE;
+ if ((*xclass_flags & XCL_MAP) == 0)
+ {
+ /* No bits are set for characters < 256. */
+ if (list[1] == 0) return (*xclass_flags & XCL_NOT) == 0;
+ /* Might be an empty repeat. */
+ continue;
+ }
+ set2 = (uint8_t *)(xclass_flags + 1);
+ break;
+#endif
+
+ case OP_NOT_DIGIT:
+ invert_bits = TRUE;
+ /* Fall through */
+ case OP_DIGIT:
+ set2 = (uint8_t *)(cb->cbits + cbit_digit);
+ break;
+
+ case OP_NOT_WHITESPACE:
+ invert_bits = TRUE;
+ /* Fall through */
+ case OP_WHITESPACE:
+ set2 = (uint8_t *)(cb->cbits + cbit_space);
+ break;
+
+ case OP_NOT_WORDCHAR:
+ invert_bits = TRUE;
+ /* Fall through */
+ case OP_WORDCHAR:
+ set2 = (uint8_t *)(cb->cbits + cbit_word);
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ /* Because the bit sets are unaligned bytes, we need to perform byte
+ comparison here. */
+
+ set_end = set1 + 32;
+ if (invert_bits)
+ {
+ do
+ {
+ if ((*set1++ & ~(*set2++)) != 0) return FALSE;
+ }
+ while (set1 < set_end);
+ }
+ else
+ {
+ do
+ {
+ if ((*set1++ & *set2++) != 0) return FALSE;
+ }
+ while (set1 < set_end);
+ }
+
+ if (list[1] == 0) return TRUE;
+ /* Might be an empty repeat. */
+ continue;
+ }
+
+ /* Some property combinations also acceptable. Unicode property opcodes are
+ processed specially; the rest can be handled with a lookup table. */
+
+ else
+ {
+ uint32_t leftop, rightop;
+
+ leftop = base_list[0];
+ rightop = list[0];
+
+#ifdef SUPPORT_UNICODE
+ accepted = FALSE; /* Always set in non-unicode case. */
+ if (leftop == OP_PROP || leftop == OP_NOTPROP)
+ {
+ if (rightop == OP_EOD)
+ accepted = TRUE;
+ else if (rightop == OP_PROP || rightop == OP_NOTPROP)
+ {
+ int n;
+ const uint8_t *p;
+ BOOL same = leftop == rightop;
+ BOOL lisprop = leftop == OP_PROP;
+ BOOL risprop = rightop == OP_PROP;
+ BOOL bothprop = lisprop && risprop;
+
+ /* There's a table that specifies how each combination is to be
+ processed:
+ 0 Always return FALSE (never auto-possessify)
+ 1 Character groups are distinct (possessify if both are OP_PROP)
+ 2 Check character categories in the same group (general or particular)
+ 3 Return TRUE if the two opcodes are not the same
+ ... see comments below
+ */
+
+ n = propposstab[base_list[2]][list[2]];
+ switch(n)
+ {
+ case 0: break;
+ case 1: accepted = bothprop; break;
+ case 2: accepted = (base_list[3] == list[3]) != same; break;
+ case 3: accepted = !same; break;
+
+ case 4: /* Left general category, right particular category */
+ accepted = risprop && catposstab[base_list[3]][list[3]] == same;
+ break;
+
+ case 5: /* Right general category, left particular category */
+ accepted = lisprop && catposstab[list[3]][base_list[3]] == same;
+ break;
+
+ /* This code is logically tricky. Think hard before fiddling with it.
+ The posspropstab table has four entries per row. Each row relates to
+ one of PCRE's special properties such as ALNUM or SPACE or WORD.
+ Only WORD actually needs all four entries, but using repeats for the
+ others means they can all use the same code below.
+
+ The first two entries in each row are Unicode general categories, and
+ apply always, because all the characters they include are part of the
+ PCRE character set. The third and fourth entries are a general and a
+ particular category, respectively, that include one or more relevant
+ characters. One or the other is used, depending on whether the check
+ is for a general or a particular category. However, in both cases the
+ category contains more characters than the specials that are defined
+ for the property being tested against. Therefore, it cannot be used
+ in a NOTPROP case.
+
+ Example: the row for WORD contains ucp_L, ucp_N, ucp_P, ucp_Po.
+ Underscore is covered by ucp_P or ucp_Po. */
+
+ case 6: /* Left alphanum vs right general category */
+ case 7: /* Left space vs right general category */
+ case 8: /* Left word vs right general category */
+ p = posspropstab[n-6];
+ accepted = risprop && lisprop ==
+ (list[3] != p[0] &&
+ list[3] != p[1] &&
+ (list[3] != p[2] || !lisprop));
+ break;
+
+ case 9: /* Right alphanum vs left general category */
+ case 10: /* Right space vs left general category */
+ case 11: /* Right word vs left general category */
+ p = posspropstab[n-9];
+ accepted = lisprop && risprop ==
+ (base_list[3] != p[0] &&
+ base_list[3] != p[1] &&
+ (base_list[3] != p[2] || !risprop));
+ break;
+
+ case 12: /* Left alphanum vs right particular category */
+ case 13: /* Left space vs right particular category */
+ case 14: /* Left word vs right particular category */
+ p = posspropstab[n-12];
+ accepted = risprop && lisprop ==
+ (catposstab[p[0]][list[3]] &&
+ catposstab[p[1]][list[3]] &&
+ (list[3] != p[3] || !lisprop));
+ break;
+
+ case 15: /* Right alphanum vs left particular category */
+ case 16: /* Right space vs left particular category */
+ case 17: /* Right word vs left particular category */
+ p = posspropstab[n-15];
+ accepted = lisprop && risprop ==
+ (catposstab[p[0]][base_list[3]] &&
+ catposstab[p[1]][base_list[3]] &&
+ (base_list[3] != p[3] || !risprop));
+ break;
+ }
+ }
+ }
+
+ else
+#endif /* SUPPORT_UNICODE */
+
+ accepted = leftop >= FIRST_AUTOTAB_OP && leftop <= LAST_AUTOTAB_LEFT_OP &&
+ rightop >= FIRST_AUTOTAB_OP && rightop <= LAST_AUTOTAB_RIGHT_OP &&
+ autoposstab[leftop - FIRST_AUTOTAB_OP][rightop - FIRST_AUTOTAB_OP];
+
+ if (!accepted) return FALSE;
+
+ if (list[1] == 0) return TRUE;
+ /* Might be an empty repeat. */
+ continue;
+ }
+
+ /* Control reaches here only if one of the items is a small character list.
+ All characters are checked against the other side. */
+
+ do
+ {
+ chr = *chr_ptr;
+
+ switch(list_ptr[0])
+ {
+ case OP_CHAR:
+ ochr_ptr = list_ptr + 2;
+ do
+ {
+ if (chr == *ochr_ptr) return FALSE;
+ ochr_ptr++;
+ }
+ while(*ochr_ptr != NOTACHAR);
+ break;
+
+ case OP_NOT:
+ ochr_ptr = list_ptr + 2;
+ do
+ {
+ if (chr == *ochr_ptr)
+ break;
+ ochr_ptr++;
+ }
+ while(*ochr_ptr != NOTACHAR);
+ if (*ochr_ptr == NOTACHAR) return FALSE; /* Not found */
+ break;
+
+ /* Note that OP_DIGIT etc. are generated only when PCRE2_UCP is *not*
+ set. When it is set, \d etc. are converted into OP_(NOT_)PROP codes. */
+
+ case OP_DIGIT:
+ if (chr < 256 && (cb->ctypes[chr] & ctype_digit) != 0) return FALSE;
+ break;
+
+ case OP_NOT_DIGIT:
+ if (chr > 255 || (cb->ctypes[chr] & ctype_digit) == 0) return FALSE;
+ break;
+
+ case OP_WHITESPACE:
+ if (chr < 256 && (cb->ctypes[chr] & ctype_space) != 0) return FALSE;
+ break;
+
+ case OP_NOT_WHITESPACE:
+ if (chr > 255 || (cb->ctypes[chr] & ctype_space) == 0) return FALSE;
+ break;
+
+ case OP_WORDCHAR:
+ if (chr < 255 && (cb->ctypes[chr] & ctype_word) != 0) return FALSE;
+ break;
+
+ case OP_NOT_WORDCHAR:
+ if (chr > 255 || (cb->ctypes[chr] & ctype_word) == 0) return FALSE;
+ break;
+
+ case OP_HSPACE:
+ switch(chr)
+ {
+ HSPACE_CASES: return FALSE;
+ default: break;
+ }
+ break;
+
+ case OP_NOT_HSPACE:
+ switch(chr)
+ {
+ HSPACE_CASES: break;
+ default: return FALSE;
+ }
+ break;
+
+ case OP_ANYNL:
+ case OP_VSPACE:
+ switch(chr)
+ {
+ VSPACE_CASES: return FALSE;
+ default: break;
+ }
+ break;
+
+ case OP_NOT_VSPACE:
+ switch(chr)
+ {
+ VSPACE_CASES: break;
+ default: return FALSE;
+ }
+ break;
+
+ case OP_DOLL:
+ case OP_EODN:
+ switch (chr)
+ {
+ case CHAR_CR:
+ case CHAR_LF:
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#ifndef EBCDIC
+ case 0x2028:
+ case 0x2029:
+#endif /* Not EBCDIC */
+ return FALSE;
+ }
+ break;
+
+ case OP_EOD: /* Can always possessify before \z */
+ break;
+
+#ifdef SUPPORT_UNICODE
+ case OP_PROP:
+ case OP_NOTPROP:
+ if (!check_char_prop(chr, list_ptr[2], list_ptr[3],
+ list_ptr[0] == OP_NOTPROP))
+ return FALSE;
+ break;
+#endif
+
+ case OP_NCLASS:
+ if (chr > 255) return FALSE;
+ /* Fall through */
+
+ case OP_CLASS:
+ if (chr > 255) break;
+ class_bitset = (uint8_t *)
+ ((list_ptr == list ? code : base_end) - list_ptr[2]);
+ if ((class_bitset[chr >> 3] & (1u << (chr & 7))) != 0) return FALSE;
+ break;
+
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+ if (PRIV(xclass)(chr, (list_ptr == list ? code : base_end) -
+ list_ptr[2] + LINK_SIZE, utf)) return FALSE;
+ break;
+#endif
+
+ default:
+ return FALSE;
+ }
+
+ chr_ptr++;
+ }
+ while(*chr_ptr != NOTACHAR);
+
+ /* At least one character must be matched from this opcode. */
+
+ if (list[1] == 0) return TRUE;
+ }
+
+/* Control never reaches here. There used to be a fail-save return FALSE; here,
+but some compilers complain about an unreachable statement. */
+}
+
+
+
+/*************************************************
+* Scan compiled regex for auto-possession *
+*************************************************/
+
+/* Replaces single character iterations with their possessive alternatives
+if appropriate. This function modifies the compiled opcode! Hitting a
+non-existent opcode may indicate a bug in PCRE2, but it can also be caused if a
+bad UTF string was compiled with PCRE2_NO_UTF_CHECK. The rec_limit catches
+overly complicated or large patterns. In these cases, the check just stops,
+leaving the remainder of the pattern unpossessified.
+
+Arguments:
+ code points to start of the byte code
+ cb compile data block
+
+Returns: 0 for success
+ -1 if a non-existant opcode is encountered
+*/
+
+int
+PRIV(auto_possessify)(PCRE2_UCHAR *code, const compile_block *cb)
+{
+PCRE2_UCHAR c;
+PCRE2_SPTR end;
+PCRE2_UCHAR *repeat_opcode;
+uint32_t list[8];
+int rec_limit = 1000; /* Was 10,000 but clang+ASAN uses a lot of stack. */
+BOOL utf = (cb->external_options & PCRE2_UTF) != 0;
+BOOL ucp = (cb->external_options & PCRE2_UCP) != 0;
+
+for (;;)
+ {
+ c = *code;
+
+ if (c >= OP_TABLE_LENGTH) return -1; /* Something gone wrong */
+
+ if (c >= OP_STAR && c <= OP_TYPEPOSUPTO)
+ {
+ c -= get_repeat_base(c) - OP_STAR;
+ end = (c <= OP_MINUPTO) ?
+ get_chr_property_list(code, utf, ucp, cb->fcc, list) : NULL;
+ list[1] = c == OP_STAR || c == OP_PLUS || c == OP_QUERY || c == OP_UPTO;
+
+ if (end != NULL && compare_opcodes(end, utf, ucp, cb, list, end,
+ &rec_limit))
+ {
+ switch(c)
+ {
+ case OP_STAR:
+ *code += OP_POSSTAR - OP_STAR;
+ break;
+
+ case OP_MINSTAR:
+ *code += OP_POSSTAR - OP_MINSTAR;
+ break;
+
+ case OP_PLUS:
+ *code += OP_POSPLUS - OP_PLUS;
+ break;
+
+ case OP_MINPLUS:
+ *code += OP_POSPLUS - OP_MINPLUS;
+ break;
+
+ case OP_QUERY:
+ *code += OP_POSQUERY - OP_QUERY;
+ break;
+
+ case OP_MINQUERY:
+ *code += OP_POSQUERY - OP_MINQUERY;
+ break;
+
+ case OP_UPTO:
+ *code += OP_POSUPTO - OP_UPTO;
+ break;
+
+ case OP_MINUPTO:
+ *code += OP_POSUPTO - OP_MINUPTO;
+ break;
+ }
+ }
+ c = *code;
+ }
+ else if (c == OP_CLASS || c == OP_NCLASS || c == OP_XCLASS)
+ {
+#ifdef SUPPORT_WIDE_CHARS
+ if (c == OP_XCLASS)
+ repeat_opcode = code + GET(code, 1);
+ else
+#endif
+ repeat_opcode = code + 1 + (32 / sizeof(PCRE2_UCHAR));
+
+ c = *repeat_opcode;
+ if (c >= OP_CRSTAR && c <= OP_CRMINRANGE)
+ {
+ /* The return from get_chr_property_list() will never be NULL when
+ *code (aka c) is one of the three class opcodes. However, gcc with
+ -fanalyzer notes that a NULL return is possible, and grumbles. Hence we
+ put in a check. */
+
+ end = get_chr_property_list(code, utf, ucp, cb->fcc, list);
+ list[1] = (c & 1) == 0;
+
+ if (end != NULL &&
+ compare_opcodes(end, utf, ucp, cb, list, end, &rec_limit))
+ {
+ switch (c)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ *repeat_opcode = OP_CRPOSSTAR;
+ break;
+
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ *repeat_opcode = OP_CRPOSPLUS;
+ break;
+
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ *repeat_opcode = OP_CRPOSQUERY;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ *repeat_opcode = OP_CRPOSRANGE;
+ break;
+ }
+ }
+ }
+ c = *code;
+ }
+
+ switch(c)
+ {
+ case OP_END:
+ return 0;
+
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ case OP_TYPEPOSQUERY:
+ if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2;
+ break;
+
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEEXACT:
+ case OP_TYPEPOSUPTO:
+ if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP)
+ code += 2;
+ break;
+
+ case OP_CALLOUT_STR:
+ code += GET(code, 1 + 2*LINK_SIZE);
+ break;
+
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+ code += GET(code, 1);
+ break;
+#endif
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_SKIP_ARG:
+ case OP_THEN_ARG:
+ code += code[1];
+ break;
+ }
+
+ /* Add in the fixed length from the table */
+
+ code += PRIV(OP_lengths)[c];
+
+ /* In UTF-8 and UTF-16 modes, opcodes that are followed by a character may be
+ followed by a multi-byte character. The length in the table is a minimum, so
+ we have to arrange to skip the extra code units. */
+
+#ifdef MAYBE_UTF_MULTI
+ if (utf) switch(c)
+ {
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_EXACT:
+ case OP_POSSTAR:
+ case OP_POSPLUS:
+ case OP_POSQUERY:
+ case OP_POSUPTO:
+ case OP_STARI:
+ case OP_MINSTARI:
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_UPTOI:
+ case OP_MINUPTOI:
+ case OP_EXACTI:
+ case OP_POSSTARI:
+ case OP_POSPLUSI:
+ case OP_POSQUERYI:
+ case OP_POSUPTOI:
+ case OP_NOTSTAR:
+ case OP_NOTMINSTAR:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTQUERY:
+ case OP_NOTMINQUERY:
+ case OP_NOTUPTO:
+ case OP_NOTMINUPTO:
+ case OP_NOTEXACT:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSUPTO:
+ case OP_NOTSTARI:
+ case OP_NOTMINSTARI:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUSI:
+ case OP_NOTQUERYI:
+ case OP_NOTMINQUERYI:
+ case OP_NOTUPTOI:
+ case OP_NOTMINUPTOI:
+ case OP_NOTEXACTI:
+ case OP_NOTPOSSTARI:
+ case OP_NOTPOSPLUSI:
+ case OP_NOTPOSQUERYI:
+ case OP_NOTPOSUPTOI:
+ if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]);
+ break;
+ }
+#else
+ (void)(utf); /* Keep compiler happy by referencing function argument */
+#endif /* SUPPORT_WIDE_CHARS */
+ }
+}
+
+/* End of pcre2_auto_possess.c */
diff --git a/contrib/libs/pcre2/src/pcre2_chartables.c b/contrib/libs/pcre2/src/pcre2_chartables.c
new file mode 100644
index 00000000000..4506b0d5ee0
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_chartables.c
@@ -0,0 +1,202 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* This file was automatically written by the pcre2_dftables auxiliary
+program. It contains character tables that are used when no external
+tables are passed to PCRE2 by the application that calls it. The tables
+are used only for characters whose code values are less than 256. */
+
+/* This set of tables was written in the C locale. */
+
+/* The pcre2_ftables program (which is distributed with PCRE2) can be used
+to build alternative versions of this file. This is necessary if you are
+running in an EBCDIC environment, or if you want to default to a different
+encoding, for example ISO-8859-1. When pcre2_dftables is run, it creates
+these tables in the "C" locale by default. This happens automatically if
+PCRE2 is configured with --enable-rebuild-chartables. However, you can run
+pcre2_dftables manually with the -L option to build tables using the LC_ALL
+locale. */
+
+/* The following #include is present because without it gcc 4.x may remove
+the array definition from the final binary if PCRE2 is built into a static
+library and dead code stripping is activated. This leads to link errors.
+Pulling in the header ensures that the array gets flagged as "someone
+outside this compilation unit might reference this" and so it will always
+be supplied to the linker. */
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+const uint8_t PRIV(default_tables)[] = {
+
+/* This table is a lower casing table. */
+
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 97, 98, 99,100,101,102,103,
+ 104,105,106,107,108,109,110,111,
+ 112,113,114,115,116,117,118,119,
+ 120,121,122, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,
+ 104,105,106,107,108,109,110,111,
+ 112,113,114,115,116,117,118,119,
+ 120,121,122,123,124,125,126,127,
+ 128,129,130,131,132,133,134,135,
+ 136,137,138,139,140,141,142,143,
+ 144,145,146,147,148,149,150,151,
+ 152,153,154,155,156,157,158,159,
+ 160,161,162,163,164,165,166,167,
+ 168,169,170,171,172,173,174,175,
+ 176,177,178,179,180,181,182,183,
+ 184,185,186,187,188,189,190,191,
+ 192,193,194,195,196,197,198,199,
+ 200,201,202,203,204,205,206,207,
+ 208,209,210,211,212,213,214,215,
+ 216,217,218,219,220,221,222,223,
+ 224,225,226,227,228,229,230,231,
+ 232,233,234,235,236,237,238,239,
+ 240,241,242,243,244,245,246,247,
+ 248,249,250,251,252,253,254,255,
+
+/* This table is a case flipping table. */
+
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 97, 98, 99,100,101,102,103,
+ 104,105,106,107,108,109,110,111,
+ 112,113,114,115,116,117,118,119,
+ 120,121,122, 91, 92, 93, 94, 95,
+ 96, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90,123,124,125,126,127,
+ 128,129,130,131,132,133,134,135,
+ 136,137,138,139,140,141,142,143,
+ 144,145,146,147,148,149,150,151,
+ 152,153,154,155,156,157,158,159,
+ 160,161,162,163,164,165,166,167,
+ 168,169,170,171,172,173,174,175,
+ 176,177,178,179,180,181,182,183,
+ 184,185,186,187,188,189,190,191,
+ 192,193,194,195,196,197,198,199,
+ 200,201,202,203,204,205,206,207,
+ 208,209,210,211,212,213,214,215,
+ 216,217,218,219,220,221,222,223,
+ 224,225,226,227,228,229,230,231,
+ 232,233,234,235,236,237,238,239,
+ 240,241,242,243,244,245,246,247,
+ 248,249,250,251,252,253,254,255,
+
+/* This table contains bit maps for various character classes. Each map is 32
+bytes long and the bits run from the least significant end of each byte. The
+classes that have their own maps are: space, xdigit, digit, upper, lower, word,
+graph, print, punct, and cntrl. Other classes are built from combinations. */
+
+ 0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00, /* space */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, /* xdigit */
+ 0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, /* digit */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* upper */
+ 0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* lower */
+ 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, /* word */
+ 0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff, /* graph */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, /* print */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc, /* punct */
+ 0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, /* cntrl */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+/* This table identifies various classes of character by individual bits:
+ 0x01 white space character
+ 0x02 letter
+ 0x04 lower case letter
+ 0x08 decimal digit
+ 0x10 alphanumeric or '_'
+*/
+
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */
+ 0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00, /* 8- 15 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */
+ 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - ' */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ( - / */
+ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, /* 0 - 7 */
+ 0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00, /* 8 - ? */
+ 0x00,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* @ - G */
+ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */
+ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */
+ 0x12,0x12,0x12,0x00,0x00,0x00,0x00,0x10, /* X - _ */
+ 0x00,0x16,0x16,0x16,0x16,0x16,0x16,0x16, /* ` - g */
+ 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, /* h - o */
+ 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, /* p - w */
+ 0x16,0x16,0x16,0x00,0x00,0x00,0x00,0x00, /* x -127 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */
+
+/* End of pcre2_chartables.c */
diff --git a/contrib/libs/pcre2/src/pcre2_compile.c b/contrib/libs/pcre2/src/pcre2_compile.c
new file mode 100644
index 00000000000..4caeecf6a49
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_compile.c
@@ -0,0 +1,10631 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#define NLBLOCK cb /* Block containing newline information */
+#define PSSTART start_pattern /* Field containing processed string start */
+#define PSEND end_pattern /* Field containing processed string end */
+
+#include "pcre2_internal.h"
+
+/* In rare error cases debugging might require calling pcre2_printint(). */
+
+#if 0
+#ifdef EBCDIC
+#define PRINTABLE(c) ((c) >= 64 && (c) < 255)
+#else
+#define PRINTABLE(c) ((c) >= 32 && (c) < 127)
+#endif
+#error #include "pcre2_printint.c"
+#define DEBUG_CALL_PRINTINT
+#endif
+
+/* Other debugging code can be enabled by these defines. */
+
+/* #define DEBUG_SHOW_CAPTURES */
+/* #define DEBUG_SHOW_PARSED */
+
+/* There are a few things that vary with different code unit sizes. Handle them
+by defining macros in order to minimize #if usage. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#define STRING_UTFn_RIGHTPAR STRING_UTF8_RIGHTPAR, 5
+#define XDIGIT(c) xdigitab[c]
+
+#else /* Either 16-bit or 32-bit */
+#define XDIGIT(c) (MAX_255(c)? xdigitab[c] : 0xff)
+
+#if PCRE2_CODE_UNIT_WIDTH == 16
+#define STRING_UTFn_RIGHTPAR STRING_UTF16_RIGHTPAR, 6
+
+#else /* 32-bit */
+#define STRING_UTFn_RIGHTPAR STRING_UTF32_RIGHTPAR, 6
+#endif
+#endif
+
+/* Macros to store and retrieve a PCRE2_SIZE value in the parsed pattern, which
+consists of uint32_t elements. Assume that if uint32_t can't hold it, two of
+them will be able to (i.e. assume a 64-bit world). */
+
+#if PCRE2_SIZE_MAX <= UINT32_MAX
+#define PUTOFFSET(s,p) *p++ = s
+#define GETOFFSET(s,p) s = *p++
+#define GETPLUSOFFSET(s,p) s = *(++p)
+#define READPLUSOFFSET(s,p) s = p[1]
+#define SKIPOFFSET(p) p++
+#define SIZEOFFSET 1
+#else
+#define PUTOFFSET(s,p) \
+ { *p++ = (uint32_t)(s >> 32); *p++ = (uint32_t)(s & 0xffffffff); }
+#define GETOFFSET(s,p) \
+ { s = ((PCRE2_SIZE)p[0] << 32) | (PCRE2_SIZE)p[1]; p += 2; }
+#define GETPLUSOFFSET(s,p) \
+ { s = ((PCRE2_SIZE)p[1] << 32) | (PCRE2_SIZE)p[2]; p += 2; }
+#define READPLUSOFFSET(s,p) \
+ { s = ((PCRE2_SIZE)p[1] << 32) | (PCRE2_SIZE)p[2]; }
+#define SKIPOFFSET(p) p += 2
+#define SIZEOFFSET 2
+#endif
+
+/* Macros for manipulating elements of the parsed pattern vector. */
+
+#define META_CODE(x) (x & 0xffff0000u)
+#define META_DATA(x) (x & 0x0000ffffu)
+#define META_DIFF(x,y) ((x-y)>>16)
+
+/* Function definitions to allow mutual recursion */
+
+#ifdef SUPPORT_UNICODE
+static unsigned int
+ add_list_to_class_internal(uint8_t *, PCRE2_UCHAR **, uint32_t,
+ compile_block *, const uint32_t *, unsigned int);
+#endif
+
+static int
+ compile_regex(uint32_t, PCRE2_UCHAR **, uint32_t **, int *, uint32_t,
+ uint32_t *, uint32_t *, uint32_t *, uint32_t *, branch_chain *,
+ compile_block *, PCRE2_SIZE *);
+
+static int
+ get_branchlength(uint32_t **, int *, int *, parsed_recurse_check *,
+ compile_block *);
+
+static BOOL
+ set_lookbehind_lengths(uint32_t **, int *, int *, parsed_recurse_check *,
+ compile_block *);
+
+static int
+ check_lookbehinds(uint32_t *, uint32_t **, parsed_recurse_check *,
+ compile_block *, int *);
+
+
+/*************************************************
+* Code parameters and static tables *
+*************************************************/
+
+#define MAX_GROUP_NUMBER 65535u
+#define MAX_REPEAT_COUNT 65535u
+#define REPEAT_UNLIMITED (MAX_REPEAT_COUNT+1)
+
+/* COMPILE_WORK_SIZE specifies the size of stack workspace, which is used in
+different ways in the different pattern scans. The parsing and group-
+identifying pre-scan uses it to handle nesting, and needs it to be 16-bit
+aligned for this. Having defined the size in code units, we set up
+C16_WORK_SIZE as the number of elements in the 16-bit vector.
+
+During the first compiling phase, when determining how much memory is required,
+the regex is partly compiled into this space, but the compiled parts are
+discarded as soon as they can be, so that hopefully there will never be an
+overrun. The code does, however, check for an overrun, which can occur for
+pathological patterns. The size of the workspace depends on LINK_SIZE because
+the length of compiled items varies with this.
+
+In the real compile phase, this workspace is not currently used. */
+
+#define COMPILE_WORK_SIZE (3000*LINK_SIZE) /* Size in code units */
+
+#define C16_WORK_SIZE \
+ ((COMPILE_WORK_SIZE * sizeof(PCRE2_UCHAR))/sizeof(uint16_t))
+
+/* A uint32_t vector is used for caching information about the size of
+capturing groups, to improve performance. A default is created on the stack of
+this size. */
+
+#define GROUPINFO_DEFAULT_SIZE 256
+
+/* The overrun tests check for a slightly smaller size so that they detect the
+overrun before it actually does run off the end of the data block. */
+
+#define WORK_SIZE_SAFETY_MARGIN (100)
+
+/* This value determines the size of the initial vector that is used for
+remembering named groups during the pre-compile. It is allocated on the stack,
+but if it is too small, it is expanded, in a similar way to the workspace. The
+value is the number of slots in the list. */
+
+#define NAMED_GROUP_LIST_SIZE 20
+
+/* The pre-compiling pass over the pattern creates a parsed pattern in a vector
+of uint32_t. For short patterns this lives on the stack, with this size. Heap
+memory is used for longer patterns. */
+
+#define PARSED_PATTERN_DEFAULT_SIZE 1024
+
+/* Maximum length value to check against when making sure that the variable
+that holds the compiled pattern length does not overflow. We make it a bit less
+than INT_MAX to allow for adding in group terminating code units, so that we
+don't have to check them every time. */
+
+#define OFLOW_MAX (INT_MAX - 20)
+
+/* Code values for parsed patterns, which are stored in a vector of 32-bit
+unsigned ints. Values less than META_END are literal data values. The coding
+for identifying the item is in the top 16-bits, leaving 16 bits for the
+additional data that some of them need. The META_CODE, META_DATA, and META_DIFF
+macros are used to manipulate parsed pattern elements.
+
+NOTE: When these definitions are changed, the table of extra lengths for each
+code (meta_extra_lengths, just below) must be updated to remain in step. */
+
+#define META_END 0x80000000u /* End of pattern */
+
+#define META_ALT 0x80010000u /* alternation */
+#define META_ATOMIC 0x80020000u /* atomic group */
+#define META_BACKREF 0x80030000u /* Back ref */
+#define META_BACKREF_BYNAME 0x80040000u /* \k'name' */
+#define META_BIGVALUE 0x80050000u /* Next is a literal > META_END */
+#define META_CALLOUT_NUMBER 0x80060000u /* (?C with numerical argument */
+#define META_CALLOUT_STRING 0x80070000u /* (?C with string argument */
+#define META_CAPTURE 0x80080000u /* Capturing parenthesis */
+#define META_CIRCUMFLEX 0x80090000u /* ^ metacharacter */
+#define META_CLASS 0x800a0000u /* start non-empty class */
+#define META_CLASS_EMPTY 0x800b0000u /* empty class */
+#define META_CLASS_EMPTY_NOT 0x800c0000u /* negative empty class */
+#define META_CLASS_END 0x800d0000u /* end of non-empty class */
+#define META_CLASS_NOT 0x800e0000u /* start non-empty negative class */
+#define META_COND_ASSERT 0x800f0000u /* (?(?assertion)... */
+#define META_COND_DEFINE 0x80100000u /* (?(DEFINE)... */
+#define META_COND_NAME 0x80110000u /* (?(<name>)... */
+#define META_COND_NUMBER 0x80120000u /* (?(digits)... */
+#define META_COND_RNAME 0x80130000u /* (?(R&name)... */
+#define META_COND_RNUMBER 0x80140000u /* (?(Rdigits)... */
+#define META_COND_VERSION 0x80150000u /* (?(VERSION<op>x.y)... */
+#define META_DOLLAR 0x80160000u /* $ metacharacter */
+#define META_DOT 0x80170000u /* . metacharacter */
+#define META_ESCAPE 0x80180000u /* \d and friends */
+#define META_KET 0x80190000u /* closing parenthesis */
+#define META_NOCAPTURE 0x801a0000u /* no capture parens */
+#define META_OPTIONS 0x801b0000u /* (?i) and friends */
+#define META_POSIX 0x801c0000u /* POSIX class item */
+#define META_POSIX_NEG 0x801d0000u /* negative POSIX class item */
+#define META_RANGE_ESCAPED 0x801e0000u /* range with at least one escape */
+#define META_RANGE_LITERAL 0x801f0000u /* range defined literally */
+#define META_RECURSE 0x80200000u /* Recursion */
+#define META_RECURSE_BYNAME 0x80210000u /* (?&name) */
+#define META_SCRIPT_RUN 0x80220000u /* (*script_run:...) */
+
+/* These must be kept together to make it easy to check that an assertion
+is present where expected in a conditional group. */
+
+#define META_LOOKAHEAD 0x80230000u /* (?= */
+#define META_LOOKAHEADNOT 0x80240000u /* (?! */
+#define META_LOOKBEHIND 0x80250000u /* (?<= */
+#define META_LOOKBEHINDNOT 0x80260000u /* (?<! */
+
+/* These cannot be conditions */
+
+#define META_LOOKAHEAD_NA 0x80270000u /* (*napla: */
+#define META_LOOKBEHIND_NA 0x80280000u /* (*naplb: */
+
+/* These must be kept in this order, with consecutive values, and the _ARG
+versions of COMMIT, PRUNE, SKIP, and THEN immediately after their non-argument
+versions. */
+
+#define META_MARK 0x80290000u /* (*MARK) */
+#define META_ACCEPT 0x802a0000u /* (*ACCEPT) */
+#define META_FAIL 0x802b0000u /* (*FAIL) */
+#define META_COMMIT 0x802c0000u /* These */
+#define META_COMMIT_ARG 0x802d0000u /* pairs */
+#define META_PRUNE 0x802e0000u /* must */
+#define META_PRUNE_ARG 0x802f0000u /* be */
+#define META_SKIP 0x80300000u /* kept */
+#define META_SKIP_ARG 0x80310000u /* in */
+#define META_THEN 0x80320000u /* this */
+#define META_THEN_ARG 0x80330000u /* order */
+
+/* These must be kept in groups of adjacent 3 values, and all together. */
+
+#define META_ASTERISK 0x80340000u /* * */
+#define META_ASTERISK_PLUS 0x80350000u /* *+ */
+#define META_ASTERISK_QUERY 0x80360000u /* *? */
+#define META_PLUS 0x80370000u /* + */
+#define META_PLUS_PLUS 0x80380000u /* ++ */
+#define META_PLUS_QUERY 0x80390000u /* +? */
+#define META_QUERY 0x803a0000u /* ? */
+#define META_QUERY_PLUS 0x803b0000u /* ?+ */
+#define META_QUERY_QUERY 0x803c0000u /* ?? */
+#define META_MINMAX 0x803d0000u /* {n,m} repeat */
+#define META_MINMAX_PLUS 0x803e0000u /* {n,m}+ repeat */
+#define META_MINMAX_QUERY 0x803f0000u /* {n,m}? repeat */
+
+#define META_FIRST_QUANTIFIER META_ASTERISK
+#define META_LAST_QUANTIFIER META_MINMAX_QUERY
+
+/* This is a special "meta code" that is used only to distinguish (*asr: from
+(*sr: in the table of aphabetic assertions. It is never stored in the parsed
+pattern because (*asr: is turned into (*sr:(*atomic: at that stage. There is
+therefore no need for it to have a length entry, so use a high value. */
+
+#define META_ATOMIC_SCRIPT_RUN 0x8fff0000u
+
+/* Table of extra lengths for each of the meta codes. Must be kept in step with
+the definitions above. For some items these values are a basic length to which
+a variable amount has to be added. */
+
+static unsigned char meta_extra_lengths[] = {
+ 0, /* META_END */
+ 0, /* META_ALT */
+ 0, /* META_ATOMIC */
+ 0, /* META_BACKREF - more if group is >= 10 */
+ 1+SIZEOFFSET, /* META_BACKREF_BYNAME */
+ 1, /* META_BIGVALUE */
+ 3, /* META_CALLOUT_NUMBER */
+ 3+SIZEOFFSET, /* META_CALLOUT_STRING */
+ 0, /* META_CAPTURE */
+ 0, /* META_CIRCUMFLEX */
+ 0, /* META_CLASS */
+ 0, /* META_CLASS_EMPTY */
+ 0, /* META_CLASS_EMPTY_NOT */
+ 0, /* META_CLASS_END */
+ 0, /* META_CLASS_NOT */
+ 0, /* META_COND_ASSERT */
+ SIZEOFFSET, /* META_COND_DEFINE */
+ 1+SIZEOFFSET, /* META_COND_NAME */
+ 1+SIZEOFFSET, /* META_COND_NUMBER */
+ 1+SIZEOFFSET, /* META_COND_RNAME */
+ 1+SIZEOFFSET, /* META_COND_RNUMBER */
+ 3, /* META_COND_VERSION */
+ 0, /* META_DOLLAR */
+ 0, /* META_DOT */
+ 0, /* META_ESCAPE - more for ESC_P, ESC_p, ESC_g, ESC_k */
+ 0, /* META_KET */
+ 0, /* META_NOCAPTURE */
+ 1, /* META_OPTIONS */
+ 1, /* META_POSIX */
+ 1, /* META_POSIX_NEG */
+ 0, /* META_RANGE_ESCAPED */
+ 0, /* META_RANGE_LITERAL */
+ SIZEOFFSET, /* META_RECURSE */
+ 1+SIZEOFFSET, /* META_RECURSE_BYNAME */
+ 0, /* META_SCRIPT_RUN */
+ 0, /* META_LOOKAHEAD */
+ 0, /* META_LOOKAHEADNOT */
+ SIZEOFFSET, /* META_LOOKBEHIND */
+ SIZEOFFSET, /* META_LOOKBEHINDNOT */
+ 0, /* META_LOOKAHEAD_NA */
+ SIZEOFFSET, /* META_LOOKBEHIND_NA */
+ 1, /* META_MARK - plus the string length */
+ 0, /* META_ACCEPT */
+ 0, /* META_FAIL */
+ 0, /* META_COMMIT */
+ 1, /* META_COMMIT_ARG - plus the string length */
+ 0, /* META_PRUNE */
+ 1, /* META_PRUNE_ARG - plus the string length */
+ 0, /* META_SKIP */
+ 1, /* META_SKIP_ARG - plus the string length */
+ 0, /* META_THEN */
+ 1, /* META_THEN_ARG - plus the string length */
+ 0, /* META_ASTERISK */
+ 0, /* META_ASTERISK_PLUS */
+ 0, /* META_ASTERISK_QUERY */
+ 0, /* META_PLUS */
+ 0, /* META_PLUS_PLUS */
+ 0, /* META_PLUS_QUERY */
+ 0, /* META_QUERY */
+ 0, /* META_QUERY_PLUS */
+ 0, /* META_QUERY_QUERY */
+ 2, /* META_MINMAX */
+ 2, /* META_MINMAX_PLUS */
+ 2 /* META_MINMAX_QUERY */
+};
+
+/* Types for skipping parts of a parsed pattern. */
+
+enum { PSKIP_ALT, PSKIP_CLASS, PSKIP_KET };
+
+/* Macro for setting individual bits in class bitmaps. It took some
+experimenting to figure out how to stop gcc 5.3.0 from warning with
+-Wconversion. This version gets a warning:
+
+ #define SETBIT(a,b) a[(b)/8] |= (uint8_t)(1u << ((b)&7))
+
+Let's hope the apparently less efficient version isn't actually so bad if the
+compiler is clever with identical subexpressions. */
+
+#define SETBIT(a,b) a[(b)/8] = (uint8_t)(a[(b)/8] | (1u << ((b)&7)))
+
+/* Values and flags for the unsigned xxcuflags variables that accompany xxcu
+variables, which are concerned with first and required code units. A value
+greater than or equal to REQ_NONE means "no code unit set"; otherwise the
+matching xxcu variable is set, and the low valued bits are relevant. */
+
+#define REQ_UNSET 0xffffffffu /* Not yet found anything */
+#define REQ_NONE 0xfffffffeu /* Found not fixed character */
+#define REQ_CASELESS 0x00000001u /* Code unit in xxcu is caseless */
+#define REQ_VARY 0x00000002u /* Code unit is followed by non-literal */
+
+/* These flags are used in the groupinfo vector. */
+
+#define GI_SET_FIXED_LENGTH 0x80000000u
+#define GI_NOT_FIXED_LENGTH 0x40000000u
+#define GI_FIXED_LENGTH_MASK 0x0000ffffu
+
+/* This simple test for a decimal digit works for both ASCII/Unicode and EBCDIC
+and is fast (a good compiler can turn it into a subtraction and unsigned
+comparison). */
+
+#define IS_DIGIT(x) ((x) >= CHAR_0 && (x) <= CHAR_9)
+
+/* Table to identify hex digits. The tables in chartables are dependent on the
+locale, and may mark arbitrary characters as digits. We want to recognize only
+0-9, a-z, and A-Z as hex digits, which is why we have a private table here. It
+costs 256 bytes, but it is a lot faster than doing character value tests (at
+least in some simple cases I timed), and in some applications one wants PCRE2
+to compile efficiently as well as match efficiently. The value in the table is
+the binary hex digit value, or 0xff for non-hex digits. */
+
+/* This is the "normal" case, for ASCII systems, and EBCDIC systems running in
+UTF-8 mode. */
+
+#ifndef EBCDIC
+static const uint8_t xdigitab[] =
+ {
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0- 7 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 8- 15 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 16- 23 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 24- 31 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - ' */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* ( - / */
+ 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, /* 0 - 7 */
+ 0x08,0x09,0xff,0xff,0xff,0xff,0xff,0xff, /* 8 - ? */
+ 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* @ - G */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* H - O */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* P - W */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* X - _ */
+ 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* ` - g */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* h - o */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* p - w */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* x -127 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 128-135 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 136-143 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 144-151 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 152-159 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 160-167 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 168-175 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 176-183 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 184-191 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 192-199 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 2ff-207 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 208-215 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 216-223 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 224-231 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 232-239 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 240-247 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};/* 248-255 */
+
+#else
+
+/* This is the "abnormal" case, for EBCDIC systems not running in UTF-8 mode. */
+
+static const uint8_t xdigitab[] =
+ {
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0- 7 0 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 8- 15 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 16- 23 10 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 24- 31 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 32- 39 20 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 40- 47 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 48- 55 30 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 56- 63 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - 71 40 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 72- | */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* & - 87 50 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 88- 95 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - -103 60 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 104- ? */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 112-119 70 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 120- " */
+ 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* 128- g 80 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* h -143 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 144- p 90 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* q -159 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 160- x A0 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* y -175 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* ^ -183 B0 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 184-191 */
+ 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* { - G C0 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* H -207 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* } - P D0 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* Q -223 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* \ - X E0 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* Y -239 */
+ 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, /* 0 - 7 F0 */
+ 0x08,0x09,0xff,0xff,0xff,0xff,0xff,0xff};/* 8 -255 */
+#endif /* EBCDIC */
+
+
+/* Table for handling alphanumeric escaped characters. Positive returns are
+simple data values; negative values are for special things like \d and so on.
+Zero means further processing is needed (for things like \x), or the escape is
+invalid. */
+
+/* This is the "normal" table for ASCII systems or for EBCDIC systems running
+in UTF-8 mode. It runs from '0' to 'z'. */
+
+#ifndef EBCDIC
+#define ESCAPES_FIRST CHAR_0
+#define ESCAPES_LAST CHAR_z
+#define UPPER_CASE(c) (c-32)
+
+static const short int escapes[] = {
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ CHAR_COLON, CHAR_SEMICOLON,
+ CHAR_LESS_THAN_SIGN, CHAR_EQUALS_SIGN,
+ CHAR_GREATER_THAN_SIGN, CHAR_QUESTION_MARK,
+ CHAR_COMMERCIAL_AT, -ESC_A,
+ -ESC_B, -ESC_C,
+ -ESC_D, -ESC_E,
+ 0, -ESC_G,
+ -ESC_H, 0,
+ 0, -ESC_K,
+ 0, 0,
+ -ESC_N, 0,
+ -ESC_P, -ESC_Q,
+ -ESC_R, -ESC_S,
+ 0, 0,
+ -ESC_V, -ESC_W,
+ -ESC_X, 0,
+ -ESC_Z, CHAR_LEFT_SQUARE_BRACKET,
+ CHAR_BACKSLASH, CHAR_RIGHT_SQUARE_BRACKET,
+ CHAR_CIRCUMFLEX_ACCENT, CHAR_UNDERSCORE,
+ CHAR_GRAVE_ACCENT, CHAR_BEL,
+ -ESC_b, 0,
+ -ESC_d, CHAR_ESC,
+ CHAR_FF, 0,
+ -ESC_h, 0,
+ 0, -ESC_k,
+ 0, 0,
+ CHAR_LF, 0,
+ -ESC_p, 0,
+ CHAR_CR, -ESC_s,
+ CHAR_HT, 0,
+ -ESC_v, -ESC_w,
+ 0, 0,
+ -ESC_z
+};
+
+#else
+
+/* This is the "abnormal" table for EBCDIC systems without UTF-8 support.
+It runs from 'a' to '9'. For some minimal testing of EBCDIC features, the code
+is sometimes compiled on an ASCII system. In this case, we must not use CHAR_a
+because it is defined as 'a', which of course picks up the ASCII value. */
+
+#if 'a' == 0x81 /* Check for a real EBCDIC environment */
+#define ESCAPES_FIRST CHAR_a
+#define ESCAPES_LAST CHAR_9
+#define UPPER_CASE(c) (c+64)
+#else /* Testing in an ASCII environment */
+#define ESCAPES_FIRST ((unsigned char)'\x81') /* EBCDIC 'a' */
+#define ESCAPES_LAST ((unsigned char)'\xf9') /* EBCDIC '9' */
+#define UPPER_CASE(c) (c-32)
+#endif
+
+static const short int escapes[] = {
+/* 80 */ CHAR_BEL, -ESC_b, 0, -ESC_d, CHAR_ESC, CHAR_FF, 0,
+/* 88 */ -ESC_h, 0, 0, '{', 0, 0, 0, 0,
+/* 90 */ 0, 0, -ESC_k, 0, 0, CHAR_LF, 0, -ESC_p,
+/* 98 */ 0, CHAR_CR, 0, '}', 0, 0, 0, 0,
+/* A0 */ 0, '~', -ESC_s, CHAR_HT, 0, -ESC_v, -ESC_w, 0,
+/* A8 */ 0, -ESC_z, 0, 0, 0, '[', 0, 0,
+/* B0 */ 0, 0, 0, 0, 0, 0, 0, 0,
+/* B8 */ 0, 0, 0, 0, 0, ']', '=', '-',
+/* C0 */ '{', -ESC_A, -ESC_B, -ESC_C, -ESC_D, -ESC_E, 0, -ESC_G,
+/* C8 */ -ESC_H, 0, 0, 0, 0, 0, 0, 0,
+/* D0 */ '}', 0, -ESC_K, 0, 0, -ESC_N, 0, -ESC_P,
+/* D8 */ -ESC_Q, -ESC_R, 0, 0, 0, 0, 0, 0,
+/* E0 */ '\\', 0, -ESC_S, 0, 0, -ESC_V, -ESC_W, -ESC_X,
+/* E8 */ 0, -ESC_Z, 0, 0, 0, 0, 0, 0,
+/* F0 */ 0, 0, 0, 0, 0, 0, 0, 0,
+/* F8 */ 0, 0
+};
+
+/* We also need a table of characters that may follow \c in an EBCDIC
+environment for characters 0-31. */
+
+static unsigned char ebcdic_escape_c[] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_";
+
+#endif /* EBCDIC */
+
+
+/* Table of special "verbs" like (*PRUNE). This is a short table, so it is
+searched linearly. Put all the names into a single string, in order to reduce
+the number of relocations when a shared library is dynamically linked. The
+string is built from string macros so that it works in UTF-8 mode on EBCDIC
+platforms. */
+
+typedef struct verbitem {
+ unsigned int len; /* Length of verb name */
+ uint32_t meta; /* Base META_ code */
+ int has_arg; /* Argument requirement */
+} verbitem;
+
+static const char verbnames[] =
+ "\0" /* Empty name is a shorthand for MARK */
+ STRING_MARK0
+ STRING_ACCEPT0
+ STRING_F0
+ STRING_FAIL0
+ STRING_COMMIT0
+ STRING_PRUNE0
+ STRING_SKIP0
+ STRING_THEN;
+
+static const verbitem verbs[] = {
+ { 0, META_MARK, +1 }, /* > 0 => must have an argument */
+ { 4, META_MARK, +1 },
+ { 6, META_ACCEPT, -1 }, /* < 0 => Optional argument, convert to pre-MARK */
+ { 1, META_FAIL, -1 },
+ { 4, META_FAIL, -1 },
+ { 6, META_COMMIT, 0 },
+ { 5, META_PRUNE, 0 }, /* Optional argument; bump META code if found */
+ { 4, META_SKIP, 0 },
+ { 4, META_THEN, 0 }
+};
+
+static const int verbcount = sizeof(verbs)/sizeof(verbitem);
+
+/* Verb opcodes, indexed by their META code offset from META_MARK. */
+
+static const uint32_t verbops[] = {
+ OP_MARK, OP_ACCEPT, OP_FAIL, OP_COMMIT, OP_COMMIT_ARG, OP_PRUNE,
+ OP_PRUNE_ARG, OP_SKIP, OP_SKIP_ARG, OP_THEN, OP_THEN_ARG };
+
+/* Table of "alpha assertions" like (*pla:...), similar to the (*VERB) table. */
+
+typedef struct alasitem {
+ unsigned int len; /* Length of name */
+ uint32_t meta; /* Base META_ code */
+} alasitem;
+
+static const char alasnames[] =
+ STRING_pla0
+ STRING_plb0
+ STRING_napla0
+ STRING_naplb0
+ STRING_nla0
+ STRING_nlb0
+ STRING_positive_lookahead0
+ STRING_positive_lookbehind0
+ STRING_non_atomic_positive_lookahead0
+ STRING_non_atomic_positive_lookbehind0
+ STRING_negative_lookahead0
+ STRING_negative_lookbehind0
+ STRING_atomic0
+ STRING_sr0
+ STRING_asr0
+ STRING_script_run0
+ STRING_atomic_script_run;
+
+static const alasitem alasmeta[] = {
+ { 3, META_LOOKAHEAD },
+ { 3, META_LOOKBEHIND },
+ { 5, META_LOOKAHEAD_NA },
+ { 5, META_LOOKBEHIND_NA },
+ { 3, META_LOOKAHEADNOT },
+ { 3, META_LOOKBEHINDNOT },
+ { 18, META_LOOKAHEAD },
+ { 19, META_LOOKBEHIND },
+ { 29, META_LOOKAHEAD_NA },
+ { 30, META_LOOKBEHIND_NA },
+ { 18, META_LOOKAHEADNOT },
+ { 19, META_LOOKBEHINDNOT },
+ { 6, META_ATOMIC },
+ { 2, META_SCRIPT_RUN }, /* sr = script run */
+ { 3, META_ATOMIC_SCRIPT_RUN }, /* asr = atomic script run */
+ { 10, META_SCRIPT_RUN }, /* script run */
+ { 17, META_ATOMIC_SCRIPT_RUN } /* atomic script run */
+};
+
+static const int alascount = sizeof(alasmeta)/sizeof(alasitem);
+
+/* Offsets from OP_STAR for case-independent and negative repeat opcodes. */
+
+static uint32_t chartypeoffset[] = {
+ OP_STAR - OP_STAR, OP_STARI - OP_STAR,
+ OP_NOTSTAR - OP_STAR, OP_NOTSTARI - OP_STAR };
+
+/* Tables of names of POSIX character classes and their lengths. The names are
+now all in a single string, to reduce the number of relocations when a shared
+library is dynamically loaded. The list of lengths is terminated by a zero
+length entry. The first three must be alpha, lower, upper, as this is assumed
+for handling case independence. The indices for graph, print, and punct are
+needed, so identify them. */
+
+static const char posix_names[] =
+ STRING_alpha0 STRING_lower0 STRING_upper0 STRING_alnum0
+ STRING_ascii0 STRING_blank0 STRING_cntrl0 STRING_digit0
+ STRING_graph0 STRING_print0 STRING_punct0 STRING_space0
+ STRING_word0 STRING_xdigit;
+
+static const uint8_t posix_name_lengths[] = {
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 };
+
+#define PC_GRAPH 8
+#define PC_PRINT 9
+#define PC_PUNCT 10
+
+/* Table of class bit maps for each POSIX class. Each class is formed from a
+base map, with an optional addition or removal of another map. Then, for some
+classes, there is some additional tweaking: for [:blank:] the vertical space
+characters are removed, and for [:alpha:] and [:alnum:] the underscore
+character is removed. The triples in the table consist of the base map offset,
+second map offset or -1 if no second map, and a non-negative value for map
+addition or a negative value for map subtraction (if there are two maps). The
+absolute value of the third field has these meanings: 0 => no tweaking, 1 =>
+remove vertical space characters, 2 => remove underscore. */
+
+static const int posix_class_maps[] = {
+ cbit_word, cbit_digit, -2, /* alpha */
+ cbit_lower, -1, 0, /* lower */
+ cbit_upper, -1, 0, /* upper */
+ cbit_word, -1, 2, /* alnum - word without underscore */
+ cbit_print, cbit_cntrl, 0, /* ascii */
+ cbit_space, -1, 1, /* blank - a GNU extension */
+ cbit_cntrl, -1, 0, /* cntrl */
+ cbit_digit, -1, 0, /* digit */
+ cbit_graph, -1, 0, /* graph */
+ cbit_print, -1, 0, /* print */
+ cbit_punct, -1, 0, /* punct */
+ cbit_space, -1, 0, /* space */
+ cbit_word, -1, 0, /* word - a Perl extension */
+ cbit_xdigit,-1, 0 /* xdigit */
+};
+
+#ifdef SUPPORT_UNICODE
+
+/* The POSIX class Unicode property substitutes that are used in UCP mode must
+be in the order of the POSIX class names, defined above. */
+
+static int posix_substitutes[] = {
+ PT_GC, ucp_L, /* alpha */
+ PT_PC, ucp_Ll, /* lower */
+ PT_PC, ucp_Lu, /* upper */
+ PT_ALNUM, 0, /* alnum */
+ -1, 0, /* ascii, treat as non-UCP */
+ -1, 1, /* blank, treat as \h */
+ PT_PC, ucp_Cc, /* cntrl */
+ PT_PC, ucp_Nd, /* digit */
+ PT_PXGRAPH, 0, /* graph */
+ PT_PXPRINT, 0, /* print */
+ PT_PXPUNCT, 0, /* punct */
+ PT_PXSPACE, 0, /* space */ /* Xps is POSIX space, but from 8.34 */
+ PT_WORD, 0, /* word */ /* Perl and POSIX space are the same */
+ -1, 0 /* xdigit, treat as non-UCP */
+};
+#define POSIX_SUBSIZE (sizeof(posix_substitutes) / (2*sizeof(uint32_t)))
+#endif /* SUPPORT_UNICODE */
+
+/* Masks for checking option settings. When PCRE2_LITERAL is set, only a subset
+are allowed. */
+
+#define PUBLIC_LITERAL_COMPILE_OPTIONS \
+ (PCRE2_ANCHORED|PCRE2_AUTO_CALLOUT|PCRE2_CASELESS|PCRE2_ENDANCHORED| \
+ PCRE2_FIRSTLINE|PCRE2_LITERAL|PCRE2_MATCH_INVALID_UTF| \
+ PCRE2_NO_START_OPTIMIZE|PCRE2_NO_UTF_CHECK|PCRE2_USE_OFFSET_LIMIT|PCRE2_UTF)
+
+#define PUBLIC_COMPILE_OPTIONS \
+ (PUBLIC_LITERAL_COMPILE_OPTIONS| \
+ PCRE2_ALLOW_EMPTY_CLASS|PCRE2_ALT_BSUX|PCRE2_ALT_CIRCUMFLEX| \
+ PCRE2_ALT_VERBNAMES|PCRE2_DOLLAR_ENDONLY|PCRE2_DOTALL|PCRE2_DUPNAMES| \
+ PCRE2_EXTENDED|PCRE2_EXTENDED_MORE|PCRE2_MATCH_UNSET_BACKREF| \
+ PCRE2_MULTILINE|PCRE2_NEVER_BACKSLASH_C|PCRE2_NEVER_UCP| \
+ PCRE2_NEVER_UTF|PCRE2_NO_AUTO_CAPTURE|PCRE2_NO_AUTO_POSSESS| \
+ PCRE2_NO_DOTSTAR_ANCHOR|PCRE2_UCP|PCRE2_UNGREEDY)
+
+#define PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS \
+ (PCRE2_EXTRA_MATCH_LINE|PCRE2_EXTRA_MATCH_WORD)
+
+#define PUBLIC_COMPILE_EXTRA_OPTIONS \
+ (PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS| \
+ PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES|PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL| \
+ PCRE2_EXTRA_ESCAPED_CR_IS_LF|PCRE2_EXTRA_ALT_BSUX| \
+ PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK)
+
+/* Compile time error code numbers. They are given names so that they can more
+easily be tracked. When a new number is added, the tables called eint1 and
+eint2 in pcre2posix.c may need to be updated, and a new error text must be
+added to compile_error_texts in pcre2_error.c. Also, the error codes in
+pcre2.h.in must be updated - their values are exactly 100 greater than these
+values. */
+
+enum { ERR0 = COMPILE_ERROR_BASE,
+ ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9, ERR10,
+ ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17, ERR18, ERR19, ERR20,
+ ERR21, ERR22, ERR23, ERR24, ERR25, ERR26, ERR27, ERR28, ERR29, ERR30,
+ ERR31, ERR32, ERR33, ERR34, ERR35, ERR36, ERR37, ERR38, ERR39, ERR40,
+ ERR41, ERR42, ERR43, ERR44, ERR45, ERR46, ERR47, ERR48, ERR49, ERR50,
+ ERR51, ERR52, ERR53, ERR54, ERR55, ERR56, ERR57, ERR58, ERR59, ERR60,
+ ERR61, ERR62, ERR63, ERR64, ERR65, ERR66, ERR67, ERR68, ERR69, ERR70,
+ ERR71, ERR72, ERR73, ERR74, ERR75, ERR76, ERR77, ERR78, ERR79, ERR80,
+ ERR81, ERR82, ERR83, ERR84, ERR85, ERR86, ERR87, ERR88, ERR89, ERR90,
+ ERR91, ERR92, ERR93, ERR94, ERR95, ERR96, ERR97, ERR98, ERR99 };
+
+/* This is a table of start-of-pattern options such as (*UTF) and settings such
+as (*LIMIT_MATCH=nnnn) and (*CRLF). For completeness and backward
+compatibility, (*UTFn) is supported in the relevant libraries, but (*UTF) is
+generic and always supported. */
+
+enum { PSO_OPT, /* Value is an option bit */
+ PSO_FLG, /* Value is a flag bit */
+ PSO_NL, /* Value is a newline type */
+ PSO_BSR, /* Value is a \R type */
+ PSO_LIMH, /* Read integer value for heap limit */
+ PSO_LIMM, /* Read integer value for match limit */
+ PSO_LIMD }; /* Read integer value for depth limit */
+
+typedef struct pso {
+ const uint8_t *name;
+ uint16_t length;
+ uint16_t type;
+ uint32_t value;
+} pso;
+
+/* NB: STRING_UTFn_RIGHTPAR contains the length as well */
+
+static pso pso_list[] = {
+ { (uint8_t *)STRING_UTFn_RIGHTPAR, PSO_OPT, PCRE2_UTF },
+ { (uint8_t *)STRING_UTF_RIGHTPAR, 4, PSO_OPT, PCRE2_UTF },
+ { (uint8_t *)STRING_UCP_RIGHTPAR, 4, PSO_OPT, PCRE2_UCP },
+ { (uint8_t *)STRING_NOTEMPTY_RIGHTPAR, 9, PSO_FLG, PCRE2_NOTEMPTY_SET },
+ { (uint8_t *)STRING_NOTEMPTY_ATSTART_RIGHTPAR, 17, PSO_FLG, PCRE2_NE_ATST_SET },
+ { (uint8_t *)STRING_NO_AUTO_POSSESS_RIGHTPAR, 16, PSO_OPT, PCRE2_NO_AUTO_POSSESS },
+ { (uint8_t *)STRING_NO_DOTSTAR_ANCHOR_RIGHTPAR, 18, PSO_OPT, PCRE2_NO_DOTSTAR_ANCHOR },
+ { (uint8_t *)STRING_NO_JIT_RIGHTPAR, 7, PSO_FLG, PCRE2_NOJIT },
+ { (uint8_t *)STRING_NO_START_OPT_RIGHTPAR, 13, PSO_OPT, PCRE2_NO_START_OPTIMIZE },
+ { (uint8_t *)STRING_LIMIT_HEAP_EQ, 11, PSO_LIMH, 0 },
+ { (uint8_t *)STRING_LIMIT_MATCH_EQ, 12, PSO_LIMM, 0 },
+ { (uint8_t *)STRING_LIMIT_DEPTH_EQ, 12, PSO_LIMD, 0 },
+ { (uint8_t *)STRING_LIMIT_RECURSION_EQ, 16, PSO_LIMD, 0 },
+ { (uint8_t *)STRING_CR_RIGHTPAR, 3, PSO_NL, PCRE2_NEWLINE_CR },
+ { (uint8_t *)STRING_LF_RIGHTPAR, 3, PSO_NL, PCRE2_NEWLINE_LF },
+ { (uint8_t *)STRING_CRLF_RIGHTPAR, 5, PSO_NL, PCRE2_NEWLINE_CRLF },
+ { (uint8_t *)STRING_ANY_RIGHTPAR, 4, PSO_NL, PCRE2_NEWLINE_ANY },
+ { (uint8_t *)STRING_NUL_RIGHTPAR, 4, PSO_NL, PCRE2_NEWLINE_NUL },
+ { (uint8_t *)STRING_ANYCRLF_RIGHTPAR, 8, PSO_NL, PCRE2_NEWLINE_ANYCRLF },
+ { (uint8_t *)STRING_BSR_ANYCRLF_RIGHTPAR, 12, PSO_BSR, PCRE2_BSR_ANYCRLF },
+ { (uint8_t *)STRING_BSR_UNICODE_RIGHTPAR, 12, PSO_BSR, PCRE2_BSR_UNICODE }
+};
+
+/* This table is used when converting repeating opcodes into possessified
+versions as a result of an explicit possessive quantifier such as ++. A zero
+value means there is no possessified version - in those cases the item in
+question must be wrapped in ONCE brackets. The table is truncated at OP_CALLOUT
+because all relevant opcodes are less than that. */
+
+static const uint8_t opcode_possessify[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 15 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */
+
+ 0, /* NOTI */
+ OP_POSSTAR, 0, /* STAR, MINSTAR */
+ OP_POSPLUS, 0, /* PLUS, MINPLUS */
+ OP_POSQUERY, 0, /* QUERY, MINQUERY */
+ OP_POSUPTO, 0, /* UPTO, MINUPTO */
+ 0, /* EXACT */
+ 0, 0, 0, 0, /* POS{STAR,PLUS,QUERY,UPTO} */
+
+ OP_POSSTARI, 0, /* STARI, MINSTARI */
+ OP_POSPLUSI, 0, /* PLUSI, MINPLUSI */
+ OP_POSQUERYI, 0, /* QUERYI, MINQUERYI */
+ OP_POSUPTOI, 0, /* UPTOI, MINUPTOI */
+ 0, /* EXACTI */
+ 0, 0, 0, 0, /* POS{STARI,PLUSI,QUERYI,UPTOI} */
+
+ OP_NOTPOSSTAR, 0, /* NOTSTAR, NOTMINSTAR */
+ OP_NOTPOSPLUS, 0, /* NOTPLUS, NOTMINPLUS */
+ OP_NOTPOSQUERY, 0, /* NOTQUERY, NOTMINQUERY */
+ OP_NOTPOSUPTO, 0, /* NOTUPTO, NOTMINUPTO */
+ 0, /* NOTEXACT */
+ 0, 0, 0, 0, /* NOTPOS{STAR,PLUS,QUERY,UPTO} */
+
+ OP_NOTPOSSTARI, 0, /* NOTSTARI, NOTMINSTARI */
+ OP_NOTPOSPLUSI, 0, /* NOTPLUSI, NOTMINPLUSI */
+ OP_NOTPOSQUERYI, 0, /* NOTQUERYI, NOTMINQUERYI */
+ OP_NOTPOSUPTOI, 0, /* NOTUPTOI, NOTMINUPTOI */
+ 0, /* NOTEXACTI */
+ 0, 0, 0, 0, /* NOTPOS{STARI,PLUSI,QUERYI,UPTOI} */
+
+ OP_TYPEPOSSTAR, 0, /* TYPESTAR, TYPEMINSTAR */
+ OP_TYPEPOSPLUS, 0, /* TYPEPLUS, TYPEMINPLUS */
+ OP_TYPEPOSQUERY, 0, /* TYPEQUERY, TYPEMINQUERY */
+ OP_TYPEPOSUPTO, 0, /* TYPEUPTO, TYPEMINUPTO */
+ 0, /* TYPEEXACT */
+ 0, 0, 0, 0, /* TYPEPOS{STAR,PLUS,QUERY,UPTO} */
+
+ OP_CRPOSSTAR, 0, /* CRSTAR, CRMINSTAR */
+ OP_CRPOSPLUS, 0, /* CRPLUS, CRMINPLUS */
+ OP_CRPOSQUERY, 0, /* CRQUERY, CRMINQUERY */
+ OP_CRPOSRANGE, 0, /* CRRANGE, CRMINRANGE */
+ 0, 0, 0, 0, /* CRPOS{STAR,PLUS,QUERY,RANGE} */
+
+ 0, 0, 0, /* CLASS, NCLASS, XCLASS */
+ 0, 0, /* REF, REFI */
+ 0, 0, /* DNREF, DNREFI */
+ 0, 0 /* RECURSE, CALLOUT */
+};
+
+
+#ifdef DEBUG_SHOW_PARSED
+/*************************************************
+* Show the parsed pattern for debugging *
+*************************************************/
+
+/* For debugging the pre-scan, this code, which outputs the parsed data vector,
+can be enabled. */
+
+static void show_parsed(compile_block *cb)
+{
+uint32_t *pptr = cb->parsed_pattern;
+
+for (;;)
+ {
+ int max, min;
+ PCRE2_SIZE offset;
+ uint32_t i;
+ uint32_t length;
+ uint32_t meta_arg = META_DATA(*pptr);
+
+ fprintf(stderr, "+++ %02d %.8x ", (int)(pptr - cb->parsed_pattern), *pptr);
+
+ if (*pptr < META_END)
+ {
+ if (*pptr > 32 && *pptr < 128) fprintf(stderr, "%c", *pptr);
+ pptr++;
+ }
+
+ else switch (META_CODE(*pptr++))
+ {
+ default:
+ fprintf(stderr, "**** OOPS - unknown META value - giving up ****\n");
+ return;
+
+ case META_END:
+ fprintf(stderr, "META_END\n");
+ return;
+
+ case META_CAPTURE:
+ fprintf(stderr, "META_CAPTURE %d", meta_arg);
+ break;
+
+ case META_RECURSE:
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "META_RECURSE %d %zd", meta_arg, offset);
+ break;
+
+ case META_BACKREF:
+ if (meta_arg < 10)
+ offset = cb->small_ref_offset[meta_arg];
+ else
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "META_BACKREF %d %zd", meta_arg, offset);
+ break;
+
+ case META_ESCAPE:
+ if (meta_arg == ESC_P || meta_arg == ESC_p)
+ {
+ uint32_t ptype = *pptr >> 16;
+ uint32_t pvalue = *pptr++ & 0xffff;
+ fprintf(stderr, "META \\%c %d %d", (meta_arg == ESC_P)? 'P':'p',
+ ptype, pvalue);
+ }
+ else
+ {
+ uint32_t cc;
+ /* There's just one escape we might have here that isn't negated in the
+ escapes table. */
+ if (meta_arg == ESC_g) cc = CHAR_g;
+ else for (cc = ESCAPES_FIRST; cc <= ESCAPES_LAST; cc++)
+ {
+ if (meta_arg == (uint32_t)(-escapes[cc - ESCAPES_FIRST])) break;
+ }
+ if (cc > ESCAPES_LAST) cc = CHAR_QUESTION_MARK;
+ fprintf(stderr, "META \\%c", cc);
+ }
+ break;
+
+ case META_MINMAX:
+ min = *pptr++;
+ max = *pptr++;
+ if (max != REPEAT_UNLIMITED)
+ fprintf(stderr, "META {%d,%d}", min, max);
+ else
+ fprintf(stderr, "META {%d,}", min);
+ break;
+
+ case META_MINMAX_QUERY:
+ min = *pptr++;
+ max = *pptr++;
+ if (max != REPEAT_UNLIMITED)
+ fprintf(stderr, "META {%d,%d}?", min, max);
+ else
+ fprintf(stderr, "META {%d,}?", min);
+ break;
+
+ case META_MINMAX_PLUS:
+ min = *pptr++;
+ max = *pptr++;
+ if (max != REPEAT_UNLIMITED)
+ fprintf(stderr, "META {%d,%d}+", min, max);
+ else
+ fprintf(stderr, "META {%d,}+", min);
+ break;
+
+ case META_BIGVALUE: fprintf(stderr, "META_BIGVALUE %.8x", *pptr++); break;
+ case META_CIRCUMFLEX: fprintf(stderr, "META_CIRCUMFLEX"); break;
+ case META_COND_ASSERT: fprintf(stderr, "META_COND_ASSERT"); break;
+ case META_DOLLAR: fprintf(stderr, "META_DOLLAR"); break;
+ case META_DOT: fprintf(stderr, "META_DOT"); break;
+ case META_ASTERISK: fprintf(stderr, "META *"); break;
+ case META_ASTERISK_QUERY: fprintf(stderr, "META *?"); break;
+ case META_ASTERISK_PLUS: fprintf(stderr, "META *+"); break;
+ case META_PLUS: fprintf(stderr, "META +"); break;
+ case META_PLUS_QUERY: fprintf(stderr, "META +?"); break;
+ case META_PLUS_PLUS: fprintf(stderr, "META ++"); break;
+ case META_QUERY: fprintf(stderr, "META ?"); break;
+ case META_QUERY_QUERY: fprintf(stderr, "META ??"); break;
+ case META_QUERY_PLUS: fprintf(stderr, "META ?+"); break;
+
+ case META_ATOMIC: fprintf(stderr, "META (?>"); break;
+ case META_NOCAPTURE: fprintf(stderr, "META (?:"); break;
+ case META_LOOKAHEAD: fprintf(stderr, "META (?="); break;
+ case META_LOOKAHEADNOT: fprintf(stderr, "META (?!"); break;
+ case META_LOOKAHEAD_NA: fprintf(stderr, "META (*napla:"); break;
+ case META_SCRIPT_RUN: fprintf(stderr, "META (*sr:"); break;
+ case META_KET: fprintf(stderr, "META )"); break;
+ case META_ALT: fprintf(stderr, "META | %d", meta_arg); break;
+
+ case META_CLASS: fprintf(stderr, "META ["); break;
+ case META_CLASS_NOT: fprintf(stderr, "META [^"); break;
+ case META_CLASS_END: fprintf(stderr, "META ]"); break;
+ case META_CLASS_EMPTY: fprintf(stderr, "META []"); break;
+ case META_CLASS_EMPTY_NOT: fprintf(stderr, "META [^]"); break;
+
+ case META_RANGE_LITERAL: fprintf(stderr, "META - (literal)"); break;
+ case META_RANGE_ESCAPED: fprintf(stderr, "META - (escaped)"); break;
+
+ case META_POSIX: fprintf(stderr, "META_POSIX %d", *pptr++); break;
+ case META_POSIX_NEG: fprintf(stderr, "META_POSIX_NEG %d", *pptr++); break;
+
+ case META_ACCEPT: fprintf(stderr, "META (*ACCEPT)"); break;
+ case META_FAIL: fprintf(stderr, "META (*FAIL)"); break;
+ case META_COMMIT: fprintf(stderr, "META (*COMMIT)"); break;
+ case META_PRUNE: fprintf(stderr, "META (*PRUNE)"); break;
+ case META_SKIP: fprintf(stderr, "META (*SKIP)"); break;
+ case META_THEN: fprintf(stderr, "META (*THEN)"); break;
+
+ case META_OPTIONS: fprintf(stderr, "META_OPTIONS 0x%02x", *pptr++); break;
+
+ case META_LOOKBEHIND:
+ fprintf(stderr, "META (?<= %d offset=", meta_arg);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ case META_LOOKBEHIND_NA:
+ fprintf(stderr, "META (*naplb: %d offset=", meta_arg);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ case META_LOOKBEHINDNOT:
+ fprintf(stderr, "META (?<! %d offset=", meta_arg);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ case META_CALLOUT_NUMBER:
+ fprintf(stderr, "META (?C%d) next=%d/%d", pptr[2], pptr[0],
+ pptr[1]);
+ pptr += 3;
+ break;
+
+ case META_CALLOUT_STRING:
+ {
+ uint32_t patoffset = *pptr++; /* Offset of next pattern item */
+ uint32_t patlength = *pptr++; /* Length of next pattern item */
+ fprintf(stderr, "META (?Cstring) length=%d offset=", *pptr++);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd next=%d/%d", offset, patoffset, patlength);
+ }
+ break;
+
+ case META_RECURSE_BYNAME:
+ fprintf(stderr, "META (?(&name) length=%d offset=", *pptr++);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ case META_BACKREF_BYNAME:
+ fprintf(stderr, "META_BACKREF_BYNAME length=%d offset=", *pptr++);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ case META_COND_NUMBER:
+ fprintf(stderr, "META_COND_NUMBER %d offset=", pptr[SIZEOFFSET]);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ pptr++;
+ break;
+
+ case META_COND_DEFINE:
+ fprintf(stderr, "META (?(DEFINE) offset=");
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ case META_COND_VERSION:
+ fprintf(stderr, "META (?(VERSION%s", (*pptr++ == 0)? "=" : ">=");
+ fprintf(stderr, "%d.", *pptr++);
+ fprintf(stderr, "%d)", *pptr++);
+ break;
+
+ case META_COND_NAME:
+ fprintf(stderr, "META (?(<name>) length=%d offset=", *pptr++);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ case META_COND_RNAME:
+ fprintf(stderr, "META (?(R&name) length=%d offset=", *pptr++);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ /* This is kept as a name, because it might be. */
+
+ case META_COND_RNUMBER:
+ fprintf(stderr, "META (?(Rnumber) length=%d offset=", *pptr++);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ case META_MARK:
+ fprintf(stderr, "META (*MARK:");
+ goto SHOWARG;
+
+ case META_COMMIT_ARG:
+ fprintf(stderr, "META (*COMMIT:");
+ goto SHOWARG;
+
+ case META_PRUNE_ARG:
+ fprintf(stderr, "META (*PRUNE:");
+ goto SHOWARG;
+
+ case META_SKIP_ARG:
+ fprintf(stderr, "META (*SKIP:");
+ goto SHOWARG;
+
+ case META_THEN_ARG:
+ fprintf(stderr, "META (*THEN:");
+ SHOWARG:
+ length = *pptr++;
+ for (i = 0; i < length; i++)
+ {
+ uint32_t cc = *pptr++;
+ if (cc > 32 && cc < 128) fprintf(stderr, "%c", cc);
+ else fprintf(stderr, "\\x{%x}", cc);
+ }
+ fprintf(stderr, ") length=%u", length);
+ break;
+ }
+ fprintf(stderr, "\n");
+ }
+return;
+}
+#endif /* DEBUG_SHOW_PARSED */
+
+
+
+/*************************************************
+* Copy compiled code *
+*************************************************/
+
+/* Compiled JIT code cannot be copied, so the new compiled block has no
+associated JIT data. */
+
+PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION
+pcre2_code_copy(const pcre2_code *code)
+{
+PCRE2_SIZE* ref_count;
+pcre2_code *newcode;
+
+if (code == NULL) return NULL;
+newcode = code->memctl.malloc(code->blocksize, code->memctl.memory_data);
+if (newcode == NULL) return NULL;
+memcpy(newcode, code, code->blocksize);
+newcode->executable_jit = NULL;
+
+/* If the code is one that has been deserialized, increment the reference count
+in the decoded tables. */
+
+if ((code->flags & PCRE2_DEREF_TABLES) != 0)
+ {
+ ref_count = (PCRE2_SIZE *)(code->tables + TABLES_LENGTH);
+ (*ref_count)++;
+ }
+
+return newcode;
+}
+
+
+
+/*************************************************
+* Copy compiled code and character tables *
+*************************************************/
+
+/* Compiled JIT code cannot be copied, so the new compiled block has no
+associated JIT data. This version of code_copy also makes a separate copy of
+the character tables. */
+
+PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION
+pcre2_code_copy_with_tables(const pcre2_code *code)
+{
+PCRE2_SIZE* ref_count;
+pcre2_code *newcode;
+uint8_t *newtables;
+
+if (code == NULL) return NULL;
+newcode = code->memctl.malloc(code->blocksize, code->memctl.memory_data);
+if (newcode == NULL) return NULL;
+memcpy(newcode, code, code->blocksize);
+newcode->executable_jit = NULL;
+
+newtables = code->memctl.malloc(TABLES_LENGTH + sizeof(PCRE2_SIZE),
+ code->memctl.memory_data);
+if (newtables == NULL)
+ {
+ code->memctl.free((void *)newcode, code->memctl.memory_data);
+ return NULL;
+ }
+memcpy(newtables, code->tables, TABLES_LENGTH);
+ref_count = (PCRE2_SIZE *)(newtables + TABLES_LENGTH);
+*ref_count = 1;
+
+newcode->tables = newtables;
+newcode->flags |= PCRE2_DEREF_TABLES;
+return newcode;
+}
+
+
+
+/*************************************************
+* Free compiled code *
+*************************************************/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_code_free(pcre2_code *code)
+{
+PCRE2_SIZE* ref_count;
+
+if (code != NULL)
+ {
+#ifdef SUPPORT_JIT
+ if (code->executable_jit != NULL)
+ PRIV(jit_free)(code->executable_jit, &code->memctl);
+#endif
+
+ if ((code->flags & PCRE2_DEREF_TABLES) != 0)
+ {
+ /* Decoded tables belong to the codes after deserialization, and they must
+ be freed when there are no more references to them. The *ref_count should
+ always be > 0. */
+
+ ref_count = (PCRE2_SIZE *)(code->tables + TABLES_LENGTH);
+ if (*ref_count > 0)
+ {
+ (*ref_count)--;
+ if (*ref_count == 0)
+ code->memctl.free((void *)code->tables, code->memctl.memory_data);
+ }
+ }
+
+ code->memctl.free(code, code->memctl.memory_data);
+ }
+}
+
+
+
+/*************************************************
+* Read a number, possibly signed *
+*************************************************/
+
+/* This function is used to read numbers in the pattern. The initial pointer
+must be the sign or first digit of the number. When relative values (introduced
+by + or -) are allowed, they are relative group numbers, and the result must be
+greater than zero.
+
+Arguments:
+ ptrptr points to the character pointer variable
+ ptrend points to the end of the input string
+ allow_sign if < 0, sign not allowed; if >= 0, sign is relative to this
+ max_value the largest number allowed
+ max_error the error to give for an over-large number
+ intptr where to put the result
+ errcodeptr where to put an error code
+
+Returns: TRUE - a number was read
+ FALSE - errorcode == 0 => no number was found
+ errorcode != 0 => an error occurred
+*/
+
+static BOOL
+read_number(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, int32_t allow_sign,
+ uint32_t max_value, uint32_t max_error, int *intptr, int *errorcodeptr)
+{
+int sign = 0;
+uint32_t n = 0;
+PCRE2_SPTR ptr = *ptrptr;
+BOOL yield = FALSE;
+
+*errorcodeptr = 0;
+
+if (allow_sign >= 0 && ptr < ptrend)
+ {
+ if (*ptr == CHAR_PLUS)
+ {
+ sign = +1;
+ max_value -= allow_sign;
+ ptr++;
+ }
+ else if (*ptr == CHAR_MINUS)
+ {
+ sign = -1;
+ ptr++;
+ }
+ }
+
+if (ptr >= ptrend || !IS_DIGIT(*ptr)) return FALSE;
+while (ptr < ptrend && IS_DIGIT(*ptr))
+ {
+ n = n * 10 + *ptr++ - CHAR_0;
+ if (n > max_value)
+ {
+ *errorcodeptr = max_error;
+ goto EXIT;
+ }
+ }
+
+if (allow_sign >= 0 && sign != 0)
+ {
+ if (n == 0)
+ {
+ *errorcodeptr = ERR26; /* +0 and -0 are not allowed */
+ goto EXIT;
+ }
+
+ if (sign > 0) n += allow_sign;
+ else if ((int)n > allow_sign)
+ {
+ *errorcodeptr = ERR15; /* Non-existent subpattern */
+ goto EXIT;
+ }
+ else n = allow_sign + 1 - n;
+ }
+
+yield = TRUE;
+
+EXIT:
+*intptr = n;
+*ptrptr = ptr;
+return yield;
+}
+
+
+
+/*************************************************
+* Read repeat counts *
+*************************************************/
+
+/* Read an item of the form {n,m} and return the values if non-NULL pointers
+are supplied. Repeat counts must be less than 65536 (MAX_REPEAT_COUNT); a
+larger value is used for "unlimited". We have to use signed arguments for
+read_number() because it is capable of returning a signed value.
+
+Arguments:
+ ptrptr points to pointer to character after'{'
+ ptrend pointer to end of input
+ minp if not NULL, pointer to int for min
+ maxp if not NULL, pointer to int for max (-1 if no max)
+ returned as -1 if no max
+ errorcodeptr points to error code variable
+
+Returns: FALSE if not a repeat quantifier, errorcode set zero
+ FALSE on error, with errorcode set non-zero
+ TRUE on success, with pointer updated to point after '}'
+*/
+
+static BOOL
+read_repeat_counts(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, uint32_t *minp,
+ uint32_t *maxp, int *errorcodeptr)
+{
+PCRE2_SPTR p;
+BOOL yield = FALSE;
+BOOL had_comma = FALSE;
+int32_t min = 0;
+int32_t max = REPEAT_UNLIMITED; /* This value is larger than MAX_REPEAT_COUNT */
+
+/* Check the syntax */
+
+*errorcodeptr = 0;
+for (p = *ptrptr;; p++)
+ {
+ uint32_t c;
+ if (p >= ptrend) return FALSE;
+ c = *p;
+ if (IS_DIGIT(c)) continue;
+ if (c == CHAR_RIGHT_CURLY_BRACKET) break;
+ if (c == CHAR_COMMA)
+ {
+ if (had_comma) return FALSE;
+ had_comma = TRUE;
+ }
+ else return FALSE;
+ }
+
+/* The only error from read_number() is for a number that is too big. */
+
+p = *ptrptr;
+if (!read_number(&p, ptrend, -1, MAX_REPEAT_COUNT, ERR5, &min, errorcodeptr))
+ goto EXIT;
+
+if (*p == CHAR_RIGHT_CURLY_BRACKET)
+ {
+ p++;
+ max = min;
+ }
+else
+ {
+ if (*(++p) != CHAR_RIGHT_CURLY_BRACKET)
+ {
+ if (!read_number(&p, ptrend, -1, MAX_REPEAT_COUNT, ERR5, &max,
+ errorcodeptr))
+ goto EXIT;
+ if (max < min)
+ {
+ *errorcodeptr = ERR4;
+ goto EXIT;
+ }
+ }
+ p++;
+ }
+
+yield = TRUE;
+if (minp != NULL) *minp = (uint32_t)min;
+if (maxp != NULL) *maxp = (uint32_t)max;
+
+/* Update the pattern pointer */
+
+EXIT:
+*ptrptr = p;
+return yield;
+}
+
+
+
+/*************************************************
+* Handle escapes *
+*************************************************/
+
+/* This function is called when a \ has been encountered. It either returns a
+positive value for a simple escape such as \d, or 0 for a data character, which
+is placed in chptr. A backreference to group n is returned as negative n. On
+entry, ptr is pointing at the character after \. On exit, it points after the
+final code unit of the escape sequence.
+
+This function is also called from pcre2_substitute() to handle escape sequences
+in replacement strings. In this case, the cb argument is NULL, and in the case
+of escapes that have further processing, only sequences that define a data
+character are recognised. The isclass argument is not relevant; the options
+argument is the final value of the compiled pattern's options.
+
+Arguments:
+ ptrptr points to the input position pointer
+ ptrend points to the end of the input
+ chptr points to a returned data character
+ errorcodeptr points to the errorcode variable (containing zero)
+ options the current options bits
+ isclass TRUE if inside a character class
+ cb compile data block or NULL when called from pcre2_substitute()
+
+Returns: zero => a data character
+ positive => a special escape sequence
+ negative => a numerical back reference
+ on error, errorcodeptr is set non-zero
+*/
+
+int
+PRIV(check_escape)(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, uint32_t *chptr,
+ int *errorcodeptr, uint32_t options, uint32_t extra_options, BOOL isclass,
+ compile_block *cb)
+{
+BOOL utf = (options & PCRE2_UTF) != 0;
+PCRE2_SPTR ptr = *ptrptr;
+uint32_t c, cc;
+int escape = 0;
+int i;
+
+/* If backslash is at the end of the string, it's an error. */
+
+if (ptr >= ptrend)
+ {
+ *errorcodeptr = ERR1;
+ return 0;
+ }
+
+GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */
+*errorcodeptr = 0; /* Be optimistic */
+
+/* Non-alphanumerics are literals, so we just leave the value in c. An initial
+value test saves a memory lookup for code points outside the alphanumeric
+range. */
+
+if (c < ESCAPES_FIRST || c > ESCAPES_LAST) {} /* Definitely literal */
+
+/* Otherwise, do a table lookup. Non-zero values need little processing here. A
+positive value is a literal value for something like \n. A negative value is
+the negation of one of the ESC_ macros that is passed back for handling by the
+calling function. Some extra checking is needed for \N because only \N{U+dddd}
+is supported. If the value is zero, further processing is handled below. */
+
+else if ((i = escapes[c - ESCAPES_FIRST]) != 0)
+ {
+ if (i > 0)
+ {
+ c = (uint32_t)i;
+ if (c == CHAR_CR && (extra_options & PCRE2_EXTRA_ESCAPED_CR_IS_LF) != 0)
+ c = CHAR_LF;
+ }
+ else /* Negative table entry */
+ {
+ escape = -i; /* Else return a special escape */
+ if (cb != NULL && (escape == ESC_P || escape == ESC_p || escape == ESC_X))
+ cb->external_flags |= PCRE2_HASBKPORX; /* Note \P, \p, or \X */
+
+ /* Perl supports \N{name} for character names and \N{U+dddd} for numerical
+ Unicode code points, as well as plain \N for "not newline". PCRE does not
+ support \N{name}. However, it does support quantification such as \N{2,3},
+ so if \N{ is not followed by U+dddd we check for a quantifier. */
+
+ if (escape == ESC_N && ptr < ptrend && *ptr == CHAR_LEFT_CURLY_BRACKET)
+ {
+ PCRE2_SPTR p = ptr + 1;
+
+ /* \N{U+ can be handled by the \x{ code. However, this construction is
+ not valid in EBCDIC environments because it specifies a Unicode
+ character, not a codepoint in the local code. For example \N{U+0041}
+ must be "A" in all environments. Also, in Perl, \N{U+ forces Unicode
+ casing semantics for the entire pattern, so allow it only in UTF (i.e.
+ Unicode) mode. */
+
+ if (ptrend - p > 1 && *p == CHAR_U && p[1] == CHAR_PLUS)
+ {
+#ifdef EBCDIC
+ *errorcodeptr = ERR93;
+#else
+ if (utf)
+ {
+ ptr = p + 1;
+ escape = 0; /* Not a fancy escape after all */
+ goto COME_FROM_NU;
+ }
+ else *errorcodeptr = ERR93;
+#endif
+ }
+
+ /* Give an error if what follows is not a quantifier, but don't override
+ an error set by the quantifier reader (e.g. number overflow). */
+
+ else
+ {
+ if (!read_repeat_counts(&p, ptrend, NULL, NULL, errorcodeptr) &&
+ *errorcodeptr == 0)
+ *errorcodeptr = ERR37;
+ }
+ }
+ }
+ }
+
+/* Escapes that need further processing, including those that are unknown, have
+a zero entry in the lookup table. When called from pcre2_substitute(), only \c,
+\o, and \x are recognized (\u and \U can never appear as they are used for case
+forcing). */
+
+else
+ {
+ int s;
+ PCRE2_SPTR oldptr;
+ BOOL overflow;
+ BOOL alt_bsux =
+ ((options & PCRE2_ALT_BSUX) | (extra_options & PCRE2_EXTRA_ALT_BSUX)) != 0;
+
+ /* Filter calls from pcre2_substitute(). */
+
+ if (cb == NULL)
+ {
+ if (c != CHAR_c && c != CHAR_o && c != CHAR_x)
+ {
+ *errorcodeptr = ERR3;
+ return 0;
+ }
+ alt_bsux = FALSE; /* Do not modify \x handling */
+ }
+
+ switch (c)
+ {
+ /* A number of Perl escapes are not handled by PCRE. We give an explicit
+ error. */
+
+ case CHAR_F:
+ case CHAR_l:
+ case CHAR_L:
+ *errorcodeptr = ERR37;
+ break;
+
+ /* \u is unrecognized when neither PCRE2_ALT_BSUX nor PCRE2_EXTRA_ALT_BSUX
+ is set. Otherwise, \u must be followed by exactly four hex digits or, if
+ PCRE2_EXTRA_ALT_BSUX is set, by any number of hex digits in braces.
+ Otherwise it is a lowercase u letter. This gives some compatibility with
+ ECMAScript (aka JavaScript). */
+
+ case CHAR_u:
+ if (!alt_bsux) *errorcodeptr = ERR37; else
+ {
+ uint32_t xc;
+
+ if (ptr >= ptrend) break;
+ if (*ptr == CHAR_LEFT_CURLY_BRACKET &&
+ (extra_options & PCRE2_EXTRA_ALT_BSUX) != 0)
+ {
+ PCRE2_SPTR hptr = ptr + 1;
+ cc = 0;
+
+ while (hptr < ptrend && (xc = XDIGIT(*hptr)) != 0xff)
+ {
+ if ((cc & 0xf0000000) != 0) /* Test for 32-bit overflow */
+ {
+ *errorcodeptr = ERR77;
+ ptr = hptr; /* Show where */
+ break; /* *hptr != } will cause another break below */
+ }
+ cc = (cc << 4) | xc;
+ hptr++;
+ }
+
+ if (hptr == ptr + 1 || /* No hex digits */
+ hptr >= ptrend || /* Hit end of input */
+ *hptr != CHAR_RIGHT_CURLY_BRACKET) /* No } terminator */
+ break; /* Hex escape not recognized */
+
+ c = cc; /* Accept the code point */
+ ptr = hptr + 1;
+ }
+
+ else /* Must be exactly 4 hex digits */
+ {
+ if (ptrend - ptr < 4) break; /* Less than 4 chars */
+ if ((cc = XDIGIT(ptr[0])) == 0xff) break; /* Not a hex digit */
+ if ((xc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */
+ cc = (cc << 4) | xc;
+ if ((xc = XDIGIT(ptr[2])) == 0xff) break; /* Not a hex digit */
+ cc = (cc << 4) | xc;
+ if ((xc = XDIGIT(ptr[3])) == 0xff) break; /* Not a hex digit */
+ c = (cc << 4) | xc;
+ ptr += 4;
+ }
+
+ if (utf)
+ {
+ if (c > 0x10ffffU) *errorcodeptr = ERR77;
+ else
+ if (c >= 0xd800 && c <= 0xdfff &&
+ (extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0)
+ *errorcodeptr = ERR73;
+ }
+ else if (c > MAX_NON_UTF_CHAR) *errorcodeptr = ERR77;
+ }
+ break;
+
+ /* \U is unrecognized unless PCRE2_ALT_BSUX or PCRE2_EXTRA_ALT_BSUX is set,
+ in which case it is an upper case letter. */
+
+ case CHAR_U:
+ if (!alt_bsux) *errorcodeptr = ERR37;
+ break;
+
+ /* In a character class, \g is just a literal "g". Outside a character
+ class, \g must be followed by one of a number of specific things:
+
+ (1) A number, either plain or braced. If positive, it is an absolute
+ backreference. If negative, it is a relative backreference. This is a Perl
+ 5.10 feature.
+
+ (2) Perl 5.10 also supports \g{name} as a reference to a named group. This
+ is part of Perl's movement towards a unified syntax for back references. As
+ this is synonymous with \k{name}, we fudge it up by pretending it really
+ was \k{name}.
+
+ (3) For Oniguruma compatibility we also support \g followed by a name or a
+ number either in angle brackets or in single quotes. However, these are
+ (possibly recursive) subroutine calls, _not_ backreferences. We return
+ the ESC_g code.
+
+ Summary: Return a negative number for a numerical back reference, ESC_k for
+ a named back reference, and ESC_g for a named or numbered subroutine call.
+ */
+
+ case CHAR_g:
+ if (isclass) break;
+
+ if (ptr >= ptrend)
+ {
+ *errorcodeptr = ERR57;
+ break;
+ }
+
+ if (*ptr == CHAR_LESS_THAN_SIGN || *ptr == CHAR_APOSTROPHE)
+ {
+ escape = ESC_g;
+ break;
+ }
+
+ /* If there is a brace delimiter, try to read a numerical reference. If
+ there isn't one, assume we have a name and treat it as \k. */
+
+ if (*ptr == CHAR_LEFT_CURLY_BRACKET)
+ {
+ PCRE2_SPTR p = ptr + 1;
+ if (!read_number(&p, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &s,
+ errorcodeptr))
+ {
+ if (*errorcodeptr == 0) escape = ESC_k; /* No number found */
+ break;
+ }
+ if (p >= ptrend || *p != CHAR_RIGHT_CURLY_BRACKET)
+ {
+ *errorcodeptr = ERR57;
+ break;
+ }
+ ptr = p + 1;
+ }
+
+ /* Read an undelimited number */
+
+ else
+ {
+ if (!read_number(&ptr, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &s,
+ errorcodeptr))
+ {
+ if (*errorcodeptr == 0) *errorcodeptr = ERR57; /* No number found */
+ break;
+ }
+ }
+
+ if (s <= 0)
+ {
+ *errorcodeptr = ERR15;
+ break;
+ }
+
+ escape = -s;
+ break;
+
+ /* The handling of escape sequences consisting of a string of digits
+ starting with one that is not zero is not straightforward. Perl has changed
+ over the years. Nowadays \g{} for backreferences and \o{} for octal are
+ recommended to avoid the ambiguities in the old syntax.
+
+ Outside a character class, the digits are read as a decimal number. If the
+ number is less than 10, or if there are that many previous extracting left
+ brackets, it is a back reference. Otherwise, up to three octal digits are
+ read to form an escaped character code. Thus \123 is likely to be octal 123
+ (cf \0123, which is octal 012 followed by the literal 3).
+
+ Inside a character class, \ followed by a digit is always either a literal
+ 8 or 9 or an octal number. */
+
+ case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4: case CHAR_5:
+ case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9:
+
+ if (!isclass)
+ {
+ oldptr = ptr;
+ ptr--; /* Back to the digit */
+
+ /* As we know we are at a digit, the only possible error from
+ read_number() is a number that is too large to be a group number. In this
+ case we fall through handle this as not a group reference. If we have
+ read a small enough number, check for a back reference.
+
+ \1 to \9 are always back references. \8x and \9x are too; \1x to \7x
+ are octal escapes if there are not that many previous captures. */
+
+ if (read_number(&ptr, ptrend, -1, INT_MAX/10 - 1, 0, &s, errorcodeptr) &&
+ (s < 10 || oldptr[-1] >= CHAR_8 || s <= (int)cb->bracount))
+ {
+ if (s > (int)MAX_GROUP_NUMBER) *errorcodeptr = ERR61;
+ else escape = -s; /* Indicates a back reference */
+ break;
+ }
+
+ ptr = oldptr; /* Put the pointer back and fall through */
+ }
+
+ /* Handle a digit following \ when the number is not a back reference, or
+ we are within a character class. If the first digit is 8 or 9, Perl used to
+ generate a binary zero and then treat the digit as a following literal. At
+ least by Perl 5.18 this changed so as not to insert the binary zero. */
+
+ if (c >= CHAR_8) break;
+
+ /* Fall through */
+
+ /* \0 always starts an octal number, but we may drop through to here with a
+ larger first octal digit. The original code used just to take the least
+ significant 8 bits of octal numbers (I think this is what early Perls used
+ to do). Nowadays we allow for larger numbers in UTF-8 mode and 16-bit mode,
+ but no more than 3 octal digits. */
+
+ case CHAR_0:
+ c -= CHAR_0;
+ while(i++ < 2 && ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7)
+ c = c * 8 + *ptr++ - CHAR_0;
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (!utf && c > 0xff) *errorcodeptr = ERR51;
+#endif
+ break;
+
+ /* \o is a relatively new Perl feature, supporting a more general way of
+ specifying character codes in octal. The only supported form is \o{ddd}. */
+
+ case CHAR_o:
+ if (ptr >= ptrend || *ptr++ != CHAR_LEFT_CURLY_BRACKET)
+ {
+ ptr--;
+ *errorcodeptr = ERR55;
+ }
+ else if (ptr >= ptrend || *ptr == CHAR_RIGHT_CURLY_BRACKET)
+ *errorcodeptr = ERR78;
+ else
+ {
+ c = 0;
+ overflow = FALSE;
+ while (ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7)
+ {
+ cc = *ptr++;
+ if (c == 0 && cc == CHAR_0) continue; /* Leading zeroes */
+#if PCRE2_CODE_UNIT_WIDTH == 32
+ if (c >= 0x20000000l) { overflow = TRUE; break; }
+#endif
+ c = (c << 3) + (cc - CHAR_0);
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (c > (utf ? 0x10ffffU : 0xffU)) { overflow = TRUE; break; }
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+ if (c > (utf ? 0x10ffffU : 0xffffU)) { overflow = TRUE; break; }
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+ if (utf && c > 0x10ffffU) { overflow = TRUE; break; }
+#endif
+ }
+ if (overflow)
+ {
+ while (ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7) ptr++;
+ *errorcodeptr = ERR34;
+ }
+ else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET)
+ {
+ if (utf && c >= 0xd800 && c <= 0xdfff &&
+ (extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0)
+ {
+ ptr--;
+ *errorcodeptr = ERR73;
+ }
+ }
+ else
+ {
+ ptr--;
+ *errorcodeptr = ERR64;
+ }
+ }
+ break;
+
+ /* When PCRE2_ALT_BSUX or PCRE2_EXTRA_ALT_BSUX is set, \x must be followed
+ by two hexadecimal digits. Otherwise it is a lowercase x letter. */
+
+ case CHAR_x:
+ if (alt_bsux)
+ {
+ uint32_t xc;
+ if (ptrend - ptr < 2) break; /* Less than 2 characters */
+ if ((cc = XDIGIT(ptr[0])) == 0xff) break; /* Not a hex digit */
+ if ((xc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */
+ c = (cc << 4) | xc;
+ ptr += 2;
+ }
+
+ /* Handle \x in Perl's style. \x{ddd} is a character code which can be
+ greater than 0xff in UTF-8 or non-8bit mode, but only if the ddd are hex
+ digits. If not, { used to be treated as a data character. However, Perl
+ seems to read hex digits up to the first non-such, and ignore the rest, so
+ that, for example \x{zz} matches a binary zero. This seems crazy, so PCRE
+ now gives an error. */
+
+ else
+ {
+ if (ptr < ptrend && *ptr == CHAR_LEFT_CURLY_BRACKET)
+ {
+#ifndef EBCDIC
+ COME_FROM_NU:
+#endif
+ if (++ptr >= ptrend || *ptr == CHAR_RIGHT_CURLY_BRACKET)
+ {
+ *errorcodeptr = ERR78;
+ break;
+ }
+ c = 0;
+ overflow = FALSE;
+
+ while (ptr < ptrend && (cc = XDIGIT(*ptr)) != 0xff)
+ {
+ ptr++;
+ if (c == 0 && cc == 0) continue; /* Leading zeroes */
+#if PCRE2_CODE_UNIT_WIDTH == 32
+ if (c >= 0x10000000l) { overflow = TRUE; break; }
+#endif
+ c = (c << 4) | cc;
+ if ((utf && c > 0x10ffffU) || (!utf && c > MAX_NON_UTF_CHAR))
+ {
+ overflow = TRUE;
+ break;
+ }
+ }
+
+ if (overflow)
+ {
+ while (ptr < ptrend && XDIGIT(*ptr) != 0xff) ptr++;
+ *errorcodeptr = ERR34;
+ }
+ else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET)
+ {
+ if (utf && c >= 0xd800 && c <= 0xdfff &&
+ (extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0)
+ {
+ ptr--;
+ *errorcodeptr = ERR73;
+ }
+ }
+
+ /* If the sequence of hex digits does not end with '}', give an error.
+ We used just to recognize this construct and fall through to the normal
+ \x handling, but nowadays Perl gives an error, which seems much more
+ sensible, so we do too. */
+
+ else
+ {
+ ptr--;
+ *errorcodeptr = ERR67;
+ }
+ } /* End of \x{} processing */
+
+ /* Read a up to two hex digits after \x */
+
+ else
+ {
+ c = 0;
+ if (ptr >= ptrend || (cc = XDIGIT(*ptr)) == 0xff) break; /* Not a hex digit */
+ ptr++;
+ c = cc;
+ if (ptr >= ptrend || (cc = XDIGIT(*ptr)) == 0xff) break; /* Not a hex digit */
+ ptr++;
+ c = (c << 4) | cc;
+ } /* End of \xdd handling */
+ } /* End of Perl-style \x handling */
+ break;
+
+ /* The handling of \c is different in ASCII and EBCDIC environments. In an
+ ASCII (or Unicode) environment, an error is given if the character
+ following \c is not a printable ASCII character. Otherwise, the following
+ character is upper-cased if it is a letter, and after that the 0x40 bit is
+ flipped. The result is the value of the escape.
+
+ In an EBCDIC environment the handling of \c is compatible with the
+ specification in the perlebcdic document. The following character must be
+ a letter or one of small number of special characters. These provide a
+ means of defining the character values 0-31.
+
+ For testing the EBCDIC handling of \c in an ASCII environment, recognize
+ the EBCDIC value of 'c' explicitly. */
+
+#if defined EBCDIC && 'a' != 0x81
+ case 0x83:
+#else
+ case CHAR_c:
+#endif
+ if (ptr >= ptrend)
+ {
+ *errorcodeptr = ERR2;
+ break;
+ }
+ c = *ptr;
+ if (c >= CHAR_a && c <= CHAR_z) c = UPPER_CASE(c);
+
+ /* Handle \c in an ASCII/Unicode environment. */
+
+#ifndef EBCDIC /* ASCII/UTF-8 coding */
+ if (c < 32 || c > 126) /* Excludes all non-printable ASCII */
+ {
+ *errorcodeptr = ERR68;
+ break;
+ }
+ c ^= 0x40;
+
+ /* Handle \c in an EBCDIC environment. The special case \c? is converted to
+ 255 (0xff) or 95 (0x5f) if other characters suggest we are using the
+ POSIX-BC encoding. (This is the way Perl indicates that it handles \c?.)
+ The other valid sequences correspond to a list of specific characters. */
+
+#else
+ if (c == CHAR_QUESTION_MARK)
+ c = ('\\' == 188 && '`' == 74)? 0x5f : 0xff;
+ else
+ {
+ for (i = 0; i < 32; i++)
+ {
+ if (c == ebcdic_escape_c[i]) break;
+ }
+ if (i < 32) c = i; else *errorcodeptr = ERR68;
+ }
+#endif /* EBCDIC */
+
+ ptr++;
+ break;
+
+ /* Any other alphanumeric following \ is an error. Perl gives an error only
+ if in warning mode, but PCRE doesn't have a warning mode. */
+
+ default:
+ *errorcodeptr = ERR3;
+ *ptrptr = ptr - 1; /* Point to the character at fault */
+ return 0;
+ }
+ }
+
+/* Set the pointer to the next character before returning. */
+
+*ptrptr = ptr;
+*chptr = c;
+return escape;
+}
+
+
+
+#ifdef SUPPORT_UNICODE
+/*************************************************
+* Handle \P and \p *
+*************************************************/
+
+/* This function is called after \P or \p has been encountered, provided that
+PCRE2 is compiled with support for UTF and Unicode properties. On entry, the
+contents of ptrptr are pointing after the P or p. On exit, it is left pointing
+after the final code unit of the escape sequence.
+
+Arguments:
+ ptrptr the pattern position pointer
+ negptr a boolean that is set TRUE for negation else FALSE
+ ptypeptr an unsigned int that is set to the type value
+ pdataptr an unsigned int that is set to the detailed property value
+ errorcodeptr the error code variable
+ cb the compile data
+
+Returns: TRUE if the type value was found, or FALSE for an invalid type
+*/
+
+static BOOL
+get_ucp(PCRE2_SPTR *ptrptr, BOOL *negptr, uint16_t *ptypeptr,
+ uint16_t *pdataptr, int *errorcodeptr, compile_block *cb)
+{
+PCRE2_UCHAR c;
+PCRE2_SIZE i, bot, top;
+PCRE2_SPTR ptr = *ptrptr;
+PCRE2_UCHAR name[50];
+PCRE2_UCHAR *vptr = NULL;
+uint16_t ptscript = PT_NOTSCRIPT;
+
+if (ptr >= cb->end_pattern) goto ERROR_RETURN;
+c = *ptr++;
+*negptr = FALSE;
+
+/* \P or \p can be followed by a name in {}, optionally preceded by ^ for
+negation. */
+
+if (c == CHAR_LEFT_CURLY_BRACKET)
+ {
+ if (ptr >= cb->end_pattern) goto ERROR_RETURN;
+
+ if (*ptr == CHAR_CIRCUMFLEX_ACCENT)
+ {
+ *negptr = TRUE;
+ ptr++;
+ }
+
+ for (i = 0; i < (int)(sizeof(name) / sizeof(PCRE2_UCHAR)) - 1; i++)
+ {
+ if (ptr >= cb->end_pattern) goto ERROR_RETURN;
+ c = *ptr++;
+ while (c == '_' || c == '-' || isspace(c))
+ {
+ if (ptr >= cb->end_pattern) goto ERROR_RETURN;
+ c = *ptr++;
+ }
+ if (c == CHAR_NUL) goto ERROR_RETURN;
+ if (c == CHAR_RIGHT_CURLY_BRACKET) break;
+ name[i] = tolower(c);
+ if ((c == ':' || c == '=') && vptr == NULL) vptr = name + i;
+ }
+
+ if (c != CHAR_RIGHT_CURLY_BRACKET) goto ERROR_RETURN;
+ name[i] = 0;
+ }
+
+/* If { doesn't follow \p or \P there is just one following character, which
+must be an ASCII letter. */
+
+else if (MAX_255(c) && (cb->ctypes[c] & ctype_letter) != 0)
+ {
+ name[0] = tolower(c);
+ name[1] = 0;
+ }
+else goto ERROR_RETURN;
+
+*ptrptr = ptr;
+
+/* If the property contains ':' or '=' we have class name and value separately
+specified. The following are supported:
+
+ . Bidi_Class (synonym bc), for which the property names are "bidi<name>".
+ . Script (synonym sc) for which the property name is the script name
+ . Script_Extensions (synonym scx), ditto
+
+As this is a small number, we currently just check the names directly. If this
+grows, a sorted table and a switch will be neater.
+
+For both the script properties, set a PT_xxx value so that (1) they can be
+distinguished and (2) invalid script names that happen to be the name of
+another property can be diagnosed. */
+
+if (vptr != NULL)
+ {
+ int offset = 0;
+ PCRE2_UCHAR sname[8];
+
+ *vptr = 0; /* Terminate property name */
+ if (PRIV(strcmp_c8)(name, STRING_bidiclass) == 0 ||
+ PRIV(strcmp_c8)(name, STRING_bc) == 0)
+ {
+ offset = 4;
+ sname[0] = CHAR_b;
+ sname[1] = CHAR_i; /* There is no strcpy_c8 function */
+ sname[2] = CHAR_d;
+ sname[3] = CHAR_i;
+ }
+
+ else if (PRIV(strcmp_c8)(name, STRING_script) == 0 ||
+ PRIV(strcmp_c8)(name, STRING_sc) == 0)
+ ptscript = PT_SC;
+
+ else if (PRIV(strcmp_c8)(name, STRING_scriptextensions) == 0 ||
+ PRIV(strcmp_c8)(name, STRING_scx) == 0)
+ ptscript = PT_SCX;
+
+ else
+ {
+ *errorcodeptr = ERR47;
+ return FALSE;
+ }
+
+ /* Adjust the string in name[] as needed */
+
+ memmove(name + offset, vptr + 1, (name + i - vptr)*sizeof(PCRE2_UCHAR));
+ if (offset != 0) memmove(name, sname, offset*sizeof(PCRE2_UCHAR));
+ }
+
+/* Search for a recognized property using binary chop. */
+
+bot = 0;
+top = PRIV(utt_size);
+
+while (bot < top)
+ {
+ int r;
+ i = (bot + top) >> 1;
+ r = PRIV(strcmp_c8)(name, PRIV(utt_names) + PRIV(utt)[i].name_offset);
+
+ /* When a matching property is found, some extra checking is needed when the
+ \p{xx:yy} syntax is used and xx is either sc or scx. */
+
+ if (r == 0)
+ {
+ *pdataptr = PRIV(utt)[i].value;
+ if (vptr == NULL || ptscript == PT_NOTSCRIPT)
+ {
+ *ptypeptr = PRIV(utt)[i].type;
+ return TRUE;
+ }
+
+ switch (PRIV(utt)[i].type)
+ {
+ case PT_SC:
+ *ptypeptr = PT_SC;
+ return TRUE;
+
+ case PT_SCX:
+ *ptypeptr = ptscript;
+ return TRUE;
+ }
+
+ break; /* Non-script found */
+ }
+
+ if (r > 0) bot = i + 1; else top = i;
+ }
+
+*errorcodeptr = ERR47; /* Unrecognized property */
+return FALSE;
+
+ERROR_RETURN: /* Malformed \P or \p */
+*errorcodeptr = ERR46;
+*ptrptr = ptr;
+return FALSE;
+}
+#endif
+
+
+
+/*************************************************
+* Check for POSIX class syntax *
+*************************************************/
+
+/* This function is called when the sequence "[:" or "[." or "[=" is
+encountered in a character class. It checks whether this is followed by a
+sequence of characters terminated by a matching ":]" or ".]" or "=]". If we
+reach an unescaped ']' without the special preceding character, return FALSE.
+
+Originally, this function only recognized a sequence of letters between the
+terminators, but it seems that Perl recognizes any sequence of characters,
+though of course unknown POSIX names are subsequently rejected. Perl gives an
+"Unknown POSIX class" error for [:f\oo:] for example, where previously PCRE
+didn't consider this to be a POSIX class. Likewise for [:1234:].
+
+The problem in trying to be exactly like Perl is in the handling of escapes. We
+have to be sure that [abc[:x\]pqr] is *not* treated as containing a POSIX
+class, but [abc[:x\]pqr:]] is (so that an error can be generated). The code
+below handles the special cases \\ and \], but does not try to do any other
+escape processing. This makes it different from Perl for cases such as
+[:l\ower:] where Perl recognizes it as the POSIX class "lower" but PCRE does
+not recognize "l\ower". This is a lesser evil than not diagnosing bad classes
+when Perl does, I think.
+
+A user pointed out that PCRE was rejecting [:a[:digit:]] whereas Perl was not.
+It seems that the appearance of a nested POSIX class supersedes an apparent
+external class. For example, [:a[:digit:]b:] matches "a", "b", ":", or
+a digit. This is handled by returning FALSE if the start of a new group with
+the same terminator is encountered, since the next closing sequence must close
+the nested group, not the outer one.
+
+In Perl, unescaped square brackets may also appear as part of class names. For
+example, [:a[:abc]b:] gives unknown POSIX class "[:abc]b:]". However, for
+[:a[:abc]b][b:] it gives unknown POSIX class "[:abc]b][b:]", which does not
+seem right at all. PCRE does not allow closing square brackets in POSIX class
+names.
+
+Arguments:
+ ptr pointer to the character after the initial [ (colon, dot, equals)
+ ptrend pointer to the end of the pattern
+ endptr where to return a pointer to the terminating ':', '.', or '='
+
+Returns: TRUE or FALSE
+*/
+
+static BOOL
+check_posix_syntax(PCRE2_SPTR ptr, PCRE2_SPTR ptrend, PCRE2_SPTR *endptr)
+{
+PCRE2_UCHAR terminator; /* Don't combine these lines; the Solaris cc */
+terminator = *ptr++; /* compiler warns about "non-constant" initializer. */
+
+for (; ptrend - ptr >= 2; ptr++)
+ {
+ if (*ptr == CHAR_BACKSLASH &&
+ (ptr[1] == CHAR_RIGHT_SQUARE_BRACKET || ptr[1] == CHAR_BACKSLASH))
+ ptr++;
+
+ else if ((*ptr == CHAR_LEFT_SQUARE_BRACKET && ptr[1] == terminator) ||
+ *ptr == CHAR_RIGHT_SQUARE_BRACKET) return FALSE;
+
+ else if (*ptr == terminator && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ *endptr = ptr;
+ return TRUE;
+ }
+ }
+
+return FALSE;
+}
+
+
+
+/*************************************************
+* Check POSIX class name *
+*************************************************/
+
+/* This function is called to check the name given in a POSIX-style class entry
+such as [:alnum:].
+
+Arguments:
+ ptr points to the first letter
+ len the length of the name
+
+Returns: a value representing the name, or -1 if unknown
+*/
+
+static int
+check_posix_name(PCRE2_SPTR ptr, int len)
+{
+const char *pn = posix_names;
+int yield = 0;
+while (posix_name_lengths[yield] != 0)
+ {
+ if (len == posix_name_lengths[yield] &&
+ PRIV(strncmp_c8)(ptr, pn, (unsigned int)len) == 0) return yield;
+ pn += posix_name_lengths[yield] + 1;
+ yield++;
+ }
+return -1;
+}
+
+
+
+/*************************************************
+* Read a subpattern or VERB name *
+*************************************************/
+
+/* This function is called from parse_regex() below whenever it needs to read
+the name of a subpattern or a (*VERB) or an (*alpha_assertion). The initial
+pointer must be to the character before the name. If that character is '*' we
+are reading a verb or alpha assertion name. The pointer is updated to point
+after the name, for a VERB or alpha assertion name, or after tha name's
+terminator for a subpattern name. Returning both the offset and the name
+pointer is redundant information, but some callers use one and some the other,
+so it is simplest just to return both.
+
+Arguments:
+ ptrptr points to the character pointer variable
+ ptrend points to the end of the input string
+ utf true if the input is UTF-encoded
+ terminator the terminator of a subpattern name must be this
+ offsetptr where to put the offset from the start of the pattern
+ nameptr where to put a pointer to the name in the input
+ namelenptr where to put the length of the name
+ errcodeptr where to put an error code
+ cb pointer to the compile data block
+
+Returns: TRUE if a name was read
+ FALSE otherwise, with error code set
+*/
+
+static BOOL
+read_name(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, BOOL utf, uint32_t terminator,
+ PCRE2_SIZE *offsetptr, PCRE2_SPTR *nameptr, uint32_t *namelenptr,
+ int *errorcodeptr, compile_block *cb)
+{
+PCRE2_SPTR ptr = *ptrptr;
+BOOL is_group = (*ptr != CHAR_ASTERISK);
+
+if (++ptr >= ptrend) /* No characters in name */
+ {
+ *errorcodeptr = is_group? ERR62: /* Subpattern name expected */
+ ERR60; /* Verb not recognized or malformed */
+ goto FAILED;
+ }
+
+*nameptr = ptr;
+*offsetptr = (PCRE2_SIZE)(ptr - cb->start_pattern);
+
+/* In UTF mode, a group name may contain letters and decimal digits as defined
+by Unicode properties, and underscores, but must not start with a digit. */
+
+#ifdef SUPPORT_UNICODE
+if (utf && is_group)
+ {
+ uint32_t c, type;
+
+ GETCHAR(c, ptr);
+ type = UCD_CHARTYPE(c);
+
+ if (type == ucp_Nd)
+ {
+ *errorcodeptr = ERR44;
+ goto FAILED;
+ }
+
+ for(;;)
+ {
+ if (type != ucp_Nd && PRIV(ucp_gentype)[type] != ucp_L &&
+ c != CHAR_UNDERSCORE) break;
+ ptr++;
+ FORWARDCHARTEST(ptr, ptrend);
+ if (ptr >= ptrend) break;
+ GETCHAR(c, ptr);
+ type = UCD_CHARTYPE(c);
+ }
+ }
+else
+#else
+(void)utf; /* Avoid compiler warning */
+#endif /* SUPPORT_UNICODE */
+
+/* Handle non-group names and group names in non-UTF modes. A group name must
+not start with a digit. If either of the others start with a digit it just
+won't be recognized. */
+
+ {
+ if (is_group && IS_DIGIT(*ptr))
+ {
+ *errorcodeptr = ERR44;
+ goto FAILED;
+ }
+
+ while (ptr < ptrend && MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype_word) != 0)
+ {
+ ptr++;
+ }
+ }
+
+/* Check name length */
+
+if (ptr > *nameptr + MAX_NAME_SIZE)
+ {
+ *errorcodeptr = ERR48;
+ goto FAILED;
+ }
+*namelenptr = (uint32_t)(ptr - *nameptr);
+
+/* Subpattern names must not be empty, and their terminator is checked here.
+(What follows a verb or alpha assertion name is checked separately.) */
+
+if (is_group)
+ {
+ if (ptr == *nameptr)
+ {
+ *errorcodeptr = ERR62; /* Subpattern name expected */
+ goto FAILED;
+ }
+ if (ptr >= ptrend || *ptr != (PCRE2_UCHAR)terminator)
+ {
+ *errorcodeptr = ERR42;
+ goto FAILED;
+ }
+ ptr++;
+ }
+
+*ptrptr = ptr;
+return TRUE;
+
+FAILED:
+*ptrptr = ptr;
+return FALSE;
+}
+
+
+
+/*************************************************
+* Manage callouts at start of cycle *
+*************************************************/
+
+/* At the start of a new item in parse_regex() we are able to record the
+details of the previous item in a prior callout, and also to set up an
+automatic callout if enabled. Avoid having two adjacent automatic callouts,
+which would otherwise happen for items such as \Q that contribute nothing to
+the parsed pattern.
+
+Arguments:
+ ptr current pattern pointer
+ pcalloutptr points to a pointer to previous callout, or NULL
+ auto_callout TRUE if auto_callouts are enabled
+ parsed_pattern the parsed pattern pointer
+ cb compile block
+
+Returns: possibly updated parsed_pattern pointer.
+*/
+
+static uint32_t *
+manage_callouts(PCRE2_SPTR ptr, uint32_t **pcalloutptr, BOOL auto_callout,
+ uint32_t *parsed_pattern, compile_block *cb)
+{
+uint32_t *previous_callout = *pcalloutptr;
+
+if (previous_callout != NULL) previous_callout[2] = (uint32_t)(ptr -
+ cb->start_pattern - (PCRE2_SIZE)previous_callout[1]);
+
+if (!auto_callout) previous_callout = NULL; else
+ {
+ if (previous_callout == NULL ||
+ previous_callout != parsed_pattern - 4 ||
+ previous_callout[3] != 255)
+ {
+ previous_callout = parsed_pattern; /* Set up new automatic callout */
+ parsed_pattern += 4;
+ previous_callout[0] = META_CALLOUT_NUMBER;
+ previous_callout[2] = 0;
+ previous_callout[3] = 255;
+ }
+ previous_callout[1] = (uint32_t)(ptr - cb->start_pattern);
+ }
+
+*pcalloutptr = previous_callout;
+return parsed_pattern;
+}
+
+
+
+/*************************************************
+* Parse regex and identify named groups *
+*************************************************/
+
+/* This function is called first of all. It scans the pattern and does two
+things: (1) It identifies capturing groups and makes a table of named capturing
+groups so that information about them is fully available to both the compiling
+scans. (2) It writes a parsed version of the pattern with comments omitted and
+escapes processed into the parsed_pattern vector.
+
+Arguments:
+ ptr points to the start of the pattern
+ options compiling dynamic options (may change during the scan)
+ has_lookbehind points to a boolean, set TRUE if a lookbehind is found
+ cb pointer to the compile data block
+
+Returns: zero on success or a non-zero error code, with the
+ error offset placed in the cb field
+*/
+
+/* A structure and some flags for dealing with nested groups. */
+
+typedef struct nest_save {
+ uint16_t nest_depth;
+ uint16_t reset_group;
+ uint16_t max_group;
+ uint16_t flags;
+ uint32_t options;
+} nest_save;
+
+#define NSF_RESET 0x0001u
+#define NSF_CONDASSERT 0x0002u
+#define NSF_ATOMICSR 0x0004u
+
+/* Options that are changeable within the pattern must be tracked during
+parsing. Some (e.g. PCRE2_EXTENDED) are implemented entirely during parsing,
+but all must be tracked so that META_OPTIONS items set the correct values for
+the main compiling phase. */
+
+#define PARSE_TRACKED_OPTIONS (PCRE2_CASELESS|PCRE2_DOTALL|PCRE2_DUPNAMES| \
+ PCRE2_EXTENDED|PCRE2_EXTENDED_MORE|PCRE2_MULTILINE|PCRE2_NO_AUTO_CAPTURE| \
+ PCRE2_UNGREEDY)
+
+/* States used for analyzing ranges in character classes. The two OK values
+must be last. */
+
+enum { RANGE_NO, RANGE_STARTED, RANGE_OK_ESCAPED, RANGE_OK_LITERAL };
+
+/* Only in 32-bit mode can there be literals > META_END. A macro encapsulates
+the storing of literal values in the main parsed pattern, where they can always
+be quantified. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+#define PARSED_LITERAL(c, p) \
+ { \
+ if (c >= META_END) *p++ = META_BIGVALUE; \
+ *p++ = c; \
+ okquantifier = TRUE; \
+ }
+#else
+#define PARSED_LITERAL(c, p) *p++ = c; okquantifier = TRUE;
+#endif
+
+/* Here's the actual function. */
+
+static int parse_regex(PCRE2_SPTR ptr, uint32_t options, BOOL *has_lookbehind,
+ compile_block *cb)
+{
+uint32_t c;
+uint32_t delimiter;
+uint32_t namelen;
+uint32_t class_range_state;
+uint32_t *verblengthptr = NULL; /* Value avoids compiler warning */
+uint32_t *verbstartptr = NULL;
+uint32_t *previous_callout = NULL;
+uint32_t *parsed_pattern = cb->parsed_pattern;
+uint32_t *parsed_pattern_end = cb->parsed_pattern_end;
+uint32_t meta_quantifier = 0;
+uint32_t add_after_mark = 0;
+uint32_t extra_options = cb->cx->extra_options;
+uint16_t nest_depth = 0;
+int after_manual_callout = 0;
+int expect_cond_assert = 0;
+int errorcode = 0;
+int escape;
+int i;
+BOOL inescq = FALSE;
+BOOL inverbname = FALSE;
+BOOL utf = (options & PCRE2_UTF) != 0;
+BOOL auto_callout = (options & PCRE2_AUTO_CALLOUT) != 0;
+BOOL isdupname;
+BOOL negate_class;
+BOOL okquantifier = FALSE;
+PCRE2_SPTR thisptr;
+PCRE2_SPTR name;
+PCRE2_SPTR ptrend = cb->end_pattern;
+PCRE2_SPTR verbnamestart = NULL; /* Value avoids compiler warning */
+named_group *ng;
+nest_save *top_nest, *end_nests;
+
+/* Insert leading items for word and line matching (features provided for the
+benefit of pcre2grep). */
+
+if ((extra_options & PCRE2_EXTRA_MATCH_LINE) != 0)
+ {
+ *parsed_pattern++ = META_CIRCUMFLEX;
+ *parsed_pattern++ = META_NOCAPTURE;
+ }
+else if ((extra_options & PCRE2_EXTRA_MATCH_WORD) != 0)
+ {
+ *parsed_pattern++ = META_ESCAPE + ESC_b;
+ *parsed_pattern++ = META_NOCAPTURE;
+ }
+
+/* If the pattern is actually a literal string, process it separately to avoid
+cluttering up the main loop. */
+
+if ((options & PCRE2_LITERAL) != 0)
+ {
+ while (ptr < ptrend)
+ {
+ if (parsed_pattern >= parsed_pattern_end)
+ {
+ errorcode = ERR63; /* Internal error (parsed pattern overflow) */
+ goto FAILED;
+ }
+ thisptr = ptr;
+ GETCHARINCTEST(c, ptr);
+ if (auto_callout)
+ parsed_pattern = manage_callouts(thisptr, &previous_callout,
+ auto_callout, parsed_pattern, cb);
+ PARSED_LITERAL(c, parsed_pattern);
+ }
+ goto PARSED_END;
+ }
+
+/* Process a real regex which may contain meta-characters. */
+
+top_nest = NULL;
+end_nests = (nest_save *)(cb->start_workspace + cb->workspace_size);
+
+/* The size of the nest_save structure might not be a factor of the size of the
+workspace. Therefore we must round down end_nests so as to correctly avoid
+creating a nest_save that spans the end of the workspace. */
+
+end_nests = (nest_save *)((char *)end_nests -
+ ((cb->workspace_size * sizeof(PCRE2_UCHAR)) % sizeof(nest_save)));
+
+/* PCRE2_EXTENDED_MORE implies PCRE2_EXTENDED */
+
+if ((options & PCRE2_EXTENDED_MORE) != 0) options |= PCRE2_EXTENDED;
+
+/* Now scan the pattern */
+
+while (ptr < ptrend)
+ {
+ int prev_expect_cond_assert;
+ uint32_t min_repeat = 0, max_repeat = 0;
+ uint32_t set, unset, *optset;
+ uint32_t terminator;
+ uint32_t prev_meta_quantifier;
+ BOOL prev_okquantifier;
+ PCRE2_SPTR tempptr;
+ PCRE2_SIZE offset;
+
+ if (parsed_pattern >= parsed_pattern_end)
+ {
+ errorcode = ERR63; /* Internal error (parsed pattern overflow) */
+ goto FAILED;
+ }
+
+ if (nest_depth > cb->cx->parens_nest_limit)
+ {
+ errorcode = ERR19;
+ goto FAILED; /* Parentheses too deeply nested */
+ }
+
+ /* Get next input character, save its position for callout handling. */
+
+ thisptr = ptr;
+ GETCHARINCTEST(c, ptr);
+
+ /* Copy quoted literals until \E, allowing for the possibility of automatic
+ callouts, except when processing a (*VERB) "name". */
+
+ if (inescq)
+ {
+ if (c == CHAR_BACKSLASH && ptr < ptrend && *ptr == CHAR_E)
+ {
+ inescq = FALSE;
+ ptr++; /* Skip E */
+ }
+ else
+ {
+ if (expect_cond_assert > 0) /* A literal is not allowed if we are */
+ { /* expecting a conditional assertion, */
+ ptr--; /* but an empty \Q\E sequence is OK. */
+ errorcode = ERR28;
+ goto FAILED;
+ }
+ if (inverbname)
+ { /* Don't use PARSED_LITERAL() because it */
+#if PCRE2_CODE_UNIT_WIDTH == 32 /* sets okquantifier. */
+ if (c >= META_END) *parsed_pattern++ = META_BIGVALUE;
+#endif
+ *parsed_pattern++ = c;
+ }
+ else
+ {
+ if (after_manual_callout-- <= 0)
+ parsed_pattern = manage_callouts(thisptr, &previous_callout,
+ auto_callout, parsed_pattern, cb);
+ PARSED_LITERAL(c, parsed_pattern);
+ }
+ meta_quantifier = 0;
+ }
+ continue; /* Next character */
+ }
+
+ /* If we are processing the "name" part of a (*VERB:NAME) item, all
+ characters up to the closing parenthesis are literals except when
+ PCRE2_ALT_VERBNAMES is set. That causes backslash interpretation, but only \Q
+ and \E and escaped characters are allowed (no character types such as \d). If
+ PCRE2_EXTENDED is also set, we must ignore white space and # comments. Do
+ this by not entering the special (*VERB:NAME) processing - they are then
+ picked up below. Note that c is a character, not a code unit, so we must not
+ use MAX_255 to test its size because MAX_255 tests code units and is assumed
+ TRUE in 8-bit mode. */
+
+ if (inverbname &&
+ (
+ /* EITHER: not both options set */
+ ((options & (PCRE2_EXTENDED | PCRE2_ALT_VERBNAMES)) !=
+ (PCRE2_EXTENDED | PCRE2_ALT_VERBNAMES)) ||
+#ifdef SUPPORT_UNICODE
+ /* OR: character > 255 AND not Unicode Pattern White Space */
+ (c > 255 && (c|1) != 0x200f && (c|1) != 0x2029) ||
+#endif
+ /* OR: not a # comment or isspace() white space */
+ (c < 256 && c != CHAR_NUMBER_SIGN && (cb->ctypes[c] & ctype_space) == 0
+#ifdef SUPPORT_UNICODE
+ /* and not CHAR_NEL when Unicode is supported */
+ && c != CHAR_NEL
+#endif
+ )))
+ {
+ PCRE2_SIZE verbnamelength;
+
+ switch(c)
+ {
+ default: /* Don't use PARSED_LITERAL() because it */
+#if PCRE2_CODE_UNIT_WIDTH == 32 /* sets okquantifier. */
+ if (c >= META_END) *parsed_pattern++ = META_BIGVALUE;
+#endif
+ *parsed_pattern++ = c;
+ break;
+
+ case CHAR_RIGHT_PARENTHESIS:
+ inverbname = FALSE;
+ /* This is the length in characters */
+ verbnamelength = (PCRE2_SIZE)(parsed_pattern - verblengthptr - 1);
+ /* But the limit on the length is in code units */
+ if (ptr - verbnamestart - 1 > (int)MAX_MARK)
+ {
+ ptr--;
+ errorcode = ERR76;
+ goto FAILED;
+ }
+ *verblengthptr = (uint32_t)verbnamelength;
+
+ /* If this name was on a verb such as (*ACCEPT) which does not continue,
+ a (*MARK) was generated for the name. We now add the original verb as the
+ next item. */
+
+ if (add_after_mark != 0)
+ {
+ *parsed_pattern++ = add_after_mark;
+ add_after_mark = 0;
+ }
+ break;
+
+ case CHAR_BACKSLASH:
+ if ((options & PCRE2_ALT_VERBNAMES) != 0)
+ {
+ escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options,
+ cb->cx->extra_options, FALSE, cb);
+ if (errorcode != 0) goto FAILED;
+ }
+ else escape = 0; /* Treat all as literal */
+
+ switch(escape)
+ {
+ case 0: /* Don't use PARSED_LITERAL() because it */
+#if PCRE2_CODE_UNIT_WIDTH == 32 /* sets okquantifier. */
+ if (c >= META_END) *parsed_pattern++ = META_BIGVALUE;
+#endif
+ *parsed_pattern++ = c;
+ break;
+
+ case ESC_Q:
+ inescq = TRUE;
+ break;
+
+ case ESC_E: /* Ignore */
+ break;
+
+ default:
+ errorcode = ERR40; /* Invalid in verb name */
+ goto FAILED;
+ }
+ }
+ continue; /* Next character in pattern */
+ }
+
+ /* Not a verb name character. At this point we must process everything that
+ must not change the quantification state. This is mainly comments, but we
+ handle \Q and \E here as well, so that an item such as A\Q\E+ is treated as
+ A+, as in Perl. An isolated \E is ignored. */
+
+ if (c == CHAR_BACKSLASH && ptr < ptrend)
+ {
+ if (*ptr == CHAR_Q || *ptr == CHAR_E)
+ {
+ inescq = *ptr == CHAR_Q;
+ ptr++;
+ continue;
+ }
+ }
+
+ /* Skip over whitespace and # comments in extended mode. Note that c is a
+ character, not a code unit, so we must not use MAX_255 to test its size
+ because MAX_255 tests code units and is assumed TRUE in 8-bit mode. The
+ whitespace characters are those designated as "Pattern White Space" by
+ Unicode, which are the isspace() characters plus CHAR_NEL (newline), which is
+ U+0085 in Unicode, plus U+200E, U+200F, U+2028, and U+2029. These are a
+ subset of space characters that match \h and \v. */
+
+ if ((options & PCRE2_EXTENDED) != 0)
+ {
+ if (c < 256 && (cb->ctypes[c] & ctype_space) != 0) continue;
+#ifdef SUPPORT_UNICODE
+ if (c == CHAR_NEL || (c|1) == 0x200f || (c|1) == 0x2029) continue;
+#endif
+ if (c == CHAR_NUMBER_SIGN)
+ {
+ while (ptr < ptrend)
+ {
+ if (IS_NEWLINE(ptr)) /* For non-fixed-length newline cases, */
+ { /* IS_NEWLINE sets cb->nllen. */
+ ptr += cb->nllen;
+ break;
+ }
+ ptr++;
+#ifdef SUPPORT_UNICODE
+ if (utf) FORWARDCHARTEST(ptr, ptrend);
+#endif
+ }
+ continue; /* Next character in pattern */
+ }
+ }
+
+ /* Skip over bracketed comments */
+
+ if (c == CHAR_LEFT_PARENTHESIS && ptrend - ptr >= 2 &&
+ ptr[0] == CHAR_QUESTION_MARK && ptr[1] == CHAR_NUMBER_SIGN)
+ {
+ while (++ptr < ptrend && *ptr != CHAR_RIGHT_PARENTHESIS);
+ if (ptr >= ptrend)
+ {
+ errorcode = ERR18; /* A special error for missing ) in a comment */
+ goto FAILED; /* to make it easier to debug. */
+ }
+ ptr++;
+ continue; /* Next character in pattern */
+ }
+
+ /* If the next item is not a quantifier, fill in length of any previous
+ callout and create an auto callout if required. */
+
+ if (c != CHAR_ASTERISK && c != CHAR_PLUS && c != CHAR_QUESTION_MARK &&
+ (c != CHAR_LEFT_CURLY_BRACKET ||
+ (tempptr = ptr,
+ !read_repeat_counts(&tempptr, ptrend, NULL, NULL, &errorcode))))
+ {
+ if (after_manual_callout-- <= 0)
+ parsed_pattern = manage_callouts(thisptr, &previous_callout, auto_callout,
+ parsed_pattern, cb);
+ }
+
+ /* If expect_cond_assert is 2, we have just passed (?( and are expecting an
+ assertion, possibly preceded by a callout. If the value is 1, we have just
+ had the callout and expect an assertion. There must be at least 3 more
+ characters in all cases. When expect_cond_assert is 2, we know that the
+ current character is an opening parenthesis, as otherwise we wouldn't be
+ here. However, when it is 1, we need to check, and it's easiest just to check
+ always. Note that expect_cond_assert may be negative, since all callouts just
+ decrement it. */
+
+ if (expect_cond_assert > 0)
+ {
+ BOOL ok = c == CHAR_LEFT_PARENTHESIS && ptrend - ptr >= 3 &&
+ (ptr[0] == CHAR_QUESTION_MARK || ptr[0] == CHAR_ASTERISK);
+ if (ok)
+ {
+ if (ptr[0] == CHAR_ASTERISK) /* New alpha assertion format, possibly */
+ {
+ ok = MAX_255(ptr[1]) && (cb->ctypes[ptr[1]] & ctype_lcletter) != 0;
+ }
+ else switch(ptr[1]) /* Traditional symbolic format */
+ {
+ case CHAR_C:
+ ok = expect_cond_assert == 2;
+ break;
+
+ case CHAR_EQUALS_SIGN:
+ case CHAR_EXCLAMATION_MARK:
+ break;
+
+ case CHAR_LESS_THAN_SIGN:
+ ok = ptr[2] == CHAR_EQUALS_SIGN || ptr[2] == CHAR_EXCLAMATION_MARK;
+ break;
+
+ default:
+ ok = FALSE;
+ }
+ }
+
+ if (!ok)
+ {
+ ptr--; /* Adjust error offset */
+ errorcode = ERR28;
+ goto FAILED;
+ }
+ }
+
+ /* Remember whether we are expecting a conditional assertion, and set the
+ default for this item. */
+
+ prev_expect_cond_assert = expect_cond_assert;
+ expect_cond_assert = 0;
+
+ /* Remember quantification status for the previous significant item, then set
+ default for this item. */
+
+ prev_okquantifier = okquantifier;
+ prev_meta_quantifier = meta_quantifier;
+ okquantifier = FALSE;
+ meta_quantifier = 0;
+
+ /* If the previous significant item was a quantifier, adjust the parsed code
+ if there is a following modifier. The base meta value is always followed by
+ the PLUS and QUERY values, in that order. We do this here rather than after
+ reading a quantifier so that intervening comments and /x whitespace can be
+ ignored without having to replicate code. */
+
+ if (prev_meta_quantifier != 0 && (c == CHAR_QUESTION_MARK || c == CHAR_PLUS))
+ {
+ parsed_pattern[(prev_meta_quantifier == META_MINMAX)? -3 : -1] =
+ prev_meta_quantifier + ((c == CHAR_QUESTION_MARK)?
+ 0x00020000u : 0x00010000u);
+ continue; /* Next character in pattern */
+ }
+
+
+ /* Process the next item in the main part of a pattern. */
+
+ switch(c)
+ {
+ default: /* Non-special character */
+ PARSED_LITERAL(c, parsed_pattern);
+ break;
+
+
+ /* ---- Escape sequence ---- */
+
+ case CHAR_BACKSLASH:
+ tempptr = ptr;
+ escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options,
+ cb->cx->extra_options, FALSE, cb);
+ if (errorcode != 0)
+ {
+ ESCAPE_FAILED:
+ if ((extra_options & PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) == 0)
+ goto FAILED;
+ ptr = tempptr;
+ if (ptr >= ptrend) c = CHAR_BACKSLASH; else
+ {
+ GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */
+ }
+ escape = 0; /* Treat as literal character */
+ }
+
+ /* The escape was a data escape or literal character. */
+
+ if (escape == 0)
+ {
+ PARSED_LITERAL(c, parsed_pattern);
+ }
+
+ /* The escape was a back (or forward) reference. We keep the offset in
+ order to give a more useful diagnostic for a bad forward reference. For
+ references to groups numbered less than 10 we can't use more than two items
+ in parsed_pattern because they may be just two characters in the input (and
+ in a 64-bit world an offset may need two elements). So for them, the offset
+ of the first occurrent is held in a special vector. */
+
+ else if (escape < 0)
+ {
+ offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 1);
+ escape = -escape;
+ *parsed_pattern++ = META_BACKREF | (uint32_t)escape;
+ if (escape < 10)
+ {
+ if (cb->small_ref_offset[escape] == PCRE2_UNSET)
+ cb->small_ref_offset[escape] = offset;
+ }
+ else
+ {
+ PUTOFFSET(offset, parsed_pattern);
+ }
+ okquantifier = TRUE;
+ }
+
+ /* The escape was a character class such as \d etc. or other special
+ escape indicator such as \A or \X. Most of them generate just a single
+ parsed item, but \P and \p are followed by a 16-bit type and a 16-bit
+ value. They are supported only when Unicode is available. The type and
+ value are packed into a single 32-bit value so that the whole sequences
+ uses only two elements in the parsed_vector. This is because the same
+ coding is used if \d (for example) is turned into \p{Nd} when PCRE2_UCP is
+ set.
+
+ There are also some cases where the escape sequence is followed by a name:
+ \k{name}, \k<name>, and \k'name' are backreferences by name, and \g<name>
+ and \g'name' are subroutine calls by name; \g{name} is a synonym for
+ \k{name}. Note that \g<number> and \g'number' are handled by check_escape()
+ and returned as a negative value (handled above). A name is coded as an
+ offset into the pattern and a length. */
+
+ else switch (escape)
+ {
+ case ESC_C:
+#ifdef NEVER_BACKSLASH_C
+ errorcode = ERR85;
+ goto ESCAPE_FAILED;
+#else
+ if ((options & PCRE2_NEVER_BACKSLASH_C) != 0)
+ {
+ errorcode = ERR83;
+ goto ESCAPE_FAILED;
+ }
+#endif
+ okquantifier = TRUE;
+ *parsed_pattern++ = META_ESCAPE + escape;
+ break;
+
+ case ESC_X:
+#ifndef SUPPORT_UNICODE
+ errorcode = ERR45; /* Supported only with Unicode support */
+ goto ESCAPE_FAILED;
+#endif
+ case ESC_H:
+ case ESC_h:
+ case ESC_N:
+ case ESC_R:
+ case ESC_V:
+ case ESC_v:
+ okquantifier = TRUE;
+ *parsed_pattern++ = META_ESCAPE + escape;
+ break;
+
+ default: /* \A, \B, \b, \G, \K, \Z, \z cannot be quantified. */
+ *parsed_pattern++ = META_ESCAPE + escape;
+ break;
+
+ /* Escapes that change in UCP mode. Note that PCRE2_UCP will never be set
+ without Unicode support because it is checked when pcre2_compile() is
+ called. */
+
+ case ESC_d:
+ case ESC_D:
+ case ESC_s:
+ case ESC_S:
+ case ESC_w:
+ case ESC_W:
+ okquantifier = TRUE;
+ if ((options & PCRE2_UCP) == 0)
+ {
+ *parsed_pattern++ = META_ESCAPE + escape;
+ }
+ else
+ {
+ *parsed_pattern++ = META_ESCAPE +
+ ((escape == ESC_d || escape == ESC_s || escape == ESC_w)?
+ ESC_p : ESC_P);
+ switch(escape)
+ {
+ case ESC_d:
+ case ESC_D:
+ *parsed_pattern++ = (PT_PC << 16) | ucp_Nd;
+ break;
+
+ case ESC_s:
+ case ESC_S:
+ *parsed_pattern++ = PT_SPACE << 16;
+ break;
+
+ case ESC_w:
+ case ESC_W:
+ *parsed_pattern++ = PT_WORD << 16;
+ break;
+ }
+ }
+ break;
+
+ /* Unicode property matching */
+
+ case ESC_P:
+ case ESC_p:
+#ifdef SUPPORT_UNICODE
+ {
+ BOOL negated;
+ uint16_t ptype = 0, pdata = 0;
+ if (!get_ucp(&ptr, &negated, &ptype, &pdata, &errorcode, cb))
+ goto ESCAPE_FAILED;
+ if (negated) escape = (escape == ESC_P)? ESC_p : ESC_P;
+ *parsed_pattern++ = META_ESCAPE + escape;
+ *parsed_pattern++ = (ptype << 16) | pdata;
+ okquantifier = TRUE;
+ }
+#else
+ errorcode = ERR45;
+ goto ESCAPE_FAILED;
+#endif
+ break; /* End \P and \p */
+
+ /* When \g is used with quotes or angle brackets as delimiters, it is a
+ numerical or named subroutine call, and control comes here. When used
+ with brace delimiters it is a numberical back reference and does not come
+ here because check_escape() returns it directly as a reference. \k is
+ always a named back reference. */
+
+ case ESC_g:
+ case ESC_k:
+ if (ptr >= ptrend || (*ptr != CHAR_LEFT_CURLY_BRACKET &&
+ *ptr != CHAR_LESS_THAN_SIGN && *ptr != CHAR_APOSTROPHE))
+ {
+ errorcode = (escape == ESC_g)? ERR57 : ERR69;
+ goto ESCAPE_FAILED;
+ }
+ terminator = (*ptr == CHAR_LESS_THAN_SIGN)?
+ CHAR_GREATER_THAN_SIGN : (*ptr == CHAR_APOSTROPHE)?
+ CHAR_APOSTROPHE : CHAR_RIGHT_CURLY_BRACKET;
+
+ /* For a non-braced \g, check for a numerical recursion. */
+
+ if (escape == ESC_g && terminator != CHAR_RIGHT_CURLY_BRACKET)
+ {
+ PCRE2_SPTR p = ptr + 1;
+
+ if (read_number(&p, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &i,
+ &errorcode))
+ {
+ if (p >= ptrend || *p != terminator)
+ {
+ errorcode = ERR57;
+ goto ESCAPE_FAILED;
+ }
+ ptr = p;
+ goto SET_RECURSION;
+ }
+ if (errorcode != 0) goto ESCAPE_FAILED;
+ }
+
+ /* Not a numerical recursion */
+
+ if (!read_name(&ptr, ptrend, utf, terminator, &offset, &name, &namelen,
+ &errorcode, cb)) goto ESCAPE_FAILED;
+
+ /* \k and \g when used with braces are back references, whereas \g used
+ with quotes or angle brackets is a recursion */
+
+ *parsed_pattern++ =
+ (escape == ESC_k || terminator == CHAR_RIGHT_CURLY_BRACKET)?
+ META_BACKREF_BYNAME : META_RECURSE_BYNAME;
+ *parsed_pattern++ = namelen;
+
+ PUTOFFSET(offset, parsed_pattern);
+ okquantifier = TRUE;
+ break; /* End special escape processing */
+ }
+ break; /* End escape sequence processing */
+
+
+ /* ---- Single-character special items ---- */
+
+ case CHAR_CIRCUMFLEX_ACCENT:
+ *parsed_pattern++ = META_CIRCUMFLEX;
+ break;
+
+ case CHAR_DOLLAR_SIGN:
+ *parsed_pattern++ = META_DOLLAR;
+ break;
+
+ case CHAR_DOT:
+ *parsed_pattern++ = META_DOT;
+ okquantifier = TRUE;
+ break;
+
+
+ /* ---- Single-character quantifiers ---- */
+
+ case CHAR_ASTERISK:
+ meta_quantifier = META_ASTERISK;
+ goto CHECK_QUANTIFIER;
+
+ case CHAR_PLUS:
+ meta_quantifier = META_PLUS;
+ goto CHECK_QUANTIFIER;
+
+ case CHAR_QUESTION_MARK:
+ meta_quantifier = META_QUERY;
+ goto CHECK_QUANTIFIER;
+
+
+ /* ---- Potential {n,m} quantifier ---- */
+
+ case CHAR_LEFT_CURLY_BRACKET:
+ if (!read_repeat_counts(&ptr, ptrend, &min_repeat, &max_repeat,
+ &errorcode))
+ {
+ if (errorcode != 0) goto FAILED; /* Error in quantifier. */
+ PARSED_LITERAL(c, parsed_pattern); /* Not a quantifier */
+ break; /* No more quantifier processing */
+ }
+ meta_quantifier = META_MINMAX;
+ /* Fall through */
+
+
+ /* ---- Quantifier post-processing ---- */
+
+ /* Check that a quantifier is allowed after the previous item. */
+
+ CHECK_QUANTIFIER:
+ if (!prev_okquantifier)
+ {
+ errorcode = ERR9;
+ goto FAILED_BACK;
+ }
+
+ /* Most (*VERB)s are not allowed to be quantified, but an ungreedy
+ quantifier can be useful for (*ACCEPT) - meaning "succeed on backtrack", a
+ sort of negated (*COMMIT). We therefore allow (*ACCEPT) to be quantified by
+ wrapping it in non-capturing brackets, but we have to allow for a preceding
+ (*MARK) for when (*ACCEPT) has an argument. */
+
+ if (parsed_pattern[-1] == META_ACCEPT)
+ {
+ uint32_t *p;
+ for (p = parsed_pattern - 1; p >= verbstartptr; p--) p[1] = p[0];
+ *verbstartptr = META_NOCAPTURE;
+ parsed_pattern[1] = META_KET;
+ parsed_pattern += 2;
+ }
+
+ /* Now we can put the quantifier into the parsed pattern vector. At this
+ stage, we have only the basic quantifier. The check for a following + or ?
+ modifier happens at the top of the loop, after any intervening comments
+ have been removed. */
+
+ *parsed_pattern++ = meta_quantifier;
+ if (c == CHAR_LEFT_CURLY_BRACKET)
+ {
+ *parsed_pattern++ = min_repeat;
+ *parsed_pattern++ = max_repeat;
+ }
+ break;
+
+
+ /* ---- Character class ---- */
+
+ case CHAR_LEFT_SQUARE_BRACKET:
+ okquantifier = TRUE;
+
+ /* In another (POSIX) regex library, the ugly syntax [[:<:]] and [[:>:]] is
+ used for "start of word" and "end of word". As these are otherwise illegal
+ sequences, we don't break anything by recognizing them. They are replaced
+ by \b(?=\w) and \b(?<=\w) respectively. Sequences like [a[:<:]] are
+ erroneous and are handled by the normal code below. */
+
+ if (ptrend - ptr >= 6 &&
+ (PRIV(strncmp_c8)(ptr, STRING_WEIRD_STARTWORD, 6) == 0 ||
+ PRIV(strncmp_c8)(ptr, STRING_WEIRD_ENDWORD, 6) == 0))
+ {
+ *parsed_pattern++ = META_ESCAPE + ESC_b;
+
+ if (ptr[2] == CHAR_LESS_THAN_SIGN)
+ {
+ *parsed_pattern++ = META_LOOKAHEAD;
+ }
+ else
+ {
+ *parsed_pattern++ = META_LOOKBEHIND;
+ *has_lookbehind = TRUE;
+
+ /* The offset is used only for the "non-fixed length" error; this won't
+ occur here, so just store zero. */
+
+ PUTOFFSET((PCRE2_SIZE)0, parsed_pattern);
+ }
+
+ if ((options & PCRE2_UCP) == 0)
+ *parsed_pattern++ = META_ESCAPE + ESC_w;
+ else
+ {
+ *parsed_pattern++ = META_ESCAPE + ESC_p;
+ *parsed_pattern++ = PT_WORD << 16;
+ }
+ *parsed_pattern++ = META_KET;
+ ptr += 6;
+ break;
+ }
+
+ /* PCRE supports POSIX class stuff inside a class. Perl gives an error if
+ they are encountered at the top level, so we'll do that too. */
+
+ if (ptr < ptrend && (*ptr == CHAR_COLON || *ptr == CHAR_DOT ||
+ *ptr == CHAR_EQUALS_SIGN) &&
+ check_posix_syntax(ptr, ptrend, &tempptr))
+ {
+ errorcode = (*ptr-- == CHAR_COLON)? ERR12 : ERR13;
+ goto FAILED;
+ }
+
+ /* Process a regular character class. If the first character is '^', set
+ the negation flag. If the first few characters (either before or after ^)
+ are \Q\E or \E or space or tab in extended-more mode, we skip them too.
+ This makes for compatibility with Perl. */
+
+ negate_class = FALSE;
+ while (ptr < ptrend)
+ {
+ GETCHARINCTEST(c, ptr);
+ if (c == CHAR_BACKSLASH)
+ {
+ if (ptr < ptrend && *ptr == CHAR_E) ptr++;
+ else if (ptrend - ptr >= 3 &&
+ PRIV(strncmp_c8)(ptr, STR_Q STR_BACKSLASH STR_E, 3) == 0)
+ ptr += 3;
+ else
+ break;
+ }
+ else if ((options & PCRE2_EXTENDED_MORE) != 0 &&
+ (c == CHAR_SPACE || c == CHAR_HT)) /* Note: just these two */
+ continue;
+ else if (!negate_class && c == CHAR_CIRCUMFLEX_ACCENT)
+ negate_class = TRUE;
+ else break;
+ }
+
+ /* Now the real contents of the class; c has the first "real" character.
+ Empty classes are permitted only if the option is set. */
+
+ if (c == CHAR_RIGHT_SQUARE_BRACKET &&
+ (cb->external_options & PCRE2_ALLOW_EMPTY_CLASS) != 0)
+ {
+ *parsed_pattern++ = negate_class? META_CLASS_EMPTY_NOT : META_CLASS_EMPTY;
+ break; /* End of class processing */
+ }
+
+ /* Process a non-empty class. */
+
+ *parsed_pattern++ = negate_class? META_CLASS_NOT : META_CLASS;
+ class_range_state = RANGE_NO;
+
+ /* In an EBCDIC environment, Perl treats alphabetic ranges specially
+ because there are holes in the encoding, and simply using the range A-Z
+ (for example) would include the characters in the holes. This applies only
+ to ranges where both values are literal; [\xC1-\xE9] is different to [A-Z]
+ in this respect. In order to accommodate this, we keep track of whether
+ character values are literal or not, and a state variable for handling
+ ranges. */
+
+ /* Loop for the contents of the class */
+
+ for (;;)
+ {
+ BOOL char_is_literal = TRUE;
+
+ /* Inside \Q...\E everything is literal except \E */
+
+ if (inescq)
+ {
+ if (c == CHAR_BACKSLASH && ptr < ptrend && *ptr == CHAR_E)
+ {
+ inescq = FALSE; /* Reset literal state */
+ ptr++; /* Skip the 'E' */
+ goto CLASS_CONTINUE;
+ }
+ goto CLASS_LITERAL;
+ }
+
+ /* Skip over space and tab (only) in extended-more mode. */
+
+ if ((options & PCRE2_EXTENDED_MORE) != 0 &&
+ (c == CHAR_SPACE || c == CHAR_HT))
+ goto CLASS_CONTINUE;
+
+ /* Handle POSIX class names. Perl allows a negation extension of the
+ form [:^name:]. A square bracket that doesn't match the syntax is
+ treated as a literal. We also recognize the POSIX constructions
+ [.ch.] and [=ch=] ("collating elements") and fault them, as Perl
+ 5.6 and 5.8 do. */
+
+ if (c == CHAR_LEFT_SQUARE_BRACKET &&
+ ptrend - ptr >= 3 &&
+ (*ptr == CHAR_COLON || *ptr == CHAR_DOT ||
+ *ptr == CHAR_EQUALS_SIGN) &&
+ check_posix_syntax(ptr, ptrend, &tempptr))
+ {
+ BOOL posix_negate = FALSE;
+ int posix_class;
+
+ /* Perl treats a hyphen before a POSIX class as a literal, not the
+ start of a range. However, it gives a warning in its warning mode. PCRE
+ does not have a warning mode, so we give an error, because this is
+ likely an error on the user's part. */
+
+ if (class_range_state == RANGE_STARTED)
+ {
+ errorcode = ERR50;
+ goto FAILED;
+ }
+
+ if (*ptr != CHAR_COLON)
+ {
+ errorcode = ERR13;
+ goto FAILED_BACK;
+ }
+
+ if (*(++ptr) == CHAR_CIRCUMFLEX_ACCENT)
+ {
+ posix_negate = TRUE;
+ ptr++;
+ }
+
+ posix_class = check_posix_name(ptr, (int)(tempptr - ptr));
+ if (posix_class < 0)
+ {
+ errorcode = ERR30;
+ goto FAILED;
+ }
+ ptr = tempptr + 2;
+
+ /* Perl treats a hyphen after a POSIX class as a literal, not the
+ start of a range. However, it gives a warning in its warning mode
+ unless the hyphen is the last character in the class. PCRE does not
+ have a warning mode, so we give an error, because this is likely an
+ error on the user's part. */
+
+ if (ptr < ptrend - 1 && *ptr == CHAR_MINUS &&
+ ptr[1] != CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ errorcode = ERR50;
+ goto FAILED;
+ }
+
+ /* Set "a hyphen is not the start of a range" for the -] case, and also
+ in case the POSIX class is followed by \E or \Q\E (possibly repeated -
+ fuzzers do that kind of thing) and *then* a hyphen. This causes that
+ hyphen to be treated as a literal. I don't think it's worth setting up
+ special apparatus to do otherwise. */
+
+ class_range_state = RANGE_NO;
+
+ /* When PCRE2_UCP is set, some of the POSIX classes are converted to
+ use Unicode properties \p or \P or, in one case, \h or \H. The
+ substitutes table has two values per class, containing the type and
+ value of a \p or \P item. The special cases are specified with a
+ negative type: a non-zero value causes \h or \H to be used, and a zero
+ value falls through to behave like a non-UCP POSIX class. */
+
+#ifdef SUPPORT_UNICODE
+ if ((options & PCRE2_UCP) != 0)
+ {
+ int ptype = posix_substitutes[2*posix_class];
+ int pvalue = posix_substitutes[2*posix_class + 1];
+ if (ptype >= 0)
+ {
+ *parsed_pattern++ = META_ESCAPE + (posix_negate? ESC_P : ESC_p);
+ *parsed_pattern++ = (ptype << 16) | pvalue;
+ goto CLASS_CONTINUE;
+ }
+
+ if (pvalue != 0)
+ {
+ *parsed_pattern++ = META_ESCAPE + (posix_negate? ESC_H : ESC_h);
+ goto CLASS_CONTINUE;
+ }
+
+ /* Fall through */
+ }
+#endif /* SUPPORT_UNICODE */
+
+ /* Non-UCP POSIX class */
+
+ *parsed_pattern++ = posix_negate? META_POSIX_NEG : META_POSIX;
+ *parsed_pattern++ = posix_class;
+ }
+
+ /* Handle potential start of range */
+
+ else if (c == CHAR_MINUS && class_range_state >= RANGE_OK_ESCAPED)
+ {
+ *parsed_pattern++ = (class_range_state == RANGE_OK_LITERAL)?
+ META_RANGE_LITERAL : META_RANGE_ESCAPED;
+ class_range_state = RANGE_STARTED;
+ }
+
+ /* Handle a literal character */
+
+ else if (c != CHAR_BACKSLASH)
+ {
+ CLASS_LITERAL:
+ if (class_range_state == RANGE_STARTED)
+ {
+ if (c == parsed_pattern[-2]) /* Optimize one-char range */
+ parsed_pattern--;
+ else if (parsed_pattern[-2] > c) /* Check range is in order */
+ {
+ errorcode = ERR8;
+ goto FAILED_BACK;
+ }
+ else
+ {
+ if (!char_is_literal && parsed_pattern[-1] == META_RANGE_LITERAL)
+ parsed_pattern[-1] = META_RANGE_ESCAPED;
+ PARSED_LITERAL(c, parsed_pattern);
+ }
+ class_range_state = RANGE_NO;
+ }
+ else /* Potential start of range */
+ {
+ class_range_state = char_is_literal?
+ RANGE_OK_LITERAL : RANGE_OK_ESCAPED;
+ PARSED_LITERAL(c, parsed_pattern);
+ }
+ }
+
+ /* Handle escapes in a class */
+
+ else
+ {
+ tempptr = ptr;
+ escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options,
+ cb->cx->extra_options, TRUE, cb);
+
+ if (errorcode != 0)
+ {
+ if ((extra_options & PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) == 0)
+ goto FAILED;
+ ptr = tempptr;
+ if (ptr >= ptrend) c = CHAR_BACKSLASH; else
+ {
+ GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */
+ }
+ escape = 0; /* Treat as literal character */
+ }
+
+ switch(escape)
+ {
+ case 0: /* Escaped character code point is in c */
+ char_is_literal = FALSE;
+ goto CLASS_LITERAL;
+
+ case ESC_b:
+ c = CHAR_BS; /* \b is backspace in a class */
+ char_is_literal = FALSE;
+ goto CLASS_LITERAL;
+
+ case ESC_Q:
+ inescq = TRUE; /* Enter literal mode */
+ goto CLASS_CONTINUE;
+
+ case ESC_E: /* Ignore orphan \E */
+ goto CLASS_CONTINUE;
+
+ case ESC_B: /* Always an error in a class */
+ case ESC_R:
+ case ESC_X:
+ errorcode = ERR7;
+ ptr--;
+ goto FAILED;
+ }
+
+ /* The second part of a range can be a single-character escape
+ sequence (detected above), but not any of the other escapes. Perl
+ treats a hyphen as a literal in such circumstances. However, in Perl's
+ warning mode, a warning is given, so PCRE now faults it, as it is
+ almost certainly a mistake on the user's part. */
+
+ if (class_range_state == RANGE_STARTED)
+ {
+ errorcode = ERR50;
+ goto FAILED; /* Not CLASS_ESCAPE_FAILED; always an error */
+ }
+
+ /* Of the remaining escapes, only those that define characters are
+ allowed in a class. None may start a range. */
+
+ class_range_state = RANGE_NO;
+ switch(escape)
+ {
+ case ESC_N:
+ errorcode = ERR71;
+ goto FAILED;
+
+ case ESC_H:
+ case ESC_h:
+ case ESC_V:
+ case ESC_v:
+ *parsed_pattern++ = META_ESCAPE + escape;
+ break;
+
+ /* These escapes are converted to Unicode property tests when
+ PCRE2_UCP is set. */
+
+ case ESC_d:
+ case ESC_D:
+ case ESC_s:
+ case ESC_S:
+ case ESC_w:
+ case ESC_W:
+ if ((options & PCRE2_UCP) == 0)
+ {
+ *parsed_pattern++ = META_ESCAPE + escape;
+ }
+ else
+ {
+ *parsed_pattern++ = META_ESCAPE +
+ ((escape == ESC_d || escape == ESC_s || escape == ESC_w)?
+ ESC_p : ESC_P);
+ switch(escape)
+ {
+ case ESC_d:
+ case ESC_D:
+ *parsed_pattern++ = (PT_PC << 16) | ucp_Nd;
+ break;
+
+ case ESC_s:
+ case ESC_S:
+ *parsed_pattern++ = PT_SPACE << 16;
+ break;
+
+ case ESC_w:
+ case ESC_W:
+ *parsed_pattern++ = PT_WORD << 16;
+ break;
+ }
+ }
+ break;
+
+ /* Explicit Unicode property matching */
+
+ case ESC_P:
+ case ESC_p:
+#ifdef SUPPORT_UNICODE
+ {
+ BOOL negated;
+ uint16_t ptype = 0, pdata = 0;
+ if (!get_ucp(&ptr, &negated, &ptype, &pdata, &errorcode, cb))
+ goto FAILED;
+ if (negated) escape = (escape == ESC_P)? ESC_p : ESC_P;
+ *parsed_pattern++ = META_ESCAPE + escape;
+ *parsed_pattern++ = (ptype << 16) | pdata;
+ }
+#else
+ errorcode = ERR45;
+ goto FAILED;
+#endif
+ break; /* End \P and \p */
+
+ default: /* All others are not allowed in a class */
+ errorcode = ERR7;
+ ptr--;
+ goto FAILED;
+ }
+
+ /* Perl gives a warning unless a following hyphen is the last character
+ in the class. PCRE throws an error. */
+
+ if (ptr < ptrend - 1 && *ptr == CHAR_MINUS &&
+ ptr[1] != CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ errorcode = ERR50;
+ goto FAILED;
+ }
+ }
+
+ /* Proceed to next thing in the class. */
+
+ CLASS_CONTINUE:
+ if (ptr >= ptrend)
+ {
+ errorcode = ERR6; /* Missing terminating ']' */
+ goto FAILED;
+ }
+ GETCHARINCTEST(c, ptr);
+ if (c == CHAR_RIGHT_SQUARE_BRACKET && !inescq) break;
+ } /* End of class-processing loop */
+
+ /* -] at the end of a class is a literal '-' */
+
+ if (class_range_state == RANGE_STARTED)
+ {
+ parsed_pattern[-1] = CHAR_MINUS;
+ class_range_state = RANGE_NO;
+ }
+
+ *parsed_pattern++ = META_CLASS_END;
+ break; /* End of character class */
+
+
+ /* ---- Opening parenthesis ---- */
+
+ case CHAR_LEFT_PARENTHESIS:
+ if (ptr >= ptrend) goto UNCLOSED_PARENTHESIS;
+
+ /* If ( is not followed by ? it is either a capture or a special verb or an
+ alpha assertion or a positive non-atomic lookahead. */
+
+ if (*ptr != CHAR_QUESTION_MARK)
+ {
+ const char *vn;
+
+ /* Handle capturing brackets (or non-capturing if auto-capture is turned
+ off). */
+
+ if (*ptr != CHAR_ASTERISK)
+ {
+ nest_depth++;
+ if ((options & PCRE2_NO_AUTO_CAPTURE) == 0)
+ {
+ if (cb->bracount >= MAX_GROUP_NUMBER)
+ {
+ errorcode = ERR97;
+ goto FAILED;
+ }
+ cb->bracount++;
+ *parsed_pattern++ = META_CAPTURE | cb->bracount;
+ }
+ else *parsed_pattern++ = META_NOCAPTURE;
+ }
+
+ /* Do nothing for (* followed by end of pattern or ) so it gives a "bad
+ quantifier" error rather than "(*MARK) must have an argument". */
+
+ else if (ptrend - ptr <= 1 || (c = ptr[1]) == CHAR_RIGHT_PARENTHESIS)
+ break;
+
+ /* Handle "alpha assertions" such as (*pla:...). Most of these are
+ synonyms for the historical symbolic assertions, but the script run and
+ non-atomic lookaround ones are new. They are distinguished by starting
+ with a lower case letter. Checking both ends of the alphabet makes this
+ work in all character codes. */
+
+ else if (CHMAX_255(c) && (cb->ctypes[c] & ctype_lcletter) != 0)
+ {
+ uint32_t meta;
+
+ vn = alasnames;
+ if (!read_name(&ptr, ptrend, utf, 0, &offset, &name, &namelen,
+ &errorcode, cb)) goto FAILED;
+ if (ptr >= ptrend || *ptr != CHAR_COLON)
+ {
+ errorcode = ERR95; /* Malformed */
+ goto FAILED;
+ }
+
+ /* Scan the table of alpha assertion names */
+
+ for (i = 0; i < alascount; i++)
+ {
+ if (namelen == alasmeta[i].len &&
+ PRIV(strncmp_c8)(name, vn, namelen) == 0)
+ break;
+ vn += alasmeta[i].len + 1;
+ }
+
+ if (i >= alascount)
+ {
+ errorcode = ERR95; /* Alpha assertion not recognized */
+ goto FAILED;
+ }
+
+ /* Check for expecting an assertion condition. If so, only atomic
+ lookaround assertions are valid. */
+
+ meta = alasmeta[i].meta;
+ if (prev_expect_cond_assert > 0 &&
+ (meta < META_LOOKAHEAD || meta > META_LOOKBEHINDNOT))
+ {
+ errorcode = (meta == META_LOOKAHEAD_NA || meta == META_LOOKBEHIND_NA)?
+ ERR98 : ERR28; /* (Atomic) assertion expected */
+ goto FAILED;
+ }
+
+ /* The lookaround alphabetic synonyms can mostly be handled by jumping
+ to the code that handles the traditional symbolic forms. */
+
+ switch(meta)
+ {
+ default:
+ errorcode = ERR89; /* Unknown code; should never occur because */
+ goto FAILED; /* the meta values come from a table above. */
+
+ case META_ATOMIC:
+ goto ATOMIC_GROUP;
+
+ case META_LOOKAHEAD:
+ goto POSITIVE_LOOK_AHEAD;
+
+ case META_LOOKAHEAD_NA:
+ goto POSITIVE_NONATOMIC_LOOK_AHEAD;
+
+ case META_LOOKAHEADNOT:
+ goto NEGATIVE_LOOK_AHEAD;
+
+ case META_LOOKBEHIND:
+ case META_LOOKBEHINDNOT:
+ case META_LOOKBEHIND_NA:
+ *parsed_pattern++ = meta;
+ ptr--;
+ goto POST_LOOKBEHIND;
+
+ /* The script run facilities are handled here. Unicode support is
+ required (give an error if not, as this is a security issue). Always
+ record a META_SCRIPT_RUN item. Then, for the atomic version, insert
+ META_ATOMIC and remember that we need two META_KETs at the end. */
+
+ case META_SCRIPT_RUN:
+ case META_ATOMIC_SCRIPT_RUN:
+#ifdef SUPPORT_UNICODE
+ *parsed_pattern++ = META_SCRIPT_RUN;
+ nest_depth++;
+ ptr++;
+ if (meta == META_ATOMIC_SCRIPT_RUN)
+ {
+ *parsed_pattern++ = META_ATOMIC;
+ if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace);
+ else if (++top_nest >= end_nests)
+ {
+ errorcode = ERR84;
+ goto FAILED;
+ }
+ top_nest->nest_depth = nest_depth;
+ top_nest->flags = NSF_ATOMICSR;
+ top_nest->options = options & PARSE_TRACKED_OPTIONS;
+ }
+ break;
+#else /* SUPPORT_UNICODE */
+ errorcode = ERR96;
+ goto FAILED;
+#endif
+ }
+ }
+
+
+ /* ---- Handle (*VERB) and (*VERB:NAME) ---- */
+
+ else
+ {
+ vn = verbnames;
+ if (!read_name(&ptr, ptrend, utf, 0, &offset, &name, &namelen,
+ &errorcode, cb)) goto FAILED;
+ if (ptr >= ptrend || (*ptr != CHAR_COLON &&
+ *ptr != CHAR_RIGHT_PARENTHESIS))
+ {
+ errorcode = ERR60; /* Malformed */
+ goto FAILED;
+ }
+
+ /* Scan the table of verb names */
+
+ for (i = 0; i < verbcount; i++)
+ {
+ if (namelen == verbs[i].len &&
+ PRIV(strncmp_c8)(name, vn, namelen) == 0)
+ break;
+ vn += verbs[i].len + 1;
+ }
+
+ if (i >= verbcount)
+ {
+ errorcode = ERR60; /* Verb not recognized */
+ goto FAILED;
+ }
+
+ /* An empty argument is treated as no argument. */
+
+ if (*ptr == CHAR_COLON && ptr + 1 < ptrend &&
+ ptr[1] == CHAR_RIGHT_PARENTHESIS)
+ ptr++; /* Advance to the closing parens */
+
+ /* Check for mandatory non-empty argument; this is (*MARK) */
+
+ if (verbs[i].has_arg > 0 && *ptr != CHAR_COLON)
+ {
+ errorcode = ERR66;
+ goto FAILED;
+ }
+
+ /* Remember where this verb, possibly with a preceding (*MARK), starts,
+ for handling quantified (*ACCEPT). */
+
+ verbstartptr = parsed_pattern;
+ okquantifier = (verbs[i].meta == META_ACCEPT);
+
+ /* It appears that Perl allows any characters whatsoever, other than a
+ closing parenthesis, to appear in arguments ("names"), so we no longer
+ insist on letters, digits, and underscores. Perl does not, however, do
+ any interpretation within arguments, and has no means of including a
+ closing parenthesis. PCRE supports escape processing but only when it
+ is requested by an option. We set inverbname TRUE here, and let the
+ main loop take care of this so that escape and \x processing is done by
+ the main code above. */
+
+ if (*ptr++ == CHAR_COLON) /* Skip past : or ) */
+ {
+ /* Some optional arguments can be treated as a preceding (*MARK) */
+
+ if (verbs[i].has_arg < 0)
+ {
+ add_after_mark = verbs[i].meta;
+ *parsed_pattern++ = META_MARK;
+ }
+
+ /* The remaining verbs with arguments (except *MARK) need a different
+ opcode. */
+
+ else
+ {
+ *parsed_pattern++ = verbs[i].meta +
+ ((verbs[i].meta != META_MARK)? 0x00010000u:0);
+ }
+
+ /* Set up for reading the name in the main loop. */
+
+ verblengthptr = parsed_pattern++;
+ verbnamestart = ptr;
+ inverbname = TRUE;
+ }
+ else /* No verb "name" argument */
+ {
+ *parsed_pattern++ = verbs[i].meta;
+ }
+ } /* End of (*VERB) handling */
+ break; /* Done with this parenthesis */
+ } /* End of groups that don't start with (? */
+
+
+ /* ---- Items starting (? ---- */
+
+ /* The type of item is determined by what follows (?. Handle (?| and option
+ changes under "default" because both need a new block on the nest stack.
+ Comments starting with (?# are handled above. Note that there is some
+ ambiguity about the sequence (?- because if a digit follows it's a relative
+ recursion or subroutine call whereas otherwise it's an option unsetting. */
+
+ if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS;
+
+ switch(*ptr)
+ {
+ default:
+ if (*ptr == CHAR_MINUS && ptrend - ptr > 1 && IS_DIGIT(ptr[1]))
+ goto RECURSION_BYNUMBER; /* The + case is handled by CHAR_PLUS */
+
+ /* We now have either (?| or a (possibly empty) option setting,
+ optionally followed by a non-capturing group. */
+
+ nest_depth++;
+ if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace);
+ else if (++top_nest >= end_nests)
+ {
+ errorcode = ERR84;
+ goto FAILED;
+ }
+ top_nest->nest_depth = nest_depth;
+ top_nest->flags = 0;
+ top_nest->options = options & PARSE_TRACKED_OPTIONS;
+
+ /* Start of non-capturing group that resets the capture count for each
+ branch. */
+
+ if (*ptr == CHAR_VERTICAL_LINE)
+ {
+ top_nest->reset_group = (uint16_t)cb->bracount;
+ top_nest->max_group = (uint16_t)cb->bracount;
+ top_nest->flags |= NSF_RESET;
+ cb->external_flags |= PCRE2_DUPCAPUSED;
+ *parsed_pattern++ = META_NOCAPTURE;
+ ptr++;
+ }
+
+ /* Scan for options imnsxJU to be set or unset. */
+
+ else
+ {
+ BOOL hyphenok = TRUE;
+ uint32_t oldoptions = options;
+
+ top_nest->reset_group = 0;
+ top_nest->max_group = 0;
+ set = unset = 0;
+ optset = &set;
+
+ /* ^ at the start unsets imnsx and disables the subsequent use of - */
+
+ if (ptr < ptrend && *ptr == CHAR_CIRCUMFLEX_ACCENT)
+ {
+ options &= ~(PCRE2_CASELESS|PCRE2_MULTILINE|PCRE2_NO_AUTO_CAPTURE|
+ PCRE2_DOTALL|PCRE2_EXTENDED|PCRE2_EXTENDED_MORE);
+ hyphenok = FALSE;
+ ptr++;
+ }
+
+ while (ptr < ptrend && *ptr != CHAR_RIGHT_PARENTHESIS &&
+ *ptr != CHAR_COLON)
+ {
+ switch (*ptr++)
+ {
+ case CHAR_MINUS:
+ if (!hyphenok)
+ {
+ errorcode = ERR94;
+ ptr--; /* Correct the offset */
+ goto FAILED;
+ }
+ optset = &unset;
+ hyphenok = FALSE;
+ break;
+
+ case CHAR_J: /* Record that it changed in the external options */
+ *optset |= PCRE2_DUPNAMES;
+ cb->external_flags |= PCRE2_JCHANGED;
+ break;
+
+ case CHAR_i: *optset |= PCRE2_CASELESS; break;
+ case CHAR_m: *optset |= PCRE2_MULTILINE; break;
+ case CHAR_n: *optset |= PCRE2_NO_AUTO_CAPTURE; break;
+ case CHAR_s: *optset |= PCRE2_DOTALL; break;
+ case CHAR_U: *optset |= PCRE2_UNGREEDY; break;
+
+ /* If x appears twice it sets the extended extended option. */
+
+ case CHAR_x:
+ *optset |= PCRE2_EXTENDED;
+ if (ptr < ptrend && *ptr == CHAR_x)
+ {
+ *optset |= PCRE2_EXTENDED_MORE;
+ ptr++;
+ }
+ break;
+
+ default:
+ errorcode = ERR11;
+ ptr--; /* Correct the offset */
+ goto FAILED;
+ }
+ }
+
+ /* If we are setting extended without extended-more, ensure that any
+ existing extended-more gets unset. Also, unsetting extended must also
+ unset extended-more. */
+
+ if ((set & (PCRE2_EXTENDED|PCRE2_EXTENDED_MORE)) == PCRE2_EXTENDED ||
+ (unset & PCRE2_EXTENDED) != 0)
+ unset |= PCRE2_EXTENDED_MORE;
+
+ options = (options | set) & (~unset);
+
+ /* If the options ended with ')' this is not the start of a nested
+ group with option changes, so the options change at this level.
+ In this case, if the previous level set up a nest block, discard the
+ one we have just created. Otherwise adjust it for the previous level.
+ If the options ended with ':' we are starting a non-capturing group,
+ possibly with an options setting. */
+
+ if (ptr >= ptrend) goto UNCLOSED_PARENTHESIS;
+ if (*ptr++ == CHAR_RIGHT_PARENTHESIS)
+ {
+ nest_depth--; /* This is not a nested group after all. */
+ if (top_nest > (nest_save *)(cb->start_workspace) &&
+ (top_nest-1)->nest_depth == nest_depth) top_nest--;
+ else top_nest->nest_depth = nest_depth;
+ }
+ else *parsed_pattern++ = META_NOCAPTURE;
+
+ /* If nothing changed, no need to record. */
+
+ if (options != oldoptions)
+ {
+ *parsed_pattern++ = META_OPTIONS;
+ *parsed_pattern++ = options;
+ }
+ } /* End options processing */
+ break; /* End default case after (? */
+
+
+ /* ---- Python syntax support ---- */
+
+ case CHAR_P:
+ if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS;
+
+ /* (?P<name> is the same as (?<name>, which defines a named group. */
+
+ if (*ptr == CHAR_LESS_THAN_SIGN)
+ {
+ terminator = CHAR_GREATER_THAN_SIGN;
+ goto DEFINE_NAME;
+ }
+
+ /* (?P>name) is the same as (?&name), which is a recursion or subroutine
+ call. */
+
+ if (*ptr == CHAR_GREATER_THAN_SIGN) goto RECURSE_BY_NAME;
+
+ /* (?P=name) is the same as \k<name>, a back reference by name. Anything
+ else after (?P is an error. */
+
+ if (*ptr != CHAR_EQUALS_SIGN)
+ {
+ errorcode = ERR41;
+ goto FAILED;
+ }
+ if (!read_name(&ptr, ptrend, utf, CHAR_RIGHT_PARENTHESIS, &offset, &name,
+ &namelen, &errorcode, cb)) goto FAILED;
+ *parsed_pattern++ = META_BACKREF_BYNAME;
+ *parsed_pattern++ = namelen;
+ PUTOFFSET(offset, parsed_pattern);
+ okquantifier = TRUE;
+ break; /* End of (?P processing */
+
+
+ /* ---- Recursion/subroutine calls by number ---- */
+
+ case CHAR_R:
+ i = 0; /* (?R) == (?R0) */
+ ptr++;
+ if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS)
+ {
+ errorcode = ERR58;
+ goto FAILED;
+ }
+ goto SET_RECURSION;
+
+ /* An item starting (?- followed by a digit comes here via the "default"
+ case because (?- followed by a non-digit is an options setting. */
+
+ case CHAR_PLUS:
+ if (ptrend - ptr < 2 || !IS_DIGIT(ptr[1]))
+ {
+ errorcode = ERR29; /* Missing number */
+ goto FAILED;
+ }
+ /* Fall through */
+
+ case CHAR_0: case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4:
+ case CHAR_5: case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9:
+ RECURSION_BYNUMBER:
+ if (!read_number(&ptr, ptrend,
+ (IS_DIGIT(*ptr))? -1:(int)(cb->bracount), /* + and - are relative */
+ MAX_GROUP_NUMBER, ERR61,
+ &i, &errorcode)) goto FAILED;
+ if (i < 0) /* NB (?0) is permitted */
+ {
+ errorcode = ERR15; /* Unknown group */
+ goto FAILED_BACK;
+ }
+ if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS)
+ goto UNCLOSED_PARENTHESIS;
+
+ SET_RECURSION:
+ *parsed_pattern++ = META_RECURSE | (uint32_t)i;
+ offset = (PCRE2_SIZE)(ptr - cb->start_pattern);
+ ptr++;
+ PUTOFFSET(offset, parsed_pattern);
+ okquantifier = TRUE;
+ break; /* End of recursive call by number handling */
+
+
+ /* ---- Recursion/subroutine calls by name ---- */
+
+ case CHAR_AMPERSAND:
+ RECURSE_BY_NAME:
+ if (!read_name(&ptr, ptrend, utf, CHAR_RIGHT_PARENTHESIS, &offset, &name,
+ &namelen, &errorcode, cb)) goto FAILED;
+ *parsed_pattern++ = META_RECURSE_BYNAME;
+ *parsed_pattern++ = namelen;
+ PUTOFFSET(offset, parsed_pattern);
+ okquantifier = TRUE;
+ break;
+
+ /* ---- Callout with numerical or string argument ---- */
+
+ case CHAR_C:
+ if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS;
+
+ /* If the previous item was a condition starting (?(? an assertion,
+ optionally preceded by a callout, is expected. This is checked later on,
+ during actual compilation. However we need to identify this kind of
+ assertion in this pass because it must not be qualified. The value of
+ expect_cond_assert is set to 2 after (?(? is processed. We decrement it
+ for a callout - still leaving a positive value that identifies the
+ assertion. Multiple callouts or any other items will make it zero or
+ less, which doesn't matter because they will cause an error later. */
+
+ expect_cond_assert = prev_expect_cond_assert - 1;
+
+ /* If previous_callout is not NULL, it means this follows a previous
+ callout. If it was a manual callout, do nothing; this means its "length
+ of next pattern item" field will remain zero. If it was an automatic
+ callout, abolish it. */
+
+ if (previous_callout != NULL && (options & PCRE2_AUTO_CALLOUT) != 0 &&
+ previous_callout == parsed_pattern - 4 &&
+ parsed_pattern[-1] == 255)
+ parsed_pattern = previous_callout;
+
+ /* Save for updating next pattern item length, and skip one item before
+ completing. */
+
+ previous_callout = parsed_pattern;
+ after_manual_callout = 1;
+
+ /* Handle a string argument; specific delimiter is required. */
+
+ if (*ptr != CHAR_RIGHT_PARENTHESIS && !IS_DIGIT(*ptr))
+ {
+ PCRE2_SIZE calloutlength;
+ PCRE2_SPTR startptr = ptr;
+
+ delimiter = 0;
+ for (i = 0; PRIV(callout_start_delims)[i] != 0; i++)
+ {
+ if (*ptr == PRIV(callout_start_delims)[i])
+ {
+ delimiter = PRIV(callout_end_delims)[i];
+ break;
+ }
+ }
+ if (delimiter == 0)
+ {
+ errorcode = ERR82;
+ goto FAILED;
+ }
+
+ *parsed_pattern = META_CALLOUT_STRING;
+ parsed_pattern += 3; /* Skip pattern info */
+
+ for (;;)
+ {
+ if (++ptr >= ptrend)
+ {
+ errorcode = ERR81;
+ ptr = startptr; /* To give a more useful message */
+ goto FAILED;
+ }
+ if (*ptr == delimiter && (++ptr >= ptrend || *ptr != delimiter))
+ break;
+ }
+
+ calloutlength = (PCRE2_SIZE)(ptr - startptr);
+ if (calloutlength > UINT32_MAX)
+ {
+ errorcode = ERR72;
+ goto FAILED;
+ }
+ *parsed_pattern++ = (uint32_t)calloutlength;
+ offset = (PCRE2_SIZE)(startptr - cb->start_pattern);
+ PUTOFFSET(offset, parsed_pattern);
+ }
+
+ /* Handle a callout with an optional numerical argument, which must be
+ less than or equal to 255. A missing argument gives 0. */
+
+ else
+ {
+ int n = 0;
+ *parsed_pattern = META_CALLOUT_NUMBER; /* Numerical callout */
+ parsed_pattern += 3; /* Skip pattern info */
+ while (ptr < ptrend && IS_DIGIT(*ptr))
+ {
+ n = n * 10 + *ptr++ - CHAR_0;
+ if (n > 255)
+ {
+ errorcode = ERR38;
+ goto FAILED;
+ }
+ }
+ *parsed_pattern++ = n;
+ }
+
+ /* Both formats must have a closing parenthesis */
+
+ if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS)
+ {
+ errorcode = ERR39;
+ goto FAILED;
+ }
+ ptr++;
+
+ /* Remember the offset to the next item in the pattern, and set a default
+ length. This should get updated after the next item is read. */
+
+ previous_callout[1] = (uint32_t)(ptr - cb->start_pattern);
+ previous_callout[2] = 0;
+ break; /* End callout */
+
+
+ /* ---- Conditional group ---- */
+
+ /* A condition can be an assertion, a number (referring to a numbered
+ group's having been set), a name (referring to a named group), or 'R',
+ referring to overall recursion. R<digits> and R&name are also permitted
+ for recursion state tests. Numbers may be preceded by + or - to specify a
+ relative group number.
+
+ There are several syntaxes for testing a named group: (?(name)) is used
+ by Python; Perl 5.10 onwards uses (?(<name>) or (?('name')).
+
+ There are two unfortunate ambiguities. 'R' can be the recursive thing or
+ the name 'R' (and similarly for 'R' followed by digits). 'DEFINE' can be
+ the Perl DEFINE feature or the Python named test. We look for a name
+ first; if not found, we try the other case.
+
+ For compatibility with auto-callouts, we allow a callout to be specified
+ before a condition that is an assertion. */
+
+ case CHAR_LEFT_PARENTHESIS:
+ if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS;
+ nest_depth++;
+
+ /* If the next character is ? or * there must be an assertion next
+ (optionally preceded by a callout). We do not check this here, but
+ instead we set expect_cond_assert to 2. If this is still greater than
+ zero (callouts decrement it) when the next assertion is read, it will be
+ marked as a condition that must not be repeated. A value greater than
+ zero also causes checking that an assertion (possibly with callout)
+ follows. */
+
+ if (*ptr == CHAR_QUESTION_MARK || *ptr == CHAR_ASTERISK)
+ {
+ *parsed_pattern++ = META_COND_ASSERT;
+ ptr--; /* Pull pointer back to the opening parenthesis. */
+ expect_cond_assert = 2;
+ break; /* End of conditional */
+ }
+
+ /* Handle (?([+-]number)... */
+
+ if (read_number(&ptr, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &i,
+ &errorcode))
+ {
+ if (i <= 0)
+ {
+ errorcode = ERR15;
+ goto FAILED;
+ }
+ *parsed_pattern++ = META_COND_NUMBER;
+ offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 2);
+ PUTOFFSET(offset, parsed_pattern);
+ *parsed_pattern++ = i;
+ }
+ else if (errorcode != 0) goto FAILED; /* Number too big */
+
+ /* No number found. Handle the special case (?(VERSION[>]=n.m)... */
+
+ else if (ptrend - ptr >= 10 &&
+ PRIV(strncmp_c8)(ptr, STRING_VERSION, 7) == 0 &&
+ ptr[7] != CHAR_RIGHT_PARENTHESIS)
+ {
+ uint32_t ge = 0;
+ int major = 0;
+ int minor = 0;
+
+ ptr += 7;
+ if (*ptr == CHAR_GREATER_THAN_SIGN)
+ {
+ ge = 1;
+ ptr++;
+ }
+
+ /* NOTE: cannot write IS_DIGIT(*(++ptr)) here because IS_DIGIT
+ references its argument twice. */
+
+ if (*ptr != CHAR_EQUALS_SIGN || (ptr++, !IS_DIGIT(*ptr)))
+ goto BAD_VERSION_CONDITION;
+
+ if (!read_number(&ptr, ptrend, -1, 1000, ERR79, &major, &errorcode))
+ goto FAILED;
+
+ if (ptr >= ptrend) goto BAD_VERSION_CONDITION;
+ if (*ptr == CHAR_DOT)
+ {
+ if (++ptr >= ptrend || !IS_DIGIT(*ptr)) goto BAD_VERSION_CONDITION;
+ minor = (*ptr++ - CHAR_0) * 10;
+ if (ptr >= ptrend) goto BAD_VERSION_CONDITION;
+ if (IS_DIGIT(*ptr)) minor += *ptr++ - CHAR_0;
+ if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS)
+ goto BAD_VERSION_CONDITION;
+ }
+
+ *parsed_pattern++ = META_COND_VERSION;
+ *parsed_pattern++ = ge;
+ *parsed_pattern++ = major;
+ *parsed_pattern++ = minor;
+ }
+
+ /* All the remaining cases now require us to read a name. We cannot at
+ this stage distinguish ambiguous cases such as (?(R12) which might be a
+ recursion test by number or a name, because the named groups have not yet
+ all been identified. Those cases are treated as names, but given a
+ different META code. */
+
+ else
+ {
+ BOOL was_r_ampersand = FALSE;
+
+ if (*ptr == CHAR_R && ptrend - ptr > 1 && ptr[1] == CHAR_AMPERSAND)
+ {
+ terminator = CHAR_RIGHT_PARENTHESIS;
+ was_r_ampersand = TRUE;
+ ptr++;
+ }
+ else if (*ptr == CHAR_LESS_THAN_SIGN)
+ terminator = CHAR_GREATER_THAN_SIGN;
+ else if (*ptr == CHAR_APOSTROPHE)
+ terminator = CHAR_APOSTROPHE;
+ else
+ {
+ terminator = CHAR_RIGHT_PARENTHESIS;
+ ptr--; /* Point to char before name */
+ }
+ if (!read_name(&ptr, ptrend, utf, terminator, &offset, &name, &namelen,
+ &errorcode, cb)) goto FAILED;
+
+ /* Handle (?(R&name) */
+
+ if (was_r_ampersand)
+ {
+ *parsed_pattern = META_COND_RNAME;
+ ptr--; /* Back to closing parens */
+ }
+
+ /* Handle (?(name). If the name is "DEFINE" we identify it with a
+ special code. Likewise if the name consists of R followed only by
+ digits. Otherwise, handle it like a quoted name. */
+
+ else if (terminator == CHAR_RIGHT_PARENTHESIS)
+ {
+ if (namelen == 6 && PRIV(strncmp_c8)(name, STRING_DEFINE, 6) == 0)
+ *parsed_pattern = META_COND_DEFINE;
+ else
+ {
+ for (i = 1; i < (int)namelen; i++)
+ if (!IS_DIGIT(name[i])) break;
+ *parsed_pattern = (*name == CHAR_R && i >= (int)namelen)?
+ META_COND_RNUMBER : META_COND_NAME;
+ }
+ ptr--; /* Back to closing parens */
+ }
+
+ /* Handle (?('name') or (?(<name>) */
+
+ else *parsed_pattern = META_COND_NAME;
+
+ /* All these cases except DEFINE end with the name length and offset;
+ DEFINE just has an offset (for the "too many branches" error). */
+
+ if (*parsed_pattern++ != META_COND_DEFINE) *parsed_pattern++ = namelen;
+ PUTOFFSET(offset, parsed_pattern);
+ } /* End cases that read a name */
+
+ /* Check the closing parenthesis of the condition */
+
+ if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS)
+ {
+ errorcode = ERR24;
+ goto FAILED;
+ }
+ ptr++;
+ break; /* End of condition processing */
+
+
+ /* ---- Atomic group ---- */
+
+ case CHAR_GREATER_THAN_SIGN:
+ ATOMIC_GROUP: /* Come from (*atomic: */
+ *parsed_pattern++ = META_ATOMIC;
+ nest_depth++;
+ ptr++;
+ break;
+
+
+ /* ---- Lookahead assertions ---- */
+
+ case CHAR_EQUALS_SIGN:
+ POSITIVE_LOOK_AHEAD: /* Come from (*pla: */
+ *parsed_pattern++ = META_LOOKAHEAD;
+ ptr++;
+ goto POST_ASSERTION;
+
+ case CHAR_ASTERISK:
+ POSITIVE_NONATOMIC_LOOK_AHEAD: /* Come from (?* */
+ *parsed_pattern++ = META_LOOKAHEAD_NA;
+ ptr++;
+ goto POST_ASSERTION;
+
+ case CHAR_EXCLAMATION_MARK:
+ NEGATIVE_LOOK_AHEAD: /* Come from (*nla: */
+ *parsed_pattern++ = META_LOOKAHEADNOT;
+ ptr++;
+ goto POST_ASSERTION;
+
+
+ /* ---- Lookbehind assertions ---- */
+
+ /* (?< followed by = or ! or * is a lookbehind assertion. Otherwise (?<
+ is the start of the name of a capturing group. */
+
+ case CHAR_LESS_THAN_SIGN:
+ if (ptrend - ptr <= 1 ||
+ (ptr[1] != CHAR_EQUALS_SIGN &&
+ ptr[1] != CHAR_EXCLAMATION_MARK &&
+ ptr[1] != CHAR_ASTERISK))
+ {
+ terminator = CHAR_GREATER_THAN_SIGN;
+ goto DEFINE_NAME;
+ }
+ *parsed_pattern++ = (ptr[1] == CHAR_EQUALS_SIGN)?
+ META_LOOKBEHIND : (ptr[1] == CHAR_EXCLAMATION_MARK)?
+ META_LOOKBEHINDNOT : META_LOOKBEHIND_NA;
+
+ POST_LOOKBEHIND: /* Come from (*plb: (*naplb: and (*nlb: */
+ *has_lookbehind = TRUE;
+ offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 2);
+ PUTOFFSET(offset, parsed_pattern);
+ ptr += 2;
+ /* Fall through */
+
+ /* If the previous item was a condition starting (?(? an assertion,
+ optionally preceded by a callout, is expected. This is checked later on,
+ during actual compilation. However we need to identify this kind of
+ assertion in this pass because it must not be qualified. The value of
+ expect_cond_assert is set to 2 after (?(? is processed. We decrement it
+ for a callout - still leaving a positive value that identifies the
+ assertion. Multiple callouts or any other items will make it zero or
+ less, which doesn't matter because they will cause an error later. */
+
+ POST_ASSERTION:
+ nest_depth++;
+ if (prev_expect_cond_assert > 0)
+ {
+ if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace);
+ else if (++top_nest >= end_nests)
+ {
+ errorcode = ERR84;
+ goto FAILED;
+ }
+ top_nest->nest_depth = nest_depth;
+ top_nest->flags = NSF_CONDASSERT;
+ top_nest->options = options & PARSE_TRACKED_OPTIONS;
+ }
+ break;
+
+
+ /* ---- Define a named group ---- */
+
+ /* A named group may be defined as (?'name') or (?<name>). In the latter
+ case we jump to DEFINE_NAME from the disambiguation of (?< above with the
+ terminator set to '>'. */
+
+ case CHAR_APOSTROPHE:
+ terminator = CHAR_APOSTROPHE; /* Terminator */
+
+ DEFINE_NAME:
+ if (!read_name(&ptr, ptrend, utf, terminator, &offset, &name, &namelen,
+ &errorcode, cb)) goto FAILED;
+
+ /* We have a name for this capturing group. It is also assigned a number,
+ which is its primary means of identification. */
+
+ if (cb->bracount >= MAX_GROUP_NUMBER)
+ {
+ errorcode = ERR97;
+ goto FAILED;
+ }
+ cb->bracount++;
+ *parsed_pattern++ = META_CAPTURE | cb->bracount;
+ nest_depth++;
+
+ /* Check not too many names */
+
+ if (cb->names_found >= MAX_NAME_COUNT)
+ {
+ errorcode = ERR49;
+ goto FAILED;
+ }
+
+ /* Adjust the entry size to accommodate the longest name found. */
+
+ if (namelen + IMM2_SIZE + 1 > cb->name_entry_size)
+ cb->name_entry_size = (uint16_t)(namelen + IMM2_SIZE + 1);
+
+ /* Scan the list to check for duplicates. For duplicate names, if the
+ number is the same, break the loop, which causes the name to be
+ discarded; otherwise, if DUPNAMES is not set, give an error.
+ If it is set, allow the name with a different number, but continue
+ scanning in case this is a duplicate with the same number. For
+ non-duplicate names, give an error if the number is duplicated. */
+
+ isdupname = FALSE;
+ ng = cb->named_groups;
+ for (i = 0; i < cb->names_found; i++, ng++)
+ {
+ if (namelen == ng->length &&
+ PRIV(strncmp)(name, ng->name, (PCRE2_SIZE)namelen) == 0)
+ {
+ if (ng->number == cb->bracount) break;
+ if ((options & PCRE2_DUPNAMES) == 0)
+ {
+ errorcode = ERR43;
+ goto FAILED;
+ }
+ isdupname = ng->isdup = TRUE; /* Mark as a duplicate */
+ cb->dupnames = TRUE; /* Duplicate names exist */
+ }
+ else if (ng->number == cb->bracount)
+ {
+ errorcode = ERR65;
+ goto FAILED;
+ }
+ }
+
+ if (i < cb->names_found) break; /* Ignore duplicate with same number */
+
+ /* Increase the list size if necessary */
+
+ if (cb->names_found >= cb->named_group_list_size)
+ {
+ uint32_t newsize = cb->named_group_list_size * 2;
+ named_group *newspace =
+ cb->cx->memctl.malloc(newsize * sizeof(named_group),
+ cb->cx->memctl.memory_data);
+ if (newspace == NULL)
+ {
+ errorcode = ERR21;
+ goto FAILED;
+ }
+
+ memcpy(newspace, cb->named_groups,
+ cb->named_group_list_size * sizeof(named_group));
+ if (cb->named_group_list_size > NAMED_GROUP_LIST_SIZE)
+ cb->cx->memctl.free((void *)cb->named_groups,
+ cb->cx->memctl.memory_data);
+ cb->named_groups = newspace;
+ cb->named_group_list_size = newsize;
+ }
+
+ /* Add this name to the list */
+
+ cb->named_groups[cb->names_found].name = name;
+ cb->named_groups[cb->names_found].length = (uint16_t)namelen;
+ cb->named_groups[cb->names_found].number = cb->bracount;
+ cb->named_groups[cb->names_found].isdup = (uint16_t)isdupname;
+ cb->names_found++;
+ break;
+ } /* End of (? switch */
+ break; /* End of ( handling */
+
+
+ /* ---- Branch terminators ---- */
+
+ /* Alternation: reset the capture count if we are in a (?| group. */
+
+ case CHAR_VERTICAL_LINE:
+ if (top_nest != NULL && top_nest->nest_depth == nest_depth &&
+ (top_nest->flags & NSF_RESET) != 0)
+ {
+ if (cb->bracount > top_nest->max_group)
+ top_nest->max_group = (uint16_t)cb->bracount;
+ cb->bracount = top_nest->reset_group;
+ }
+ *parsed_pattern++ = META_ALT;
+ break;
+
+ /* End of group; reset the capture count to the maximum if we are in a (?|
+ group and/or reset the options that are tracked during parsing. Disallow
+ quantifier for a condition that is an assertion. */
+
+ case CHAR_RIGHT_PARENTHESIS:
+ okquantifier = TRUE;
+ if (top_nest != NULL && top_nest->nest_depth == nest_depth)
+ {
+ options = (options & ~PARSE_TRACKED_OPTIONS) | top_nest->options;
+ if ((top_nest->flags & NSF_RESET) != 0 &&
+ top_nest->max_group > cb->bracount)
+ cb->bracount = top_nest->max_group;
+ if ((top_nest->flags & NSF_CONDASSERT) != 0)
+ okquantifier = FALSE;
+
+ if ((top_nest->flags & NSF_ATOMICSR) != 0)
+ {
+ *parsed_pattern++ = META_KET;
+ }
+
+ if (top_nest == (nest_save *)(cb->start_workspace)) top_nest = NULL;
+ else top_nest--;
+ }
+ if (nest_depth == 0) /* Unmatched closing parenthesis */
+ {
+ errorcode = ERR22;
+ goto FAILED_BACK;
+ }
+ nest_depth--;
+ *parsed_pattern++ = META_KET;
+ break;
+ } /* End of switch on pattern character */
+ } /* End of main character scan loop */
+
+/* End of pattern reached. Check for missing ) at the end of a verb name. */
+
+if (inverbname && ptr >= ptrend)
+ {
+ errorcode = ERR60;
+ goto FAILED;
+ }
+
+/* Manage callout for the final item */
+
+PARSED_END:
+parsed_pattern = manage_callouts(ptr, &previous_callout, auto_callout,
+ parsed_pattern, cb);
+
+/* Insert trailing items for word and line matching (features provided for the
+benefit of pcre2grep). */
+
+if ((extra_options & PCRE2_EXTRA_MATCH_LINE) != 0)
+ {
+ *parsed_pattern++ = META_KET;
+ *parsed_pattern++ = META_DOLLAR;
+ }
+else if ((extra_options & PCRE2_EXTRA_MATCH_WORD) != 0)
+ {
+ *parsed_pattern++ = META_KET;
+ *parsed_pattern++ = META_ESCAPE + ESC_b;
+ }
+
+/* Terminate the parsed pattern, then return success if all groups are closed.
+Otherwise we have unclosed parentheses. */
+
+if (parsed_pattern >= parsed_pattern_end)
+ {
+ errorcode = ERR63; /* Internal error (parsed pattern overflow) */
+ goto FAILED;
+ }
+
+*parsed_pattern = META_END;
+if (nest_depth == 0) return 0;
+
+UNCLOSED_PARENTHESIS:
+errorcode = ERR14;
+
+/* Come here for all failures. */
+
+FAILED:
+cb->erroroffset = (PCRE2_SIZE)(ptr - cb->start_pattern);
+return errorcode;
+
+/* Some errors need to indicate the previous character. */
+
+FAILED_BACK:
+ptr--;
+goto FAILED;
+
+/* This failure happens several times. */
+
+BAD_VERSION_CONDITION:
+errorcode = ERR79;
+goto FAILED;
+}
+
+
+
+/*************************************************
+* Find first significant opcode *
+*************************************************/
+
+/* This is called by several functions that scan a compiled expression looking
+for a fixed first character, or an anchoring opcode etc. It skips over things
+that do not influence this. For some calls, it makes sense to skip negative
+forward and all backward assertions, and also the \b assertion; for others it
+does not.
+
+Arguments:
+ code pointer to the start of the group
+ skipassert TRUE if certain assertions are to be skipped
+
+Returns: pointer to the first significant opcode
+*/
+
+static const PCRE2_UCHAR*
+first_significant_code(PCRE2_SPTR code, BOOL skipassert)
+{
+for (;;)
+ {
+ switch ((int)*code)
+ {
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERTBACK_NA:
+ if (!skipassert) return code;
+ do code += GET(code, 1); while (*code == OP_ALT);
+ code += PRIV(OP_lengths)[*code];
+ break;
+
+ case OP_WORD_BOUNDARY:
+ case OP_NOT_WORD_BOUNDARY:
+ if (!skipassert) return code;
+ /* Fall through */
+
+ case OP_CALLOUT:
+ case OP_CREF:
+ case OP_DNCREF:
+ case OP_RREF:
+ case OP_DNRREF:
+ case OP_FALSE:
+ case OP_TRUE:
+ code += PRIV(OP_lengths)[*code];
+ break;
+
+ case OP_CALLOUT_STR:
+ code += GET(code, 1 + 2*LINK_SIZE);
+ break;
+
+ case OP_SKIPZERO:
+ code += 2 + GET(code, 2) + LINK_SIZE;
+ break;
+
+ case OP_COND:
+ case OP_SCOND:
+ if (code[1+LINK_SIZE] != OP_FALSE || /* Not DEFINE */
+ code[GET(code, 1)] != OP_KET) /* More than one branch */
+ return code;
+ code += GET(code, 1) + 1 + LINK_SIZE;
+ break;
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_SKIP_ARG:
+ case OP_THEN_ARG:
+ code += code[1] + PRIV(OP_lengths)[*code];
+ break;
+
+ default:
+ return code;
+ }
+ }
+/* Control never reaches here */
+}
+
+
+
+#ifdef SUPPORT_UNICODE
+/*************************************************
+* Get othercase range *
+*************************************************/
+
+/* This function is passed the start and end of a class range in UCP mode. It
+searches up the characters, looking for ranges of characters in the "other"
+case. Each call returns the next one, updating the start address. A character
+with multiple other cases is returned on its own with a special return value.
+
+Arguments:
+ cptr points to starting character value; updated
+ d end value
+ ocptr where to put start of othercase range
+ odptr where to put end of othercase range
+
+Yield: -1 when no more
+ 0 when a range is returned
+ >0 the CASESET offset for char with multiple other cases
+ in this case, ocptr contains the original
+*/
+
+static int
+get_othercase_range(uint32_t *cptr, uint32_t d, uint32_t *ocptr,
+ uint32_t *odptr)
+{
+uint32_t c, othercase, next;
+unsigned int co;
+
+/* Find the first character that has an other case. If it has multiple other
+cases, return its case offset value. */
+
+for (c = *cptr; c <= d; c++)
+ {
+ if ((co = UCD_CASESET(c)) != 0)
+ {
+ *ocptr = c++; /* Character that has the set */
+ *cptr = c; /* Rest of input range */
+ return (int)co;
+ }
+ if ((othercase = UCD_OTHERCASE(c)) != c) break;
+ }
+
+if (c > d) return -1; /* Reached end of range */
+
+/* Found a character that has a single other case. Search for the end of the
+range, which is either the end of the input range, or a character that has zero
+or more than one other cases. */
+
+*ocptr = othercase;
+next = othercase + 1;
+
+for (++c; c <= d; c++)
+ {
+ if ((co = UCD_CASESET(c)) != 0 || UCD_OTHERCASE(c) != next) break;
+ next++;
+ }
+
+*odptr = next - 1; /* End of othercase range */
+*cptr = c; /* Rest of input range */
+return 0;
+}
+#endif /* SUPPORT_UNICODE */
+
+
+
+/*************************************************
+* Add a character or range to a class (internal) *
+*************************************************/
+
+/* This function packages up the logic of adding a character or range of
+characters to a class. The character values in the arguments will be within the
+valid values for the current mode (8-bit, 16-bit, UTF, etc). This function is
+called only from within the "add to class" group of functions, some of which
+are recursive and mutually recursive. The external entry point is
+add_to_class().
+
+Arguments:
+ classbits the bit map for characters < 256
+ uchardptr points to the pointer for extra data
+ options the options word
+ cb compile data
+ start start of range character
+ end end of range character
+
+Returns: the number of < 256 characters added
+ the pointer to extra data is updated
+*/
+
+static unsigned int
+add_to_class_internal(uint8_t *classbits, PCRE2_UCHAR **uchardptr,
+ uint32_t options, compile_block *cb, uint32_t start, uint32_t end)
+{
+uint32_t c;
+uint32_t classbits_end = (end <= 0xff ? end : 0xff);
+unsigned int n8 = 0;
+
+/* If caseless matching is required, scan the range and process alternate
+cases. In Unicode, there are 8-bit characters that have alternate cases that
+are greater than 255 and vice-versa. Sometimes we can just extend the original
+range. */
+
+if ((options & PCRE2_CASELESS) != 0)
+ {
+#ifdef SUPPORT_UNICODE
+ if ((options & (PCRE2_UTF|PCRE2_UCP)) != 0)
+ {
+ int rc;
+ uint32_t oc, od;
+
+ options &= ~PCRE2_CASELESS; /* Remove for recursive calls */
+ c = start;
+
+ while ((rc = get_othercase_range(&c, end, &oc, &od)) >= 0)
+ {
+ /* Handle a single character that has more than one other case. */
+
+ if (rc > 0) n8 += add_list_to_class_internal(classbits, uchardptr, options, cb,
+ PRIV(ucd_caseless_sets) + rc, oc);
+
+ /* Do nothing if the other case range is within the original range. */
+
+ else if (oc >= cb->class_range_start && od <= cb->class_range_end) continue;
+
+ /* Extend the original range if there is overlap, noting that if oc < c, we
+ can't have od > end because a subrange is always shorter than the basic
+ range. Otherwise, use a recursive call to add the additional range. */
+
+ else if (oc < start && od >= start - 1) start = oc; /* Extend downwards */
+ else if (od > end && oc <= end + 1)
+ {
+ end = od; /* Extend upwards */
+ if (end > classbits_end) classbits_end = (end <= 0xff ? end : 0xff);
+ }
+ else n8 += add_to_class_internal(classbits, uchardptr, options, cb, oc, od);
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Not UTF mode */
+
+ for (c = start; c <= classbits_end; c++)
+ {
+ SETBIT(classbits, cb->fcc[c]);
+ n8++;
+ }
+ }
+
+/* Now handle the originally supplied range. Adjust the final value according
+to the bit length - this means that the same lists of (e.g.) horizontal spaces
+can be used in all cases. */
+
+if ((options & PCRE2_UTF) == 0 && end > MAX_NON_UTF_CHAR)
+ end = MAX_NON_UTF_CHAR;
+
+if (start > cb->class_range_start && end < cb->class_range_end) return n8;
+
+/* Use the bitmap for characters < 256. Otherwise use extra data.*/
+
+for (c = start; c <= classbits_end; c++)
+ {
+ /* Regardless of start, c will always be <= 255. */
+ SETBIT(classbits, c);
+ n8++;
+ }
+
+#ifdef SUPPORT_WIDE_CHARS
+if (start <= 0xff) start = 0xff + 1;
+
+if (end >= start)
+ {
+ PCRE2_UCHAR *uchardata = *uchardptr;
+
+#ifdef SUPPORT_UNICODE
+ if ((options & PCRE2_UTF) != 0)
+ {
+ if (start < end)
+ {
+ *uchardata++ = XCL_RANGE;
+ uchardata += PRIV(ord2utf)(start, uchardata);
+ uchardata += PRIV(ord2utf)(end, uchardata);
+ }
+ else if (start == end)
+ {
+ *uchardata++ = XCL_SINGLE;
+ uchardata += PRIV(ord2utf)(start, uchardata);
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Without UTF support, character values are constrained by the bit length,
+ and can only be > 256 for 16-bit and 32-bit libraries. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ {}
+#else
+ if (start < end)
+ {
+ *uchardata++ = XCL_RANGE;
+ *uchardata++ = start;
+ *uchardata++ = end;
+ }
+ else if (start == end)
+ {
+ *uchardata++ = XCL_SINGLE;
+ *uchardata++ = start;
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ *uchardptr = uchardata; /* Updata extra data pointer */
+ }
+#else /* SUPPORT_WIDE_CHARS */
+ (void)uchardptr; /* Avoid compiler warning */
+#endif /* SUPPORT_WIDE_CHARS */
+
+return n8; /* Number of 8-bit characters */
+}
+
+
+
+#ifdef SUPPORT_UNICODE
+/*************************************************
+* Add a list of characters to a class (internal) *
+*************************************************/
+
+/* This function is used for adding a list of case-equivalent characters to a
+class when in UTF mode. This function is called only from within
+add_to_class_internal(), with which it is mutually recursive.
+
+Arguments:
+ classbits the bit map for characters < 256
+ uchardptr points to the pointer for extra data
+ options the options word
+ cb contains pointers to tables etc.
+ p points to row of 32-bit values, terminated by NOTACHAR
+ except character to omit; this is used when adding lists of
+ case-equivalent characters to avoid including the one we
+ already know about
+
+Returns: the number of < 256 characters added
+ the pointer to extra data is updated
+*/
+
+static unsigned int
+add_list_to_class_internal(uint8_t *classbits, PCRE2_UCHAR **uchardptr,
+ uint32_t options, compile_block *cb, const uint32_t *p, unsigned int except)
+{
+unsigned int n8 = 0;
+while (p[0] < NOTACHAR)
+ {
+ unsigned int n = 0;
+ if (p[0] != except)
+ {
+ while(p[n+1] == p[0] + n + 1) n++;
+ n8 += add_to_class_internal(classbits, uchardptr, options, cb, p[0], p[n]);
+ }
+ p += n + 1;
+ }
+return n8;
+}
+#endif
+
+
+
+/*************************************************
+* External entry point for add range to class *
+*************************************************/
+
+/* This function sets the overall range so that the internal functions can try
+to avoid duplication when handling case-independence.
+
+Arguments:
+ classbits the bit map for characters < 256
+ uchardptr points to the pointer for extra data
+ options the options word
+ cb compile data
+ start start of range character
+ end end of range character
+
+Returns: the number of < 256 characters added
+ the pointer to extra data is updated
+*/
+
+static unsigned int
+add_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options,
+ compile_block *cb, uint32_t start, uint32_t end)
+{
+cb->class_range_start = start;
+cb->class_range_end = end;
+return add_to_class_internal(classbits, uchardptr, options, cb, start, end);
+}
+
+
+/*************************************************
+* External entry point for add list to class *
+*************************************************/
+
+/* This function is used for adding a list of horizontal or vertical whitespace
+characters to a class. The list must be in order so that ranges of characters
+can be detected and handled appropriately. This function sets the overall range
+so that the internal functions can try to avoid duplication when handling
+case-independence.
+
+Arguments:
+ classbits the bit map for characters < 256
+ uchardptr points to the pointer for extra data
+ options the options word
+ cb contains pointers to tables etc.
+ p points to row of 32-bit values, terminated by NOTACHAR
+ except character to omit; this is used when adding lists of
+ case-equivalent characters to avoid including the one we
+ already know about
+
+Returns: the number of < 256 characters added
+ the pointer to extra data is updated
+*/
+
+static unsigned int
+add_list_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options,
+ compile_block *cb, const uint32_t *p, unsigned int except)
+{
+unsigned int n8 = 0;
+while (p[0] < NOTACHAR)
+ {
+ unsigned int n = 0;
+ if (p[0] != except)
+ {
+ while(p[n+1] == p[0] + n + 1) n++;
+ cb->class_range_start = p[0];
+ cb->class_range_end = p[n];
+ n8 += add_to_class_internal(classbits, uchardptr, options, cb, p[0], p[n]);
+ }
+ p += n + 1;
+ }
+return n8;
+}
+
+
+
+/*************************************************
+* Add characters not in a list to a class *
+*************************************************/
+
+/* This function is used for adding the complement of a list of horizontal or
+vertical whitespace to a class. The list must be in order.
+
+Arguments:
+ classbits the bit map for characters < 256
+ uchardptr points to the pointer for extra data
+ options the options word
+ cb contains pointers to tables etc.
+ p points to row of 32-bit values, terminated by NOTACHAR
+
+Returns: the number of < 256 characters added
+ the pointer to extra data is updated
+*/
+
+static unsigned int
+add_not_list_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr,
+ uint32_t options, compile_block *cb, const uint32_t *p)
+{
+BOOL utf = (options & PCRE2_UTF) != 0;
+unsigned int n8 = 0;
+if (p[0] > 0)
+ n8 += add_to_class(classbits, uchardptr, options, cb, 0, p[0] - 1);
+while (p[0] < NOTACHAR)
+ {
+ while (p[1] == p[0] + 1) p++;
+ n8 += add_to_class(classbits, uchardptr, options, cb, p[0] + 1,
+ (p[1] == NOTACHAR) ? (utf ? 0x10ffffu : 0xffffffffu) : p[1] - 1);
+ p++;
+ }
+return n8;
+}
+
+
+
+/*************************************************
+* Find details of duplicate group names *
+*************************************************/
+
+/* This is called from compile_branch() when it needs to know the index and
+count of duplicates in the names table when processing named backreferences,
+either directly, or as conditions.
+
+Arguments:
+ name points to the name
+ length the length of the name
+ indexptr where to put the index
+ countptr where to put the count of duplicates
+ errorcodeptr where to put an error code
+ cb the compile block
+
+Returns: TRUE if OK, FALSE if not, error code set
+*/
+
+static BOOL
+find_dupname_details(PCRE2_SPTR name, uint32_t length, int *indexptr,
+ int *countptr, int *errorcodeptr, compile_block *cb)
+{
+uint32_t i, groupnumber;
+int count;
+PCRE2_UCHAR *slot = cb->name_table;
+
+/* Find the first entry in the table */
+
+for (i = 0; i < cb->names_found; i++)
+ {
+ if (PRIV(strncmp)(name, slot+IMM2_SIZE, length) == 0 &&
+ slot[IMM2_SIZE+length] == 0) break;
+ slot += cb->name_entry_size;
+ }
+
+/* This should not occur, because this function is called only when we know we
+have duplicate names. Give an internal error. */
+
+if (i >= cb->names_found)
+ {
+ *errorcodeptr = ERR53;
+ cb->erroroffset = name - cb->start_pattern;
+ return FALSE;
+ }
+
+/* Record the index and then see how many duplicates there are, updating the
+backref map and maximum back reference as we do. */
+
+*indexptr = i;
+count = 0;
+
+for (;;)
+ {
+ count++;
+ groupnumber = GET2(slot,0);
+ cb->backref_map |= (groupnumber < 32)? (1u << groupnumber) : 1;
+ if (groupnumber > cb->top_backref) cb->top_backref = groupnumber;
+ if (++i >= cb->names_found) break;
+ slot += cb->name_entry_size;
+ if (PRIV(strncmp)(name, slot+IMM2_SIZE, length) != 0 ||
+ (slot+IMM2_SIZE)[length] != 0) break;
+ }
+
+*countptr = count;
+return TRUE;
+}
+
+
+
+/*************************************************
+* Compile one branch *
+*************************************************/
+
+/* Scan the parsed pattern, compiling it into the a vector of PCRE2_UCHAR. If
+the options are changed during the branch, the pointer is used to change the
+external options bits. This function is used during the pre-compile phase when
+we are trying to find out the amount of memory needed, as well as during the
+real compile phase. The value of lengthptr distinguishes the two phases.
+
+Arguments:
+ optionsptr pointer to the option bits
+ codeptr points to the pointer to the current code point
+ pptrptr points to the current parsed pattern pointer
+ errorcodeptr points to error code variable
+ firstcuptr place to put the first required code unit
+ firstcuflagsptr place to put the first code unit flags
+ reqcuptr place to put the last required code unit
+ reqcuflagsptr place to put the last required code unit flags
+ bcptr points to current branch chain
+ cb contains pointers to tables etc.
+ lengthptr NULL during the real compile phase
+ points to length accumulator during pre-compile phase
+
+Returns: 0 There's been an error, *errorcodeptr is non-zero
+ +1 Success, this branch must match at least one character
+ -1 Success, this branch may match an empty string
+*/
+
+static int
+compile_branch(uint32_t *optionsptr, PCRE2_UCHAR **codeptr, uint32_t **pptrptr,
+ int *errorcodeptr, uint32_t *firstcuptr, uint32_t *firstcuflagsptr,
+ uint32_t *reqcuptr, uint32_t *reqcuflagsptr, branch_chain *bcptr,
+ compile_block *cb, PCRE2_SIZE *lengthptr)
+{
+int bravalue = 0;
+int okreturn = -1;
+int group_return = 0;
+uint32_t repeat_min = 0, repeat_max = 0; /* To please picky compilers */
+uint32_t greedy_default, greedy_non_default;
+uint32_t repeat_type, op_type;
+uint32_t options = *optionsptr; /* May change dynamically */
+uint32_t firstcu, reqcu;
+uint32_t zeroreqcu, zerofirstcu;
+uint32_t escape;
+uint32_t *pptr = *pptrptr;
+uint32_t meta, meta_arg;
+uint32_t firstcuflags, reqcuflags;
+uint32_t zeroreqcuflags, zerofirstcuflags;
+uint32_t req_caseopt, reqvary, tempreqvary;
+PCRE2_SIZE offset = 0;
+PCRE2_SIZE length_prevgroup = 0;
+PCRE2_UCHAR *code = *codeptr;
+PCRE2_UCHAR *last_code = code;
+PCRE2_UCHAR *orig_code = code;
+PCRE2_UCHAR *tempcode;
+PCRE2_UCHAR *previous = NULL;
+PCRE2_UCHAR op_previous;
+BOOL groupsetfirstcu = FALSE;
+BOOL had_accept = FALSE;
+BOOL matched_char = FALSE;
+BOOL previous_matched_char = FALSE;
+BOOL reset_caseful = FALSE;
+const uint8_t *cbits = cb->cbits;
+uint8_t classbits[32];
+
+/* We can fish out the UTF setting once and for all into a BOOL, but we must
+not do this for other options (e.g. PCRE2_EXTENDED) because they may change
+dynamically as we process the pattern. */
+
+#ifdef SUPPORT_UNICODE
+BOOL utf = (options & PCRE2_UTF) != 0;
+BOOL ucp = (options & PCRE2_UCP) != 0;
+#else /* No Unicode support */
+BOOL utf = FALSE;
+#endif
+
+/* Helper variables for OP_XCLASS opcode (for characters > 255). We define
+class_uchardata always so that it can be passed to add_to_class() always,
+though it will not be used in non-UTF 8-bit cases. This avoids having to supply
+alternative calls for the different cases. */
+
+PCRE2_UCHAR *class_uchardata;
+#ifdef SUPPORT_WIDE_CHARS
+BOOL xclass;
+PCRE2_UCHAR *class_uchardata_base;
+#endif
+
+/* Set up the default and non-default settings for greediness */
+
+greedy_default = ((options & PCRE2_UNGREEDY) != 0);
+greedy_non_default = greedy_default ^ 1;
+
+/* Initialize no first unit, no required unit. REQ_UNSET means "no char
+matching encountered yet". It gets changed to REQ_NONE if we hit something that
+matches a non-fixed first unit; reqcu just remains unset if we never find one.
+
+When we hit a repeat whose minimum is zero, we may have to adjust these values
+to take the zero repeat into account. This is implemented by setting them to
+zerofirstcu and zeroreqcu when such a repeat is encountered. The individual
+item types that can be repeated set these backoff variables appropriately. */
+
+firstcu = reqcu = zerofirstcu = zeroreqcu = 0;
+firstcuflags = reqcuflags = zerofirstcuflags = zeroreqcuflags = REQ_UNSET;
+
+/* The variable req_caseopt contains either the REQ_CASELESS bit or zero,
+according to the current setting of the caseless flag. The REQ_CASELESS value
+leaves the lower 28 bit empty. It is added into the firstcu or reqcu variables
+to record the case status of the value. This is used only for ASCII characters.
+*/
+
+req_caseopt = ((options & PCRE2_CASELESS) != 0)? REQ_CASELESS : 0;
+
+/* Switch on next META item until the end of the branch */
+
+for (;; pptr++)
+ {
+#ifdef SUPPORT_WIDE_CHARS
+ BOOL xclass_has_prop;
+#endif
+ BOOL negate_class;
+ BOOL should_flip_negation;
+ BOOL match_all_or_no_wide_chars;
+ BOOL possessive_quantifier;
+ BOOL note_group_empty;
+ int class_has_8bitchar;
+ uint32_t mclength;
+ uint32_t skipunits;
+ uint32_t subreqcu, subfirstcu;
+ uint32_t groupnumber;
+ uint32_t verbarglen, verbculen;
+ uint32_t subreqcuflags, subfirstcuflags;
+ open_capitem *oc;
+ PCRE2_UCHAR mcbuffer[8];
+
+ /* Get next META item in the pattern and its potential argument. */
+
+ meta = META_CODE(*pptr);
+ meta_arg = META_DATA(*pptr);
+
+ /* If we are in the pre-compile phase, accumulate the length used for the
+ previous cycle of this loop, unless the next item is a quantifier. */
+
+ if (lengthptr != NULL)
+ {
+ if (code > cb->start_workspace + cb->workspace_size -
+ WORK_SIZE_SAFETY_MARGIN) /* Check for overrun */
+ {
+ *errorcodeptr = (code >= cb->start_workspace + cb->workspace_size)?
+ ERR52 : ERR86;
+ return 0;
+ }
+
+ /* There is at least one situation where code goes backwards: this is the
+ case of a zero quantifier after a class (e.g. [ab]{0}). When the quantifier
+ is processed, the whole class is eliminated. However, it is created first,
+ so we have to allow memory for it. Therefore, don't ever reduce the length
+ at this point. */
+
+ if (code < last_code) code = last_code;
+
+ /* If the next thing is not a quantifier, we add the length of the previous
+ item into the total, and reset the code pointer to the start of the
+ workspace. Otherwise leave the previous item available to be quantified. */
+
+ if (meta < META_ASTERISK || meta > META_MINMAX_QUERY)
+ {
+ if (OFLOW_MAX - *lengthptr < (PCRE2_SIZE)(code - orig_code))
+ {
+ *errorcodeptr = ERR20; /* Integer overflow */
+ return 0;
+ }
+ *lengthptr += (PCRE2_SIZE)(code - orig_code);
+ if (*lengthptr > MAX_PATTERN_SIZE)
+ {
+ *errorcodeptr = ERR20; /* Pattern is too large */
+ return 0;
+ }
+ code = orig_code;
+ }
+
+ /* Remember where this code item starts so we can catch the "backwards"
+ case above next time round. */
+
+ last_code = code;
+ }
+
+ /* Process the next parsed pattern item. If it is not a quantifier, remember
+ where it starts so that it can be quantified when a quantifier follows.
+ Checking for the legality of quantifiers happens in parse_regex(), except for
+ a quantifier after an assertion that is a condition. */
+
+ if (meta < META_ASTERISK || meta > META_MINMAX_QUERY)
+ {
+ previous = code;
+ if (matched_char && !had_accept) okreturn = 1;
+ }
+
+ previous_matched_char = matched_char;
+ matched_char = FALSE;
+ note_group_empty = FALSE;
+ skipunits = 0; /* Default value for most subgroups */
+
+ switch(meta)
+ {
+ /* ===================================================================*/
+ /* The branch terminates at pattern end or | or ) */
+
+ case META_END:
+ case META_ALT:
+ case META_KET:
+ *firstcuptr = firstcu;
+ *firstcuflagsptr = firstcuflags;
+ *reqcuptr = reqcu;
+ *reqcuflagsptr = reqcuflags;
+ *codeptr = code;
+ *pptrptr = pptr;
+ return okreturn;
+
+
+ /* ===================================================================*/
+ /* Handle single-character metacharacters. In multiline mode, ^ disables
+ the setting of any following char as a first character. */
+
+ case META_CIRCUMFLEX:
+ if ((options & PCRE2_MULTILINE) != 0)
+ {
+ if (firstcuflags == REQ_UNSET)
+ zerofirstcuflags = firstcuflags = REQ_NONE;
+ *code++ = OP_CIRCM;
+ }
+ else *code++ = OP_CIRC;
+ break;
+
+ case META_DOLLAR:
+ *code++ = ((options & PCRE2_MULTILINE) != 0)? OP_DOLLM : OP_DOLL;
+ break;
+
+ /* There can never be a first char if '.' is first, whatever happens about
+ repeats. The value of reqcu doesn't change either. */
+
+ case META_DOT:
+ matched_char = TRUE;
+ if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;
+ zerofirstcu = firstcu;
+ zerofirstcuflags = firstcuflags;
+ zeroreqcu = reqcu;
+ zeroreqcuflags = reqcuflags;
+ *code++ = ((options & PCRE2_DOTALL) != 0)? OP_ALLANY: OP_ANY;
+ break;
+
+
+ /* ===================================================================*/
+ /* Empty character classes are allowed if PCRE2_ALLOW_EMPTY_CLASS is set.
+ Otherwise, an initial ']' is taken as a data character. When empty classes
+ are allowed, [] must always fail, so generate OP_FAIL, whereas [^] must
+ match any character, so generate OP_ALLANY. */
+
+ case META_CLASS_EMPTY:
+ case META_CLASS_EMPTY_NOT:
+ matched_char = TRUE;
+ *code++ = (meta == META_CLASS_EMPTY_NOT)? OP_ALLANY : OP_FAIL;
+ if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;
+ zerofirstcu = firstcu;
+ zerofirstcuflags = firstcuflags;
+ break;
+
+
+ /* ===================================================================*/
+ /* Non-empty character class. If the included characters are all < 256, we
+ build a 32-byte bitmap of the permitted characters, except in the special
+ case where there is only one such character. For negated classes, we build
+ the map as usual, then invert it at the end. However, we use a different
+ opcode so that data characters > 255 can be handled correctly.
+
+ If the class contains characters outside the 0-255 range, a different
+ opcode is compiled. It may optionally have a bit map for characters < 256,
+ but those above are are explicitly listed afterwards. A flag code unit
+ tells whether the bitmap is present, and whether this is a negated class or
+ not. */
+
+ case META_CLASS_NOT:
+ case META_CLASS:
+ matched_char = TRUE;
+ negate_class = meta == META_CLASS_NOT;
+
+ /* We can optimize the case of a single character in a class by generating
+ OP_CHAR or OP_CHARI if it's positive, or OP_NOT or OP_NOTI if it's
+ negative. In the negative case there can be no first char if this item is
+ first, whatever repeat count may follow. In the case of reqcu, save the
+ previous value for reinstating. */
+
+ /* NOTE: at present this optimization is not effective if the only
+ character in a class in 32-bit, non-UCP mode has its top bit set. */
+
+ if (pptr[1] < META_END && pptr[2] == META_CLASS_END)
+ {
+#ifdef SUPPORT_UNICODE
+ uint32_t d;
+#endif
+ uint32_t c = pptr[1];
+
+ pptr += 2; /* Move on to class end */
+ if (meta == META_CLASS) /* A positive one-char class can be */
+ { /* handled as a normal literal character. */
+ meta = c; /* Set up the character */
+ goto NORMAL_CHAR_SET;
+ }
+
+ /* Handle a negative one-character class */
+
+ zeroreqcu = reqcu;
+ zeroreqcuflags = reqcuflags;
+ if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;
+ zerofirstcu = firstcu;
+ zerofirstcuflags = firstcuflags;
+
+ /* For caseless UTF or UCP mode, check whether this character has more
+ than one other case. If so, generate a special OP_NOTPROP item instead of
+ OP_NOTI. */
+
+#ifdef SUPPORT_UNICODE
+ if ((utf||ucp) && (options & PCRE2_CASELESS) != 0 &&
+ (d = UCD_CASESET(c)) != 0)
+ {
+ *code++ = OP_NOTPROP;
+ *code++ = PT_CLIST;
+ *code++ = d;
+ break; /* We are finished with this class */
+ }
+#endif
+ /* Char has only one other case, or UCP not available */
+
+ *code++ = ((options & PCRE2_CASELESS) != 0)? OP_NOTI: OP_NOT;
+ code += PUTCHAR(c, code);
+ break; /* We are finished with this class */
+ } /* End of 1-char optimization */
+
+ /* Handle character classes that contain more than just one literal
+ character. If there are exactly two characters in a positive class, see if
+ they are case partners. This can be optimized to generate a caseless single
+ character match (which also sets first/required code units if relevant). */
+
+ if (meta == META_CLASS && pptr[1] < META_END && pptr[2] < META_END &&
+ pptr[3] == META_CLASS_END)
+ {
+ uint32_t c = pptr[1];
+
+#ifdef SUPPORT_UNICODE
+ if (UCD_CASESET(c) == 0)
+#endif
+ {
+ uint32_t d;
+
+#ifdef SUPPORT_UNICODE
+ if ((utf || ucp) && c > 127) d = UCD_OTHERCASE(c); else
+#endif
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (c > 255) d = c; else
+#endif
+ d = TABLE_GET(c, cb->fcc, c);
+ }
+
+ if (c != d && pptr[2] == d)
+ {
+ pptr += 3; /* Move on to class end */
+ meta = c;
+ if ((options & PCRE2_CASELESS) == 0)
+ {
+ reset_caseful = TRUE;
+ options |= PCRE2_CASELESS;
+ req_caseopt = REQ_CASELESS;
+ }
+ goto CLASS_CASELESS_CHAR;
+ }
+ }
+ }
+
+ /* If a non-extended class contains a negative special such as \S, we need
+ to flip the negation flag at the end, so that support for characters > 255
+ works correctly (they are all included in the class). An extended class may
+ need to insert specific matching or non-matching code for wide characters.
+ */
+
+ should_flip_negation = match_all_or_no_wide_chars = FALSE;
+
+ /* Extended class (xclass) will be used when characters > 255
+ might match. */
+
+#ifdef SUPPORT_WIDE_CHARS
+ xclass = FALSE;
+ class_uchardata = code + LINK_SIZE + 2; /* For XCLASS items */
+ class_uchardata_base = class_uchardata; /* Save the start */
+#endif
+
+ /* For optimization purposes, we track some properties of the class:
+ class_has_8bitchar will be non-zero if the class contains at least one
+ character with a code point less than 256; xclass_has_prop will be TRUE if
+ Unicode property checks are present in the class. */
+
+ class_has_8bitchar = 0;
+#ifdef SUPPORT_WIDE_CHARS
+ xclass_has_prop = FALSE;
+#endif
+
+ /* Initialize the 256-bit (32-byte) bit map to all zeros. We build the map
+ in a temporary bit of memory, in case the class contains fewer than two
+ 8-bit characters because in that case the compiled code doesn't use the bit
+ map. */
+
+ memset(classbits, 0, 32 * sizeof(uint8_t));
+
+ /* Process items until META_CLASS_END is reached. */
+
+ while ((meta = *(++pptr)) != META_CLASS_END)
+ {
+ /* Handle POSIX classes such as [:alpha:] etc. */
+
+ if (meta == META_POSIX || meta == META_POSIX_NEG)
+ {
+ BOOL local_negate = (meta == META_POSIX_NEG);
+ int posix_class = *(++pptr);
+ int taboffset, tabopt;
+ uint8_t pbits[32];
+
+ should_flip_negation = local_negate; /* Note negative special */
+
+ /* If matching is caseless, upper and lower are converted to alpha.
+ This relies on the fact that the class table starts with alpha,
+ lower, upper as the first 3 entries. */
+
+ if ((options & PCRE2_CASELESS) != 0 && posix_class <= 2)
+ posix_class = 0;
+
+ /* When PCRE2_UCP is set, some of the POSIX classes are converted to
+ different escape sequences that use Unicode properties \p or \P.
+ Others that are not available via \p or \P have to generate
+ XCL_PROP/XCL_NOTPROP directly, which is done here. */
+
+#ifdef SUPPORT_UNICODE
+ if ((options & PCRE2_UCP) != 0) switch(posix_class)
+ {
+ case PC_GRAPH:
+ case PC_PRINT:
+ case PC_PUNCT:
+ *class_uchardata++ = local_negate? XCL_NOTPROP : XCL_PROP;
+ *class_uchardata++ = (PCRE2_UCHAR)
+ ((posix_class == PC_GRAPH)? PT_PXGRAPH :
+ (posix_class == PC_PRINT)? PT_PXPRINT : PT_PXPUNCT);
+ *class_uchardata++ = 0;
+ xclass_has_prop = TRUE;
+ goto CONTINUE_CLASS;
+
+ /* For the other POSIX classes (ascii, xdigit) we are going to
+ fall through to the non-UCP case and build a bit map for
+ characters with code points less than 256. However, if we are in
+ a negated POSIX class, characters with code points greater than
+ 255 must either all match or all not match, depending on whether
+ the whole class is not or is negated. For example, for
+ [[:^ascii:]... they must all match, whereas for [^[:^xdigit:]...
+ they must not.
+
+ In the special case where there are no xclass items, this is
+ automatically handled by the use of OP_CLASS or OP_NCLASS, but an
+ explicit range is needed for OP_XCLASS. Setting a flag here
+ causes the range to be generated later when it is known that
+ OP_XCLASS is required. In the 8-bit library this is relevant only in
+ utf mode, since no wide characters can exist otherwise. */
+
+ default:
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (utf)
+#endif
+ match_all_or_no_wide_chars |= local_negate;
+ break;
+ }
+#endif /* SUPPORT_UNICODE */
+
+ /* In the non-UCP case, or when UCP makes no difference, we build the
+ bit map for the POSIX class in a chunk of local store because we may
+ be adding and subtracting from it, and we don't want to subtract bits
+ that may be in the main map already. At the end we or the result into
+ the bit map that is being built. */
+
+ posix_class *= 3;
+
+ /* Copy in the first table (always present) */
+
+ memcpy(pbits, cbits + posix_class_maps[posix_class],
+ 32 * sizeof(uint8_t));
+
+ /* If there is a second table, add or remove it as required. */
+
+ taboffset = posix_class_maps[posix_class + 1];
+ tabopt = posix_class_maps[posix_class + 2];
+
+ if (taboffset >= 0)
+ {
+ if (tabopt >= 0)
+ for (int i = 0; i < 32; i++) pbits[i] |= cbits[(int)i + taboffset];
+ else
+ for (int i = 0; i < 32; i++) pbits[i] &= ~cbits[(int)i + taboffset];
+ }
+
+ /* Now see if we need to remove any special characters. An option
+ value of 1 removes vertical space and 2 removes underscore. */
+
+ if (tabopt < 0) tabopt = -tabopt;
+ if (tabopt == 1) pbits[1] &= ~0x3c;
+ else if (tabopt == 2) pbits[11] &= 0x7f;
+
+ /* Add the POSIX table or its complement into the main table that is
+ being built and we are done. */
+
+ if (local_negate)
+ for (int i = 0; i < 32; i++) classbits[i] |= (uint8_t)(~pbits[i]);
+ else
+ for (int i = 0; i < 32; i++) classbits[i] |= pbits[i];
+
+ /* Every class contains at least one < 256 character. */
+
+ class_has_8bitchar = 1;
+ goto CONTINUE_CLASS; /* End of POSIX handling */
+ }
+
+ /* Other than POSIX classes, the only items we should encounter are
+ \d-type escapes and literal characters (possibly as ranges). */
+
+ if (meta == META_BIGVALUE)
+ {
+ meta = *(++pptr);
+ goto CLASS_LITERAL;
+ }
+
+ /* Any other non-literal must be an escape */
+
+ if (meta >= META_END)
+ {
+ if (META_CODE(meta) != META_ESCAPE)
+ {
+#ifdef DEBUG_SHOW_PARSED
+ fprintf(stderr, "** Unrecognized parsed pattern item 0x%.8x "
+ "in character class\n", meta);
+#endif
+ *errorcodeptr = ERR89; /* Internal error - unrecognized. */
+ return 0;
+ }
+ escape = META_DATA(meta);
+
+ /* Every class contains at least one < 256 character. */
+
+ class_has_8bitchar++;
+
+ switch(escape)
+ {
+ case ESC_d:
+ for (int i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_digit];
+ break;
+
+ case ESC_D:
+ should_flip_negation = TRUE;
+ for (int i = 0; i < 32; i++)
+ classbits[i] |= (uint8_t)(~cbits[i+cbit_digit]);
+ break;
+
+ case ESC_w:
+ for (int i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_word];
+ break;
+
+ case ESC_W:
+ should_flip_negation = TRUE;
+ for (int i = 0; i < 32; i++)
+ classbits[i] |= (uint8_t)(~cbits[i+cbit_word]);
+ break;
+
+ /* Perl 5.004 onwards omitted VT from \s, but restored it at Perl
+ 5.18. Before PCRE 8.34, we had to preserve the VT bit if it was
+ previously set by something earlier in the character class.
+ Luckily, the value of CHAR_VT is 0x0b in both ASCII and EBCDIC, so
+ we could just adjust the appropriate bit. From PCRE 8.34 we no
+ longer treat \s and \S specially. */
+
+ case ESC_s:
+ for (int i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_space];
+ break;
+
+ case ESC_S:
+ should_flip_negation = TRUE;
+ for (int i = 0; i < 32; i++)
+ classbits[i] |= (uint8_t)(~cbits[i+cbit_space]);
+ break;
+
+ /* When adding the horizontal or vertical space lists to a class, or
+ their complements, disable PCRE2_CASELESS, because it justs wastes
+ time, and in the "not-x" UTF cases can create unwanted duplicates in
+ the XCLASS list (provoked by characters that have more than one other
+ case and by both cases being in the same "not-x" sublist). */
+
+ case ESC_h:
+ (void)add_list_to_class(classbits, &class_uchardata,
+ options & ~PCRE2_CASELESS, cb, PRIV(hspace_list), NOTACHAR);
+ break;
+
+ case ESC_H:
+ (void)add_not_list_to_class(classbits, &class_uchardata,
+ options & ~PCRE2_CASELESS, cb, PRIV(hspace_list));
+ break;
+
+ case ESC_v:
+ (void)add_list_to_class(classbits, &class_uchardata,
+ options & ~PCRE2_CASELESS, cb, PRIV(vspace_list), NOTACHAR);
+ break;
+
+ case ESC_V:
+ (void)add_not_list_to_class(classbits, &class_uchardata,
+ options & ~PCRE2_CASELESS, cb, PRIV(vspace_list));
+ break;
+
+ /* If Unicode is not supported, \P and \p are not allowed and are
+ faulted at parse time, so will never appear here. */
+
+#ifdef SUPPORT_UNICODE
+ case ESC_p:
+ case ESC_P:
+ {
+ uint32_t ptype = *(++pptr) >> 16;
+ uint32_t pdata = *pptr & 0xffff;
+ *class_uchardata++ = (escape == ESC_p)? XCL_PROP : XCL_NOTPROP;
+ *class_uchardata++ = ptype;
+ *class_uchardata++ = pdata;
+ xclass_has_prop = TRUE;
+ class_has_8bitchar--; /* Undo! */
+ }
+ break;
+#endif
+ }
+
+ goto CONTINUE_CLASS;
+ } /* End handling \d-type escapes */
+
+ /* A literal character may be followed by a range meta. At parse time
+ there are checks for out-of-order characters, for ranges where the two
+ characters are equal, and for hyphens that cannot indicate a range. At
+ this point, therefore, no checking is needed. */
+
+ else
+ {
+ uint32_t c, d;
+
+ CLASS_LITERAL:
+ c = d = meta;
+
+ /* Remember if \r or \n were explicitly used */
+
+ if (c == CHAR_CR || c == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF;
+
+ /* Process a character range */
+
+ if (pptr[1] == META_RANGE_LITERAL || pptr[1] == META_RANGE_ESCAPED)
+ {
+#ifdef EBCDIC
+ BOOL range_is_literal = (pptr[1] == META_RANGE_LITERAL);
+#endif
+ pptr += 2;
+ d = *pptr;
+ if (d == META_BIGVALUE) d = *(++pptr);
+
+ /* Remember an explicit \r or \n, and add the range to the class. */
+
+ if (d == CHAR_CR || d == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF;
+
+ /* In an EBCDIC environment, Perl treats alphabetic ranges specially
+ because there are holes in the encoding, and simply using the range
+ A-Z (for example) would include the characters in the holes. This
+ applies only to literal ranges; [\xC1-\xE9] is different to [A-Z]. */
+
+#ifdef EBCDIC
+ if (range_is_literal &&
+ (cb->ctypes[c] & ctype_letter) != 0 &&
+ (cb->ctypes[d] & ctype_letter) != 0 &&
+ (c <= CHAR_z) == (d <= CHAR_z))
+ {
+ uint32_t uc = (d <= CHAR_z)? 0 : 64;
+ uint32_t C = c - uc;
+ uint32_t D = d - uc;
+
+ if (C <= CHAR_i)
+ {
+ class_has_8bitchar +=
+ add_to_class(classbits, &class_uchardata, options, cb, C + uc,
+ ((D < CHAR_i)? D : CHAR_i) + uc);
+ C = CHAR_j;
+ }
+
+ if (C <= D && C <= CHAR_r)
+ {
+ class_has_8bitchar +=
+ add_to_class(classbits, &class_uchardata, options, cb, C + uc,
+ ((D < CHAR_r)? D : CHAR_r) + uc);
+ C = CHAR_s;
+ }
+
+ if (C <= D)
+ {
+ class_has_8bitchar +=
+ add_to_class(classbits, &class_uchardata, options, cb, C + uc,
+ D + uc);
+ }
+ }
+ else
+#endif
+ /* Not an EBCDIC special range */
+
+ class_has_8bitchar +=
+ add_to_class(classbits, &class_uchardata, options, cb, c, d);
+ goto CONTINUE_CLASS; /* Go get the next char in the class */
+ } /* End of range handling */
+
+
+ /* Handle a single character. */
+
+ class_has_8bitchar +=
+ add_to_class(classbits, &class_uchardata, options, cb, meta, meta);
+ }
+
+ /* Continue to the next item in the class. */
+
+ CONTINUE_CLASS:
+
+#ifdef SUPPORT_WIDE_CHARS
+ /* If any wide characters or Unicode properties have been encountered,
+ set xclass = TRUE. Then, in the pre-compile phase, accumulate the length
+ of the extra data and reset the pointer. This is so that very large
+ classes that contain a zillion wide characters or Unicode property tests
+ do not overwrite the workspace (which is on the stack). */
+
+ if (class_uchardata > class_uchardata_base)
+ {
+ xclass = TRUE;
+ if (lengthptr != NULL)
+ {
+ *lengthptr += class_uchardata - class_uchardata_base;
+ class_uchardata = class_uchardata_base;
+ }
+ }
+#endif
+
+ continue; /* Needed to avoid error when not supporting wide chars */
+ } /* End of main class-processing loop */
+
+ /* If this class is the first thing in the branch, there can be no first
+ char setting, whatever the repeat count. Any reqcu setting must remain
+ unchanged after any kind of repeat. */
+
+ if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;
+ zerofirstcu = firstcu;
+ zerofirstcuflags = firstcuflags;
+ zeroreqcu = reqcu;
+ zeroreqcuflags = reqcuflags;
+
+ /* If there are characters with values > 255, or Unicode property settings
+ (\p or \P), we have to compile an extended class, with its own opcode,
+ unless there were no property settings and there was a negated special such
+ as \S in the class, and PCRE2_UCP is not set, because in that case all
+ characters > 255 are in or not in the class, so any that were explicitly
+ given as well can be ignored.
+
+ In the UCP case, if certain negated POSIX classes ([:^ascii:] or
+ [^:xdigit:]) were present in a class, we either have to match or not match
+ all wide characters (depending on whether the whole class is or is not
+ negated). This requirement is indicated by match_all_or_no_wide_chars being
+ true. We do this by including an explicit range, which works in both cases.
+ This applies only in UTF and 16-bit and 32-bit non-UTF modes, since there
+ cannot be any wide characters in 8-bit non-UTF mode.
+
+ When there *are* properties in a positive UTF-8 or any 16-bit or 32_bit
+ class where \S etc is present without PCRE2_UCP, causing an extended class
+ to be compiled, we make sure that all characters > 255 are included by
+ forcing match_all_or_no_wide_chars to be true.
+
+ If, when generating an xclass, there are no characters < 256, we can omit
+ the bitmap in the actual compiled code. */
+
+#ifdef SUPPORT_WIDE_CHARS /* Defined for 16/32 bits, or 8-bit with Unicode */
+ if (xclass && (
+#ifdef SUPPORT_UNICODE
+ (options & PCRE2_UCP) != 0 ||
+#endif
+ xclass_has_prop || !should_flip_negation))
+ {
+ if (match_all_or_no_wide_chars || (
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ utf &&
+#endif
+ should_flip_negation && !negate_class && (options & PCRE2_UCP) == 0))
+ {
+ *class_uchardata++ = XCL_RANGE;
+ if (utf) /* Will always be utf in the 8-bit library */
+ {
+ class_uchardata += PRIV(ord2utf)(0x100, class_uchardata);
+ class_uchardata += PRIV(ord2utf)(MAX_UTF_CODE_POINT, class_uchardata);
+ }
+ else /* Can only happen for the 16-bit & 32-bit libraries */
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 16
+ *class_uchardata++ = 0x100;
+ *class_uchardata++ = 0xffffu;
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+ *class_uchardata++ = 0x100;
+ *class_uchardata++ = 0xffffffffu;
+#endif
+ }
+ }
+ *class_uchardata++ = XCL_END; /* Marks the end of extra data */
+ *code++ = OP_XCLASS;
+ code += LINK_SIZE;
+ *code = negate_class? XCL_NOT:0;
+ if (xclass_has_prop) *code |= XCL_HASPROP;
+
+ /* If the map is required, move up the extra data to make room for it;
+ otherwise just move the code pointer to the end of the extra data. */
+
+ if (class_has_8bitchar > 0)
+ {
+ *code++ |= XCL_MAP;
+ (void)memmove(code + (32 / sizeof(PCRE2_UCHAR)), code,
+ CU2BYTES(class_uchardata - code));
+ if (negate_class && !xclass_has_prop)
+ {
+ /* Using 255 ^ instead of ~ avoids clang sanitize warning. */
+ for (int i = 0; i < 32; i++) classbits[i] = 255 ^ classbits[i];
+ }
+ memcpy(code, classbits, 32);
+ code = class_uchardata + (32 / sizeof(PCRE2_UCHAR));
+ }
+ else code = class_uchardata;
+
+ /* Now fill in the complete length of the item */
+
+ PUT(previous, 1, (int)(code - previous));
+ break; /* End of class handling */
+ }
+#endif /* SUPPORT_WIDE_CHARS */
+
+ /* If there are no characters > 255, or they are all to be included or
+ excluded, set the opcode to OP_CLASS or OP_NCLASS, depending on whether the
+ whole class was negated and whether there were negative specials such as \S
+ (non-UCP) in the class. Then copy the 32-byte map into the code vector,
+ negating it if necessary. */
+
+ *code++ = (negate_class == should_flip_negation) ? OP_CLASS : OP_NCLASS;
+ if (lengthptr == NULL) /* Save time in the pre-compile phase */
+ {
+ if (negate_class)
+ {
+ /* Using 255 ^ instead of ~ avoids clang sanitize warning. */
+ for (int i = 0; i < 32; i++) classbits[i] = 255 ^ classbits[i];
+ }
+ memcpy(code, classbits, 32);
+ }
+ code += 32 / sizeof(PCRE2_UCHAR);
+ break; /* End of class processing */
+
+
+ /* ===================================================================*/
+ /* Deal with (*VERB)s. */
+
+ /* Check for open captures before ACCEPT and close those that are within
+ the same assertion level, also converting ACCEPT to ASSERT_ACCEPT in an
+ assertion. In the first pass, just accumulate the length required;
+ otherwise hitting (*ACCEPT) inside many nested parentheses can cause
+ workspace overflow. Do not set firstcu after *ACCEPT. */
+
+ case META_ACCEPT:
+ cb->had_accept = had_accept = TRUE;
+ for (oc = cb->open_caps;
+ oc != NULL && oc->assert_depth >= cb->assert_depth;
+ oc = oc->next)
+ {
+ if (lengthptr != NULL)
+ {
+ *lengthptr += CU2BYTES(1) + IMM2_SIZE;
+ }
+ else
+ {
+ *code++ = OP_CLOSE;
+ PUT2INC(code, 0, oc->number);
+ }
+ }
+ *code++ = (cb->assert_depth > 0)? OP_ASSERT_ACCEPT : OP_ACCEPT;
+ if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;
+ break;
+
+ case META_PRUNE:
+ case META_SKIP:
+ cb->had_pruneorskip = TRUE;
+ /* Fall through */
+ case META_COMMIT:
+ case META_FAIL:
+ *code++ = verbops[(meta - META_MARK) >> 16];
+ break;
+
+ case META_THEN:
+ cb->external_flags |= PCRE2_HASTHEN;
+ *code++ = OP_THEN;
+ break;
+
+ /* Handle verbs with arguments. Arguments can be very long, especially in
+ 16- and 32-bit modes, and can overflow the workspace in the first pass.
+ However, the argument length is constrained to be small enough to fit in
+ one code unit. This check happens in parse_regex(). In the first pass,
+ instead of putting the argument into memory, we just update the length
+ counter and set up an empty argument. */
+
+ case META_THEN_ARG:
+ cb->external_flags |= PCRE2_HASTHEN;
+ goto VERB_ARG;
+
+ case META_PRUNE_ARG:
+ case META_SKIP_ARG:
+ cb->had_pruneorskip = TRUE;
+ /* Fall through */
+ case META_MARK:
+ case META_COMMIT_ARG:
+ VERB_ARG:
+ *code++ = verbops[(meta - META_MARK) >> 16];
+ /* The length is in characters. */
+ verbarglen = *(++pptr);
+ verbculen = 0;
+ tempcode = code++;
+ for (int i = 0; i < (int)verbarglen; i++)
+ {
+ meta = *(++pptr);
+#ifdef SUPPORT_UNICODE
+ if (utf) mclength = PRIV(ord2utf)(meta, mcbuffer); else
+#endif
+ {
+ mclength = 1;
+ mcbuffer[0] = meta;
+ }
+ if (lengthptr != NULL) *lengthptr += mclength; else
+ {
+ memcpy(code, mcbuffer, CU2BYTES(mclength));
+ code += mclength;
+ verbculen += mclength;
+ }
+ }
+
+ *tempcode = verbculen; /* Fill in the code unit length */
+ *code++ = 0; /* Terminating zero */
+ break;
+
+
+ /* ===================================================================*/
+ /* Handle options change. The new setting must be passed back for use in
+ subsequent branches. Reset the greedy defaults and the case value for
+ firstcu and reqcu. */
+
+ case META_OPTIONS:
+ *optionsptr = options = *(++pptr);
+ greedy_default = ((options & PCRE2_UNGREEDY) != 0);
+ greedy_non_default = greedy_default ^ 1;
+ req_caseopt = ((options & PCRE2_CASELESS) != 0)? REQ_CASELESS : 0;
+ break;
+
+
+ /* ===================================================================*/
+ /* Handle conditional subpatterns. The case of (?(Rdigits) is ambiguous
+ because it could be a numerical check on recursion, or a name check on a
+ group's being set. The pre-pass sets up META_COND_RNUMBER as a name so that
+ we can handle it either way. We first try for a name; if not found, process
+ the number. */
+
+ case META_COND_RNUMBER: /* (?(Rdigits) */
+ case META_COND_NAME: /* (?(name) or (?'name') or ?(<name>) */
+ case META_COND_RNAME: /* (?(R&name) - test for recursion */
+ bravalue = OP_COND;
+ {
+ int count, index;
+ unsigned int i;
+ PCRE2_SPTR name;
+ named_group *ng = cb->named_groups;
+ uint32_t length = *(++pptr);
+
+ GETPLUSOFFSET(offset, pptr);
+ name = cb->start_pattern + offset;
+
+ /* In the first pass, the names generated in the pre-pass are available,
+ but the main name table has not yet been created. Scan the list of names
+ generated in the pre-pass in order to get a number and whether or not
+ this name is duplicated. If it is not duplicated, we can handle it as a
+ numerical group. */
+
+ for (i = 0; i < cb->names_found; i++, ng++)
+ {
+ if (length == ng->length &&
+ PRIV(strncmp)(name, ng->name, length) == 0)
+ {
+ if (!ng->isdup)
+ {
+ code[1+LINK_SIZE] = (meta == META_COND_RNAME)? OP_RREF : OP_CREF;
+ PUT2(code, 2+LINK_SIZE, ng->number);
+ if (ng->number > cb->top_backref) cb->top_backref = ng->number;
+ skipunits = 1+IMM2_SIZE;
+ goto GROUP_PROCESS_NOTE_EMPTY;
+ }
+ break; /* Found a duplicated name */
+ }
+ }
+
+ /* If the name was not found we have a bad reference, unless we are
+ dealing with R<digits>, which is treated as a recursion test by number.
+ */
+
+ if (i >= cb->names_found)
+ {
+ groupnumber = 0;
+ if (meta == META_COND_RNUMBER)
+ {
+ for (i = 1; i < length; i++)
+ {
+ groupnumber = groupnumber * 10 + name[i] - CHAR_0;
+ if (groupnumber > MAX_GROUP_NUMBER)
+ {
+ *errorcodeptr = ERR61;
+ cb->erroroffset = offset + i;
+ return 0;
+ }
+ }
+ }
+
+ if (meta != META_COND_RNUMBER || groupnumber > cb->bracount)
+ {
+ *errorcodeptr = ERR15;
+ cb->erroroffset = offset;
+ return 0;
+ }
+
+ /* (?Rdigits) treated as a recursion reference by number. A value of
+ zero (which is the result of both (?R) and (?R0)) means "any", and is
+ translated into RREF_ANY (which is 0xffff). */
+
+ if (groupnumber == 0) groupnumber = RREF_ANY;
+ code[1+LINK_SIZE] = OP_RREF;
+ PUT2(code, 2+LINK_SIZE, groupnumber);
+ skipunits = 1+IMM2_SIZE;
+ goto GROUP_PROCESS_NOTE_EMPTY;
+ }
+
+ /* A duplicated name was found. Note that if an R<digits> name is found
+ (META_COND_RNUMBER), it is a reference test, not a recursion test. */
+
+ code[1+LINK_SIZE] = (meta == META_COND_RNAME)? OP_RREF : OP_CREF;
+
+ /* We have a duplicated name. In the compile pass we have to search the
+ main table in order to get the index and count values. */
+
+ count = 0; /* Values for first pass (avoids compiler warning) */
+ index = 0;
+ if (lengthptr == NULL && !find_dupname_details(name, length, &index,
+ &count, errorcodeptr, cb)) return 0;
+
+ /* Add one to the opcode to change CREF/RREF into DNCREF/DNRREF and
+ insert appropriate data values. */
+
+ code[1+LINK_SIZE]++;
+ skipunits = 1+2*IMM2_SIZE;
+ PUT2(code, 2+LINK_SIZE, index);
+ PUT2(code, 2+LINK_SIZE+IMM2_SIZE, count);
+ }
+ goto GROUP_PROCESS_NOTE_EMPTY;
+
+ /* The DEFINE condition is always false. Its internal groups may never
+ be called, so matched_char must remain false, hence the jump to
+ GROUP_PROCESS rather than GROUP_PROCESS_NOTE_EMPTY. */
+
+ case META_COND_DEFINE:
+ bravalue = OP_COND;
+ GETPLUSOFFSET(offset, pptr);
+ code[1+LINK_SIZE] = OP_DEFINE;
+ skipunits = 1;
+ goto GROUP_PROCESS;
+
+ /* Conditional test of a group's being set. */
+
+ case META_COND_NUMBER:
+ bravalue = OP_COND;
+ GETPLUSOFFSET(offset, pptr);
+ groupnumber = *(++pptr);
+ if (groupnumber > cb->bracount)
+ {
+ *errorcodeptr = ERR15;
+ cb->erroroffset = offset;
+ return 0;
+ }
+ if (groupnumber > cb->top_backref) cb->top_backref = groupnumber;
+ offset -= 2; /* Point at initial ( for too many branches error */
+ code[1+LINK_SIZE] = OP_CREF;
+ skipunits = 1+IMM2_SIZE;
+ PUT2(code, 2+LINK_SIZE, groupnumber);
+ goto GROUP_PROCESS_NOTE_EMPTY;
+
+ /* Test for the PCRE2 version. */
+
+ case META_COND_VERSION:
+ bravalue = OP_COND;
+ if (pptr[1] > 0)
+ code[1+LINK_SIZE] = ((PCRE2_MAJOR > pptr[2]) ||
+ (PCRE2_MAJOR == pptr[2] && PCRE2_MINOR >= pptr[3]))?
+ OP_TRUE : OP_FALSE;
+ else
+ code[1+LINK_SIZE] = (PCRE2_MAJOR == pptr[2] && PCRE2_MINOR == pptr[3])?
+ OP_TRUE : OP_FALSE;
+ skipunits = 1;
+ pptr += 3;
+ goto GROUP_PROCESS_NOTE_EMPTY;
+
+ /* The condition is an assertion, possibly preceded by a callout. */
+
+ case META_COND_ASSERT:
+ bravalue = OP_COND;
+ goto GROUP_PROCESS_NOTE_EMPTY;
+
+
+ /* ===================================================================*/
+ /* Handle all kinds of nested bracketed groups. The non-capturing,
+ non-conditional cases are here; others come to GROUP_PROCESS via goto. */
+
+ case META_LOOKAHEAD:
+ bravalue = OP_ASSERT;
+ cb->assert_depth += 1;
+ goto GROUP_PROCESS;
+
+ case META_LOOKAHEAD_NA:
+ bravalue = OP_ASSERT_NA;
+ cb->assert_depth += 1;
+ goto GROUP_PROCESS;
+
+ /* Optimize (?!) to (*FAIL) unless it is quantified - which is a weird
+ thing to do, but Perl allows all assertions to be quantified, and when
+ they contain capturing parentheses there may be a potential use for
+ this feature. Not that that applies to a quantified (?!) but we allow
+ it for uniformity. */
+
+ case META_LOOKAHEADNOT:
+ if (pptr[1] == META_KET &&
+ (pptr[2] < META_ASTERISK || pptr[2] > META_MINMAX_QUERY))
+ {
+ *code++ = OP_FAIL;
+ pptr++;
+ }
+ else
+ {
+ bravalue = OP_ASSERT_NOT;
+ cb->assert_depth += 1;
+ goto GROUP_PROCESS;
+ }
+ break;
+
+ case META_LOOKBEHIND:
+ bravalue = OP_ASSERTBACK;
+ cb->assert_depth += 1;
+ goto GROUP_PROCESS;
+
+ case META_LOOKBEHINDNOT:
+ bravalue = OP_ASSERTBACK_NOT;
+ cb->assert_depth += 1;
+ goto GROUP_PROCESS;
+
+ case META_LOOKBEHIND_NA:
+ bravalue = OP_ASSERTBACK_NA;
+ cb->assert_depth += 1;
+ goto GROUP_PROCESS;
+
+ case META_ATOMIC:
+ bravalue = OP_ONCE;
+ goto GROUP_PROCESS_NOTE_EMPTY;
+
+ case META_SCRIPT_RUN:
+ bravalue = OP_SCRIPT_RUN;
+ goto GROUP_PROCESS_NOTE_EMPTY;
+
+ case META_NOCAPTURE:
+ bravalue = OP_BRA;
+ /* Fall through */
+
+ /* Process nested bracketed regex. The nesting depth is maintained for the
+ benefit of the stackguard function. The test for too deep nesting is now
+ done in parse_regex(). Assertion and DEFINE groups come to GROUP_PROCESS;
+ others come to GROUP_PROCESS_NOTE_EMPTY, to indicate that we need to take
+ note of whether or not they may match an empty string. */
+
+ GROUP_PROCESS_NOTE_EMPTY:
+ note_group_empty = TRUE;
+
+ GROUP_PROCESS:
+ cb->parens_depth += 1;
+ *code = bravalue;
+ pptr++;
+ tempcode = code;
+ tempreqvary = cb->req_varyopt; /* Save value before group */
+ length_prevgroup = 0; /* Initialize for pre-compile phase */
+
+ if ((group_return =
+ compile_regex(
+ options, /* The option state */
+ &tempcode, /* Where to put code (updated) */
+ &pptr, /* Input pointer (updated) */
+ errorcodeptr, /* Where to put an error message */
+ skipunits, /* Skip over bracket number */
+ &subfirstcu, /* For possible first char */
+ &subfirstcuflags,
+ &subreqcu, /* For possible last char */
+ &subreqcuflags,
+ bcptr, /* Current branch chain */
+ cb, /* Compile data block */
+ (lengthptr == NULL)? NULL : /* Actual compile phase */
+ &length_prevgroup /* Pre-compile phase */
+ )) == 0)
+ return 0; /* Error */
+
+ cb->parens_depth -= 1;
+
+ /* If that was a non-conditional significant group (not an assertion, not a
+ DEFINE) that matches at least one character, then the current item matches
+ a character. Conditionals are handled below. */
+
+ if (note_group_empty && bravalue != OP_COND && group_return > 0)
+ matched_char = TRUE;
+
+ /* If we've just compiled an assertion, pop the assert depth. */
+
+ if (bravalue >= OP_ASSERT && bravalue <= OP_ASSERTBACK_NA)
+ cb->assert_depth -= 1;
+
+ /* At the end of compiling, code is still pointing to the start of the
+ group, while tempcode has been updated to point past the end of the group.
+ The parsed pattern pointer (pptr) is on the closing META_KET.
+
+ If this is a conditional bracket, check that there are no more than
+ two branches in the group, or just one if it's a DEFINE group. We do this
+ in the real compile phase, not in the pre-pass, where the whole group may
+ not be available. */
+
+ if (bravalue == OP_COND && lengthptr == NULL)
+ {
+ PCRE2_UCHAR *tc = code;
+ int condcount = 0;
+
+ do {
+ condcount++;
+ tc += GET(tc,1);
+ }
+ while (*tc != OP_KET);
+
+ /* A DEFINE group is never obeyed inline (the "condition" is always
+ false). It must have only one branch. Having checked this, change the
+ opcode to OP_FALSE. */
+
+ if (code[LINK_SIZE+1] == OP_DEFINE)
+ {
+ if (condcount > 1)
+ {
+ cb->erroroffset = offset;
+ *errorcodeptr = ERR54;
+ return 0;
+ }
+ code[LINK_SIZE+1] = OP_FALSE;
+ bravalue = OP_DEFINE; /* A flag to suppress char handling below */
+ }
+
+ /* A "normal" conditional group. If there is just one branch, we must not
+ make use of its firstcu or reqcu, because this is equivalent to an
+ empty second branch. Also, it may match an empty string. If there are two
+ branches, this item must match a character if the group must. */
+
+ else
+ {
+ if (condcount > 2)
+ {
+ cb->erroroffset = offset;
+ *errorcodeptr = ERR27;
+ return 0;
+ }
+ if (condcount == 1) subfirstcuflags = subreqcuflags = REQ_NONE;
+ else if (group_return > 0) matched_char = TRUE;
+ }
+ }
+
+ /* In the pre-compile phase, update the length by the length of the group,
+ less the brackets at either end. Then reduce the compiled code to just a
+ set of non-capturing brackets so that it doesn't use much memory if it is
+ duplicated by a quantifier.*/
+
+ if (lengthptr != NULL)
+ {
+ if (OFLOW_MAX - *lengthptr < length_prevgroup - 2 - 2*LINK_SIZE)
+ {
+ *errorcodeptr = ERR20;
+ return 0;
+ }
+ *lengthptr += length_prevgroup - 2 - 2*LINK_SIZE;
+ code++; /* This already contains bravalue */
+ PUTINC(code, 0, 1 + LINK_SIZE);
+ *code++ = OP_KET;
+ PUTINC(code, 0, 1 + LINK_SIZE);
+ break; /* No need to waste time with special character handling */
+ }
+
+ /* Otherwise update the main code pointer to the end of the group. */
+
+ code = tempcode;
+
+ /* For a DEFINE group, required and first character settings are not
+ relevant. */
+
+ if (bravalue == OP_DEFINE) break;
+
+ /* Handle updating of the required and first code units for other types of
+ group. Update for normal brackets of all kinds, and conditions with two
+ branches (see code above). If the bracket is followed by a quantifier with
+ zero repeat, we have to back off. Hence the definition of zeroreqcu and
+ zerofirstcu outside the main loop so that they can be accessed for the back
+ off. */
+
+ zeroreqcu = reqcu;
+ zeroreqcuflags = reqcuflags;
+ zerofirstcu = firstcu;
+ zerofirstcuflags = firstcuflags;
+ groupsetfirstcu = FALSE;
+
+ if (bravalue >= OP_ONCE) /* Not an assertion */
+ {
+ /* If we have not yet set a firstcu in this branch, take it from the
+ subpattern, remembering that it was set here so that a repeat of more
+ than one can replicate it as reqcu if necessary. If the subpattern has
+ no firstcu, set "none" for the whole branch. In both cases, a zero
+ repeat forces firstcu to "none". */
+
+ if (firstcuflags == REQ_UNSET && subfirstcuflags != REQ_UNSET)
+ {
+ if (subfirstcuflags < REQ_NONE)
+ {
+ firstcu = subfirstcu;
+ firstcuflags = subfirstcuflags;
+ groupsetfirstcu = TRUE;
+ }
+ else firstcuflags = REQ_NONE;
+ zerofirstcuflags = REQ_NONE;
+ }
+
+ /* If firstcu was previously set, convert the subpattern's firstcu
+ into reqcu if there wasn't one, using the vary flag that was in
+ existence beforehand. */
+
+ else if (subfirstcuflags < REQ_NONE && subreqcuflags >= REQ_NONE)
+ {
+ subreqcu = subfirstcu;
+ subreqcuflags = subfirstcuflags | tempreqvary;
+ }
+
+ /* If the subpattern set a required code unit (or set a first code unit
+ that isn't really the first code unit - see above), set it. */
+
+ if (subreqcuflags < REQ_NONE)
+ {
+ reqcu = subreqcu;
+ reqcuflags = subreqcuflags;
+ }
+ }
+
+ /* For a forward assertion, we take the reqcu, if set, provided that the
+ group has also set a firstcu. This can be helpful if the pattern that
+ follows the assertion doesn't set a different char. For example, it's
+ useful for /(?=abcde).+/. We can't set firstcu for an assertion, however
+ because it leads to incorrect effect for patterns such as /(?=a)a.+/ when
+ the "real" "a" would then become a reqcu instead of a firstcu. This is
+ overcome by a scan at the end if there's no firstcu, looking for an
+ asserted first char. A similar effect for patterns like /(?=.*X)X$/ means
+ we must only take the reqcu when the group also set a firstcu. Otherwise,
+ in that example, 'X' ends up set for both. */
+
+ else if ((bravalue == OP_ASSERT || bravalue == OP_ASSERT_NA) &&
+ subreqcuflags < REQ_NONE && subfirstcuflags < REQ_NONE)
+ {
+ reqcu = subreqcu;
+ reqcuflags = subreqcuflags;
+ }
+
+ break; /* End of nested group handling */
+
+
+ /* ===================================================================*/
+ /* Handle named backreferences and recursions. */
+
+ case META_BACKREF_BYNAME:
+ case META_RECURSE_BYNAME:
+ {
+ int count, index;
+ PCRE2_SPTR name;
+ BOOL is_dupname = FALSE;
+ named_group *ng = cb->named_groups;
+ uint32_t length = *(++pptr);
+
+ GETPLUSOFFSET(offset, pptr);
+ name = cb->start_pattern + offset;
+
+ /* In the first pass, the names generated in the pre-pass are available,
+ but the main name table has not yet been created. Scan the list of names
+ generated in the pre-pass in order to get a number and whether or not
+ this name is duplicated. */
+
+ groupnumber = 0;
+ for (unsigned int i = 0; i < cb->names_found; i++, ng++)
+ {
+ if (length == ng->length &&
+ PRIV(strncmp)(name, ng->name, length) == 0)
+ {
+ is_dupname = ng->isdup;
+ groupnumber = ng->number;
+
+ /* For a recursion, that's all that is needed. We can now go to
+ the code that handles numerical recursion, applying it to the first
+ group with the given name. */
+
+ if (meta == META_RECURSE_BYNAME)
+ {
+ meta_arg = groupnumber;
+ goto HANDLE_NUMERICAL_RECURSION;
+ }
+
+ /* For a back reference, update the back reference map and the
+ maximum back reference. */
+
+ cb->backref_map |= (groupnumber < 32)? (1u << groupnumber) : 1;
+ if (groupnumber > cb->top_backref)
+ cb->top_backref = groupnumber;
+ }
+ }
+
+ /* If the name was not found we have a bad reference. */
+
+ if (groupnumber == 0)
+ {
+ *errorcodeptr = ERR15;
+ cb->erroroffset = offset;
+ return 0;
+ }
+
+ /* If a back reference name is not duplicated, we can handle it as
+ a numerical reference. */
+
+ if (!is_dupname)
+ {
+ meta_arg = groupnumber;
+ goto HANDLE_SINGLE_REFERENCE;
+ }
+
+ /* If a back reference name is duplicated, we generate a different
+ opcode to a numerical back reference. In the second pass we must
+ search for the index and count in the final name table. */
+
+ count = 0; /* Values for first pass (avoids compiler warning) */
+ index = 0;
+ if (lengthptr == NULL && !find_dupname_details(name, length, &index,
+ &count, errorcodeptr, cb)) return 0;
+
+ if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;
+ *code++ = ((options & PCRE2_CASELESS) != 0)? OP_DNREFI : OP_DNREF;
+ PUT2INC(code, 0, index);
+ PUT2INC(code, 0, count);
+ }
+ break;
+
+
+ /* ===================================================================*/
+ /* Handle a numerical callout. */
+
+ case META_CALLOUT_NUMBER:
+ code[0] = OP_CALLOUT;
+ PUT(code, 1, pptr[1]); /* Offset to next pattern item */
+ PUT(code, 1 + LINK_SIZE, pptr[2]); /* Length of next pattern item */
+ code[1 + 2*LINK_SIZE] = pptr[3];
+ pptr += 3;
+ code += PRIV(OP_lengths)[OP_CALLOUT];
+ break;
+
+
+ /* ===================================================================*/
+ /* Handle a callout with a string argument. In the pre-pass we just compute
+ the length without generating anything. The length in pptr[3] includes both
+ delimiters; in the actual compile only the first one is copied, but a
+ terminating zero is added. Any doubled delimiters within the string make
+ this an overestimate, but it is not worth bothering about. */
+
+ case META_CALLOUT_STRING:
+ if (lengthptr != NULL)
+ {
+ *lengthptr += pptr[3] + (1 + 4*LINK_SIZE);
+ pptr += 3;
+ SKIPOFFSET(pptr);
+ }
+
+ /* In the real compile we can copy the string. The starting delimiter is
+ included so that the client can discover it if they want. We also pass the
+ start offset to help a script language give better error messages. */
+
+ else
+ {
+ PCRE2_SPTR pp;
+ uint32_t delimiter;
+ uint32_t length = pptr[3];
+ PCRE2_UCHAR *callout_string = code + (1 + 4*LINK_SIZE);
+
+ code[0] = OP_CALLOUT_STR;
+ PUT(code, 1, pptr[1]); /* Offset to next pattern item */
+ PUT(code, 1 + LINK_SIZE, pptr[2]); /* Length of next pattern item */
+
+ pptr += 3;
+ GETPLUSOFFSET(offset, pptr); /* Offset to string in pattern */
+ pp = cb->start_pattern + offset;
+ delimiter = *callout_string++ = *pp++;
+ if (delimiter == CHAR_LEFT_CURLY_BRACKET)
+ delimiter = CHAR_RIGHT_CURLY_BRACKET;
+ PUT(code, 1 + 3*LINK_SIZE, (int)(offset + 1)); /* One after delimiter */
+
+ /* The syntax of the pattern was checked in the parsing scan. The length
+ includes both delimiters, but we have passed the opening one just above,
+ so we reduce length before testing it. The test is for > 1 because we do
+ not want to copy the final delimiter. This also ensures that pp[1] is
+ accessible. */
+
+ while (--length > 1)
+ {
+ if (*pp == delimiter && pp[1] == delimiter)
+ {
+ *callout_string++ = delimiter;
+ pp += 2;
+ length--;
+ }
+ else *callout_string++ = *pp++;
+ }
+ *callout_string++ = CHAR_NUL;
+
+ /* Set the length of the entire item, the advance to its end. */
+
+ PUT(code, 1 + 2*LINK_SIZE, (int)(callout_string - code));
+ code = callout_string;
+ }
+ break;
+
+
+ /* ===================================================================*/
+ /* Handle repetition. The different types are all sorted out in the parsing
+ pass. */
+
+ case META_MINMAX_PLUS:
+ case META_MINMAX_QUERY:
+ case META_MINMAX:
+ repeat_min = *(++pptr);
+ repeat_max = *(++pptr);
+ goto REPEAT;
+
+ case META_ASTERISK:
+ case META_ASTERISK_PLUS:
+ case META_ASTERISK_QUERY:
+ repeat_min = 0;
+ repeat_max = REPEAT_UNLIMITED;
+ goto REPEAT;
+
+ case META_PLUS:
+ case META_PLUS_PLUS:
+ case META_PLUS_QUERY:
+ repeat_min = 1;
+ repeat_max = REPEAT_UNLIMITED;
+ goto REPEAT;
+
+ case META_QUERY:
+ case META_QUERY_PLUS:
+ case META_QUERY_QUERY:
+ repeat_min = 0;
+ repeat_max = 1;
+
+ REPEAT:
+ if (previous_matched_char && repeat_min > 0) matched_char = TRUE;
+
+ /* Remember whether this is a variable length repeat, and default to
+ single-char opcodes. */
+
+ reqvary = (repeat_min == repeat_max)? 0 : REQ_VARY;
+ op_type = 0;
+
+ /* Adjust first and required code units for a zero repeat. */
+
+ if (repeat_min == 0)
+ {
+ firstcu = zerofirstcu;
+ firstcuflags = zerofirstcuflags;
+ reqcu = zeroreqcu;
+ reqcuflags = zeroreqcuflags;
+ }
+
+ /* Note the greediness and possessiveness. */
+
+ switch (meta)
+ {
+ case META_MINMAX_PLUS:
+ case META_ASTERISK_PLUS:
+ case META_PLUS_PLUS:
+ case META_QUERY_PLUS:
+ repeat_type = 0; /* Force greedy */
+ possessive_quantifier = TRUE;
+ break;
+
+ case META_MINMAX_QUERY:
+ case META_ASTERISK_QUERY:
+ case META_PLUS_QUERY:
+ case META_QUERY_QUERY:
+ repeat_type = greedy_non_default;
+ possessive_quantifier = FALSE;
+ break;
+
+ default:
+ repeat_type = greedy_default;
+ possessive_quantifier = FALSE;
+ break;
+ }
+
+ /* Save start of previous item, in case we have to move it up in order to
+ insert something before it, and remember what it was. */
+
+ tempcode = previous;
+ op_previous = *previous;
+
+ /* Now handle repetition for the different types of item. If the repeat
+ minimum and the repeat maximum are both 1, we can ignore the quantifier for
+ non-parenthesized items, as they have only one alternative. For anything in
+ parentheses, we must not ignore if {1} is possessive. */
+
+ switch (op_previous)
+ {
+ /* If previous was a character or negated character match, abolish the
+ item and generate a repeat item instead. If a char item has a minimum of
+ more than one, ensure that it is set in reqcu - it might not be if a
+ sequence such as x{3} is the first thing in a branch because the x will
+ have gone into firstcu instead. */
+
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT;
+ op_type = chartypeoffset[op_previous - OP_CHAR];
+
+ /* Deal with UTF characters that take up more than one code unit. */
+
+#ifdef MAYBE_UTF_MULTI
+ if (utf && NOT_FIRSTCU(code[-1]))
+ {
+ PCRE2_UCHAR *lastchar = code - 1;
+ BACKCHAR(lastchar);
+ mclength = (uint32_t)(code - lastchar); /* Length of UTF character */
+ memcpy(mcbuffer, lastchar, CU2BYTES(mclength)); /* Save the char */
+ }
+ else
+#endif /* MAYBE_UTF_MULTI */
+
+ /* Handle the case of a single code unit - either with no UTF support, or
+ with UTF disabled, or for a single-code-unit UTF character. In the latter
+ case, for a repeated positive match, get the caseless flag for the
+ required code unit from the previous character, because a class like [Aa]
+ sets a caseless A but by now the req_caseopt flag has been reset. */
+
+ {
+ mcbuffer[0] = code[-1];
+ mclength = 1;
+ if (op_previous <= OP_CHARI && repeat_min > 1)
+ {
+ reqcu = mcbuffer[0];
+ reqcuflags = cb->req_varyopt;
+ if (op_previous == OP_CHARI) reqcuflags |= REQ_CASELESS;
+ }
+ }
+ goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */
+
+ /* If previous was a character class or a back reference, we put the
+ repeat stuff after it, but just skip the item if the repeat was {0,0}. */
+
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+#endif
+ case OP_CLASS:
+ case OP_NCLASS:
+ case OP_REF:
+ case OP_REFI:
+ case OP_DNREF:
+ case OP_DNREFI:
+
+ if (repeat_max == 0)
+ {
+ code = previous;
+ goto END_REPEAT;
+ }
+ if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT;
+
+ if (repeat_min == 0 && repeat_max == REPEAT_UNLIMITED)
+ *code++ = OP_CRSTAR + repeat_type;
+ else if (repeat_min == 1 && repeat_max == REPEAT_UNLIMITED)
+ *code++ = OP_CRPLUS + repeat_type;
+ else if (repeat_min == 0 && repeat_max == 1)
+ *code++ = OP_CRQUERY + repeat_type;
+ else
+ {
+ *code++ = OP_CRRANGE + repeat_type;
+ PUT2INC(code, 0, repeat_min);
+ if (repeat_max == REPEAT_UNLIMITED) repeat_max = 0; /* 2-byte encoding for max */
+ PUT2INC(code, 0, repeat_max);
+ }
+ break;
+
+ /* If previous is OP_FAIL, it was generated by an empty class []
+ (PCRE2_ALLOW_EMPTY_CLASS is set). The other ways in which OP_FAIL can be
+ generated, that is by (*FAIL) or (?!), disallow a quantifier at parse
+ time. We can just ignore this repeat. */
+
+ case OP_FAIL:
+ goto END_REPEAT;
+
+ /* Prior to 10.30, repeated recursions were wrapped in OP_ONCE brackets
+ because pcre2_match() could not handle backtracking into recursively
+ called groups. Now that this backtracking is available, we no longer need
+ to do this. However, we still need to replicate recursions as we do for
+ groups so as to have independent backtracking points. We can replicate
+ for the minimum number of repeats directly. For optional repeats we now
+ wrap the recursion in OP_BRA brackets and make use of the bracket
+ repetition. */
+
+ case OP_RECURSE:
+ if (repeat_max == 1 && repeat_min == 1 && !possessive_quantifier)
+ goto END_REPEAT;
+
+ /* Generate unwrapped repeats for a non-zero minimum, except when the
+ minimum is 1 and the maximum unlimited, because that can be handled with
+ OP_BRA terminated by OP_KETRMAX/MIN. When the maximum is equal to the
+ minimum, we just need to generate the appropriate additional copies.
+ Otherwise we need to generate one more, to simulate the situation when
+ the minimum is zero. */
+
+ if (repeat_min > 0 && (repeat_min != 1 || repeat_max != REPEAT_UNLIMITED))
+ {
+ int replicate = repeat_min;
+ if (repeat_min == repeat_max) replicate--;
+
+ /* In the pre-compile phase, we don't actually do the replication. We
+ just adjust the length as if we had. Do some paranoid checks for
+ potential integer overflow. The INT64_OR_DOUBLE type is a 64-bit
+ integer type when available, otherwise double. */
+
+ if (lengthptr != NULL)
+ {
+ PCRE2_SIZE delta = replicate*(1 + LINK_SIZE);
+ if ((INT64_OR_DOUBLE)replicate*
+ (INT64_OR_DOUBLE)(1 + LINK_SIZE) >
+ (INT64_OR_DOUBLE)INT_MAX ||
+ OFLOW_MAX - *lengthptr < delta)
+ {
+ *errorcodeptr = ERR20;
+ return 0;
+ }
+ *lengthptr += delta;
+ }
+
+ else for (int i = 0; i < replicate; i++)
+ {
+ memcpy(code, previous, CU2BYTES(1 + LINK_SIZE));
+ previous = code;
+ code += 1 + LINK_SIZE;
+ }
+
+ /* If the number of repeats is fixed, we are done. Otherwise, adjust
+ the counts and fall through. */
+
+ if (repeat_min == repeat_max) break;
+ if (repeat_max != REPEAT_UNLIMITED) repeat_max -= repeat_min;
+ repeat_min = 0;
+ }
+
+ /* Wrap the recursion call in OP_BRA brackets. */
+
+ (void)memmove(previous + 1 + LINK_SIZE, previous, CU2BYTES(1 + LINK_SIZE));
+ op_previous = *previous = OP_BRA;
+ PUT(previous, 1, 2 + 2*LINK_SIZE);
+ previous[2 + 2*LINK_SIZE] = OP_KET;
+ PUT(previous, 3 + 2*LINK_SIZE, 2 + 2*LINK_SIZE);
+ code += 2 + 2 * LINK_SIZE;
+ length_prevgroup = 3 + 3*LINK_SIZE;
+ group_return = -1; /* Set "may match empty string" */
+
+ /* Now treat as a repeated OP_BRA. */
+ /* Fall through */
+
+ /* If previous was a bracket group, we may have to replicate it in
+ certain cases. Note that at this point we can encounter only the "basic"
+ bracket opcodes such as BRA and CBRA, as this is the place where they get
+ converted into the more special varieties such as BRAPOS and SBRA.
+ Originally, PCRE did not allow repetition of assertions, but now it does,
+ for Perl compatibility. */
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERTBACK_NA:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_BRA:
+ case OP_CBRA:
+ case OP_COND:
+ {
+ int len = (int)(code - previous);
+ PCRE2_UCHAR *bralink = NULL;
+ PCRE2_UCHAR *brazeroptr = NULL;
+
+ if (repeat_max == 1 && repeat_min == 1 && !possessive_quantifier)
+ goto END_REPEAT;
+
+ /* Repeating a DEFINE group (or any group where the condition is always
+ FALSE and there is only one branch) is pointless, but Perl allows the
+ syntax, so we just ignore the repeat. */
+
+ if (op_previous == OP_COND && previous[LINK_SIZE+1] == OP_FALSE &&
+ previous[GET(previous, 1)] != OP_ALT)
+ goto END_REPEAT;
+
+ /* Perl allows all assertions to be quantified, and when they contain
+ capturing parentheses and/or are optional there are potential uses for
+ this feature. PCRE2 used to force the maximum quantifier to 1 on the
+ invalid grounds that further repetition was never useful. This was
+ always a bit pointless, since an assertion could be wrapped with a
+ repeated group to achieve the effect. General repetition is now
+ permitted, but if the maximum is unlimited it is set to one more than
+ the minimum. */
+
+ if (op_previous < OP_ONCE) /* Assertion */
+ {
+ if (repeat_max == REPEAT_UNLIMITED) repeat_max = repeat_min + 1;
+ }
+
+ /* The case of a zero minimum is special because of the need to stick
+ OP_BRAZERO in front of it, and because the group appears once in the
+ data, whereas in other cases it appears the minimum number of times. For
+ this reason, it is simplest to treat this case separately, as otherwise
+ the code gets far too messy. There are several special subcases when the
+ minimum is zero. */
+
+ if (repeat_min == 0)
+ {
+ /* If the maximum is also zero, we used to just omit the group from
+ the output altogether, like this:
+
+ ** if (repeat_max == 0)
+ ** {
+ ** code = previous;
+ ** goto END_REPEAT;
+ ** }
+
+ However, that fails when a group or a subgroup within it is
+ referenced as a subroutine from elsewhere in the pattern, so now we
+ stick in OP_SKIPZERO in front of it so that it is skipped on
+ execution. As we don't have a list of which groups are referenced, we
+ cannot do this selectively.
+
+ If the maximum is 1 or unlimited, we just have to stick in the
+ BRAZERO and do no more at this point. */
+
+ if (repeat_max <= 1 || repeat_max == REPEAT_UNLIMITED)
+ {
+ (void)memmove(previous + 1, previous, CU2BYTES(len));
+ code++;
+ if (repeat_max == 0)
+ {
+ *previous++ = OP_SKIPZERO;
+ goto END_REPEAT;
+ }
+ brazeroptr = previous; /* Save for possessive optimizing */
+ *previous++ = OP_BRAZERO + repeat_type;
+ }
+
+ /* If the maximum is greater than 1 and limited, we have to replicate
+ in a nested fashion, sticking OP_BRAZERO before each set of brackets.
+ The first one has to be handled carefully because it's the original
+ copy, which has to be moved up. The remainder can be handled by code
+ that is common with the non-zero minimum case below. We have to
+ adjust the value or repeat_max, since one less copy is required. */
+
+ else
+ {
+ int linkoffset;
+ (void)memmove(previous + 2 + LINK_SIZE, previous, CU2BYTES(len));
+ code += 2 + LINK_SIZE;
+ *previous++ = OP_BRAZERO + repeat_type;
+ *previous++ = OP_BRA;
+
+ /* We chain together the bracket link offset fields that have to be
+ filled in later when the ends of the brackets are reached. */
+
+ linkoffset = (bralink == NULL)? 0 : (int)(previous - bralink);
+ bralink = previous;
+ PUTINC(previous, 0, linkoffset);
+ }
+
+ if (repeat_max != REPEAT_UNLIMITED) repeat_max--;
+ }
+
+ /* If the minimum is greater than zero, replicate the group as many
+ times as necessary, and adjust the maximum to the number of subsequent
+ copies that we need. */
+
+ else
+ {
+ if (repeat_min > 1)
+ {
+ /* In the pre-compile phase, we don't actually do the replication.
+ We just adjust the length as if we had. Do some paranoid checks for
+ potential integer overflow. The INT64_OR_DOUBLE type is a 64-bit
+ integer type when available, otherwise double. */
+
+ if (lengthptr != NULL)
+ {
+ PCRE2_SIZE delta = (repeat_min - 1)*length_prevgroup;
+ if ((INT64_OR_DOUBLE)(repeat_min - 1)*
+ (INT64_OR_DOUBLE)length_prevgroup >
+ (INT64_OR_DOUBLE)INT_MAX ||
+ OFLOW_MAX - *lengthptr < delta)
+ {
+ *errorcodeptr = ERR20;
+ return 0;
+ }
+ *lengthptr += delta;
+ }
+
+ /* This is compiling for real. If there is a set first code unit
+ for the group, and we have not yet set a "required code unit", set
+ it. */
+
+ else
+ {
+ if (groupsetfirstcu && reqcuflags >= REQ_NONE)
+ {
+ reqcu = firstcu;
+ reqcuflags = firstcuflags;
+ }
+ for (uint32_t i = 1; i < repeat_min; i++)
+ {
+ memcpy(code, previous, CU2BYTES(len));
+ code += len;
+ }
+ }
+ }
+
+ if (repeat_max != REPEAT_UNLIMITED) repeat_max -= repeat_min;
+ }
+
+ /* This code is common to both the zero and non-zero minimum cases. If
+ the maximum is limited, it replicates the group in a nested fashion,
+ remembering the bracket starts on a stack. In the case of a zero
+ minimum, the first one was set up above. In all cases the repeat_max
+ now specifies the number of additional copies needed. Again, we must
+ remember to replicate entries on the forward reference list. */
+
+ if (repeat_max != REPEAT_UNLIMITED)
+ {
+ /* In the pre-compile phase, we don't actually do the replication. We
+ just adjust the length as if we had. For each repetition we must add
+ 1 to the length for BRAZERO and for all but the last repetition we
+ must add 2 + 2*LINKSIZE to allow for the nesting that occurs. Do some
+ paranoid checks to avoid integer overflow. The INT64_OR_DOUBLE type
+ is a 64-bit integer type when available, otherwise double. */
+
+ if (lengthptr != NULL && repeat_max > 0)
+ {
+ PCRE2_SIZE delta = repeat_max*(length_prevgroup + 1 + 2 + 2*LINK_SIZE) -
+ 2 - 2*LINK_SIZE; /* Last one doesn't nest */
+ if ((INT64_OR_DOUBLE)repeat_max *
+ (INT64_OR_DOUBLE)(length_prevgroup + 1 + 2 + 2*LINK_SIZE)
+ > (INT64_OR_DOUBLE)INT_MAX ||
+ OFLOW_MAX - *lengthptr < delta)
+ {
+ *errorcodeptr = ERR20;
+ return 0;
+ }
+ *lengthptr += delta;
+ }
+
+ /* This is compiling for real */
+
+ else for (uint32_t i = repeat_max; i >= 1; i--)
+ {
+ *code++ = OP_BRAZERO + repeat_type;
+
+ /* All but the final copy start a new nesting, maintaining the
+ chain of brackets outstanding. */
+
+ if (i != 1)
+ {
+ int linkoffset;
+ *code++ = OP_BRA;
+ linkoffset = (bralink == NULL)? 0 : (int)(code - bralink);
+ bralink = code;
+ PUTINC(code, 0, linkoffset);
+ }
+
+ memcpy(code, previous, CU2BYTES(len));
+ code += len;
+ }
+
+ /* Now chain through the pending brackets, and fill in their length
+ fields (which are holding the chain links pro tem). */
+
+ while (bralink != NULL)
+ {
+ int oldlinkoffset;
+ int linkoffset = (int)(code - bralink + 1);
+ PCRE2_UCHAR *bra = code - linkoffset;
+ oldlinkoffset = GET(bra, 1);
+ bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset;
+ *code++ = OP_KET;
+ PUTINC(code, 0, linkoffset);
+ PUT(bra, 1, linkoffset);
+ }
+ }
+
+ /* If the maximum is unlimited, set a repeater in the final copy. For
+ SCRIPT_RUN and ONCE brackets, that's all we need to do. However,
+ possessively repeated ONCE brackets can be converted into non-capturing
+ brackets, as the behaviour of (?:xx)++ is the same as (?>xx)++ and this
+ saves having to deal with possessive ONCEs specially.
+
+ Otherwise, when we are doing the actual compile phase, check to see
+ whether this group is one that could match an empty string. If so,
+ convert the initial operator to the S form (e.g. OP_BRA -> OP_SBRA) so
+ that runtime checking can be done. [This check is also applied to ONCE
+ and SCRIPT_RUN groups at runtime, but in a different way.]
+
+ Then, if the quantifier was possessive and the bracket is not a
+ conditional, we convert the BRA code to the POS form, and the KET code
+ to KETRPOS. (It turns out to be convenient at runtime to detect this
+ kind of subpattern at both the start and at the end.) The use of
+ special opcodes makes it possible to reduce greatly the stack usage in
+ pcre2_match(). If the group is preceded by OP_BRAZERO, convert this to
+ OP_BRAPOSZERO.
+
+ Then, if the minimum number of matches is 1 or 0, cancel the possessive
+ flag so that the default action below, of wrapping everything inside
+ atomic brackets, does not happen. When the minimum is greater than 1,
+ there will be earlier copies of the group, and so we still have to wrap
+ the whole thing. */
+
+ else
+ {
+ PCRE2_UCHAR *ketcode = code - 1 - LINK_SIZE;
+ PCRE2_UCHAR *bracode = ketcode - GET(ketcode, 1);
+
+ /* Convert possessive ONCE brackets to non-capturing */
+
+ if (*bracode == OP_ONCE && possessive_quantifier) *bracode = OP_BRA;
+
+ /* For non-possessive ONCE and for SCRIPT_RUN brackets, all we need
+ to do is to set the KET. */
+
+ if (*bracode == OP_ONCE || *bracode == OP_SCRIPT_RUN)
+ *ketcode = OP_KETRMAX + repeat_type;
+
+ /* Handle non-SCRIPT_RUN and non-ONCE brackets and possessive ONCEs
+ (which have been converted to non-capturing above). */
+
+ else
+ {
+ /* In the compile phase, adjust the opcode if the group can match
+ an empty string. For a conditional group with only one branch, the
+ value of group_return will not show "could be empty", so we must
+ check that separately. */
+
+ if (lengthptr == NULL)
+ {
+ if (group_return < 0) *bracode += OP_SBRA - OP_BRA;
+ if (*bracode == OP_COND && bracode[GET(bracode,1)] != OP_ALT)
+ *bracode = OP_SCOND;
+ }
+
+ /* Handle possessive quantifiers. */
+
+ if (possessive_quantifier)
+ {
+ /* For COND brackets, we wrap the whole thing in a possessively
+ repeated non-capturing bracket, because we have not invented POS
+ versions of the COND opcodes. */
+
+ if (*bracode == OP_COND || *bracode == OP_SCOND)
+ {
+ int nlen = (int)(code - bracode);
+ (void)memmove(bracode + 1 + LINK_SIZE, bracode, CU2BYTES(nlen));
+ code += 1 + LINK_SIZE;
+ nlen += 1 + LINK_SIZE;
+ *bracode = (*bracode == OP_COND)? OP_BRAPOS : OP_SBRAPOS;
+ *code++ = OP_KETRPOS;
+ PUTINC(code, 0, nlen);
+ PUT(bracode, 1, nlen);
+ }
+
+ /* For non-COND brackets, we modify the BRA code and use KETRPOS. */
+
+ else
+ {
+ *bracode += 1; /* Switch to xxxPOS opcodes */
+ *ketcode = OP_KETRPOS;
+ }
+
+ /* If the minimum is zero, mark it as possessive, then unset the
+ possessive flag when the minimum is 0 or 1. */
+
+ if (brazeroptr != NULL) *brazeroptr = OP_BRAPOSZERO;
+ if (repeat_min < 2) possessive_quantifier = FALSE;
+ }
+
+ /* Non-possessive quantifier */
+
+ else *ketcode = OP_KETRMAX + repeat_type;
+ }
+ }
+ }
+ break;
+
+ /* If previous was a character type match (\d or similar), abolish it and
+ create a suitable repeat item. The code is shared with single-character
+ repeats by setting op_type to add a suitable offset into repeat_type.
+ Note the the Unicode property types will be present only when
+ SUPPORT_UNICODE is defined, but we don't wrap the little bits of code
+ here because it just makes it horribly messy. */
+
+ default:
+ if (op_previous >= OP_EODN) /* Not a character type - internal error */
+ {
+ *errorcodeptr = ERR10;
+ return 0;
+ }
+ else
+ {
+ int prop_type, prop_value;
+ PCRE2_UCHAR *oldcode;
+
+ if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT;
+
+ op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */
+ mclength = 0; /* Not a character */
+
+ if (op_previous == OP_PROP || op_previous == OP_NOTPROP)
+ {
+ prop_type = previous[1];
+ prop_value = previous[2];
+ }
+ else
+ {
+ /* Come here from just above with a character in mcbuffer/mclength. */
+ OUTPUT_SINGLE_REPEAT:
+ prop_type = prop_value = -1;
+ }
+
+ /* At this point, if prop_type == prop_value == -1 we either have a
+ character in mcbuffer when mclength is greater than zero, or we have
+ mclength zero, in which case there is a non-property character type in
+ op_previous. If prop_type/value are not negative, we have a property
+ character type in op_previous. */
+
+ oldcode = code; /* Save where we were */
+ code = previous; /* Usually overwrite previous item */
+
+ /* If the maximum is zero then the minimum must also be zero; Perl allows
+ this case, so we do too - by simply omitting the item altogether. */
+
+ if (repeat_max == 0) goto END_REPEAT;
+
+ /* Combine the op_type with the repeat_type */
+
+ repeat_type += op_type;
+
+ /* A minimum of zero is handled either as the special case * or ?, or as
+ an UPTO, with the maximum given. */
+
+ if (repeat_min == 0)
+ {
+ if (repeat_max == REPEAT_UNLIMITED) *code++ = OP_STAR + repeat_type;
+ else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type;
+ else
+ {
+ *code++ = OP_UPTO + repeat_type;
+ PUT2INC(code, 0, repeat_max);
+ }
+ }
+
+ /* A repeat minimum of 1 is optimized into some special cases. If the
+ maximum is unlimited, we use OP_PLUS. Otherwise, the original item is
+ left in place and, if the maximum is greater than 1, we use OP_UPTO with
+ one less than the maximum. */
+
+ else if (repeat_min == 1)
+ {
+ if (repeat_max == REPEAT_UNLIMITED)
+ *code++ = OP_PLUS + repeat_type;
+ else
+ {
+ code = oldcode; /* Leave previous item in place */
+ if (repeat_max == 1) goto END_REPEAT;
+ *code++ = OP_UPTO + repeat_type;
+ PUT2INC(code, 0, repeat_max - 1);
+ }
+ }
+
+ /* The case {n,n} is just an EXACT, while the general case {n,m} is
+ handled as an EXACT followed by an UPTO or STAR or QUERY. */
+
+ else
+ {
+ *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */
+ PUT2INC(code, 0, repeat_min);
+
+ /* Unless repeat_max equals repeat_min, fill in the data for EXACT,
+ and then generate the second opcode. For a repeated Unicode property
+ match, there are two extra values that define the required property,
+ and mclength is set zero to indicate this. */
+
+ if (repeat_max != repeat_min)
+ {
+ if (mclength > 0)
+ {
+ memcpy(code, mcbuffer, CU2BYTES(mclength));
+ code += mclength;
+ }
+ else
+ {
+ *code++ = op_previous;
+ if (prop_type >= 0)
+ {
+ *code++ = prop_type;
+ *code++ = prop_value;
+ }
+ }
+
+ /* Now set up the following opcode */
+
+ if (repeat_max == REPEAT_UNLIMITED)
+ *code++ = OP_STAR + repeat_type;
+ else
+ {
+ repeat_max -= repeat_min;
+ if (repeat_max == 1)
+ {
+ *code++ = OP_QUERY + repeat_type;
+ }
+ else
+ {
+ *code++ = OP_UPTO + repeat_type;
+ PUT2INC(code, 0, repeat_max);
+ }
+ }
+ }
+ }
+
+ /* Fill in the character or character type for the final opcode. */
+
+ if (mclength > 0)
+ {
+ memcpy(code, mcbuffer, CU2BYTES(mclength));
+ code += mclength;
+ }
+ else
+ {
+ *code++ = op_previous;
+ if (prop_type >= 0)
+ {
+ *code++ = prop_type;
+ *code++ = prop_value;
+ }
+ }
+ }
+ break;
+ } /* End of switch on different op_previous values */
+
+
+ /* If the character following a repeat is '+', possessive_quantifier is
+ TRUE. For some opcodes, there are special alternative opcodes for this
+ case. For anything else, we wrap the entire repeated item inside OP_ONCE
+ brackets. Logically, the '+' notation is just syntactic sugar, taken from
+ Sun's Java package, but the special opcodes can optimize it.
+
+ Some (but not all) possessively repeated subpatterns have already been
+ completely handled in the code just above. For them, possessive_quantifier
+ is always FALSE at this stage. Note that the repeated item starts at
+ tempcode, not at previous, which might be the first part of a string whose
+ (former) last char we repeated. */
+
+ if (possessive_quantifier)
+ {
+ int len;
+
+ /* Possessifying an EXACT quantifier has no effect, so we can ignore it.
+ However, QUERY, STAR, or UPTO may follow (for quantifiers such as {5,6},
+ {5,}, or {5,10}). We skip over an EXACT item; if the length of what
+ remains is greater than zero, there's a further opcode that can be
+ handled. If not, do nothing, leaving the EXACT alone. */
+
+ switch(*tempcode)
+ {
+ case OP_TYPEEXACT:
+ tempcode += PRIV(OP_lengths)[*tempcode] +
+ ((tempcode[1 + IMM2_SIZE] == OP_PROP
+ || tempcode[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0);
+ break;
+
+ /* CHAR opcodes are used for exacts whose count is 1. */
+
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ case OP_EXACT:
+ case OP_EXACTI:
+ case OP_NOTEXACT:
+ case OP_NOTEXACTI:
+ tempcode += PRIV(OP_lengths)[*tempcode];
+#ifdef SUPPORT_UNICODE
+ if (utf && HAS_EXTRALEN(tempcode[-1]))
+ tempcode += GET_EXTRALEN(tempcode[-1]);
+#endif
+ break;
+
+ /* For the class opcodes, the repeat operator appears at the end;
+ adjust tempcode to point to it. */
+
+ case OP_CLASS:
+ case OP_NCLASS:
+ tempcode += 1 + 32/sizeof(PCRE2_UCHAR);
+ break;
+
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+ tempcode += GET(tempcode, 1);
+ break;
+#endif
+ }
+
+ /* If tempcode is equal to code (which points to the end of the repeated
+ item), it means we have skipped an EXACT item but there is no following
+ QUERY, STAR, or UPTO; the value of len will be 0, and we do nothing. In
+ all other cases, tempcode will be pointing to the repeat opcode, and will
+ be less than code, so the value of len will be greater than 0. */
+
+ len = (int)(code - tempcode);
+ if (len > 0)
+ {
+ unsigned int repcode = *tempcode;
+
+ /* There is a table for possessifying opcodes, all of which are less
+ than OP_CALLOUT. A zero entry means there is no possessified version.
+ */
+
+ if (repcode < OP_CALLOUT && opcode_possessify[repcode] > 0)
+ *tempcode = opcode_possessify[repcode];
+
+ /* For opcode without a special possessified version, wrap the item in
+ ONCE brackets. */
+
+ else
+ {
+ (void)memmove(tempcode + 1 + LINK_SIZE, tempcode, CU2BYTES(len));
+ code += 1 + LINK_SIZE;
+ len += 1 + LINK_SIZE;
+ tempcode[0] = OP_ONCE;
+ *code++ = OP_KET;
+ PUTINC(code, 0, len);
+ PUT(tempcode, 1, len);
+ }
+ }
+ }
+
+ /* We set the "follows varying string" flag for subsequently encountered
+ reqcus if it isn't already set and we have just passed a varying length
+ item. */
+
+ END_REPEAT:
+ cb->req_varyopt |= reqvary;
+ break;
+
+
+ /* ===================================================================*/
+ /* Handle a 32-bit data character with a value greater than META_END. */
+
+ case META_BIGVALUE:
+ pptr++;
+ goto NORMAL_CHAR;
+
+
+ /* ===============================================================*/
+ /* Handle a back reference by number, which is the meta argument. The
+ pattern offsets for back references to group numbers less than 10 are held
+ in a special vector, to avoid using more than two parsed pattern elements
+ in 64-bit environments. We only need the offset to the first occurrence,
+ because if that doesn't fail, subsequent ones will also be OK. */
+
+ case META_BACKREF:
+ if (meta_arg < 10) offset = cb->small_ref_offset[meta_arg];
+ else GETPLUSOFFSET(offset, pptr);
+
+ if (meta_arg > cb->bracount)
+ {
+ cb->erroroffset = offset;
+ *errorcodeptr = ERR15; /* Non-existent subpattern */
+ return 0;
+ }
+
+ /* Come here from named backref handling when the reference is to a
+ single group (that is, not to a duplicated name). The back reference
+ data will have already been updated. We must disable firstcu if not
+ set, to cope with cases like (?=(\w+))\1: which would otherwise set ':'
+ later. */
+
+ HANDLE_SINGLE_REFERENCE:
+ if (firstcuflags == REQ_UNSET) zerofirstcuflags = firstcuflags = REQ_NONE;
+ *code++ = ((options & PCRE2_CASELESS) != 0)? OP_REFI : OP_REF;
+ PUT2INC(code, 0, meta_arg);
+
+ /* Update the map of back references, and keep the highest one. We
+ could do this in parse_regex() for numerical back references, but not
+ for named back references, because we don't know the numbers to which
+ named back references refer. So we do it all in this function. */
+
+ cb->backref_map |= (meta_arg < 32)? (1u << meta_arg) : 1;
+ if (meta_arg > cb->top_backref) cb->top_backref = meta_arg;
+ break;
+
+
+ /* ===============================================================*/
+ /* Handle recursion by inserting the number of the called group (which is
+ the meta argument) after OP_RECURSE. At the end of compiling the pattern is
+ scanned and these numbers are replaced by offsets within the pattern. It is
+ done like this to avoid problems with forward references and adjusting
+ offsets when groups are duplicated and moved (as discovered in previous
+ implementations). Note that a recursion does not have a set first
+ character. */
+
+ case META_RECURSE:
+ GETPLUSOFFSET(offset, pptr);
+ if (meta_arg > cb->bracount)
+ {
+ cb->erroroffset = offset;
+ *errorcodeptr = ERR15; /* Non-existent subpattern */
+ return 0;
+ }
+ HANDLE_NUMERICAL_RECURSION:
+ *code = OP_RECURSE;
+ PUT(code, 1, meta_arg);
+ code += 1 + LINK_SIZE;
+ groupsetfirstcu = FALSE;
+ cb->had_recurse = TRUE;
+ if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;
+ zerofirstcu = firstcu;
+ zerofirstcuflags = firstcuflags;
+ break;
+
+
+ /* ===============================================================*/
+ /* Handle capturing parentheses; the number is the meta argument. */
+
+ case META_CAPTURE:
+ bravalue = OP_CBRA;
+ skipunits = IMM2_SIZE;
+ PUT2(code, 1+LINK_SIZE, meta_arg);
+ cb->lastcapture = meta_arg;
+ goto GROUP_PROCESS_NOTE_EMPTY;
+
+
+ /* ===============================================================*/
+ /* Handle escape sequence items. For ones like \d, the ESC_values are
+ arranged to be the same as the corresponding OP_values in the default case
+ when PCRE2_UCP is not set (which is the only case in which they will appear
+ here).
+
+ Note: \Q and \E are never seen here, as they were dealt with in
+ parse_pattern(). Neither are numerical back references or recursions, which
+ were turned into META_BACKREF or META_RECURSE items, respectively. \k and
+ \g, when followed by names, are turned into META_BACKREF_BYNAME or
+ META_RECURSE_BYNAME. */
+
+ case META_ESCAPE:
+
+ /* We can test for escape sequences that consume a character because their
+ values lie between ESC_b and ESC_Z; this may have to change if any new ones
+ are ever created. For these sequences, we disable the setting of a first
+ character if it hasn't already been set. */
+
+ if (meta_arg > ESC_b && meta_arg < ESC_Z)
+ {
+ matched_char = TRUE;
+ if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;
+ }
+
+ /* Set values to reset to if this is followed by a zero repeat. */
+
+ zerofirstcu = firstcu;
+ zerofirstcuflags = firstcuflags;
+ zeroreqcu = reqcu;
+ zeroreqcuflags = reqcuflags;
+
+ /* If Unicode is not supported, \P and \p are not allowed and are
+ faulted at parse time, so will never appear here. */
+
+#ifdef SUPPORT_UNICODE
+ if (meta_arg == ESC_P || meta_arg == ESC_p)
+ {
+ uint32_t ptype = *(++pptr) >> 16;
+ uint32_t pdata = *pptr & 0xffff;
+
+ /* The special case of \p{Any} is compiled to OP_ALLANY so as to benefit
+ from the auto-anchoring code. */
+
+ if (meta_arg == ESC_p && ptype == PT_ANY)
+ {
+ *code++ = OP_ALLANY;
+ }
+ else
+ {
+ *code++ = (meta_arg == ESC_p)? OP_PROP : OP_NOTPROP;
+ *code++ = ptype;
+ *code++ = pdata;
+ }
+ break; /* End META_ESCAPE */
+ }
+#endif
+
+ /* \K is forbidden in lookarounds since 10.38 because that's what Perl has
+ done. However, there's an option, in case anyone was relying on it. */
+
+ if (cb->assert_depth > 0 && meta_arg == ESC_K &&
+ (cb->cx->extra_options & PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK) == 0)
+ {
+ *errorcodeptr = ERR99;
+ return 0;
+ }
+
+ /* For the rest (including \X when Unicode is supported - if not it's
+ faulted at parse time), the OP value is the escape value when PCRE2_UCP is
+ not set; if it is set, these escapes do not show up here because they are
+ converted into Unicode property tests in parse_regex(). Note that \b and \B
+ do a one-character lookbehind, and \A also behaves as if it does. */
+
+ if (meta_arg == ESC_C) cb->external_flags |= PCRE2_HASBKC; /* Record */
+ if ((meta_arg == ESC_b || meta_arg == ESC_B || meta_arg == ESC_A) &&
+ cb->max_lookbehind == 0)
+ cb->max_lookbehind = 1;
+
+ /* In non-UTF mode, and for both 32-bit modes, we turn \C into OP_ALLANY
+ instead of OP_ANYBYTE so that it works in DFA mode and in lookbehinds. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+ *code++ = (meta_arg == ESC_C)? OP_ALLANY : meta_arg;
+#else
+ *code++ = (!utf && meta_arg == ESC_C)? OP_ALLANY : meta_arg;
+#endif
+ break; /* End META_ESCAPE */
+
+
+ /* ===================================================================*/
+ /* Handle an unrecognized meta value. A parsed pattern value less than
+ META_END is a literal. Otherwise we have a problem. */
+
+ default:
+ if (meta >= META_END)
+ {
+#ifdef DEBUG_SHOW_PARSED
+ fprintf(stderr, "** Unrecognized parsed pattern item 0x%.8x\n", *pptr);
+#endif
+ *errorcodeptr = ERR89; /* Internal error - unrecognized. */
+ return 0;
+ }
+
+ /* Handle a literal character. We come here by goto in the case of a
+ 32-bit, non-UTF character whose value is greater than META_END. */
+
+ NORMAL_CHAR:
+ meta = *pptr; /* Get the full 32 bits */
+ NORMAL_CHAR_SET: /* Character is already in meta */
+ matched_char = TRUE;
+
+ /* For caseless UTF or UCP mode, check whether this character has more than
+ one other case. If so, generate a special OP_PROP item instead of OP_CHARI.
+ */
+
+#ifdef SUPPORT_UNICODE
+ if ((utf||ucp) && (options & PCRE2_CASELESS) != 0)
+ {
+ uint32_t caseset = UCD_CASESET(meta);
+ if (caseset != 0)
+ {
+ *code++ = OP_PROP;
+ *code++ = PT_CLIST;
+ *code++ = caseset;
+ if (firstcuflags == REQ_UNSET)
+ firstcuflags = zerofirstcuflags = REQ_NONE;
+ break; /* End handling this meta item */
+ }
+ }
+#endif
+
+ /* Caseful matches, or caseless and not one of the multicase characters. We
+ come here by goto in the case of a positive class that contains only
+ case-partners of a character with just two cases; matched_char has already
+ been set TRUE and options fudged if necessary. */
+
+ CLASS_CASELESS_CHAR:
+
+ /* Get the character's code units into mcbuffer, with the length in
+ mclength. When not in UTF mode, the length is always 1. */
+
+#ifdef SUPPORT_UNICODE
+ if (utf) mclength = PRIV(ord2utf)(meta, mcbuffer); else
+#endif
+ {
+ mclength = 1;
+ mcbuffer[0] = meta;
+ }
+
+ /* Generate the appropriate code */
+
+ *code++ = ((options & PCRE2_CASELESS) != 0)? OP_CHARI : OP_CHAR;
+ memcpy(code, mcbuffer, CU2BYTES(mclength));
+ code += mclength;
+
+ /* Remember if \r or \n were seen */
+
+ if (mcbuffer[0] == CHAR_CR || mcbuffer[0] == CHAR_NL)
+ cb->external_flags |= PCRE2_HASCRORLF;
+
+ /* Set the first and required code units appropriately. If no previous
+ first code unit, set it from this character, but revert to none on a zero
+ repeat. Otherwise, leave the firstcu value alone, and don't change it on
+ a zero repeat. */
+
+ if (firstcuflags == REQ_UNSET)
+ {
+ zerofirstcuflags = REQ_NONE;
+ zeroreqcu = reqcu;
+ zeroreqcuflags = reqcuflags;
+
+ /* If the character is more than one code unit long, we can set a single
+ firstcu only if it is not to be matched caselessly. Multiple possible
+ starting code units may be picked up later in the studying code. */
+
+ if (mclength == 1 || req_caseopt == 0)
+ {
+ firstcu = mcbuffer[0];
+ firstcuflags = req_caseopt;
+ if (mclength != 1)
+ {
+ reqcu = code[-1];
+ reqcuflags = cb->req_varyopt;
+ }
+ }
+ else firstcuflags = reqcuflags = REQ_NONE;
+ }
+
+ /* firstcu was previously set; we can set reqcu only if the length is
+ 1 or the matching is caseful. */
+
+ else
+ {
+ zerofirstcu = firstcu;
+ zerofirstcuflags = firstcuflags;
+ zeroreqcu = reqcu;
+ zeroreqcuflags = reqcuflags;
+ if (mclength == 1 || req_caseopt == 0)
+ {
+ reqcu = code[-1];
+ reqcuflags = req_caseopt | cb->req_varyopt;
+ }
+ }
+
+ /* If caselessness was temporarily instated, reset it. */
+
+ if (reset_caseful)
+ {
+ options &= ~PCRE2_CASELESS;
+ req_caseopt = 0;
+ reset_caseful = FALSE;
+ }
+
+ break; /* End literal character handling */
+ } /* End of big switch */
+ } /* End of big loop */
+
+/* Control never reaches here. */
+}
+
+
+
+/*************************************************
+* Compile regex: a sequence of alternatives *
+*************************************************/
+
+/* On entry, pptr is pointing past the bracket meta, but on return it points to
+the closing bracket or META_END. The code variable is pointing at the code unit
+into which the BRA operator has been stored. This function is used during the
+pre-compile phase when we are trying to find out the amount of memory needed,
+as well as during the real compile phase. The value of lengthptr distinguishes
+the two phases.
+
+Arguments:
+ options option bits, including any changes for this subpattern
+ codeptr -> the address of the current code pointer
+ pptrptr -> the address of the current parsed pattern pointer
+ errorcodeptr -> pointer to error code variable
+ skipunits skip this many code units at start (for brackets and OP_COND)
+ firstcuptr place to put the first required code unit
+ firstcuflagsptr place to put the first code unit flags
+ reqcuptr place to put the last required code unit
+ reqcuflagsptr place to put the last required code unit flags
+ bcptr pointer to the chain of currently open branches
+ cb points to the data block with tables pointers etc.
+ lengthptr NULL during the real compile phase
+ points to length accumulator during pre-compile phase
+
+Returns: 0 There has been an error
+ +1 Success, this group must match at least one character
+ -1 Success, this group may match an empty string
+*/
+
+static int
+compile_regex(uint32_t options, PCRE2_UCHAR **codeptr, uint32_t **pptrptr,
+ int *errorcodeptr, uint32_t skipunits, uint32_t *firstcuptr,
+ uint32_t *firstcuflagsptr, uint32_t *reqcuptr, uint32_t *reqcuflagsptr,
+ branch_chain *bcptr, compile_block *cb, PCRE2_SIZE *lengthptr)
+{
+PCRE2_UCHAR *code = *codeptr;
+PCRE2_UCHAR *last_branch = code;
+PCRE2_UCHAR *start_bracket = code;
+BOOL lookbehind;
+open_capitem capitem;
+int capnumber = 0;
+int okreturn = 1;
+uint32_t *pptr = *pptrptr;
+uint32_t firstcu, reqcu;
+uint32_t lookbehindlength;
+uint32_t firstcuflags, reqcuflags;
+uint32_t branchfirstcu, branchreqcu;
+uint32_t branchfirstcuflags, branchreqcuflags;
+PCRE2_SIZE length;
+branch_chain bc;
+
+/* If set, call the external function that checks for stack availability. */
+
+if (cb->cx->stack_guard != NULL &&
+ cb->cx->stack_guard(cb->parens_depth, cb->cx->stack_guard_data))
+ {
+ *errorcodeptr= ERR33;
+ return 0;
+ }
+
+/* Miscellaneous initialization */
+
+bc.outer = bcptr;
+bc.current_branch = code;
+
+firstcu = reqcu = 0;
+firstcuflags = reqcuflags = REQ_UNSET;
+
+/* Accumulate the length for use in the pre-compile phase. Start with the
+length of the BRA and KET and any extra code units that are required at the
+beginning. We accumulate in a local variable to save frequent testing of
+lengthptr for NULL. We cannot do this by looking at the value of 'code' at the
+start and end of each alternative, because compiled items are discarded during
+the pre-compile phase so that the workspace is not exceeded. */
+
+length = 2 + 2*LINK_SIZE + skipunits;
+
+/* Remember if this is a lookbehind assertion, and if it is, save its length
+and skip over the pattern offset. */
+
+lookbehind = *code == OP_ASSERTBACK ||
+ *code == OP_ASSERTBACK_NOT ||
+ *code == OP_ASSERTBACK_NA;
+
+if (lookbehind)
+ {
+ lookbehindlength = META_DATA(pptr[-1]);
+ pptr += SIZEOFFSET;
+ }
+else lookbehindlength = 0;
+
+/* If this is a capturing subpattern, add to the chain of open capturing items
+so that we can detect them if (*ACCEPT) is encountered. Note that only OP_CBRA
+need be tested here; changing this opcode to one of its variants, e.g.
+OP_SCBRAPOS, happens later, after the group has been compiled. */
+
+if (*code == OP_CBRA)
+ {
+ capnumber = GET2(code, 1 + LINK_SIZE);
+ capitem.number = capnumber;
+ capitem.next = cb->open_caps;
+ capitem.assert_depth = cb->assert_depth;
+ cb->open_caps = &capitem;
+ }
+
+/* Offset is set zero to mark that this bracket is still open */
+
+PUT(code, 1, 0);
+code += 1 + LINK_SIZE + skipunits;
+
+/* Loop for each alternative branch */
+
+for (;;)
+ {
+ int branch_return;
+
+ /* Insert OP_REVERSE if this is as lookbehind assertion. */
+
+ if (lookbehind && lookbehindlength > 0)
+ {
+ *code++ = OP_REVERSE;
+ PUTINC(code, 0, lookbehindlength);
+ length += 1 + LINK_SIZE;
+ }
+
+ /* Now compile the branch; in the pre-compile phase its length gets added
+ into the length. */
+
+ if ((branch_return =
+ compile_branch(&options, &code, &pptr, errorcodeptr, &branchfirstcu,
+ &branchfirstcuflags, &branchreqcu, &branchreqcuflags, &bc,
+ cb, (lengthptr == NULL)? NULL : &length)) == 0)
+ return 0;
+
+ /* If a branch can match an empty string, so can the whole group. */
+
+ if (branch_return < 0) okreturn = -1;
+
+ /* In the real compile phase, there is some post-processing to be done. */
+
+ if (lengthptr == NULL)
+ {
+ /* If this is the first branch, the firstcu and reqcu values for the
+ branch become the values for the regex. */
+
+ if (*last_branch != OP_ALT)
+ {
+ firstcu = branchfirstcu;
+ firstcuflags = branchfirstcuflags;
+ reqcu = branchreqcu;
+ reqcuflags = branchreqcuflags;
+ }
+
+ /* If this is not the first branch, the first char and reqcu have to
+ match the values from all the previous branches, except that if the
+ previous value for reqcu didn't have REQ_VARY set, it can still match,
+ and we set REQ_VARY for the group from this branch's value. */
+
+ else
+ {
+ /* If we previously had a firstcu, but it doesn't match the new branch,
+ we have to abandon the firstcu for the regex, but if there was
+ previously no reqcu, it takes on the value of the old firstcu. */
+
+ if (firstcuflags != branchfirstcuflags || firstcu != branchfirstcu)
+ {
+ if (firstcuflags < REQ_NONE)
+ {
+ if (reqcuflags >= REQ_NONE)
+ {
+ reqcu = firstcu;
+ reqcuflags = firstcuflags;
+ }
+ }
+ firstcuflags = REQ_NONE;
+ }
+
+ /* If we (now or from before) have no firstcu, a firstcu from the
+ branch becomes a reqcu if there isn't a branch reqcu. */
+
+ if (firstcuflags >= REQ_NONE && branchfirstcuflags < REQ_NONE &&
+ branchreqcuflags >= REQ_NONE)
+ {
+ branchreqcu = branchfirstcu;
+ branchreqcuflags = branchfirstcuflags;
+ }
+
+ /* Now ensure that the reqcus match */
+
+ if (((reqcuflags & ~REQ_VARY) != (branchreqcuflags & ~REQ_VARY)) ||
+ reqcu != branchreqcu)
+ reqcuflags = REQ_NONE;
+ else
+ {
+ reqcu = branchreqcu;
+ reqcuflags |= branchreqcuflags; /* To "or" REQ_VARY if present */
+ }
+ }
+ }
+
+ /* Handle reaching the end of the expression, either ')' or end of pattern.
+ In the real compile phase, go back through the alternative branches and
+ reverse the chain of offsets, with the field in the BRA item now becoming an
+ offset to the first alternative. If there are no alternatives, it points to
+ the end of the group. The length in the terminating ket is always the length
+ of the whole bracketed item. Return leaving the pointer at the terminating
+ char. */
+
+ if (META_CODE(*pptr) != META_ALT)
+ {
+ if (lengthptr == NULL)
+ {
+ PCRE2_SIZE branch_length = code - last_branch;
+ do
+ {
+ PCRE2_SIZE prev_length = GET(last_branch, 1);
+ PUT(last_branch, 1, branch_length);
+ branch_length = prev_length;
+ last_branch -= branch_length;
+ }
+ while (branch_length > 0);
+ }
+
+ /* Fill in the ket */
+
+ *code = OP_KET;
+ PUT(code, 1, (int)(code - start_bracket));
+ code += 1 + LINK_SIZE;
+
+ /* If it was a capturing subpattern, remove the block from the chain. */
+
+ if (capnumber > 0) cb->open_caps = cb->open_caps->next;
+
+ /* Set values to pass back */
+
+ *codeptr = code;
+ *pptrptr = pptr;
+ *firstcuptr = firstcu;
+ *firstcuflagsptr = firstcuflags;
+ *reqcuptr = reqcu;
+ *reqcuflagsptr = reqcuflags;
+ if (lengthptr != NULL)
+ {
+ if (OFLOW_MAX - *lengthptr < length)
+ {
+ *errorcodeptr = ERR20;
+ return 0;
+ }
+ *lengthptr += length;
+ }
+ return okreturn;
+ }
+
+ /* Another branch follows. In the pre-compile phase, we can move the code
+ pointer back to where it was for the start of the first branch. (That is,
+ pretend that each branch is the only one.)
+
+ In the real compile phase, insert an ALT node. Its length field points back
+ to the previous branch while the bracket remains open. At the end the chain
+ is reversed. It's done like this so that the start of the bracket has a
+ zero offset until it is closed, making it possible to detect recursion. */
+
+ if (lengthptr != NULL)
+ {
+ code = *codeptr + 1 + LINK_SIZE + skipunits;
+ length += 1 + LINK_SIZE;
+ }
+ else
+ {
+ *code = OP_ALT;
+ PUT(code, 1, (int)(code - last_branch));
+ bc.current_branch = last_branch = code;
+ code += 1 + LINK_SIZE;
+ }
+
+ /* Set the lookbehind length (if not in a lookbehind the value will be zero)
+ and then advance past the vertical bar. */
+
+ lookbehindlength = META_DATA(*pptr);
+ pptr++;
+ }
+/* Control never reaches here */
+}
+
+
+
+/*************************************************
+* Check for anchored pattern *
+*************************************************/
+
+/* Try to find out if this is an anchored regular expression. Consider each
+alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket
+all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then
+it's anchored. However, if this is a multiline pattern, then only OP_SOD will
+be found, because ^ generates OP_CIRCM in that mode.
+
+We can also consider a regex to be anchored if OP_SOM starts all its branches.
+This is the code for \G, which means "match at start of match position, taking
+into account the match offset".
+
+A branch is also implicitly anchored if it starts with .* and DOTALL is set,
+because that will try the rest of the pattern at all possible matching points,
+so there is no point trying again.... er ....
+
+.... except when the .* appears inside capturing parentheses, and there is a
+subsequent back reference to those parentheses. We haven't enough information
+to catch that case precisely.
+
+At first, the best we could do was to detect when .* was in capturing brackets
+and the highest back reference was greater than or equal to that level.
+However, by keeping a bitmap of the first 31 back references, we can catch some
+of the more common cases more precisely.
+
+... A second exception is when the .* appears inside an atomic group, because
+this prevents the number of characters it matches from being adjusted.
+
+Arguments:
+ code points to start of the compiled pattern
+ bracket_map a bitmap of which brackets we are inside while testing; this
+ handles up to substring 31; after that we just have to take
+ the less precise approach
+ cb points to the compile data block
+ atomcount atomic group level
+ inassert TRUE if in an assertion
+
+Returns: TRUE or FALSE
+*/
+
+static BOOL
+is_anchored(PCRE2_SPTR code, uint32_t bracket_map, compile_block *cb,
+ int atomcount, BOOL inassert)
+{
+do {
+ PCRE2_SPTR scode = first_significant_code(
+ code + PRIV(OP_lengths)[*code], FALSE);
+ int op = *scode;
+
+ /* Non-capturing brackets */
+
+ if (op == OP_BRA || op == OP_BRAPOS ||
+ op == OP_SBRA || op == OP_SBRAPOS)
+ {
+ if (!is_anchored(scode, bracket_map, cb, atomcount, inassert))
+ return FALSE;
+ }
+
+ /* Capturing brackets */
+
+ else if (op == OP_CBRA || op == OP_CBRAPOS ||
+ op == OP_SCBRA || op == OP_SCBRAPOS)
+ {
+ int n = GET2(scode, 1+LINK_SIZE);
+ uint32_t new_map = bracket_map | ((n < 32)? (1u << n) : 1);
+ if (!is_anchored(scode, new_map, cb, atomcount, inassert)) return FALSE;
+ }
+
+ /* Positive forward assertion */
+
+ else if (op == OP_ASSERT || op == OP_ASSERT_NA)
+ {
+ if (!is_anchored(scode, bracket_map, cb, atomcount, TRUE)) return FALSE;
+ }
+
+ /* Condition. If there is no second branch, it can't be anchored. */
+
+ else if (op == OP_COND || op == OP_SCOND)
+ {
+ if (scode[GET(scode,1)] != OP_ALT) return FALSE;
+ if (!is_anchored(scode, bracket_map, cb, atomcount, inassert))
+ return FALSE;
+ }
+
+ /* Atomic groups */
+
+ else if (op == OP_ONCE)
+ {
+ if (!is_anchored(scode, bracket_map, cb, atomcount + 1, inassert))
+ return FALSE;
+ }
+
+ /* .* is not anchored unless DOTALL is set (which generates OP_ALLANY) and
+ it isn't in brackets that are or may be referenced or inside an atomic
+ group or an assertion. Also the pattern must not contain *PRUNE or *SKIP,
+ because these break the feature. Consider, for example, /(?s).*?(*PRUNE)b/
+ with the subject "aab", which matches "b", i.e. not at the start of a line.
+ There is also an option that disables auto-anchoring. */
+
+ else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR ||
+ op == OP_TYPEPOSSTAR))
+ {
+ if (scode[1] != OP_ALLANY || (bracket_map & cb->backref_map) != 0 ||
+ atomcount > 0 || cb->had_pruneorskip || inassert ||
+ (cb->external_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0)
+ return FALSE;
+ }
+
+ /* Check for explicit anchoring */
+
+ else if (op != OP_SOD && op != OP_SOM && op != OP_CIRC) return FALSE;
+
+ code += GET(code, 1);
+ }
+while (*code == OP_ALT); /* Loop for each alternative */
+return TRUE;
+}
+
+
+
+/*************************************************
+* Check for starting with ^ or .* *
+*************************************************/
+
+/* This is called to find out if every branch starts with ^ or .* so that
+"first char" processing can be done to speed things up in multiline
+matching and for non-DOTALL patterns that start with .* (which must start at
+the beginning or after \n). As in the case of is_anchored() (see above), we
+have to take account of back references to capturing brackets that contain .*
+because in that case we can't make the assumption. Also, the appearance of .*
+inside atomic brackets or in an assertion, or in a pattern that contains *PRUNE
+or *SKIP does not count, because once again the assumption no longer holds.
+
+Arguments:
+ code points to start of the compiled pattern or a group
+ bracket_map a bitmap of which brackets we are inside while testing; this
+ handles up to substring 31; after that we just have to take
+ the less precise approach
+ cb points to the compile data
+ atomcount atomic group level
+ inassert TRUE if in an assertion
+
+Returns: TRUE or FALSE
+*/
+
+static BOOL
+is_startline(PCRE2_SPTR code, unsigned int bracket_map, compile_block *cb,
+ int atomcount, BOOL inassert)
+{
+do {
+ PCRE2_SPTR scode = first_significant_code(
+ code + PRIV(OP_lengths)[*code], FALSE);
+ int op = *scode;
+
+ /* If we are at the start of a conditional assertion group, *both* the
+ conditional assertion *and* what follows the condition must satisfy the test
+ for start of line. Other kinds of condition fail. Note that there may be an
+ auto-callout at the start of a condition. */
+
+ if (op == OP_COND)
+ {
+ scode += 1 + LINK_SIZE;
+
+ if (*scode == OP_CALLOUT) scode += PRIV(OP_lengths)[OP_CALLOUT];
+ else if (*scode == OP_CALLOUT_STR) scode += GET(scode, 1 + 2*LINK_SIZE);
+
+ switch (*scode)
+ {
+ case OP_CREF:
+ case OP_DNCREF:
+ case OP_RREF:
+ case OP_DNRREF:
+ case OP_FAIL:
+ case OP_FALSE:
+ case OP_TRUE:
+ return FALSE;
+
+ default: /* Assertion */
+ if (!is_startline(scode, bracket_map, cb, atomcount, TRUE)) return FALSE;
+ do scode += GET(scode, 1); while (*scode == OP_ALT);
+ scode += 1 + LINK_SIZE;
+ break;
+ }
+ scode = first_significant_code(scode, FALSE);
+ op = *scode;
+ }
+
+ /* Non-capturing brackets */
+
+ if (op == OP_BRA || op == OP_BRAPOS ||
+ op == OP_SBRA || op == OP_SBRAPOS)
+ {
+ if (!is_startline(scode, bracket_map, cb, atomcount, inassert))
+ return FALSE;
+ }
+
+ /* Capturing brackets */
+
+ else if (op == OP_CBRA || op == OP_CBRAPOS ||
+ op == OP_SCBRA || op == OP_SCBRAPOS)
+ {
+ int n = GET2(scode, 1+LINK_SIZE);
+ unsigned int new_map = bracket_map | ((n < 32)? (1u << n) : 1);
+ if (!is_startline(scode, new_map, cb, atomcount, inassert)) return FALSE;
+ }
+
+ /* Positive forward assertions */
+
+ else if (op == OP_ASSERT || op == OP_ASSERT_NA)
+ {
+ if (!is_startline(scode, bracket_map, cb, atomcount, TRUE))
+ return FALSE;
+ }
+
+ /* Atomic brackets */
+
+ else if (op == OP_ONCE)
+ {
+ if (!is_startline(scode, bracket_map, cb, atomcount + 1, inassert))
+ return FALSE;
+ }
+
+ /* .* means "start at start or after \n" if it isn't in atomic brackets or
+ brackets that may be referenced or an assertion, and as long as the pattern
+ does not contain *PRUNE or *SKIP, because these break the feature. Consider,
+ for example, /.*?a(*PRUNE)b/ with the subject "aab", which matches "ab",
+ i.e. not at the start of a line. There is also an option that disables this
+ optimization. */
+
+ else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR || op == OP_TYPEPOSSTAR)
+ {
+ if (scode[1] != OP_ANY || (bracket_map & cb->backref_map) != 0 ||
+ atomcount > 0 || cb->had_pruneorskip || inassert ||
+ (cb->external_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0)
+ return FALSE;
+ }
+
+ /* Check for explicit circumflex; anything else gives a FALSE result. Note
+ in particular that this includes atomic brackets OP_ONCE because the number
+ of characters matched by .* cannot be adjusted inside them. */
+
+ else if (op != OP_CIRC && op != OP_CIRCM) return FALSE;
+
+ /* Move on to the next alternative */
+
+ code += GET(code, 1);
+ }
+while (*code == OP_ALT); /* Loop for each alternative */
+return TRUE;
+}
+
+
+
+/*************************************************
+* Scan compiled regex for recursion reference *
+*************************************************/
+
+/* This function scans through a compiled pattern until it finds an instance of
+OP_RECURSE.
+
+Arguments:
+ code points to start of expression
+ utf TRUE in UTF mode
+
+Returns: pointer to the opcode for OP_RECURSE, or NULL if not found
+*/
+
+static PCRE2_SPTR
+find_recurse(PCRE2_SPTR code, BOOL utf)
+{
+for (;;)
+ {
+ PCRE2_UCHAR c = *code;
+ if (c == OP_END) return NULL;
+ if (c == OP_RECURSE) return code;
+
+ /* XCLASS is used for classes that cannot be represented just by a bit map.
+ This includes negated single high-valued characters. CALLOUT_STR is used for
+ callouts with string arguments. In both cases the length in the table is
+ zero; the actual length is stored in the compiled code. */
+
+ if (c == OP_XCLASS) code += GET(code, 1);
+ else if (c == OP_CALLOUT_STR) code += GET(code, 1 + 2*LINK_SIZE);
+
+ /* Otherwise, we can get the item's length from the table, except that for
+ repeated character types, we have to test for \p and \P, which have an extra
+ two code units of parameters, and for MARK/PRUNE/SKIP/THEN with an argument,
+ we must add in its length. */
+
+ else
+ {
+ switch(c)
+ {
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ case OP_TYPEPOSQUERY:
+ if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2;
+ break;
+
+ case OP_TYPEPOSUPTO:
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEEXACT:
+ if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP)
+ code += 2;
+ break;
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_SKIP_ARG:
+ case OP_THEN_ARG:
+ code += code[1];
+ break;
+ }
+
+ /* Add in the fixed length from the table */
+
+ code += PRIV(OP_lengths)[c];
+
+ /* In UTF-8 and UTF-16 modes, opcodes that are followed by a character may
+ be followed by a multi-unit character. The length in the table is a
+ minimum, so we have to arrange to skip the extra units. */
+
+#ifdef MAYBE_UTF_MULTI
+ if (utf) switch(c)
+ {
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ case OP_EXACT:
+ case OP_EXACTI:
+ case OP_NOTEXACT:
+ case OP_NOTEXACTI:
+ case OP_UPTO:
+ case OP_UPTOI:
+ case OP_NOTUPTO:
+ case OP_NOTUPTOI:
+ case OP_MINUPTO:
+ case OP_MINUPTOI:
+ case OP_NOTMINUPTO:
+ case OP_NOTMINUPTOI:
+ case OP_POSUPTO:
+ case OP_POSUPTOI:
+ case OP_NOTPOSUPTO:
+ case OP_NOTPOSUPTOI:
+ case OP_STAR:
+ case OP_STARI:
+ case OP_NOTSTAR:
+ case OP_NOTSTARI:
+ case OP_MINSTAR:
+ case OP_MINSTARI:
+ case OP_NOTMINSTAR:
+ case OP_NOTMINSTARI:
+ case OP_POSSTAR:
+ case OP_POSSTARI:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSSTARI:
+ case OP_PLUS:
+ case OP_PLUSI:
+ case OP_NOTPLUS:
+ case OP_NOTPLUSI:
+ case OP_MINPLUS:
+ case OP_MINPLUSI:
+ case OP_NOTMINPLUS:
+ case OP_NOTMINPLUSI:
+ case OP_POSPLUS:
+ case OP_POSPLUSI:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSPLUSI:
+ case OP_QUERY:
+ case OP_QUERYI:
+ case OP_NOTQUERY:
+ case OP_NOTQUERYI:
+ case OP_MINQUERY:
+ case OP_MINQUERYI:
+ case OP_NOTMINQUERY:
+ case OP_NOTMINQUERYI:
+ case OP_POSQUERY:
+ case OP_POSQUERYI:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSQUERYI:
+ if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]);
+ break;
+ }
+#else
+ (void)(utf); /* Keep compiler happy by referencing function argument */
+#endif /* MAYBE_UTF_MULTI */
+ }
+ }
+}
+
+
+
+/*************************************************
+* Check for asserted fixed first code unit *
+*************************************************/
+
+/* During compilation, the "first code unit" settings from forward assertions
+are discarded, because they can cause conflicts with actual literals that
+follow. However, if we end up without a first code unit setting for an
+unanchored pattern, it is worth scanning the regex to see if there is an
+initial asserted first code unit. If all branches start with the same asserted
+code unit, or with a non-conditional bracket all of whose alternatives start
+with the same asserted code unit (recurse ad lib), then we return that code
+unit, with the flags set to zero or REQ_CASELESS; otherwise return zero with
+REQ_NONE in the flags.
+
+Arguments:
+ code points to start of compiled pattern
+ flags points to the first code unit flags
+ inassert non-zero if in an assertion
+
+Returns: the fixed first code unit, or 0 with REQ_NONE in flags
+*/
+
+static uint32_t
+find_firstassertedcu(PCRE2_SPTR code, uint32_t *flags, uint32_t inassert)
+{
+uint32_t c = 0;
+uint32_t cflags = REQ_NONE;
+
+*flags = REQ_NONE;
+do {
+ uint32_t d;
+ uint32_t dflags;
+ int xl = (*code == OP_CBRA || *code == OP_SCBRA ||
+ *code == OP_CBRAPOS || *code == OP_SCBRAPOS)? IMM2_SIZE:0;
+ PCRE2_SPTR scode = first_significant_code(code + 1+LINK_SIZE + xl, TRUE);
+ PCRE2_UCHAR op = *scode;
+
+ switch(op)
+ {
+ default:
+ return 0;
+
+ case OP_BRA:
+ case OP_BRAPOS:
+ case OP_CBRA:
+ case OP_SCBRA:
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ case OP_ASSERT:
+ case OP_ASSERT_NA:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ d = find_firstassertedcu(scode, &dflags, inassert +
+ ((op == OP_ASSERT || op == OP_ASSERT_NA)?1:0));
+ if (dflags >= REQ_NONE) return 0;
+ if (cflags >= REQ_NONE) { c = d; cflags = dflags; }
+ else if (c != d || cflags != dflags) return 0;
+ break;
+
+ case OP_EXACT:
+ scode += IMM2_SIZE;
+ /* Fall through */
+
+ case OP_CHAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_POSPLUS:
+ if (inassert == 0) return 0;
+ if (cflags >= REQ_NONE) { c = scode[1]; cflags = 0; }
+ else if (c != scode[1]) return 0;
+ break;
+
+ case OP_EXACTI:
+ scode += IMM2_SIZE;
+ /* Fall through */
+
+ case OP_CHARI:
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_POSPLUSI:
+ if (inassert == 0) return 0;
+
+ /* If the character is more than one code unit long, we cannot set its
+ first code unit when matching caselessly. Later scanning may pick up
+ multiple code units. */
+
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (scode[1] >= 0x80) return 0;
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+ if (scode[1] >= 0xd800 && scode[1] <= 0xdfff) return 0;
+#endif
+#endif
+
+ if (cflags >= REQ_NONE) { c = scode[1]; cflags = REQ_CASELESS; }
+ else if (c != scode[1]) return 0;
+ break;
+ }
+
+ code += GET(code, 1);
+ }
+while (*code == OP_ALT);
+
+*flags = cflags;
+return c;
+}
+
+
+
+/*************************************************
+* Add an entry to the name/number table *
+*************************************************/
+
+/* This function is called between compiling passes to add an entry to the
+name/number table, maintaining alphabetical order. Checking for permitted
+and forbidden duplicates has already been done.
+
+Arguments:
+ cb the compile data block
+ name the name to add
+ length the length of the name
+ groupno the group number
+ tablecount the count of names in the table so far
+
+Returns: nothing
+*/
+
+static void
+add_name_to_table(compile_block *cb, PCRE2_SPTR name, int length,
+ unsigned int groupno, uint32_t tablecount)
+{
+uint32_t i;
+PCRE2_UCHAR *slot = cb->name_table;
+
+for (i = 0; i < tablecount; i++)
+ {
+ int crc = memcmp(name, slot+IMM2_SIZE, CU2BYTES(length));
+ if (crc == 0 && slot[IMM2_SIZE+length] != 0)
+ crc = -1; /* Current name is a substring */
+
+ /* Make space in the table and break the loop for an earlier name. For a
+ duplicate or later name, carry on. We do this for duplicates so that in the
+ simple case (when ?(| is not used) they are in order of their numbers. In all
+ cases they are in the order in which they appear in the pattern. */
+
+ if (crc < 0)
+ {
+ (void)memmove(slot + cb->name_entry_size, slot,
+ CU2BYTES((tablecount - i) * cb->name_entry_size));
+ break;
+ }
+
+ /* Continue the loop for a later or duplicate name */
+
+ slot += cb->name_entry_size;
+ }
+
+PUT2(slot, 0, groupno);
+memcpy(slot + IMM2_SIZE, name, CU2BYTES(length));
+
+/* Add a terminating zero and fill the rest of the slot with zeroes so that
+the memory is all initialized. Otherwise valgrind moans about uninitialized
+memory when saving serialized compiled patterns. */
+
+memset(slot + IMM2_SIZE + length, 0,
+ CU2BYTES(cb->name_entry_size - length - IMM2_SIZE));
+}
+
+
+
+/*************************************************
+* Skip in parsed pattern *
+*************************************************/
+
+/* This function is called to skip parts of the parsed pattern when finding the
+length of a lookbehind branch. It is called after (*ACCEPT) and (*FAIL) to find
+the end of the branch, it is called to skip over an internal lookaround or
+(DEFINE) group, and it is also called to skip to the end of a class, during
+which it will never encounter nested groups (but there's no need to have
+special code for that).
+
+When called to find the end of a branch or group, pptr must point to the first
+meta code inside the branch, not the branch-starting code. In other cases it
+can point to the item that causes the function to be called.
+
+Arguments:
+ pptr current pointer to skip from
+ skiptype PSKIP_CLASS when skipping to end of class
+ PSKIP_ALT when META_ALT ends the skip
+ PSKIP_KET when only META_KET ends the skip
+
+Returns: new value of pptr
+ NULL if META_END is reached - should never occur
+ or for an unknown meta value - likewise
+*/
+
+static uint32_t *
+parsed_skip(uint32_t *pptr, uint32_t skiptype)
+{
+uint32_t nestlevel = 0;
+
+for (;; pptr++)
+ {
+ uint32_t meta = META_CODE(*pptr);
+
+ switch(meta)
+ {
+ default: /* Just skip over most items */
+ if (meta < META_END) continue; /* Literal */
+ break;
+
+ /* This should never occur. */
+
+ case META_END:
+ return NULL;
+
+ /* The data for these items is variable in length. */
+
+ case META_BACKREF: /* Offset is present only if group >= 10 */
+ if (META_DATA(*pptr) >= 10) pptr += SIZEOFFSET;
+ break;
+
+ case META_ESCAPE: /* A few escapes are followed by data items. */
+ switch (META_DATA(*pptr))
+ {
+ case ESC_P:
+ case ESC_p:
+ pptr += 1;
+ break;
+
+ case ESC_g:
+ case ESC_k:
+ pptr += 1 + SIZEOFFSET;
+ break;
+ }
+ break;
+
+ case META_MARK: /* Add the length of the name. */
+ case META_COMMIT_ARG:
+ case META_PRUNE_ARG:
+ case META_SKIP_ARG:
+ case META_THEN_ARG:
+ pptr += pptr[1];
+ break;
+
+ /* These are the "active" items in this loop. */
+
+ case META_CLASS_END:
+ if (skiptype == PSKIP_CLASS) return pptr;
+ break;
+
+ case META_ATOMIC:
+ case META_CAPTURE:
+ case META_COND_ASSERT:
+ case META_COND_DEFINE:
+ case META_COND_NAME:
+ case META_COND_NUMBER:
+ case META_COND_RNAME:
+ case META_COND_RNUMBER:
+ case META_COND_VERSION:
+ case META_LOOKAHEAD:
+ case META_LOOKAHEADNOT:
+ case META_LOOKAHEAD_NA:
+ case META_LOOKBEHIND:
+ case META_LOOKBEHINDNOT:
+ case META_LOOKBEHIND_NA:
+ case META_NOCAPTURE:
+ case META_SCRIPT_RUN:
+ nestlevel++;
+ break;
+
+ case META_ALT:
+ if (nestlevel == 0 && skiptype == PSKIP_ALT) return pptr;
+ break;
+
+ case META_KET:
+ if (nestlevel == 0) return pptr;
+ nestlevel--;
+ break;
+ }
+
+ /* The extra data item length for each meta is in a table. */
+
+ meta = (meta >> 16) & 0x7fff;
+ if (meta >= sizeof(meta_extra_lengths)) return NULL;
+ pptr += meta_extra_lengths[meta];
+ }
+/* Control never reaches here */
+return pptr;
+}
+
+
+
+/*************************************************
+* Find length of a parsed group *
+*************************************************/
+
+/* This is called for nested groups within a branch of a lookbehind whose
+length is being computed. If all the branches in the nested group have the same
+length, that is OK. On entry, the pointer must be at the first element after
+the group initializing code. On exit it points to OP_KET. Caching is used to
+improve processing speed when the same capturing group occurs many times.
+
+Arguments:
+ pptrptr pointer to pointer in the parsed pattern
+ isinline FALSE if a reference or recursion; TRUE for inline group
+ errcodeptr pointer to the errorcode
+ lcptr pointer to the loop counter
+ group number of captured group or -1 for a non-capturing group
+ recurses chain of recurse_check to catch mutual recursion
+ cb pointer to the compile data
+
+Returns: the group length or a negative number
+*/
+
+static int
+get_grouplength(uint32_t **pptrptr, BOOL isinline, int *errcodeptr, int *lcptr,
+ int group, parsed_recurse_check *recurses, compile_block *cb)
+{
+int branchlength;
+int grouplength = -1;
+
+/* The cache can be used only if there is no possibility of there being two
+groups with the same number. We do not need to set the end pointer for a group
+that is being processed as a back reference or recursion, but we must do so for
+an inline group. */
+
+if (group > 0 && (cb->external_flags & PCRE2_DUPCAPUSED) == 0)
+ {
+ uint32_t groupinfo = cb->groupinfo[group];
+ if ((groupinfo & GI_NOT_FIXED_LENGTH) != 0) return -1;
+ if ((groupinfo & GI_SET_FIXED_LENGTH) != 0)
+ {
+ if (isinline) *pptrptr = parsed_skip(*pptrptr, PSKIP_KET);
+ return groupinfo & GI_FIXED_LENGTH_MASK;
+ }
+ }
+
+/* Scan the group. In this case we find the end pointer of necessity. */
+
+for(;;)
+ {
+ branchlength = get_branchlength(pptrptr, errcodeptr, lcptr, recurses, cb);
+ if (branchlength < 0) goto ISNOTFIXED;
+ if (grouplength == -1) grouplength = branchlength;
+ else if (grouplength != branchlength) goto ISNOTFIXED;
+ if (**pptrptr == META_KET) break;
+ *pptrptr += 1; /* Skip META_ALT */
+ }
+
+if (group > 0)
+ cb->groupinfo[group] |= (uint32_t)(GI_SET_FIXED_LENGTH | grouplength);
+return grouplength;
+
+ISNOTFIXED:
+if (group > 0) cb->groupinfo[group] |= GI_NOT_FIXED_LENGTH;
+return -1;
+}
+
+
+
+/*************************************************
+* Find length of a parsed branch *
+*************************************************/
+
+/* Return a fixed length for a branch in a lookbehind, giving an error if the
+length is not fixed. On entry, *pptrptr points to the first element inside the
+branch. On exit it is set to point to the ALT or KET.
+
+Arguments:
+ pptrptr pointer to pointer in the parsed pattern
+ errcodeptr pointer to error code
+ lcptr pointer to loop counter
+ recurses chain of recurse_check to catch mutual recursion
+ cb pointer to compile block
+
+Returns: the length, or a negative value on error
+*/
+
+static int
+get_branchlength(uint32_t **pptrptr, int *errcodeptr, int *lcptr,
+ parsed_recurse_check *recurses, compile_block *cb)
+{
+int branchlength = 0;
+int grouplength;
+uint32_t lastitemlength = 0;
+uint32_t *pptr = *pptrptr;
+PCRE2_SIZE offset;
+parsed_recurse_check this_recurse;
+
+/* A large and/or complex regex can take too long to process. This can happen
+more often when (?| groups are present in the pattern because their length
+cannot be cached. */
+
+if ((*lcptr)++ > 2000)
+ {
+ *errcodeptr = ERR35; /* Lookbehind is too complicated */
+ return -1;
+ }
+
+/* Scan the branch, accumulating the length. */
+
+for (;; pptr++)
+ {
+ parsed_recurse_check *r;
+ uint32_t *gptr, *gptrend;
+ uint32_t escape;
+ uint32_t group = 0;
+ uint32_t itemlength = 0;
+
+ if (*pptr < META_END)
+ {
+ itemlength = 1;
+ }
+
+ else switch (META_CODE(*pptr))
+ {
+ case META_KET:
+ case META_ALT:
+ goto EXIT;
+
+ /* (*ACCEPT) and (*FAIL) terminate the branch, but we must skip to the
+ actual termination. */
+
+ case META_ACCEPT:
+ case META_FAIL:
+ pptr = parsed_skip(pptr, PSKIP_ALT);
+ if (pptr == NULL) goto PARSED_SKIP_FAILED;
+ goto EXIT;
+
+ case META_MARK:
+ case META_COMMIT_ARG:
+ case META_PRUNE_ARG:
+ case META_SKIP_ARG:
+ case META_THEN_ARG:
+ pptr += pptr[1] + 1;
+ break;
+
+ case META_CIRCUMFLEX:
+ case META_COMMIT:
+ case META_DOLLAR:
+ case META_PRUNE:
+ case META_SKIP:
+ case META_THEN:
+ break;
+
+ case META_OPTIONS:
+ pptr += 1;
+ break;
+
+ case META_BIGVALUE:
+ itemlength = 1;
+ pptr += 1;
+ break;
+
+ case META_CLASS:
+ case META_CLASS_NOT:
+ itemlength = 1;
+ pptr = parsed_skip(pptr, PSKIP_CLASS);
+ if (pptr == NULL) goto PARSED_SKIP_FAILED;
+ break;
+
+ case META_CLASS_EMPTY_NOT:
+ case META_DOT:
+ itemlength = 1;
+ break;
+
+ case META_CALLOUT_NUMBER:
+ pptr += 3;
+ break;
+
+ case META_CALLOUT_STRING:
+ pptr += 3 + SIZEOFFSET;
+ break;
+
+ /* Only some escapes consume a character. Of those, \R and \X are never
+ allowed because they might match more than character. \C is allowed only in
+ 32-bit and non-UTF 8/16-bit modes. */
+
+ case META_ESCAPE:
+ escape = META_DATA(*pptr);
+ if (escape == ESC_R || escape == ESC_X) return -1;
+ if (escape > ESC_b && escape < ESC_Z)
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ if ((cb->external_options & PCRE2_UTF) != 0 && escape == ESC_C)
+ {
+ *errcodeptr = ERR36;
+ return -1;
+ }
+#endif
+ itemlength = 1;
+ if (escape == ESC_p || escape == ESC_P) pptr++; /* Skip prop data */
+ }
+ break;
+
+ /* Lookaheads do not contribute to the length of this branch, but they may
+ contain lookbehinds within them whose lengths need to be set. */
+
+ case META_LOOKAHEAD:
+ case META_LOOKAHEADNOT:
+ case META_LOOKAHEAD_NA:
+ *errcodeptr = check_lookbehinds(pptr + 1, &pptr, recurses, cb, lcptr);
+ if (*errcodeptr != 0) return -1;
+
+ /* Ignore any qualifiers that follow a lookahead assertion. */
+
+ switch (pptr[1])
+ {
+ case META_ASTERISK:
+ case META_ASTERISK_PLUS:
+ case META_ASTERISK_QUERY:
+ case META_PLUS:
+ case META_PLUS_PLUS:
+ case META_PLUS_QUERY:
+ case META_QUERY:
+ case META_QUERY_PLUS:
+ case META_QUERY_QUERY:
+ pptr++;
+ break;
+
+ case META_MINMAX:
+ case META_MINMAX_PLUS:
+ case META_MINMAX_QUERY:
+ pptr += 3;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ /* A nested lookbehind does not contribute any length to this lookbehind,
+ but must itself be checked and have its lengths set. */
+
+ case META_LOOKBEHIND:
+ case META_LOOKBEHINDNOT:
+ case META_LOOKBEHIND_NA:
+ if (!set_lookbehind_lengths(&pptr, errcodeptr, lcptr, recurses, cb))
+ return -1;
+ break;
+
+ /* Back references and recursions are handled by very similar code. At this
+ stage, the names generated in the parsing pass are available, but the main
+ name table has not yet been created. So for the named varieties, scan the
+ list of names in order to get the number of the first one in the pattern,
+ and whether or not this name is duplicated. */
+
+ case META_BACKREF_BYNAME:
+ if ((cb->external_options & PCRE2_MATCH_UNSET_BACKREF) != 0)
+ goto ISNOTFIXED;
+ /* Fall through */
+
+ case META_RECURSE_BYNAME:
+ {
+ int i;
+ PCRE2_SPTR name;
+ BOOL is_dupname = FALSE;
+ named_group *ng = cb->named_groups;
+ uint32_t meta_code = META_CODE(*pptr);
+ uint32_t length = *(++pptr);
+
+ GETPLUSOFFSET(offset, pptr);
+ name = cb->start_pattern + offset;
+ for (i = 0; i < cb->names_found; i++, ng++)
+ {
+ if (length == ng->length && PRIV(strncmp)(name, ng->name, length) == 0)
+ {
+ group = ng->number;
+ is_dupname = ng->isdup;
+ break;
+ }
+ }
+
+ if (group == 0)
+ {
+ *errcodeptr = ERR15; /* Non-existent subpattern */
+ cb->erroroffset = offset;
+ return -1;
+ }
+
+ /* A numerical back reference can be fixed length if duplicate capturing
+ groups are not being used. A non-duplicate named back reference can also
+ be handled. */
+
+ if (meta_code == META_RECURSE_BYNAME ||
+ (!is_dupname && (cb->external_flags & PCRE2_DUPCAPUSED) == 0))
+ goto RECURSE_OR_BACKREF_LENGTH; /* Handle as a numbered version. */
+ }
+ goto ISNOTFIXED; /* Duplicate name or number */
+
+ /* The offset values for back references < 10 are in a separate vector
+ because otherwise they would use more than two parsed pattern elements on
+ 64-bit systems. */
+
+ case META_BACKREF:
+ if ((cb->external_options & PCRE2_MATCH_UNSET_BACKREF) != 0 ||
+ (cb->external_flags & PCRE2_DUPCAPUSED) != 0)
+ goto ISNOTFIXED;
+ group = META_DATA(*pptr);
+ if (group < 10)
+ {
+ offset = cb->small_ref_offset[group];
+ goto RECURSE_OR_BACKREF_LENGTH;
+ }
+
+ /* Fall through */
+ /* For groups >= 10 - picking up group twice does no harm. */
+
+ /* A true recursion implies not fixed length, but a subroutine call may
+ be OK. Back reference "recursions" are also failed. */
+
+ case META_RECURSE:
+ group = META_DATA(*pptr);
+ GETPLUSOFFSET(offset, pptr);
+
+ RECURSE_OR_BACKREF_LENGTH:
+ if (group > cb->bracount)
+ {
+ cb->erroroffset = offset;
+ *errcodeptr = ERR15; /* Non-existent subpattern */
+ return -1;
+ }
+ if (group == 0) goto ISNOTFIXED; /* Local recursion */
+ for (gptr = cb->parsed_pattern; *gptr != META_END; gptr++)
+ {
+ if (META_CODE(*gptr) == META_BIGVALUE) gptr++;
+ else if (*gptr == (META_CAPTURE | group)) break;
+ }
+
+ /* We must start the search for the end of the group at the first meta code
+ inside the group. Otherwise it will be treated as an enclosed group. */
+
+ gptrend = parsed_skip(gptr + 1, PSKIP_KET);
+ if (gptrend == NULL) goto PARSED_SKIP_FAILED;
+ if (pptr > gptr && pptr < gptrend) goto ISNOTFIXED; /* Local recursion */
+ for (r = recurses; r != NULL; r = r->prev) if (r->groupptr == gptr) break;
+ if (r != NULL) goto ISNOTFIXED; /* Mutual recursion */
+ this_recurse.prev = recurses;
+ this_recurse.groupptr = gptr;
+
+ /* We do not need to know the position of the end of the group, that is,
+ gptr is not used after the call to get_grouplength(). Setting the second
+ argument FALSE stops it scanning for the end when the length can be found
+ in the cache. */
+
+ gptr++;
+ grouplength = get_grouplength(&gptr, FALSE, errcodeptr, lcptr, group,
+ &this_recurse, cb);
+ if (grouplength < 0)
+ {
+ if (*errcodeptr == 0) goto ISNOTFIXED;
+ return -1; /* Error already set */
+ }
+ itemlength = grouplength;
+ break;
+
+ /* A (DEFINE) group is never obeyed inline and so it does not contribute to
+ the length of this branch. Skip from the following item to the next
+ unpaired ket. */
+
+ case META_COND_DEFINE:
+ pptr = parsed_skip(pptr + 1, PSKIP_KET);
+ break;
+
+ /* Check other nested groups - advance past the initial data for each type
+ and then seek a fixed length with get_grouplength(). */
+
+ case META_COND_NAME:
+ case META_COND_NUMBER:
+ case META_COND_RNAME:
+ case META_COND_RNUMBER:
+ pptr += 2 + SIZEOFFSET;
+ goto CHECK_GROUP;
+
+ case META_COND_ASSERT:
+ pptr += 1;
+ goto CHECK_GROUP;
+
+ case META_COND_VERSION:
+ pptr += 4;
+ goto CHECK_GROUP;
+
+ case META_CAPTURE:
+ group = META_DATA(*pptr);
+ /* Fall through */
+
+ case META_ATOMIC:
+ case META_NOCAPTURE:
+ case META_SCRIPT_RUN:
+ pptr++;
+ CHECK_GROUP:
+ grouplength = get_grouplength(&pptr, TRUE, errcodeptr, lcptr, group,
+ recurses, cb);
+ if (grouplength < 0) return -1;
+ itemlength = grouplength;
+ break;
+
+ /* Exact repetition is OK; variable repetition is not. A repetition of zero
+ must subtract the length that has already been added. */
+
+ case META_MINMAX:
+ case META_MINMAX_PLUS:
+ case META_MINMAX_QUERY:
+ if (pptr[1] == pptr[2])
+ {
+ switch(pptr[1])
+ {
+ case 0:
+ branchlength -= lastitemlength;
+ break;
+
+ case 1:
+ itemlength = 0;
+ break;
+
+ default: /* Check for integer overflow */
+ if (lastitemlength != 0 && /* Should not occur, but just in case */
+ INT_MAX/lastitemlength < pptr[1] - 1)
+ {
+ *errcodeptr = ERR87; /* Integer overflow; lookbehind too big */
+ return -1;
+ }
+ itemlength = (pptr[1] - 1) * lastitemlength;
+ break;
+ }
+ pptr += 2;
+ break;
+ }
+ /* Fall through */
+
+ /* Any other item means this branch does not have a fixed length. */
+
+ default:
+ ISNOTFIXED:
+ *errcodeptr = ERR25; /* Not fixed length */
+ return -1;
+ }
+
+ /* Add the item length to the branchlength, checking for integer overflow and
+ for the branch length exceeding the limit. */
+
+ if (INT_MAX - branchlength < (int)itemlength ||
+ (branchlength += itemlength) > LOOKBEHIND_MAX)
+ {
+ *errcodeptr = ERR87;
+ return -1;
+ }
+
+ /* Save this item length for use if the next item is a quantifier. */
+
+ lastitemlength = itemlength;
+ }
+
+EXIT:
+*pptrptr = pptr;
+return branchlength;
+
+PARSED_SKIP_FAILED:
+*errcodeptr = ERR90;
+return -1;
+}
+
+
+
+/*************************************************
+* Set lengths in a lookbehind *
+*************************************************/
+
+/* This function is called for each lookbehind, to set the lengths in its
+branches. An error occurs if any branch does not have a fixed length that is
+less than the maximum (65535). On exit, the pointer must be left on the final
+ket.
+
+The function also maintains the max_lookbehind value. Any lookbehind branch
+that contains a nested lookbehind may actually look further back than the
+length of the branch. The additional amount is passed back from
+get_branchlength() as an "extra" value.
+
+Arguments:
+ pptrptr pointer to pointer in the parsed pattern
+ errcodeptr pointer to error code
+ lcptr pointer to loop counter
+ recurses chain of recurse_check to catch mutual recursion
+ cb pointer to compile block
+
+Returns: TRUE if all is well
+ FALSE otherwise, with error code and offset set
+*/
+
+static BOOL
+set_lookbehind_lengths(uint32_t **pptrptr, int *errcodeptr, int *lcptr,
+ parsed_recurse_check *recurses, compile_block *cb)
+{
+PCRE2_SIZE offset;
+int branchlength;
+uint32_t *bptr = *pptrptr;
+
+READPLUSOFFSET(offset, bptr); /* Offset for error messages */
+*pptrptr += SIZEOFFSET;
+
+do
+ {
+ *pptrptr += 1;
+ branchlength = get_branchlength(pptrptr, errcodeptr, lcptr, recurses, cb);
+ if (branchlength < 0)
+ {
+ /* The errorcode and offset may already be set from a nested lookbehind. */
+ if (*errcodeptr == 0) *errcodeptr = ERR25;
+ if (cb->erroroffset == PCRE2_UNSET) cb->erroroffset = offset;
+ return FALSE;
+ }
+ if (branchlength > cb->max_lookbehind) cb->max_lookbehind = branchlength;
+ *bptr |= branchlength; /* branchlength never more than 65535 */
+ bptr = *pptrptr;
+ }
+while (*bptr == META_ALT);
+
+return TRUE;
+}
+
+
+
+/*************************************************
+* Check parsed pattern lookbehinds *
+*************************************************/
+
+/* This function is called at the end of parsing a pattern if any lookbehinds
+were encountered. It scans the parsed pattern for them, calling
+set_lookbehind_lengths() for each one. At the start, the errorcode is zero and
+the error offset is marked unset. The enables the functions above not to
+override settings from deeper nestings.
+
+This function is called recursively from get_branchlength() for lookaheads in
+order to process any lookbehinds that they may contain. It stops when it hits a
+non-nested closing parenthesis in this case, returning a pointer to it.
+
+Arguments
+ pptr points to where to start (start of pattern or start of lookahead)
+ retptr if not NULL, return the ket pointer here
+ recurses chain of recurse_check to catch mutual recursion
+ cb points to the compile block
+ lcptr points to loop counter
+
+Returns: 0 on success, or an errorcode (cb->erroroffset will be set)
+*/
+
+static int
+check_lookbehinds(uint32_t *pptr, uint32_t **retptr,
+ parsed_recurse_check *recurses, compile_block *cb, int *lcptr)
+{
+int errorcode = 0;
+int nestlevel = 0;
+
+cb->erroroffset = PCRE2_UNSET;
+
+for (; *pptr != META_END; pptr++)
+ {
+ if (*pptr < META_END) continue; /* Literal */
+
+ switch (META_CODE(*pptr))
+ {
+ default:
+ return ERR70; /* Unrecognized meta code */
+
+ case META_ESCAPE:
+ if (*pptr - META_ESCAPE == ESC_P || *pptr - META_ESCAPE == ESC_p)
+ pptr += 1;
+ break;
+
+ case META_KET:
+ if (--nestlevel < 0)
+ {
+ if (retptr != NULL) *retptr = pptr;
+ return 0;
+ }
+ break;
+
+ case META_ATOMIC:
+ case META_CAPTURE:
+ case META_COND_ASSERT:
+ case META_LOOKAHEAD:
+ case META_LOOKAHEADNOT:
+ case META_LOOKAHEAD_NA:
+ case META_NOCAPTURE:
+ case META_SCRIPT_RUN:
+ nestlevel++;
+ break;
+
+ case META_ACCEPT:
+ case META_ALT:
+ case META_ASTERISK:
+ case META_ASTERISK_PLUS:
+ case META_ASTERISK_QUERY:
+ case META_BACKREF:
+ case META_CIRCUMFLEX:
+ case META_CLASS:
+ case META_CLASS_EMPTY:
+ case META_CLASS_EMPTY_NOT:
+ case META_CLASS_END:
+ case META_CLASS_NOT:
+ case META_COMMIT:
+ case META_DOLLAR:
+ case META_DOT:
+ case META_FAIL:
+ case META_PLUS:
+ case META_PLUS_PLUS:
+ case META_PLUS_QUERY:
+ case META_PRUNE:
+ case META_QUERY:
+ case META_QUERY_PLUS:
+ case META_QUERY_QUERY:
+ case META_RANGE_ESCAPED:
+ case META_RANGE_LITERAL:
+ case META_SKIP:
+ case META_THEN:
+ break;
+
+ case META_RECURSE:
+ pptr += SIZEOFFSET;
+ break;
+
+ case META_BACKREF_BYNAME:
+ case META_RECURSE_BYNAME:
+ pptr += 1 + SIZEOFFSET;
+ break;
+
+ case META_COND_DEFINE:
+ pptr += SIZEOFFSET;
+ nestlevel++;
+ break;
+
+ case META_COND_NAME:
+ case META_COND_NUMBER:
+ case META_COND_RNAME:
+ case META_COND_RNUMBER:
+ pptr += 1 + SIZEOFFSET;
+ nestlevel++;
+ break;
+
+ case META_COND_VERSION:
+ pptr += 3;
+ nestlevel++;
+ break;
+
+ case META_CALLOUT_STRING:
+ pptr += 3 + SIZEOFFSET;
+ break;
+
+ case META_BIGVALUE:
+ case META_OPTIONS:
+ case META_POSIX:
+ case META_POSIX_NEG:
+ pptr += 1;
+ break;
+
+ case META_MINMAX:
+ case META_MINMAX_QUERY:
+ case META_MINMAX_PLUS:
+ pptr += 2;
+ break;
+
+ case META_CALLOUT_NUMBER:
+ pptr += 3;
+ break;
+
+ case META_MARK:
+ case META_COMMIT_ARG:
+ case META_PRUNE_ARG:
+ case META_SKIP_ARG:
+ case META_THEN_ARG:
+ pptr += 1 + pptr[1];
+ break;
+
+ case META_LOOKBEHIND:
+ case META_LOOKBEHINDNOT:
+ case META_LOOKBEHIND_NA:
+ if (!set_lookbehind_lengths(&pptr, &errorcode, lcptr, recurses, cb))
+ return errorcode;
+ break;
+ }
+ }
+
+return 0;
+}
+
+
+
+/*************************************************
+* External function to compile a pattern *
+*************************************************/
+
+/* This function reads a regular expression in the form of a string and returns
+a pointer to a block of store holding a compiled version of the expression.
+
+Arguments:
+ pattern the regular expression
+ patlen the length of the pattern, or PCRE2_ZERO_TERMINATED
+ options option bits
+ errorptr pointer to errorcode
+ erroroffset pointer to error offset
+ ccontext points to a compile context or is NULL
+
+Returns: pointer to compiled data block, or NULL on error,
+ with errorcode and erroroffset set
+*/
+
+PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION
+pcre2_compile(PCRE2_SPTR pattern, PCRE2_SIZE patlen, uint32_t options,
+ int *errorptr, PCRE2_SIZE *erroroffset, pcre2_compile_context *ccontext)
+{
+BOOL utf; /* Set TRUE for UTF mode */
+BOOL ucp; /* Set TRUE for UCP mode */
+BOOL has_lookbehind = FALSE; /* Set TRUE if a lookbehind is found */
+BOOL zero_terminated; /* Set TRUE for zero-terminated pattern */
+pcre2_real_code *re = NULL; /* What we will return */
+compile_block cb; /* "Static" compile-time data */
+const uint8_t *tables; /* Char tables base pointer */
+
+PCRE2_UCHAR *code; /* Current pointer in compiled code */
+PCRE2_SPTR codestart; /* Start of compiled code */
+PCRE2_SPTR ptr; /* Current pointer in pattern */
+uint32_t *pptr; /* Current pointer in parsed pattern */
+
+PCRE2_SIZE length = 1; /* Allow for final END opcode */
+PCRE2_SIZE usedlength; /* Actual length used */
+PCRE2_SIZE re_blocksize; /* Size of memory block */
+PCRE2_SIZE big32count = 0; /* 32-bit literals >= 0x80000000 */
+PCRE2_SIZE parsed_size_needed; /* Needed for parsed pattern */
+
+uint32_t firstcuflags, reqcuflags; /* Type of first/req code unit */
+uint32_t firstcu, reqcu; /* Value of first/req code unit */
+uint32_t setflags = 0; /* NL and BSR set flags */
+
+uint32_t skipatstart; /* When checking (*UTF) etc */
+uint32_t limit_heap = UINT32_MAX;
+uint32_t limit_match = UINT32_MAX; /* Unset match limits */
+uint32_t limit_depth = UINT32_MAX;
+
+int newline = 0; /* Unset; can be set by the pattern */
+int bsr = 0; /* Unset; can be set by the pattern */
+int errorcode = 0; /* Initialize to avoid compiler warn */
+int regexrc; /* Return from compile */
+
+uint32_t i; /* Local loop counter */
+
+/* Comments at the head of this file explain about these variables. */
+
+uint32_t stack_groupinfo[GROUPINFO_DEFAULT_SIZE];
+uint32_t stack_parsed_pattern[PARSED_PATTERN_DEFAULT_SIZE];
+named_group named_groups[NAMED_GROUP_LIST_SIZE];
+
+/* The workspace is used in different ways in the different compiling phases.
+It needs to be 16-bit aligned for the preliminary parsing scan. */
+
+uint32_t c16workspace[C16_WORK_SIZE];
+PCRE2_UCHAR *cworkspace = (PCRE2_UCHAR *)c16workspace;
+
+
+/* -------------- Check arguments and set up the pattern ----------------- */
+
+/* There must be error code and offset pointers. */
+
+if (errorptr == NULL || erroroffset == NULL) return NULL;
+*errorptr = ERR0;
+*erroroffset = 0;
+
+/* There must be a pattern! */
+
+if (pattern == NULL)
+ {
+ *errorptr = ERR16;
+ return NULL;
+ }
+
+/* A NULL compile context means "use a default context" */
+
+if (ccontext == NULL)
+ ccontext = (pcre2_compile_context *)(&PRIV(default_compile_context));
+
+/* PCRE2_MATCH_INVALID_UTF implies UTF */
+
+if ((options & PCRE2_MATCH_INVALID_UTF) != 0) options |= PCRE2_UTF;
+
+/* Check that all undefined public option bits are zero. */
+
+if ((options & ~PUBLIC_COMPILE_OPTIONS) != 0 ||
+ (ccontext->extra_options & ~PUBLIC_COMPILE_EXTRA_OPTIONS) != 0)
+ {
+ *errorptr = ERR17;
+ return NULL;
+ }
+
+if ((options & PCRE2_LITERAL) != 0 &&
+ ((options & ~PUBLIC_LITERAL_COMPILE_OPTIONS) != 0 ||
+ (ccontext->extra_options & ~PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS) != 0))
+ {
+ *errorptr = ERR92;
+ return NULL;
+ }
+
+/* A zero-terminated pattern is indicated by the special length value
+PCRE2_ZERO_TERMINATED. Check for an overlong pattern. */
+
+if ((zero_terminated = (patlen == PCRE2_ZERO_TERMINATED)))
+ patlen = PRIV(strlen)(pattern);
+
+if (patlen > ccontext->max_pattern_length)
+ {
+ *errorptr = ERR88;
+ return NULL;
+ }
+
+/* From here on, all returns from this function should end up going via the
+EXIT label. */
+
+
+/* ------------ Initialize the "static" compile data -------------- */
+
+tables = (ccontext->tables != NULL)? ccontext->tables : PRIV(default_tables);
+
+cb.lcc = tables + lcc_offset; /* Individual */
+cb.fcc = tables + fcc_offset; /* character */
+cb.cbits = tables + cbits_offset; /* tables */
+cb.ctypes = tables + ctypes_offset;
+
+cb.assert_depth = 0;
+cb.bracount = 0;
+cb.cx = ccontext;
+cb.dupnames = FALSE;
+cb.end_pattern = pattern + patlen;
+cb.erroroffset = 0;
+cb.external_flags = 0;
+cb.external_options = options;
+cb.groupinfo = stack_groupinfo;
+cb.had_recurse = FALSE;
+cb.lastcapture = 0;
+cb.max_lookbehind = 0;
+cb.name_entry_size = 0;
+cb.name_table = NULL;
+cb.named_groups = named_groups;
+cb.named_group_list_size = NAMED_GROUP_LIST_SIZE;
+cb.names_found = 0;
+cb.open_caps = NULL;
+cb.parens_depth = 0;
+cb.parsed_pattern = stack_parsed_pattern;
+cb.req_varyopt = 0;
+cb.start_code = cworkspace;
+cb.start_pattern = pattern;
+cb.start_workspace = cworkspace;
+cb.workspace_size = COMPILE_WORK_SIZE;
+
+/* Maximum back reference and backref bitmap. The bitmap records up to 31 back
+references to help in deciding whether (.*) can be treated as anchored or not.
+*/
+
+cb.top_backref = 0;
+cb.backref_map = 0;
+
+/* Escape sequences \1 to \9 are always back references, but as they are only
+two characters long, only two elements can be used in the parsed_pattern
+vector. The first contains the reference, and we'd like to use the second to
+record the offset in the pattern, so that forward references to non-existent
+groups can be diagnosed later with an offset. However, on 64-bit systems,
+PCRE2_SIZE won't fit. Instead, we have a vector of offsets for the first
+occurrence of \1 to \9, indexed by the second parsed_pattern value. All other
+references have enough space for the offset to be put into the parsed pattern.
+*/
+
+for (i = 0; i < 10; i++) cb.small_ref_offset[i] = PCRE2_UNSET;
+
+
+/* --------------- Start looking at the pattern --------------- */
+
+/* Unless PCRE2_LITERAL is set, check for global one-time option settings at
+the start of the pattern, and remember the offset to the actual regex. With
+valgrind support, make the terminator of a zero-terminated pattern
+inaccessible. This catches bugs that would otherwise only show up for
+non-zero-terminated patterns. */
+
+#ifdef SUPPORT_VALGRIND
+if (zero_terminated) VALGRIND_MAKE_MEM_NOACCESS(pattern + patlen, CU2BYTES(1));
+#endif
+
+ptr = pattern;
+skipatstart = 0;
+
+if ((options & PCRE2_LITERAL) == 0)
+ {
+ while (patlen - skipatstart >= 2 &&
+ ptr[skipatstart] == CHAR_LEFT_PARENTHESIS &&
+ ptr[skipatstart+1] == CHAR_ASTERISK)
+ {
+ for (i = 0; i < sizeof(pso_list)/sizeof(pso); i++)
+ {
+ uint32_t c, pp;
+ pso *p = pso_list + i;
+
+ if (patlen - skipatstart - 2 >= p->length &&
+ PRIV(strncmp_c8)(ptr + skipatstart + 2, (char *)(p->name),
+ p->length) == 0)
+ {
+ skipatstart += p->length + 2;
+ switch(p->type)
+ {
+ case PSO_OPT:
+ cb.external_options |= p->value;
+ break;
+
+ case PSO_FLG:
+ setflags |= p->value;
+ break;
+
+ case PSO_NL:
+ newline = p->value;
+ setflags |= PCRE2_NL_SET;
+ break;
+
+ case PSO_BSR:
+ bsr = p->value;
+ setflags |= PCRE2_BSR_SET;
+ break;
+
+ case PSO_LIMM:
+ case PSO_LIMD:
+ case PSO_LIMH:
+ c = 0;
+ pp = skipatstart;
+ if (!IS_DIGIT(ptr[pp]))
+ {
+ errorcode = ERR60;
+ ptr += pp;
+ goto HAD_EARLY_ERROR;
+ }
+ while (IS_DIGIT(ptr[pp]))
+ {
+ if (c > UINT32_MAX / 10 - 1) break; /* Integer overflow */
+ c = c*10 + (ptr[pp++] - CHAR_0);
+ }
+ if (ptr[pp++] != CHAR_RIGHT_PARENTHESIS)
+ {
+ errorcode = ERR60;
+ ptr += pp;
+ goto HAD_EARLY_ERROR;
+ }
+ if (p->type == PSO_LIMH) limit_heap = c;
+ else if (p->type == PSO_LIMM) limit_match = c;
+ else limit_depth = c;
+ skipatstart += pp - skipatstart;
+ break;
+ }
+ break; /* Out of the table scan loop */
+ }
+ }
+ if (i >= sizeof(pso_list)/sizeof(pso)) break; /* Out of pso loop */
+ }
+ }
+
+/* End of pattern-start options; advance to start of real regex. */
+
+ptr += skipatstart;
+
+/* Can't support UTF or UCP if PCRE2 was built without Unicode support. */
+
+#ifndef SUPPORT_UNICODE
+if ((cb.external_options & (PCRE2_UTF|PCRE2_UCP)) != 0)
+ {
+ errorcode = ERR32;
+ goto HAD_EARLY_ERROR;
+ }
+#endif
+
+/* Check UTF. We have the original options in 'options', with that value as
+modified by (*UTF) etc in cb->external_options. The extra option
+PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES is not permitted in UTF-16 mode because the
+surrogate code points cannot be represented in UTF-16. */
+
+utf = (cb.external_options & PCRE2_UTF) != 0;
+if (utf)
+ {
+ if ((options & PCRE2_NEVER_UTF) != 0)
+ {
+ errorcode = ERR74;
+ goto HAD_EARLY_ERROR;
+ }
+ if ((options & PCRE2_NO_UTF_CHECK) == 0 &&
+ (errorcode = PRIV(valid_utf)(pattern, patlen, erroroffset)) != 0)
+ goto HAD_ERROR; /* Offset was set by valid_utf() */
+
+#if PCRE2_CODE_UNIT_WIDTH == 16
+ if ((ccontext->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) != 0)
+ {
+ errorcode = ERR91;
+ goto HAD_EARLY_ERROR;
+ }
+#endif
+ }
+
+/* Check UCP lockout. */
+
+ucp = (cb.external_options & PCRE2_UCP) != 0;
+if (ucp && (cb.external_options & PCRE2_NEVER_UCP) != 0)
+ {
+ errorcode = ERR75;
+ goto HAD_EARLY_ERROR;
+ }
+
+/* Process the BSR setting. */
+
+if (bsr == 0) bsr = ccontext->bsr_convention;
+
+/* Process the newline setting. */
+
+if (newline == 0) newline = ccontext->newline_convention;
+cb.nltype = NLTYPE_FIXED;
+switch(newline)
+ {
+ case PCRE2_NEWLINE_CR:
+ cb.nllen = 1;
+ cb.nl[0] = CHAR_CR;
+ break;
+
+ case PCRE2_NEWLINE_LF:
+ cb.nllen = 1;
+ cb.nl[0] = CHAR_NL;
+ break;
+
+ case PCRE2_NEWLINE_NUL:
+ cb.nllen = 1;
+ cb.nl[0] = CHAR_NUL;
+ break;
+
+ case PCRE2_NEWLINE_CRLF:
+ cb.nllen = 2;
+ cb.nl[0] = CHAR_CR;
+ cb.nl[1] = CHAR_NL;
+ break;
+
+ case PCRE2_NEWLINE_ANY:
+ cb.nltype = NLTYPE_ANY;
+ break;
+
+ case PCRE2_NEWLINE_ANYCRLF:
+ cb.nltype = NLTYPE_ANYCRLF;
+ break;
+
+ default:
+ errorcode = ERR56;
+ goto HAD_EARLY_ERROR;
+ }
+
+/* Pre-scan the pattern to do two things: (1) Discover the named groups and
+their numerical equivalents, so that this information is always available for
+the remaining processing. (2) At the same time, parse the pattern and put a
+processed version into the parsed_pattern vector. This has escapes interpreted
+and comments removed (amongst other things).
+
+In all but one case, when PCRE2_AUTO_CALLOUT is not set, the number of unsigned
+32-bit ints in the parsed pattern is bounded by the length of the pattern plus
+one (for the terminator) plus four if PCRE2_EXTRA_WORD or PCRE2_EXTRA_LINE is
+set. The exceptional case is when running in 32-bit, non-UTF mode, when literal
+characters greater than META_END (0x80000000) have to be coded as two units. In
+this case, therefore, we scan the pattern to check for such values. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+if (!utf)
+ {
+ PCRE2_SPTR p;
+ for (p = ptr; p < cb.end_pattern; p++) if (*p >= META_END) big32count++;
+ }
+#endif
+
+/* Ensure that the parsed pattern buffer is big enough. When PCRE2_AUTO_CALLOUT
+is set we have to assume a numerical callout (4 elements) for each character
+plus one at the end. This is overkill, but memory is plentiful these days. For
+many smaller patterns the vector on the stack (which was set up above) can be
+used. */
+
+parsed_size_needed = patlen - skipatstart + big32count;
+
+if ((ccontext->extra_options &
+ (PCRE2_EXTRA_MATCH_WORD|PCRE2_EXTRA_MATCH_LINE)) != 0)
+ parsed_size_needed += 4;
+
+if ((options & PCRE2_AUTO_CALLOUT) != 0)
+ parsed_size_needed = (parsed_size_needed + 1) * 5;
+
+if (parsed_size_needed >= PARSED_PATTERN_DEFAULT_SIZE)
+ {
+ uint32_t *heap_parsed_pattern = ccontext->memctl.malloc(
+ (parsed_size_needed + 1) * sizeof(uint32_t), ccontext->memctl.memory_data);
+ if (heap_parsed_pattern == NULL)
+ {
+ *errorptr = ERR21;
+ goto EXIT;
+ }
+ cb.parsed_pattern = heap_parsed_pattern;
+ }
+cb.parsed_pattern_end = cb.parsed_pattern + parsed_size_needed + 1;
+
+/* Do the parsing scan. */
+
+errorcode = parse_regex(ptr, cb.external_options, &has_lookbehind, &cb);
+if (errorcode != 0) goto HAD_CB_ERROR;
+
+/* Workspace is needed to remember information about numbered groups: whether a
+group can match an empty string and what its fixed length is. This is done to
+avoid the possibility of recursive references causing very long compile times
+when checking these features. Unnumbered groups do not have this exposure since
+they cannot be referenced. We use an indexed vector for this purpose. If there
+are sufficiently few groups, the default vector on the stack, as set up above,
+can be used. Otherwise we have to get/free a special vector. The vector must be
+initialized to zero. */
+
+if (cb.bracount >= GROUPINFO_DEFAULT_SIZE)
+ {
+ cb.groupinfo = ccontext->memctl.malloc(
+ (cb.bracount + 1)*sizeof(uint32_t), ccontext->memctl.memory_data);
+ if (cb.groupinfo == NULL)
+ {
+ errorcode = ERR21;
+ cb.erroroffset = 0;
+ goto HAD_CB_ERROR;
+ }
+ }
+memset(cb.groupinfo, 0, (cb.bracount + 1) * sizeof(uint32_t));
+
+/* If there were any lookbehinds, scan the parsed pattern to figure out their
+lengths. */
+
+if (has_lookbehind)
+ {
+ int loopcount = 0;
+ errorcode = check_lookbehinds(cb.parsed_pattern, NULL, NULL, &cb, &loopcount);
+ if (errorcode != 0) goto HAD_CB_ERROR;
+ }
+
+/* For debugging, there is a function that shows the parsed data vector. */
+
+#ifdef DEBUG_SHOW_PARSED
+fprintf(stderr, "+++ Pre-scan complete:\n");
+show_parsed(&cb);
+#endif
+
+/* For debugging capturing information this code can be enabled. */
+
+#ifdef DEBUG_SHOW_CAPTURES
+ {
+ named_group *ng = cb.named_groups;
+ fprintf(stderr, "+++Captures: %d\n", cb.bracount);
+ for (i = 0; i < cb.names_found; i++, ng++)
+ {
+ fprintf(stderr, "+++%3d %.*s\n", ng->number, ng->length, ng->name);
+ }
+ }
+#endif
+
+/* Pretend to compile the pattern while actually just accumulating the amount
+of memory required in the 'length' variable. This behaviour is triggered by
+passing a non-NULL final argument to compile_regex(). We pass a block of
+workspace (cworkspace) for it to compile parts of the pattern into; the
+compiled code is discarded when it is no longer needed, so hopefully this
+workspace will never overflow, though there is a test for its doing so.
+
+On error, errorcode will be set non-zero, so we don't need to look at the
+result of the function. The initial options have been put into the cb block,
+but we still have to pass a separate options variable (the first argument)
+because the options may change as the pattern is processed. */
+
+cb.erroroffset = patlen; /* For any subsequent errors that do not set it */
+pptr = cb.parsed_pattern;
+code = cworkspace;
+*code = OP_BRA;
+
+(void)compile_regex(cb.external_options, &code, &pptr, &errorcode, 0, &firstcu,
+ &firstcuflags, &reqcu, &reqcuflags, NULL, &cb, &length);
+
+if (errorcode != 0) goto HAD_CB_ERROR; /* Offset is in cb.erroroffset */
+
+/* This should be caught in compile_regex(), but just in case... */
+
+if (length > MAX_PATTERN_SIZE)
+ {
+ errorcode = ERR20;
+ goto HAD_CB_ERROR;
+ }
+
+/* Compute the size of, and then get and initialize, the data block for storing
+the compiled pattern and names table. Integer overflow should no longer be
+possible because nowadays we limit the maximum value of cb.names_found and
+cb.name_entry_size. */
+
+re_blocksize = sizeof(pcre2_real_code) +
+ CU2BYTES(length +
+ (PCRE2_SIZE)cb.names_found * (PCRE2_SIZE)cb.name_entry_size);
+re = (pcre2_real_code *)
+ ccontext->memctl.malloc(re_blocksize, ccontext->memctl.memory_data);
+if (re == NULL)
+ {
+ errorcode = ERR21;
+ goto HAD_CB_ERROR;
+ }
+
+/* The compiler may put padding at the end of the pcre2_real_code structure in
+order to round it up to a multiple of 4 or 8 bytes. This means that when a
+compiled pattern is copied (for example, when serialized) undefined bytes are
+read, and this annoys debuggers such as valgrind. To avoid this, we explicitly
+write to the last 8 bytes of the structure before setting the fields. */
+
+memset((char *)re + sizeof(pcre2_real_code) - 8, 0, 8);
+re->memctl = ccontext->memctl;
+re->tables = tables;
+re->executable_jit = NULL;
+memset(re->start_bitmap, 0, 32 * sizeof(uint8_t));
+re->blocksize = re_blocksize;
+re->magic_number = MAGIC_NUMBER;
+re->compile_options = options;
+re->overall_options = cb.external_options;
+re->extra_options = ccontext->extra_options;
+re->flags = PCRE2_CODE_UNIT_WIDTH/8 | cb.external_flags | setflags;
+re->limit_heap = limit_heap;
+re->limit_match = limit_match;
+re->limit_depth = limit_depth;
+re->first_codeunit = 0;
+re->last_codeunit = 0;
+re->bsr_convention = bsr;
+re->newline_convention = newline;
+re->max_lookbehind = 0;
+re->minlength = 0;
+re->top_bracket = 0;
+re->top_backref = 0;
+re->name_entry_size = cb.name_entry_size;
+re->name_count = cb.names_found;
+
+/* The basic block is immediately followed by the name table, and the compiled
+code follows after that. */
+
+codestart = (PCRE2_SPTR)((uint8_t *)re + sizeof(pcre2_real_code)) +
+ re->name_entry_size * re->name_count;
+
+/* Update the compile data block for the actual compile. The starting points of
+the name/number translation table and of the code are passed around in the
+compile data block. The start/end pattern and initial options are already set
+from the pre-compile phase, as is the name_entry_size field. */
+
+cb.parens_depth = 0;
+cb.assert_depth = 0;
+cb.lastcapture = 0;
+cb.name_table = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code));
+cb.start_code = codestart;
+cb.req_varyopt = 0;
+cb.had_accept = FALSE;
+cb.had_pruneorskip = FALSE;
+cb.open_caps = NULL;
+
+/* If any named groups were found, create the name/number table from the list
+created in the pre-pass. */
+
+if (cb.names_found > 0)
+ {
+ named_group *ng = cb.named_groups;
+ for (i = 0; i < cb.names_found; i++, ng++)
+ add_name_to_table(&cb, ng->name, ng->length, ng->number, i);
+ }
+
+/* Set up a starting, non-extracting bracket, then compile the expression. On
+error, errorcode will be set non-zero, so we don't need to look at the result
+of the function here. */
+
+pptr = cb.parsed_pattern;
+code = (PCRE2_UCHAR *)codestart;
+*code = OP_BRA;
+regexrc = compile_regex(re->overall_options, &code, &pptr, &errorcode, 0,
+ &firstcu, &firstcuflags, &reqcu, &reqcuflags, NULL, &cb, NULL);
+if (regexrc < 0) re->flags |= PCRE2_MATCH_EMPTY;
+re->top_bracket = cb.bracount;
+re->top_backref = cb.top_backref;
+re->max_lookbehind = cb.max_lookbehind;
+
+if (cb.had_accept)
+ {
+ reqcu = 0; /* Must disable after (*ACCEPT) */
+ reqcuflags = REQ_NONE;
+ re->flags |= PCRE2_HASACCEPT; /* Disables minimum length */
+ }
+
+/* Fill in the final opcode and check for disastrous overflow. If no overflow,
+but the estimated length exceeds the really used length, adjust the value of
+re->blocksize, and if valgrind support is configured, mark the extra allocated
+memory as unaddressable, so that any out-of-bound reads can be detected. */
+
+*code++ = OP_END;
+usedlength = code - codestart;
+if (usedlength > length) errorcode = ERR23; else
+ {
+ re->blocksize -= CU2BYTES(length - usedlength);
+#ifdef SUPPORT_VALGRIND
+ VALGRIND_MAKE_MEM_NOACCESS(code, CU2BYTES(length - usedlength));
+#endif
+ }
+
+/* Scan the pattern for recursion/subroutine calls and convert the group
+numbers into offsets. Maintain a small cache so that repeated groups containing
+recursions are efficiently handled. */
+
+#define RSCAN_CACHE_SIZE 8
+
+if (errorcode == 0 && cb.had_recurse)
+ {
+ PCRE2_UCHAR *rcode;
+ PCRE2_SPTR rgroup;
+ unsigned int ccount = 0;
+ int start = RSCAN_CACHE_SIZE;
+ recurse_cache rc[RSCAN_CACHE_SIZE];
+
+ for (rcode = (PCRE2_UCHAR *)find_recurse(codestart, utf);
+ rcode != NULL;
+ rcode = (PCRE2_UCHAR *)find_recurse(rcode + 1 + LINK_SIZE, utf))
+ {
+ int p, groupnumber;
+
+ groupnumber = (int)GET(rcode, 1);
+ if (groupnumber == 0) rgroup = codestart; else
+ {
+ PCRE2_SPTR search_from = codestart;
+ rgroup = NULL;
+ for (i = 0, p = start; i < ccount; i++, p = (p + 1) & 7)
+ {
+ if (groupnumber == rc[p].groupnumber)
+ {
+ rgroup = rc[p].group;
+ break;
+ }
+
+ /* Group n+1 must always start to the right of group n, so we can save
+ search time below when the new group number is greater than any of the
+ previously found groups. */
+
+ if (groupnumber > rc[p].groupnumber) search_from = rc[p].group;
+ }
+
+ if (rgroup == NULL)
+ {
+ rgroup = PRIV(find_bracket)(search_from, utf, groupnumber);
+ if (rgroup == NULL)
+ {
+ errorcode = ERR53;
+ break;
+ }
+ if (--start < 0) start = RSCAN_CACHE_SIZE - 1;
+ rc[start].groupnumber = groupnumber;
+ rc[start].group = rgroup;
+ if (ccount < RSCAN_CACHE_SIZE) ccount++;
+ }
+ }
+
+ PUT(rcode, 1, rgroup - codestart);
+ }
+ }
+
+/* In rare debugging situations we sometimes need to look at the compiled code
+at this stage. */
+
+#ifdef DEBUG_CALL_PRINTINT
+pcre2_printint(re, stderr, TRUE);
+fprintf(stderr, "Length=%lu Used=%lu\n", length, usedlength);
+#endif
+
+/* Unless disabled, check whether any single character iterators can be
+auto-possessified. The function overwrites the appropriate opcode values, so
+the type of the pointer must be cast. NOTE: the intermediate variable "temp" is
+used in this code because at least one compiler gives a warning about loss of
+"const" attribute if the cast (PCRE2_UCHAR *)codestart is used directly in the
+function call. */
+
+if (errorcode == 0 && (re->overall_options & PCRE2_NO_AUTO_POSSESS) == 0)
+ {
+ PCRE2_UCHAR *temp = (PCRE2_UCHAR *)codestart;
+ if (PRIV(auto_possessify)(temp, &cb) != 0) errorcode = ERR80;
+ }
+
+/* Failed to compile, or error while post-processing. */
+
+if (errorcode != 0) goto HAD_CB_ERROR;
+
+/* Successful compile. If the anchored option was not passed, set it if
+we can determine that the pattern is anchored by virtue of ^ characters or \A
+or anything else, such as starting with non-atomic .* when DOTALL is set and
+there are no occurrences of *PRUNE or *SKIP (though there is an option to
+disable this case). */
+
+if ((re->overall_options & PCRE2_ANCHORED) == 0 &&
+ is_anchored(codestart, 0, &cb, 0, FALSE))
+ re->overall_options |= PCRE2_ANCHORED;
+
+/* Set up the first code unit or startline flag, the required code unit, and
+then study the pattern. This code need not be obeyed if PCRE2_NO_START_OPTIMIZE
+is set, as the data it would create will not be used. Note that a first code
+unit (but not the startline flag) is useful for anchored patterns because it
+can still give a quick "no match" and also avoid searching for a last code
+unit. */
+
+if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0)
+ {
+ int minminlength = 0; /* For minimal minlength from first/required CU */
+
+ /* If we do not have a first code unit, see if there is one that is asserted
+ (these are not saved during the compile because they can cause conflicts with
+ actual literals that follow). */
+
+ if (firstcuflags >= REQ_NONE)
+ firstcu = find_firstassertedcu(codestart, &firstcuflags, 0);
+
+ /* Save the data for a first code unit. The existence of one means the
+ minimum length must be at least 1. */
+
+ if (firstcuflags < REQ_NONE)
+ {
+ re->first_codeunit = firstcu;
+ re->flags |= PCRE2_FIRSTSET;
+ minminlength++;
+
+ /* Handle caseless first code units. */
+
+ if ((firstcuflags & REQ_CASELESS) != 0)
+ {
+ if (firstcu < 128 || (!utf && !ucp && firstcu < 255))
+ {
+ if (cb.fcc[firstcu] != firstcu) re->flags |= PCRE2_FIRSTCASELESS;
+ }
+
+ /* The first code unit is > 128 in UTF or UCP mode, or > 255 otherwise.
+ In 8-bit UTF mode, codepoints in the range 128-255 are introductory code
+ points and cannot have another case, but if UCP is set they may do. */
+
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ else if (ucp && !utf && UCD_OTHERCASE(firstcu) != firstcu)
+ re->flags |= PCRE2_FIRSTCASELESS;
+#else
+ else if ((utf || ucp) && firstcu <= MAX_UTF_CODE_POINT &&
+ UCD_OTHERCASE(firstcu) != firstcu)
+ re->flags |= PCRE2_FIRSTCASELESS;
+#endif
+#endif /* SUPPORT_UNICODE */
+ }
+ }
+
+ /* When there is no first code unit, for non-anchored patterns, see if we can
+ set the PCRE2_STARTLINE flag. This is helpful for multiline matches when all
+ branches start with ^ and also when all branches start with non-atomic .* for
+ non-DOTALL matches when *PRUNE and SKIP are not present. (There is an option
+ that disables this case.) */
+
+ else if ((re->overall_options & PCRE2_ANCHORED) == 0 &&
+ is_startline(codestart, 0, &cb, 0, FALSE))
+ re->flags |= PCRE2_STARTLINE;
+
+ /* Handle the "required code unit", if one is set. In the UTF case we can
+ increment the minimum minimum length only if we are sure this really is a
+ different character and not a non-starting code unit of the first character,
+ because the minimum length count is in characters, not code units. */
+
+ if (reqcuflags < REQ_NONE)
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 16
+ if ((re->overall_options & PCRE2_UTF) == 0 || /* Not UTF */
+ firstcuflags >= REQ_NONE || /* First not set */
+ (firstcu & 0xf800) != 0xd800 || /* First not surrogate */
+ (reqcu & 0xfc00) != 0xdc00) /* Req not low surrogate */
+#elif PCRE2_CODE_UNIT_WIDTH == 8
+ if ((re->overall_options & PCRE2_UTF) == 0 || /* Not UTF */
+ firstcuflags >= REQ_NONE || /* First not set */
+ (firstcu & 0x80) == 0 || /* First is ASCII */
+ (reqcu & 0x80) == 0) /* Req is ASCII */
+#endif
+ {
+ minminlength++;
+ }
+
+ /* In the case of an anchored pattern, set up the value only if it follows
+ a variable length item in the pattern. */
+
+ if ((re->overall_options & PCRE2_ANCHORED) == 0 ||
+ (reqcuflags & REQ_VARY) != 0)
+ {
+ re->last_codeunit = reqcu;
+ re->flags |= PCRE2_LASTSET;
+
+ /* Handle caseless required code units as for first code units (above). */
+
+ if ((reqcuflags & REQ_CASELESS) != 0)
+ {
+ if (reqcu < 128 || (!utf && !ucp && reqcu < 255))
+ {
+ if (cb.fcc[reqcu] != reqcu) re->flags |= PCRE2_LASTCASELESS;
+ }
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ else if (ucp && !utf && UCD_OTHERCASE(reqcu) != reqcu)
+ re->flags |= PCRE2_LASTCASELESS;
+#else
+ else if ((utf || ucp) && reqcu <= MAX_UTF_CODE_POINT &&
+ UCD_OTHERCASE(reqcu) != reqcu)
+ re->flags |= PCRE2_LASTCASELESS;
+#endif
+#endif /* SUPPORT_UNICODE */
+ }
+ }
+ }
+
+ /* Study the compiled pattern to set up information such as a bitmap of
+ starting code units and a minimum matching length. */
+
+ if (PRIV(study)(re) != 0)
+ {
+ errorcode = ERR31;
+ goto HAD_CB_ERROR;
+ }
+
+ /* If study() set a bitmap of starting code units, it implies a minimum
+ length of at least one. */
+
+ if ((re->flags & PCRE2_FIRSTMAPSET) != 0 && minminlength == 0)
+ minminlength = 1;
+
+ /* If the minimum length set (or not set) by study() is less than the minimum
+ implied by required code units, override it. */
+
+ if (re->minlength < minminlength) re->minlength = minminlength;
+ } /* End of start-of-match optimizations. */
+
+/* Control ends up here in all cases. When running under valgrind, make a
+pattern's terminating zero defined again. If memory was obtained for the parsed
+version of the pattern, free it before returning. Also free the list of named
+groups if a larger one had to be obtained, and likewise the group information
+vector. */
+
+EXIT:
+#ifdef SUPPORT_VALGRIND
+if (zero_terminated) VALGRIND_MAKE_MEM_DEFINED(pattern + patlen, CU2BYTES(1));
+#endif
+if (cb.parsed_pattern != stack_parsed_pattern)
+ ccontext->memctl.free(cb.parsed_pattern, ccontext->memctl.memory_data);
+if (cb.named_group_list_size > NAMED_GROUP_LIST_SIZE)
+ ccontext->memctl.free((void *)cb.named_groups, ccontext->memctl.memory_data);
+if (cb.groupinfo != stack_groupinfo)
+ ccontext->memctl.free((void *)cb.groupinfo, ccontext->memctl.memory_data);
+return re; /* Will be NULL after an error */
+
+/* Errors discovered in parse_regex() set the offset value in the compile
+block. Errors discovered before it is called must compute it from the ptr
+value. After parse_regex() is called, the offset in the compile block is set to
+the end of the pattern, but certain errors in compile_regex() may reset it if
+an offset is available in the parsed pattern. */
+
+HAD_CB_ERROR:
+ptr = pattern + cb.erroroffset;
+
+HAD_EARLY_ERROR:
+*erroroffset = ptr - pattern;
+
+HAD_ERROR:
+*errorptr = errorcode;
+pcre2_code_free(re);
+re = NULL;
+goto EXIT;
+}
+
+/* These #undefs are here to enable unity builds with CMake. */
+
+#undef NLBLOCK /* Block containing newline information */
+#undef PSSTART /* Field containing processed string start */
+#undef PSEND /* Field containing processed string end */
+
+/* End of pcre2_compile.c */
diff --git a/contrib/libs/pcre2/src/pcre2_config.c b/contrib/libs/pcre2/src/pcre2_config.c
new file mode 100644
index 00000000000..04a011a29c1
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_config.c
@@ -0,0 +1,252 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2020 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+/* Save the configured link size, which is in bytes. In 16-bit and 32-bit modes
+its value gets changed by pcre2_intmodedep.h (included by pcre2_internal.h) to
+be in code units. */
+
+static int configured_link_size = LINK_SIZE;
+
+#include "pcre2_internal.h"
+
+/* These macros are the standard way of turning unquoted text into C strings.
+They allow macros like PCRE2_MAJOR to be defined without quotes, which is
+convenient for user programs that want to test their values. */
+
+#define STRING(a) # a
+#define XSTRING(s) STRING(s)
+
+
+/*************************************************
+* Return info about what features are configured *
+*************************************************/
+
+/* If where is NULL, the length of memory required is returned.
+
+Arguments:
+ what what information is required
+ where where to put the information
+
+Returns: 0 if a numerical value is returned
+ >= 0 if a string value
+ PCRE2_ERROR_BADOPTION if "where" not recognized
+ or JIT target requested when JIT not enabled
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_config(uint32_t what, void *where)
+{
+if (where == NULL) /* Requests a length */
+ {
+ switch(what)
+ {
+ default:
+ return PCRE2_ERROR_BADOPTION;
+
+ case PCRE2_CONFIG_BSR:
+ case PCRE2_CONFIG_COMPILED_WIDTHS:
+ case PCRE2_CONFIG_DEPTHLIMIT:
+ case PCRE2_CONFIG_HEAPLIMIT:
+ case PCRE2_CONFIG_JIT:
+ case PCRE2_CONFIG_LINKSIZE:
+ case PCRE2_CONFIG_MATCHLIMIT:
+ case PCRE2_CONFIG_NEVER_BACKSLASH_C:
+ case PCRE2_CONFIG_NEWLINE:
+ case PCRE2_CONFIG_PARENSLIMIT:
+ case PCRE2_CONFIG_STACKRECURSE: /* Obsolete */
+ case PCRE2_CONFIG_TABLES_LENGTH:
+ case PCRE2_CONFIG_UNICODE:
+ return sizeof(uint32_t);
+
+ /* These are handled below */
+
+ case PCRE2_CONFIG_JITTARGET:
+ case PCRE2_CONFIG_UNICODE_VERSION:
+ case PCRE2_CONFIG_VERSION:
+ break;
+ }
+ }
+
+switch (what)
+ {
+ default:
+ return PCRE2_ERROR_BADOPTION;
+
+ case PCRE2_CONFIG_BSR:
+#ifdef BSR_ANYCRLF
+ *((uint32_t *)where) = PCRE2_BSR_ANYCRLF;
+#else
+ *((uint32_t *)where) = PCRE2_BSR_UNICODE;
+#endif
+ break;
+
+ case PCRE2_CONFIG_COMPILED_WIDTHS:
+ *((uint32_t *)where) = 0
+#ifdef SUPPORT_PCRE2_8
+ + 1
+#endif
+#ifdef SUPPORT_PCRE2_16
+ + 2
+#endif
+#ifdef SUPPORT_PCRE2_32
+ + 4
+#endif
+ ;
+ break;
+
+ case PCRE2_CONFIG_DEPTHLIMIT:
+ *((uint32_t *)where) = MATCH_LIMIT_DEPTH;
+ break;
+
+ case PCRE2_CONFIG_HEAPLIMIT:
+ *((uint32_t *)where) = HEAP_LIMIT;
+ break;
+
+ case PCRE2_CONFIG_JIT:
+#ifdef SUPPORT_JIT
+ *((uint32_t *)where) = 1;
+#else
+ *((uint32_t *)where) = 0;
+#endif
+ break;
+
+ case PCRE2_CONFIG_JITTARGET:
+#ifdef SUPPORT_JIT
+ {
+ const char *v = PRIV(jit_get_target)();
+ return (int)(1 + ((where == NULL)?
+ strlen(v) : PRIV(strcpy_c8)((PCRE2_UCHAR *)where, v)));
+ }
+#else
+ return PCRE2_ERROR_BADOPTION;
+#endif
+
+ case PCRE2_CONFIG_LINKSIZE:
+ *((uint32_t *)where) = (uint32_t)configured_link_size;
+ break;
+
+ case PCRE2_CONFIG_MATCHLIMIT:
+ *((uint32_t *)where) = MATCH_LIMIT;
+ break;
+
+ case PCRE2_CONFIG_NEWLINE:
+ *((uint32_t *)where) = NEWLINE_DEFAULT;
+ break;
+
+ case PCRE2_CONFIG_NEVER_BACKSLASH_C:
+#ifdef NEVER_BACKSLASH_C
+ *((uint32_t *)where) = 1;
+#else
+ *((uint32_t *)where) = 0;
+#endif
+ break;
+
+ case PCRE2_CONFIG_PARENSLIMIT:
+ *((uint32_t *)where) = PARENS_NEST_LIMIT;
+ break;
+
+ /* This is now obsolete. The stack is no longer used via recursion for
+ handling backtracking in pcre2_match(). */
+
+ case PCRE2_CONFIG_STACKRECURSE:
+ *((uint32_t *)where) = 0;
+ break;
+
+ case PCRE2_CONFIG_TABLES_LENGTH:
+ *((uint32_t *)where) = TABLES_LENGTH;
+ break;
+
+ case PCRE2_CONFIG_UNICODE_VERSION:
+ {
+#if defined SUPPORT_UNICODE
+ const char *v = PRIV(unicode_version);
+#else
+ const char *v = "Unicode not supported";
+#endif
+ return (int)(1 + ((where == NULL)?
+ strlen(v) : PRIV(strcpy_c8)((PCRE2_UCHAR *)where, v)));
+ }
+ break;
+
+ case PCRE2_CONFIG_UNICODE:
+#if defined SUPPORT_UNICODE
+ *((uint32_t *)where) = 1;
+#else
+ *((uint32_t *)where) = 0;
+#endif
+ break;
+
+ /* The hackery in setting "v" below is to cope with the case when
+ PCRE2_PRERELEASE is set to an empty string (which it is for real releases).
+ If the second alternative is used in this case, it does not leave a space
+ before the date. On the other hand, if all four macros are put into a single
+ XSTRING when PCRE2_PRERELEASE is not empty, an unwanted space is inserted.
+ There are problems using an "obvious" approach like this:
+
+ XSTRING(PCRE2_MAJOR) "." XSTRING(PCRE_MINOR)
+ XSTRING(PCRE2_PRERELEASE) " " XSTRING(PCRE_DATE)
+
+ because, when PCRE2_PRERELEASE is empty, this leads to an attempted expansion
+ of STRING(). The C standard states: "If (before argument substitution) any
+ argument consists of no preprocessing tokens, the behavior is undefined." It
+ turns out the gcc treats this case as a single empty string - which is what
+ we really want - but Visual C grumbles about the lack of an argument for the
+ macro. Unfortunately, both are within their rights. As there seems to be no
+ way to test for a macro's value being empty at compile time, we have to
+ resort to a runtime test. */
+
+ case PCRE2_CONFIG_VERSION:
+ {
+ const char *v = (XSTRING(Z PCRE2_PRERELEASE)[1] == 0)?
+ XSTRING(PCRE2_MAJOR.PCRE2_MINOR PCRE2_DATE) :
+ XSTRING(PCRE2_MAJOR.PCRE2_MINOR) XSTRING(PCRE2_PRERELEASE PCRE2_DATE);
+ return (int)(1 + ((where == NULL)?
+ strlen(v) : PRIV(strcpy_c8)((PCRE2_UCHAR *)where, v)));
+ }
+ }
+
+return 0;
+}
+
+/* End of pcre2_config.c */
diff --git a/contrib/libs/pcre2/src/pcre2_config.h b/contrib/libs/pcre2/src/pcre2_config.h
new file mode 100644
index 00000000000..14e49e08ced
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_config.h
@@ -0,0 +1,446 @@
+/* src/config.h. Generated from config.h.in by configure. */
+/* src/config.h.in. Generated from configure.ac by autoheader. */
+
+
+/* PCRE2 is written in Standard C, but there are a few non-standard things it
+can cope with, allowing it to run on SunOS4 and other "close to standard"
+systems.
+
+In environments that support the GNU autotools, config.h.in is converted into
+config.h by the "configure" script. In environments that use CMake,
+config-cmake.in is converted into config.h. If you are going to build PCRE2 "by
+hand" without using "configure" or CMake, you should copy the distributed
+config.h.generic to config.h, and edit the macro definitions to be the way you
+need them. You must then add -DHAVE_CONFIG_H to all of your compile commands,
+so that config.h is included at the start of every source.
+
+Alternatively, you can avoid editing by using -D on the compiler command line
+to set the macro values. In this case, you do not have to set -DHAVE_CONFIG_H,
+but if you do, default values will be taken from config.h for non-boolean
+macros that are not defined on the command line.
+
+Boolean macros such as HAVE_STDLIB_H and SUPPORT_PCRE2_8 should either be
+defined (conventionally to 1) for TRUE, and not defined at all for FALSE. All
+such macros are listed as a commented #undef in config.h.generic. Macros such
+as MATCH_LIMIT, whose actual value is relevant, have defaults defined, but are
+surrounded by #ifndef/#endif lines so that the value can be overridden by -D.
+
+PCRE2 uses memmove() if HAVE_MEMMOVE is defined; otherwise it uses bcopy() if
+HAVE_BCOPY is defined. If your system has neither bcopy() nor memmove(), make
+sure both macros are undefined; an emulation function will then be used. */
+
+/* By default, the \R escape sequence matches any Unicode line ending
+ character or sequence of characters. If BSR_ANYCRLF is defined (to any
+ value), this is changed so that backslash-R matches only CR, LF, or CRLF.
+ The build-time default can be overridden by the user of PCRE2 at runtime.
+ */
+/* #undef BSR_ANYCRLF */
+
+/* Define to any value to disable the use of the z and t modifiers in
+ formatting settings such as %zu or %td (this is rarely needed). */
+/* #undef DISABLE_PERCENT_ZT */
+
+/* If you are compiling for a system that uses EBCDIC instead of ASCII
+ character codes, define this macro to any value. When EBCDIC is set, PCRE2
+ assumes that all input strings are in EBCDIC. If you do not define this
+ macro, PCRE2 will assume input strings are ASCII or UTF-8/16/32 Unicode. It
+ is not possible to build a version of PCRE2 that supports both EBCDIC and
+ UTF-8/16/32. */
+/* #undef EBCDIC */
+
+/* In an EBCDIC environment, define this macro to any value to arrange for the
+ NL character to be 0x25 instead of the default 0x15. NL plays the role that
+ LF does in an ASCII/Unicode environment. */
+/* #undef EBCDIC_NL25 */
+
+/* Define this if your compiler supports __attribute__((uninitialized)) */
+/* #undef HAVE_ATTRIBUTE_UNINITIALIZED */
+
+/* Define to 1 if you have the `bcopy' function. */
+#define HAVE_BCOPY 1
+
+/* Define to 1 if you have the <bzlib.h> header file. */
+/* #undef HAVE_BZLIB_H */
+
+/* Define to 1 if you have the <dirent.h> header file. */
+#define HAVE_DIRENT_H 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <editline/readline.h> header file. */
+/* #undef HAVE_EDITLINE_READLINE_H */
+
+/* Define to 1 if you have the <edit/readline/readline.h> header file. */
+/* #undef HAVE_EDIT_READLINE_READLINE_H */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the `memfd_create' function. */
+#define HAVE_MEMFD_CREATE 1
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <minix/config.h> header file. */
+/* #undef HAVE_MINIX_CONFIG_H */
+
+/* Define to 1 if you have the `mkostemp' function. */
+#define HAVE_MKOSTEMP 1
+
+/* Define if you have POSIX threads libraries and header files. */
+#define HAVE_PTHREAD 1
+
+/* Have PTHREAD_PRIO_INHERIT. */
+#define HAVE_PTHREAD_PRIO_INHERIT 1
+
+/* Define to 1 if you have the <readline.h> header file. */
+/* #undef HAVE_READLINE_H */
+
+/* Define to 1 if you have the <readline/history.h> header file. */
+/* #undef HAVE_READLINE_HISTORY_H */
+
+/* Define to 1 if you have the <readline/readline.h> header file. */
+/* #undef HAVE_READLINE_READLINE_H */
+
+/* Define to 1 if you have the `realpath' function. */
+#define HAVE_REALPATH 1
+
+/* Define to 1 if you have the `secure_getenv' function. */
+#define HAVE_SECURE_GETENV 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strerror' function. */
+#define HAVE_STRERROR 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/wait.h> header file. */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if the compiler supports simple visibility declarations. */
+#define HAVE_VISIBILITY 1
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#define HAVE_WCHAR_H 1
+
+/* Define to 1 if you have the <windows.h> header file. */
+/* #undef HAVE_WINDOWS_H */
+
+/* Define to 1 if you have the <zlib.h> header file. */
+/* #undef HAVE_ZLIB_H */
+
+/* This limits the amount of memory that may be used while matching a pattern.
+ It applies to both pcre2_match() and pcre2_dfa_match(). It does not apply
+ to JIT matching. The value is in kibibytes (units of 1024 bytes). */
+#define HEAP_LIMIT 20000000
+
+/* The value of LINK_SIZE determines the number of bytes used to store links
+ as offsets within the compiled regex. The default is 2, which allows for
+ compiled patterns up to 65535 code units long. This covers the vast
+ majority of cases. However, PCRE2 can also be compiled to use 3 or 4 bytes
+ instead. This allows for longer patterns in extreme cases. */
+#define LINK_SIZE 2
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* The value of MATCH_LIMIT determines the default number of times the
+ pcre2_match() function can record a backtrack position during a single
+ matching attempt. The value is also used to limit a loop counter in
+ pcre2_dfa_match(). There is a runtime interface for setting a different
+ limit. The limit exists in order to catch runaway regular expressions that
+ take for ever to determine that they do not match. The default is set very
+ large so that it does not accidentally catch legitimate cases. */
+#define MATCH_LIMIT 10000000
+
+/* The above limit applies to all backtracks, whether or not they are nested.
+ In some environments it is desirable to limit the nesting of backtracking
+ (that is, the depth of tree that is searched) more strictly, in order to
+ restrict the maximum amount of heap memory that is used. The value of
+ MATCH_LIMIT_DEPTH provides this facility. To have any useful effect, it
+ must be less than the value of MATCH_LIMIT. The default is to use the same
+ value as MATCH_LIMIT. There is a runtime method for setting a different
+ limit. In the case of pcre2_dfa_match(), this limit controls the depth of
+ the internal nested function calls that are used for pattern recursions,
+ lookarounds, and atomic groups. */
+#define MATCH_LIMIT_DEPTH MATCH_LIMIT
+
+/* This limit is parameterized just in case anybody ever wants to change it.
+ Care must be taken if it is increased, because it guards against integer
+ overflow caused by enormously large patterns. */
+#define MAX_NAME_COUNT 10000
+
+/* This limit is parameterized just in case anybody ever wants to change it.
+ Care must be taken if it is increased, because it guards against integer
+ overflow caused by enormously large patterns. */
+#define MAX_NAME_SIZE 32
+
+/* Defining NEVER_BACKSLASH_C locks out the use of \C in all patterns. */
+/* #undef NEVER_BACKSLASH_C */
+
+/* The value of NEWLINE_DEFAULT determines the default newline character
+ sequence. PCRE2 client programs can override this by selecting other values
+ at run time. The valid values are 1 (CR), 2 (LF), 3 (CRLF), 4 (ANY), 5
+ (ANYCRLF), and 6 (NUL). */
+#define NEWLINE_DEFAULT 2
+
+/* Name of package */
+#define PACKAGE "pcre2"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "PCRE2"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "PCRE2 10.42"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "pcre2"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "10.42"
+
+/* The value of PARENS_NEST_LIMIT specifies the maximum depth of nested
+ parentheses (of any kind) in a pattern. This limits the amount of system
+ stack that is used while compiling a pattern. */
+#define PARENS_NEST_LIMIT 250
+
+/* The value of PCRE2GREP_BUFSIZE is the starting size of the buffer used by
+ pcre2grep to hold parts of the file it is searching. The buffer will be
+ expanded up to PCRE2GREP_MAX_BUFSIZE if necessary, for files containing
+ very long lines. The actual amount of memory used by pcre2grep is three
+ times this number, because it allows for the buffering of "before" and
+ "after" lines. */
+#define PCRE2GREP_BUFSIZE 20480
+
+/* The value of PCRE2GREP_MAX_BUFSIZE specifies the maximum size of the buffer
+ used by pcre2grep to hold parts of the file it is searching. The actual
+ amount of memory used by pcre2grep is three times this number, because it
+ allows for the buffering of "before" and "after" lines. */
+#define PCRE2GREP_MAX_BUFSIZE 1048576
+
+/* to make a symbol visible */
+#define PCRE2POSIX_EXP_DECL extern __attribute__ ((visibility ("default")))
+
+/* to make a symbol visible */
+#define PCRE2POSIX_EXP_DEFN extern __attribute__ ((visibility ("default")))
+
+/* Define to any value to include debugging code. */
+/* #undef PCRE2_DEBUG */
+
+/* to make a symbol visible */
+#define PCRE2_EXP_DECL extern __attribute__ ((visibility ("default")))
+
+
+/* If you are compiling for a system other than a Unix-like system or
+ Win32, and it needs some magic to be inserted before the definition
+ of a function that is exported by the library, define this macro to
+ contain the relevant magic. If you do not define this macro, a suitable
+ __declspec value is used for Windows systems; in other environments
+ "extern" is used for a C compiler and "extern C" for a C++ compiler.
+ This macro apears at the start of every exported function that is part
+ of the external API. It does not appear on functions that are "external"
+ in the C sense, but which are internal to the library. */
+#define PCRE2_EXP_DEFN __attribute__ ((visibility ("default")))
+
+/* Define to any value if linking statically (TODO: make nice with Libtool) */
+/* #undef PCRE2_STATIC */
+
+/* Define to necessary symbol if this constant uses a non-standard name on
+ your system. */
+/* #undef PTHREAD_CREATE_JOINABLE */
+
+/* Define to any non-zero number to enable support for SELinux compatible
+ executable memory allocator in JIT. Note that this will have no effect
+ unless SUPPORT_JIT is also defined. */
+/* #undef SLJIT_PROT_EXECUTABLE_ALLOCATOR */
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+ required in a freestanding environment). This macro is provided for
+ backward compatibility; new code need not use it. */
+#define STDC_HEADERS 1
+
+/* Define to any value to enable support for Just-In-Time compiling. */
+#define SUPPORT_JIT /**/
+
+/* Define to any value to allow pcre2grep to be linked with libbz2, so that it
+ is able to handle .bz2 files. */
+/* #undef SUPPORT_LIBBZ2 */
+
+/* Define to any value to allow pcre2test to be linked with libedit. */
+/* #undef SUPPORT_LIBEDIT */
+
+/* Define to any value to allow pcre2test to be linked with libreadline. */
+/* #undef SUPPORT_LIBREADLINE */
+
+/* Define to any value to allow pcre2grep to be linked with libz, so that it
+ is able to handle .gz files. */
+/* #undef SUPPORT_LIBZ */
+
+/* Define to any value to enable callout script support in pcre2grep. */
+#define SUPPORT_PCRE2GREP_CALLOUT /**/
+
+/* Define to any value to enable fork support in pcre2grep callout scripts.
+ This will have no effect unless SUPPORT_PCRE2GREP_CALLOUT is also defined.
+ */
+#define SUPPORT_PCRE2GREP_CALLOUT_FORK /**/
+
+/* Define to any value to enable JIT support in pcre2grep. Note that this will
+ have no effect unless SUPPORT_JIT is also defined. */
+#define SUPPORT_PCRE2GREP_JIT /**/
+
+/* Define to any value to enable the 16 bit PCRE2 library. */
+#define SUPPORT_PCRE2_16 /**/
+
+/* Define to any value to enable the 32 bit PCRE2 library. */
+#define SUPPORT_PCRE2_32 /**/
+
+/* Define to any value to enable the 8 bit PCRE2 library. */
+#define SUPPORT_PCRE2_8 /**/
+
+/* Define to any value to enable support for Unicode and UTF encoding. This
+ will work even in an EBCDIC environment, but it is incompatible with the
+ EBCDIC macro. That is, PCRE2 can support *either* EBCDIC code *or*
+ ASCII/Unicode, but not both at once. */
+#define SUPPORT_UNICODE /**/
+
+/* Define to any value for valgrind support to find invalid memory reads. */
+/* #undef SUPPORT_VALGRIND */
+
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable general extensions on macOS. */
+#ifndef _DARWIN_C_SOURCE
+# define _DARWIN_C_SOURCE 1
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable X/Open compliant socket functions that do not require linking
+ with -lxnet on HP-UX 11.11. */
+#ifndef _HPUX_ALT_XOPEN_SOCKET_API
+# define _HPUX_ALT_XOPEN_SOCKET_API 1
+#endif
+/* Identify the host operating system as Minix.
+ This macro does not affect the system headers' behavior.
+ A future release of Autoconf may stop defining this macro. */
+#ifndef _MINIX
+/* # undef _MINIX */
+#endif
+/* Enable general extensions on NetBSD.
+ Enable NetBSD compatibility extensions on Minix. */
+#ifndef _NETBSD_SOURCE
+# define _NETBSD_SOURCE 1
+#endif
+/* Enable OpenBSD compatibility extensions on NetBSD.
+ Oddly enough, this does nothing on OpenBSD. */
+#ifndef _OPENBSD_SOURCE
+# define _OPENBSD_SOURCE 1
+#endif
+/* Define to 1 if needed for POSIX-compatible behavior. */
+#ifndef _POSIX_SOURCE
+/* # undef _POSIX_SOURCE */
+#endif
+/* Define to 2 if needed for POSIX-compatible behavior. */
+#ifndef _POSIX_1_SOURCE
+/* # undef _POSIX_1_SOURCE */
+#endif
+/* Enable POSIX-compatible threading on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */
+#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
+# define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */
+#ifndef __STDC_WANT_IEC_60559_BFP_EXT__
+# define __STDC_WANT_IEC_60559_BFP_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */
+#ifndef __STDC_WANT_IEC_60559_DFP_EXT__
+# define __STDC_WANT_IEC_60559_DFP_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */
+#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__
+# define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */
+#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
+# define __STDC_WANT_IEC_60559_TYPES_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */
+#ifndef __STDC_WANT_LIB_EXT2__
+# define __STDC_WANT_LIB_EXT2__ 1
+#endif
+/* Enable extensions specified by ISO/IEC 24747:2009. */
+#ifndef __STDC_WANT_MATH_SPEC_FUNCS__
+# define __STDC_WANT_MATH_SPEC_FUNCS__ 1
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable X/Open extensions. Define to 500 only if necessary
+ to make mbstate_t available. */
+#ifndef _XOPEN_SOURCE
+/* # undef _XOPEN_SOURCE */
+#endif
+
+
+/* Version number of package */
+#define VERSION "10.42"
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to the type of a signed integer type of width exactly 64 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef int64_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
diff --git a/contrib/libs/pcre2/src/pcre2_context.c b/contrib/libs/pcre2/src/pcre2_context.c
new file mode 100644
index 00000000000..013cf6e9ede
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_context.c
@@ -0,0 +1,494 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+
+/*************************************************
+* Default malloc/free functions *
+*************************************************/
+
+/* Ignore the "user data" argument in each case. */
+
+static void *default_malloc(size_t size, void *data)
+{
+(void)data;
+return malloc(size);
+}
+
+
+static void default_free(void *block, void *data)
+{
+(void)data;
+free(block);
+}
+
+
+
+/*************************************************
+* Get a block and save memory control *
+*************************************************/
+
+/* This internal function is called to get a block of memory in which the
+memory control data is to be stored at the start for future use.
+
+Arguments:
+ size amount of memory required
+ memctl pointer to a memctl block or NULL
+
+Returns: pointer to memory or NULL on failure
+*/
+
+extern void *
+PRIV(memctl_malloc)(size_t size, pcre2_memctl *memctl)
+{
+pcre2_memctl *newmemctl;
+void *yield = (memctl == NULL)? malloc(size) :
+ memctl->malloc(size, memctl->memory_data);
+if (yield == NULL) return NULL;
+newmemctl = (pcre2_memctl *)yield;
+if (memctl == NULL)
+ {
+ newmemctl->malloc = default_malloc;
+ newmemctl->free = default_free;
+ newmemctl->memory_data = NULL;
+ }
+else *newmemctl = *memctl;
+return yield;
+}
+
+
+
+/*************************************************
+* Create and initialize contexts *
+*************************************************/
+
+/* Initializing for compile and match contexts is done in separate, private
+functions so that these can be called from functions such as pcre2_compile()
+when an external context is not supplied. The initializing functions have an
+option to set up default memory management. */
+
+PCRE2_EXP_DEFN pcre2_general_context * PCRE2_CALL_CONVENTION
+pcre2_general_context_create(void *(*private_malloc)(size_t, void *),
+ void (*private_free)(void *, void *), void *memory_data)
+{
+pcre2_general_context *gcontext;
+if (private_malloc == NULL) private_malloc = default_malloc;
+if (private_free == NULL) private_free = default_free;
+gcontext = private_malloc(sizeof(pcre2_real_general_context), memory_data);
+if (gcontext == NULL) return NULL;
+gcontext->memctl.malloc = private_malloc;
+gcontext->memctl.free = private_free;
+gcontext->memctl.memory_data = memory_data;
+return gcontext;
+}
+
+
+/* A default compile context is set up to save having to initialize at run time
+when no context is supplied to the compile function. */
+
+const pcre2_compile_context PRIV(default_compile_context) = {
+ { default_malloc, default_free, NULL }, /* Default memory handling */
+ NULL, /* Stack guard */
+ NULL, /* Stack guard data */
+ PRIV(default_tables), /* Character tables */
+ PCRE2_UNSET, /* Max pattern length */
+ BSR_DEFAULT, /* Backslash R default */
+ NEWLINE_DEFAULT, /* Newline convention */
+ PARENS_NEST_LIMIT, /* As it says */
+ 0 }; /* Extra options */
+
+/* The create function copies the default into the new memory, but must
+override the default memory handling functions if a gcontext was provided. */
+
+PCRE2_EXP_DEFN pcre2_compile_context * PCRE2_CALL_CONVENTION
+pcre2_compile_context_create(pcre2_general_context *gcontext)
+{
+pcre2_compile_context *ccontext = PRIV(memctl_malloc)(
+ sizeof(pcre2_real_compile_context), (pcre2_memctl *)gcontext);
+if (ccontext == NULL) return NULL;
+*ccontext = PRIV(default_compile_context);
+if (gcontext != NULL)
+ *((pcre2_memctl *)ccontext) = *((pcre2_memctl *)gcontext);
+return ccontext;
+}
+
+
+/* A default match context is set up to save having to initialize at run time
+when no context is supplied to a match function. */
+
+const pcre2_match_context PRIV(default_match_context) = {
+ { default_malloc, default_free, NULL },
+#ifdef SUPPORT_JIT
+ NULL, /* JIT callback */
+ NULL, /* JIT callback data */
+#endif
+ NULL, /* Callout function */
+ NULL, /* Callout data */
+ NULL, /* Substitute callout function */
+ NULL, /* Substitute callout data */
+ PCRE2_UNSET, /* Offset limit */
+ HEAP_LIMIT,
+ MATCH_LIMIT,
+ MATCH_LIMIT_DEPTH };
+
+/* The create function copies the default into the new memory, but must
+override the default memory handling functions if a gcontext was provided. */
+
+PCRE2_EXP_DEFN pcre2_match_context * PCRE2_CALL_CONVENTION
+pcre2_match_context_create(pcre2_general_context *gcontext)
+{
+pcre2_match_context *mcontext = PRIV(memctl_malloc)(
+ sizeof(pcre2_real_match_context), (pcre2_memctl *)gcontext);
+if (mcontext == NULL) return NULL;
+*mcontext = PRIV(default_match_context);
+if (gcontext != NULL)
+ *((pcre2_memctl *)mcontext) = *((pcre2_memctl *)gcontext);
+return mcontext;
+}
+
+
+/* A default convert context is set up to save having to initialize at run time
+when no context is supplied to the convert function. */
+
+const pcre2_convert_context PRIV(default_convert_context) = {
+ { default_malloc, default_free, NULL }, /* Default memory handling */
+#ifdef _WIN32
+ CHAR_BACKSLASH, /* Default path separator */
+ CHAR_GRAVE_ACCENT /* Default escape character */
+#else /* Not Windows */
+ CHAR_SLASH, /* Default path separator */
+ CHAR_BACKSLASH /* Default escape character */
+#endif
+ };
+
+/* The create function copies the default into the new memory, but must
+override the default memory handling functions if a gcontext was provided. */
+
+PCRE2_EXP_DEFN pcre2_convert_context * PCRE2_CALL_CONVENTION
+pcre2_convert_context_create(pcre2_general_context *gcontext)
+{
+pcre2_convert_context *ccontext = PRIV(memctl_malloc)(
+ sizeof(pcre2_real_convert_context), (pcre2_memctl *)gcontext);
+if (ccontext == NULL) return NULL;
+*ccontext = PRIV(default_convert_context);
+if (gcontext != NULL)
+ *((pcre2_memctl *)ccontext) = *((pcre2_memctl *)gcontext);
+return ccontext;
+}
+
+
+/*************************************************
+* Context copy functions *
+*************************************************/
+
+PCRE2_EXP_DEFN pcre2_general_context * PCRE2_CALL_CONVENTION
+pcre2_general_context_copy(pcre2_general_context *gcontext)
+{
+pcre2_general_context *new =
+ gcontext->memctl.malloc(sizeof(pcre2_real_general_context),
+ gcontext->memctl.memory_data);
+if (new == NULL) return NULL;
+memcpy(new, gcontext, sizeof(pcre2_real_general_context));
+return new;
+}
+
+
+PCRE2_EXP_DEFN pcre2_compile_context * PCRE2_CALL_CONVENTION
+pcre2_compile_context_copy(pcre2_compile_context *ccontext)
+{
+pcre2_compile_context *new =
+ ccontext->memctl.malloc(sizeof(pcre2_real_compile_context),
+ ccontext->memctl.memory_data);
+if (new == NULL) return NULL;
+memcpy(new, ccontext, sizeof(pcre2_real_compile_context));
+return new;
+}
+
+
+PCRE2_EXP_DEFN pcre2_match_context * PCRE2_CALL_CONVENTION
+pcre2_match_context_copy(pcre2_match_context *mcontext)
+{
+pcre2_match_context *new =
+ mcontext->memctl.malloc(sizeof(pcre2_real_match_context),
+ mcontext->memctl.memory_data);
+if (new == NULL) return NULL;
+memcpy(new, mcontext, sizeof(pcre2_real_match_context));
+return new;
+}
+
+
+
+PCRE2_EXP_DEFN pcre2_convert_context * PCRE2_CALL_CONVENTION
+pcre2_convert_context_copy(pcre2_convert_context *ccontext)
+{
+pcre2_convert_context *new =
+ ccontext->memctl.malloc(sizeof(pcre2_real_convert_context),
+ ccontext->memctl.memory_data);
+if (new == NULL) return NULL;
+memcpy(new, ccontext, sizeof(pcre2_real_convert_context));
+return new;
+}
+
+
+/*************************************************
+* Context free functions *
+*************************************************/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_general_context_free(pcre2_general_context *gcontext)
+{
+if (gcontext != NULL)
+ gcontext->memctl.free(gcontext, gcontext->memctl.memory_data);
+}
+
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_compile_context_free(pcre2_compile_context *ccontext)
+{
+if (ccontext != NULL)
+ ccontext->memctl.free(ccontext, ccontext->memctl.memory_data);
+}
+
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_match_context_free(pcre2_match_context *mcontext)
+{
+if (mcontext != NULL)
+ mcontext->memctl.free(mcontext, mcontext->memctl.memory_data);
+}
+
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_convert_context_free(pcre2_convert_context *ccontext)
+{
+if (ccontext != NULL)
+ ccontext->memctl.free(ccontext, ccontext->memctl.memory_data);
+}
+
+
+/*************************************************
+* Set values in contexts *
+*************************************************/
+
+/* All these functions return 0 for success or PCRE2_ERROR_BADDATA if invalid
+data is given. Only some of the functions are able to test the validity of the
+data. */
+
+
+/* ------------ Compile context ------------ */
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_character_tables(pcre2_compile_context *ccontext,
+ const uint8_t *tables)
+{
+ccontext->tables = tables;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_bsr(pcre2_compile_context *ccontext, uint32_t value)
+{
+switch(value)
+ {
+ case PCRE2_BSR_ANYCRLF:
+ case PCRE2_BSR_UNICODE:
+ ccontext->bsr_convention = value;
+ return 0;
+
+ default:
+ return PCRE2_ERROR_BADDATA;
+ }
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_max_pattern_length(pcre2_compile_context *ccontext, PCRE2_SIZE length)
+{
+ccontext->max_pattern_length = length;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_newline(pcre2_compile_context *ccontext, uint32_t newline)
+{
+switch(newline)
+ {
+ case PCRE2_NEWLINE_CR:
+ case PCRE2_NEWLINE_LF:
+ case PCRE2_NEWLINE_CRLF:
+ case PCRE2_NEWLINE_ANY:
+ case PCRE2_NEWLINE_ANYCRLF:
+ case PCRE2_NEWLINE_NUL:
+ ccontext->newline_convention = newline;
+ return 0;
+
+ default:
+ return PCRE2_ERROR_BADDATA;
+ }
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_parens_nest_limit(pcre2_compile_context *ccontext, uint32_t limit)
+{
+ccontext->parens_nest_limit = limit;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_compile_extra_options(pcre2_compile_context *ccontext, uint32_t options)
+{
+ccontext->extra_options = options;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_compile_recursion_guard(pcre2_compile_context *ccontext,
+ int (*guard)(uint32_t, void *), void *user_data)
+{
+ccontext->stack_guard = guard;
+ccontext->stack_guard_data = user_data;
+return 0;
+}
+
+
+/* ------------ Match context ------------ */
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_callout(pcre2_match_context *mcontext,
+ int (*callout)(pcre2_callout_block *, void *), void *callout_data)
+{
+mcontext->callout = callout;
+mcontext->callout_data = callout_data;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_substitute_callout(pcre2_match_context *mcontext,
+ int (*substitute_callout)(pcre2_substitute_callout_block *, void *),
+ void *substitute_callout_data)
+{
+mcontext->substitute_callout = substitute_callout;
+mcontext->substitute_callout_data = substitute_callout_data;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_heap_limit(pcre2_match_context *mcontext, uint32_t limit)
+{
+mcontext->heap_limit = limit;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_match_limit(pcre2_match_context *mcontext, uint32_t limit)
+{
+mcontext->match_limit = limit;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_depth_limit(pcre2_match_context *mcontext, uint32_t limit)
+{
+mcontext->depth_limit = limit;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_offset_limit(pcre2_match_context *mcontext, PCRE2_SIZE limit)
+{
+mcontext->offset_limit = limit;
+return 0;
+}
+
+/* These functions became obsolete at release 10.30. The first is kept as a
+synonym for backwards compatibility. The second now does nothing. Exclude both
+from coverage reports. */
+
+/* LCOV_EXCL_START */
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_recursion_limit(pcre2_match_context *mcontext, uint32_t limit)
+{
+return pcre2_set_depth_limit(mcontext, limit);
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_recursion_memory_management(pcre2_match_context *mcontext,
+ void *(*mymalloc)(size_t, void *), void (*myfree)(void *, void *),
+ void *mydata)
+{
+(void)mcontext;
+(void)mymalloc;
+(void)myfree;
+(void)mydata;
+return 0;
+}
+
+/* LCOV_EXCL_STOP */
+
+
+/* ------------ Convert context ------------ */
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_glob_separator(pcre2_convert_context *ccontext, uint32_t separator)
+{
+if (separator != CHAR_SLASH && separator != CHAR_BACKSLASH &&
+ separator != CHAR_DOT) return PCRE2_ERROR_BADDATA;
+ccontext->glob_separator = separator;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_glob_escape(pcre2_convert_context *ccontext, uint32_t escape)
+{
+if (escape > 255 || (escape != 0 && !ispunct(escape)))
+ return PCRE2_ERROR_BADDATA;
+ccontext->glob_escape = escape;
+return 0;
+}
+
+/* End of pcre2_context.c */
+
diff --git a/contrib/libs/pcre2/src/pcre2_convert.c b/contrib/libs/pcre2/src/pcre2_convert.c
new file mode 100644
index 00000000000..240828bca52
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_convert.c
@@ -0,0 +1,1181 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+#define TYPE_OPTIONS (PCRE2_CONVERT_GLOB| \
+ PCRE2_CONVERT_POSIX_BASIC|PCRE2_CONVERT_POSIX_EXTENDED)
+
+#define ALL_OPTIONS (PCRE2_CONVERT_UTF|PCRE2_CONVERT_NO_UTF_CHECK| \
+ PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR| \
+ PCRE2_CONVERT_GLOB_NO_STARSTAR| \
+ TYPE_OPTIONS)
+
+#define DUMMY_BUFFER_SIZE 100
+
+/* Generated pattern fragments */
+
+#define STR_BACKSLASH_A STR_BACKSLASH STR_A
+#define STR_BACKSLASH_z STR_BACKSLASH STR_z
+#define STR_COLON_RIGHT_SQUARE_BRACKET STR_COLON STR_RIGHT_SQUARE_BRACKET
+#define STR_DOT_STAR_LOOKBEHIND STR_DOT STR_ASTERISK STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_LESS_THAN_SIGN STR_EQUALS_SIGN
+#define STR_LOOKAHEAD_NOT_DOT STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_EXCLAMATION_MARK STR_BACKSLASH STR_DOT STR_RIGHT_PARENTHESIS
+#define STR_QUERY_s STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_s STR_RIGHT_PARENTHESIS
+#define STR_STAR_NUL STR_LEFT_PARENTHESIS STR_ASTERISK STR_N STR_U STR_L STR_RIGHT_PARENTHESIS
+
+/* States for POSIX processing */
+
+enum { POSIX_START_REGEX, POSIX_ANCHORED, POSIX_NOT_BRACKET,
+ POSIX_CLASS_NOT_STARTED, POSIX_CLASS_STARTING, POSIX_CLASS_STARTED };
+
+/* Macro to add a character string to the output buffer, checking for overflow. */
+
+#define PUTCHARS(string) \
+ { \
+ for (s = (char *)(string); *s != 0; s++) \
+ { \
+ if (p >= endp) return PCRE2_ERROR_NOMEMORY; \
+ *p++ = *s; \
+ } \
+ }
+
+/* Literals that must be escaped: \ ? * + | . ^ $ { } [ ] ( ) */
+
+static const char *pcre2_escaped_literals =
+ STR_BACKSLASH STR_QUESTION_MARK STR_ASTERISK STR_PLUS
+ STR_VERTICAL_LINE STR_DOT STR_CIRCUMFLEX_ACCENT STR_DOLLAR_SIGN
+ STR_LEFT_CURLY_BRACKET STR_RIGHT_CURLY_BRACKET
+ STR_LEFT_SQUARE_BRACKET STR_RIGHT_SQUARE_BRACKET
+ STR_LEFT_PARENTHESIS STR_RIGHT_PARENTHESIS;
+
+/* Recognized escaped metacharacters in POSIX basic patterns. */
+
+static const char *posix_meta_escapes =
+ STR_LEFT_PARENTHESIS STR_RIGHT_PARENTHESIS
+ STR_LEFT_CURLY_BRACKET STR_RIGHT_CURLY_BRACKET
+ STR_1 STR_2 STR_3 STR_4 STR_5 STR_6 STR_7 STR_8 STR_9;
+
+
+
+/*************************************************
+* Convert a POSIX pattern *
+*************************************************/
+
+/* This function handles both basic and extended POSIX patterns.
+
+Arguments:
+ pattype the pattern type
+ pattern the pattern
+ plength length in code units
+ utf TRUE if UTF
+ use_buffer where to put the output
+ use_length length of use_buffer
+ bufflenptr where to put the used length
+ dummyrun TRUE if a dummy run
+ ccontext the convert context
+
+Returns: 0 => success
+ !0 => error code
+*/
+
+static int
+convert_posix(uint32_t pattype, PCRE2_SPTR pattern, PCRE2_SIZE plength,
+ BOOL utf, PCRE2_UCHAR *use_buffer, PCRE2_SIZE use_length,
+ PCRE2_SIZE *bufflenptr, BOOL dummyrun, pcre2_convert_context *ccontext)
+{
+char *s;
+PCRE2_SPTR posix = pattern;
+PCRE2_UCHAR *p = use_buffer;
+PCRE2_UCHAR *pp = p;
+PCRE2_UCHAR *endp = p + use_length - 1; /* Allow for trailing zero */
+PCRE2_SIZE convlength = 0;
+
+uint32_t bracount = 0;
+uint32_t posix_state = POSIX_START_REGEX;
+uint32_t lastspecial = 0;
+BOOL extended = (pattype & PCRE2_CONVERT_POSIX_EXTENDED) != 0;
+BOOL nextisliteral = FALSE;
+
+(void)utf; /* Not used when Unicode not supported */
+(void)ccontext; /* Not currently used */
+
+/* Initialize default for error offset as end of input. */
+
+*bufflenptr = plength;
+PUTCHARS(STR_STAR_NUL);
+
+/* Now scan the input. */
+
+while (plength > 0)
+ {
+ uint32_t c, sc;
+ int clength = 1;
+
+ /* Add in the length of the last item, then, if in the dummy run, pull the
+ pointer back to the start of the (temporary) buffer and then remember the
+ start of the next item. */
+
+ convlength += p - pp;
+ if (dummyrun) p = use_buffer;
+ pp = p;
+
+ /* Pick up the next character */
+
+#ifndef SUPPORT_UNICODE
+ c = *posix;
+#else
+ GETCHARLENTEST(c, posix, clength);
+#endif
+ posix += clength;
+ plength -= clength;
+
+ sc = nextisliteral? 0 : c;
+ nextisliteral = FALSE;
+
+ /* Handle a character within a class. */
+
+ if (posix_state >= POSIX_CLASS_NOT_STARTED)
+ {
+ if (c == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ PUTCHARS(STR_RIGHT_SQUARE_BRACKET);
+ posix_state = POSIX_NOT_BRACKET;
+ }
+
+ /* Not the end of the class */
+
+ else
+ {
+ switch (posix_state)
+ {
+ case POSIX_CLASS_STARTED:
+ if (c <= 127 && islower(c)) break; /* Remain in started state */
+ posix_state = POSIX_CLASS_NOT_STARTED;
+ if (c == CHAR_COLON && plength > 0 &&
+ *posix == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ PUTCHARS(STR_COLON_RIGHT_SQUARE_BRACKET);
+ plength--;
+ posix++;
+ continue; /* With next character after :] */
+ }
+ /* Fall through */
+
+ case POSIX_CLASS_NOT_STARTED:
+ if (c == CHAR_LEFT_SQUARE_BRACKET)
+ posix_state = POSIX_CLASS_STARTING;
+ break;
+
+ case POSIX_CLASS_STARTING:
+ if (c == CHAR_COLON) posix_state = POSIX_CLASS_STARTED;
+ break;
+ }
+
+ if (c == CHAR_BACKSLASH) PUTCHARS(STR_BACKSLASH);
+ if (p + clength > endp) return PCRE2_ERROR_NOMEMORY;
+ memcpy(p, posix - clength, CU2BYTES(clength));
+ p += clength;
+ }
+ }
+
+ /* Handle a character not within a class. */
+
+ else switch(sc)
+ {
+ case CHAR_LEFT_SQUARE_BRACKET:
+ PUTCHARS(STR_LEFT_SQUARE_BRACKET);
+
+#ifdef NEVER
+ /* We could handle special cases [[:<:]] and [[:>:]] (which PCRE does
+ support) but they are not part of POSIX 1003.1. */
+
+ if (plength >= 6)
+ {
+ if (posix[0] == CHAR_LEFT_SQUARE_BRACKET &&
+ posix[1] == CHAR_COLON &&
+ (posix[2] == CHAR_LESS_THAN_SIGN ||
+ posix[2] == CHAR_GREATER_THAN_SIGN) &&
+ posix[3] == CHAR_COLON &&
+ posix[4] == CHAR_RIGHT_SQUARE_BRACKET &&
+ posix[5] == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ if (p + 6 > endp) return PCRE2_ERROR_NOMEMORY;
+ memcpy(p, posix, CU2BYTES(6));
+ p += 6;
+ posix += 6;
+ plength -= 6;
+ continue; /* With next character */
+ }
+ }
+#endif
+
+ /* Handle start of "normal" character classes */
+
+ posix_state = POSIX_CLASS_NOT_STARTED;
+
+ /* Handle ^ and ] as first characters */
+
+ if (plength > 0)
+ {
+ if (*posix == CHAR_CIRCUMFLEX_ACCENT)
+ {
+ posix++;
+ plength--;
+ PUTCHARS(STR_CIRCUMFLEX_ACCENT);
+ }
+ if (plength > 0 && *posix == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ posix++;
+ plength--;
+ PUTCHARS(STR_RIGHT_SQUARE_BRACKET);
+ }
+ }
+ break;
+
+ case CHAR_BACKSLASH:
+ if (plength == 0) return PCRE2_ERROR_END_BACKSLASH;
+ if (extended) nextisliteral = TRUE; else
+ {
+ if (*posix < 127 && strchr(posix_meta_escapes, *posix) != NULL)
+ {
+ if (isdigit(*posix)) PUTCHARS(STR_BACKSLASH);
+ if (p + 1 > endp) return PCRE2_ERROR_NOMEMORY;
+ lastspecial = *p++ = *posix++;
+ plength--;
+ }
+ else nextisliteral = TRUE;
+ }
+ break;
+
+ case CHAR_RIGHT_PARENTHESIS:
+ if (!extended || bracount == 0) goto ESCAPE_LITERAL;
+ bracount--;
+ goto COPY_SPECIAL;
+
+ case CHAR_LEFT_PARENTHESIS:
+ bracount++;
+ /* Fall through */
+
+ case CHAR_QUESTION_MARK:
+ case CHAR_PLUS:
+ case CHAR_LEFT_CURLY_BRACKET:
+ case CHAR_RIGHT_CURLY_BRACKET:
+ case CHAR_VERTICAL_LINE:
+ if (!extended) goto ESCAPE_LITERAL;
+ /* Fall through */
+
+ case CHAR_DOT:
+ case CHAR_DOLLAR_SIGN:
+ posix_state = POSIX_NOT_BRACKET;
+ COPY_SPECIAL:
+ lastspecial = c;
+ if (p + 1 > endp) return PCRE2_ERROR_NOMEMORY;
+ *p++ = c;
+ break;
+
+ case CHAR_ASTERISK:
+ if (lastspecial != CHAR_ASTERISK)
+ {
+ if (!extended && (posix_state < POSIX_NOT_BRACKET ||
+ lastspecial == CHAR_LEFT_PARENTHESIS))
+ goto ESCAPE_LITERAL;
+ goto COPY_SPECIAL;
+ }
+ break; /* Ignore second and subsequent asterisks */
+
+ case CHAR_CIRCUMFLEX_ACCENT:
+ if (extended) goto COPY_SPECIAL;
+ if (posix_state == POSIX_START_REGEX ||
+ lastspecial == CHAR_LEFT_PARENTHESIS)
+ {
+ posix_state = POSIX_ANCHORED;
+ goto COPY_SPECIAL;
+ }
+ /* Fall through */
+
+ default:
+ if (c < 128 && strchr(pcre2_escaped_literals, c) != NULL)
+ {
+ ESCAPE_LITERAL:
+ PUTCHARS(STR_BACKSLASH);
+ }
+ lastspecial = 0xff; /* Indicates nothing special */
+ if (p + clength > endp) return PCRE2_ERROR_NOMEMORY;
+ memcpy(p, posix - clength, CU2BYTES(clength));
+ p += clength;
+ posix_state = POSIX_NOT_BRACKET;
+ break;
+ }
+ }
+
+if (posix_state >= POSIX_CLASS_NOT_STARTED)
+ return PCRE2_ERROR_MISSING_SQUARE_BRACKET;
+convlength += p - pp; /* Final segment */
+*bufflenptr = convlength;
+*p++ = 0;
+return 0;
+}
+
+
+/*************************************************
+* Convert a glob pattern *
+*************************************************/
+
+/* Context for writing the output into a buffer. */
+
+typedef struct pcre2_output_context {
+ PCRE2_UCHAR *output; /* current output position */
+ PCRE2_SPTR output_end; /* output end */
+ PCRE2_SIZE output_size; /* size of the output */
+ uint8_t out_str[8]; /* string copied to the output */
+} pcre2_output_context;
+
+
+/* Write a character into the output.
+
+Arguments:
+ out output context
+ chr the next character
+*/
+
+static void
+convert_glob_write(pcre2_output_context *out, PCRE2_UCHAR chr)
+{
+out->output_size++;
+
+if (out->output < out->output_end)
+ *out->output++ = chr;
+}
+
+
+/* Write a string into the output.
+
+Arguments:
+ out output context
+ length length of out->out_str
+*/
+
+static void
+convert_glob_write_str(pcre2_output_context *out, PCRE2_SIZE length)
+{
+uint8_t *out_str = out->out_str;
+PCRE2_UCHAR *output = out->output;
+PCRE2_SPTR output_end = out->output_end;
+PCRE2_SIZE output_size = out->output_size;
+
+do
+ {
+ output_size++;
+
+ if (output < output_end)
+ *output++ = *out_str++;
+ }
+while (--length != 0);
+
+out->output = output;
+out->output_size = output_size;
+}
+
+
+/* Prints the separator into the output.
+
+Arguments:
+ out output context
+ separator glob separator
+ with_escape backslash is needed before separator
+*/
+
+static void
+convert_glob_print_separator(pcre2_output_context *out,
+ PCRE2_UCHAR separator, BOOL with_escape)
+{
+if (with_escape)
+ convert_glob_write(out, CHAR_BACKSLASH);
+
+convert_glob_write(out, separator);
+}
+
+
+/* Prints a wildcard into the output.
+
+Arguments:
+ out output context
+ separator glob separator
+ with_escape backslash is needed before separator
+*/
+
+static void
+convert_glob_print_wildcard(pcre2_output_context *out,
+ PCRE2_UCHAR separator, BOOL with_escape)
+{
+out->out_str[0] = CHAR_LEFT_SQUARE_BRACKET;
+out->out_str[1] = CHAR_CIRCUMFLEX_ACCENT;
+convert_glob_write_str(out, 2);
+
+convert_glob_print_separator(out, separator, with_escape);
+
+convert_glob_write(out, CHAR_RIGHT_SQUARE_BRACKET);
+}
+
+
+/* Parse a posix class.
+
+Arguments:
+ from starting point of scanning the range
+ pattern_end end of pattern
+ out output context
+
+Returns: >0 => class index
+ 0 => malformed class
+*/
+
+static int
+convert_glob_parse_class(PCRE2_SPTR *from, PCRE2_SPTR pattern_end,
+ pcre2_output_context *out)
+{
+static const char *posix_classes = "alnum:alpha:ascii:blank:cntrl:digit:"
+ "graph:lower:print:punct:space:upper:word:xdigit:";
+PCRE2_SPTR start = *from + 1;
+PCRE2_SPTR pattern = start;
+const char *class_ptr;
+PCRE2_UCHAR c;
+int class_index;
+
+while (TRUE)
+ {
+ if (pattern >= pattern_end) return 0;
+
+ c = *pattern++;
+
+ if (c < CHAR_a || c > CHAR_z) break;
+ }
+
+if (c != CHAR_COLON || pattern >= pattern_end ||
+ *pattern != CHAR_RIGHT_SQUARE_BRACKET)
+ return 0;
+
+class_ptr = posix_classes;
+class_index = 1;
+
+while (TRUE)
+ {
+ if (*class_ptr == CHAR_NUL) return 0;
+
+ pattern = start;
+
+ while (*pattern == (PCRE2_UCHAR) *class_ptr)
+ {
+ if (*pattern == CHAR_COLON)
+ {
+ pattern += 2;
+ start -= 2;
+
+ do convert_glob_write(out, *start++); while (start < pattern);
+
+ *from = pattern;
+ return class_index;
+ }
+ pattern++;
+ class_ptr++;
+ }
+
+ while (*class_ptr != CHAR_COLON) class_ptr++;
+ class_ptr++;
+ class_index++;
+ }
+}
+
+/* Checks whether the character is in the class.
+
+Arguments:
+ class_index class index
+ c character
+
+Returns: !0 => character is found in the class
+ 0 => otherwise
+*/
+
+static BOOL
+convert_glob_char_in_class(int class_index, PCRE2_UCHAR c)
+{
+switch (class_index)
+ {
+ case 1: return isalnum(c);
+ case 2: return isalpha(c);
+ case 3: return 1;
+ case 4: return c == CHAR_HT || c == CHAR_SPACE;
+ case 5: return iscntrl(c);
+ case 6: return isdigit(c);
+ case 7: return isgraph(c);
+ case 8: return islower(c);
+ case 9: return isprint(c);
+ case 10: return ispunct(c);
+ case 11: return isspace(c);
+ case 12: return isupper(c);
+ case 13: return isalnum(c) || c == CHAR_UNDERSCORE;
+ default: return isxdigit(c);
+ }
+}
+
+/* Parse a range of characters.
+
+Arguments:
+ from starting point of scanning the range
+ pattern_end end of pattern
+ out output context
+ separator glob separator
+ with_escape backslash is needed before separator
+
+Returns: 0 => success
+ !0 => error code
+*/
+
+static int
+convert_glob_parse_range(PCRE2_SPTR *from, PCRE2_SPTR pattern_end,
+ pcre2_output_context *out, BOOL utf, PCRE2_UCHAR separator,
+ BOOL with_escape, PCRE2_UCHAR escape, BOOL no_wildsep)
+{
+BOOL is_negative = FALSE;
+BOOL separator_seen = FALSE;
+BOOL has_prev_c;
+PCRE2_SPTR pattern = *from;
+PCRE2_SPTR char_start = NULL;
+uint32_t c, prev_c;
+int len, class_index;
+
+(void)utf; /* Avoid compiler warning. */
+
+if (pattern >= pattern_end)
+ {
+ *from = pattern;
+ return PCRE2_ERROR_MISSING_SQUARE_BRACKET;
+ }
+
+if (*pattern == CHAR_EXCLAMATION_MARK
+ || *pattern == CHAR_CIRCUMFLEX_ACCENT)
+ {
+ pattern++;
+
+ if (pattern >= pattern_end)
+ {
+ *from = pattern;
+ return PCRE2_ERROR_MISSING_SQUARE_BRACKET;
+ }
+
+ is_negative = TRUE;
+
+ out->out_str[0] = CHAR_LEFT_SQUARE_BRACKET;
+ out->out_str[1] = CHAR_CIRCUMFLEX_ACCENT;
+ len = 2;
+
+ if (!no_wildsep)
+ {
+ if (with_escape)
+ {
+ out->out_str[len] = CHAR_BACKSLASH;
+ len++;
+ }
+ out->out_str[len] = (uint8_t) separator;
+ }
+
+ convert_glob_write_str(out, len + 1);
+ }
+else
+ convert_glob_write(out, CHAR_LEFT_SQUARE_BRACKET);
+
+has_prev_c = FALSE;
+prev_c = 0;
+
+if (*pattern == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ out->out_str[0] = CHAR_BACKSLASH;
+ out->out_str[1] = CHAR_RIGHT_SQUARE_BRACKET;
+ convert_glob_write_str(out, 2);
+ has_prev_c = TRUE;
+ prev_c = CHAR_RIGHT_SQUARE_BRACKET;
+ pattern++;
+ }
+
+while (pattern < pattern_end)
+ {
+ char_start = pattern;
+ GETCHARINCTEST(c, pattern);
+
+ if (c == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ convert_glob_write(out, c);
+
+ if (!is_negative && !no_wildsep && separator_seen)
+ {
+ out->out_str[0] = CHAR_LEFT_PARENTHESIS;
+ out->out_str[1] = CHAR_QUESTION_MARK;
+ out->out_str[2] = CHAR_LESS_THAN_SIGN;
+ out->out_str[3] = CHAR_EXCLAMATION_MARK;
+ convert_glob_write_str(out, 4);
+
+ convert_glob_print_separator(out, separator, with_escape);
+ convert_glob_write(out, CHAR_RIGHT_PARENTHESIS);
+ }
+
+ *from = pattern;
+ return 0;
+ }
+
+ if (pattern >= pattern_end) break;
+
+ if (c == CHAR_LEFT_SQUARE_BRACKET && *pattern == CHAR_COLON)
+ {
+ *from = pattern;
+ class_index = convert_glob_parse_class(from, pattern_end, out);
+
+ if (class_index != 0)
+ {
+ pattern = *from;
+
+ has_prev_c = FALSE;
+ prev_c = 0;
+
+ if (!is_negative &&
+ convert_glob_char_in_class (class_index, separator))
+ separator_seen = TRUE;
+ continue;
+ }
+ }
+ else if (c == CHAR_MINUS && has_prev_c &&
+ *pattern != CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ convert_glob_write(out, CHAR_MINUS);
+
+ char_start = pattern;
+ GETCHARINCTEST(c, pattern);
+
+ if (pattern >= pattern_end) break;
+
+ if (escape != 0 && c == escape)
+ {
+ char_start = pattern;
+ GETCHARINCTEST(c, pattern);
+ }
+ else if (c == CHAR_LEFT_SQUARE_BRACKET && *pattern == CHAR_COLON)
+ {
+ *from = pattern;
+ return PCRE2_ERROR_CONVERT_SYNTAX;
+ }
+
+ if (prev_c > c)
+ {
+ *from = pattern;
+ return PCRE2_ERROR_CONVERT_SYNTAX;
+ }
+
+ if (prev_c < separator && separator < c) separator_seen = TRUE;
+
+ has_prev_c = FALSE;
+ prev_c = 0;
+ }
+ else
+ {
+ if (escape != 0 && c == escape)
+ {
+ char_start = pattern;
+ GETCHARINCTEST(c, pattern);
+
+ if (pattern >= pattern_end) break;
+ }
+
+ has_prev_c = TRUE;
+ prev_c = c;
+ }
+
+ if (c == CHAR_LEFT_SQUARE_BRACKET || c == CHAR_RIGHT_SQUARE_BRACKET ||
+ c == CHAR_BACKSLASH || c == CHAR_MINUS)
+ convert_glob_write(out, CHAR_BACKSLASH);
+
+ if (c == separator) separator_seen = TRUE;
+
+ do convert_glob_write(out, *char_start++); while (char_start < pattern);
+ }
+
+*from = pattern;
+return PCRE2_ERROR_MISSING_SQUARE_BRACKET;
+}
+
+
+/* Prints a (*COMMIT) into the output.
+
+Arguments:
+ out output context
+*/
+
+static void
+convert_glob_print_commit(pcre2_output_context *out)
+{
+out->out_str[0] = CHAR_LEFT_PARENTHESIS;
+out->out_str[1] = CHAR_ASTERISK;
+out->out_str[2] = CHAR_C;
+out->out_str[3] = CHAR_O;
+out->out_str[4] = CHAR_M;
+out->out_str[5] = CHAR_M;
+out->out_str[6] = CHAR_I;
+out->out_str[7] = CHAR_T;
+convert_glob_write_str(out, 8);
+convert_glob_write(out, CHAR_RIGHT_PARENTHESIS);
+}
+
+
+/* Bash glob converter.
+
+Arguments:
+ pattype the pattern type
+ pattern the pattern
+ plength length in code units
+ utf TRUE if UTF
+ use_buffer where to put the output
+ use_length length of use_buffer
+ bufflenptr where to put the used length
+ dummyrun TRUE if a dummy run
+ ccontext the convert context
+
+Returns: 0 => success
+ !0 => error code
+*/
+
+static int
+convert_glob(uint32_t options, PCRE2_SPTR pattern, PCRE2_SIZE plength,
+ BOOL utf, PCRE2_UCHAR *use_buffer, PCRE2_SIZE use_length,
+ PCRE2_SIZE *bufflenptr, BOOL dummyrun, pcre2_convert_context *ccontext)
+{
+pcre2_output_context out;
+PCRE2_SPTR pattern_start = pattern;
+PCRE2_SPTR pattern_end = pattern + plength;
+PCRE2_UCHAR separator = ccontext->glob_separator;
+PCRE2_UCHAR escape = ccontext->glob_escape;
+PCRE2_UCHAR c;
+BOOL no_wildsep = (options & PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR) != 0;
+BOOL no_starstar = (options & PCRE2_CONVERT_GLOB_NO_STARSTAR) != 0;
+BOOL in_atomic = FALSE;
+BOOL after_starstar = FALSE;
+BOOL no_slash_z = FALSE;
+BOOL with_escape, is_start, after_separator;
+int result = 0;
+
+(void)utf; /* Avoid compiler warning. */
+
+#ifdef SUPPORT_UNICODE
+if (utf && (separator >= 128 || escape >= 128))
+ {
+ /* Currently only ASCII characters are supported. */
+ *bufflenptr = 0;
+ return PCRE2_ERROR_CONVERT_SYNTAX;
+ }
+#endif
+
+with_escape = strchr(pcre2_escaped_literals, separator) != NULL;
+
+/* Initialize default for error offset as end of input. */
+out.output = use_buffer;
+out.output_end = use_buffer + use_length;
+out.output_size = 0;
+
+out.out_str[0] = CHAR_LEFT_PARENTHESIS;
+out.out_str[1] = CHAR_QUESTION_MARK;
+out.out_str[2] = CHAR_s;
+out.out_str[3] = CHAR_RIGHT_PARENTHESIS;
+convert_glob_write_str(&out, 4);
+
+is_start = TRUE;
+
+if (pattern < pattern_end && pattern[0] == CHAR_ASTERISK)
+ {
+ if (no_wildsep)
+ is_start = FALSE;
+ else if (!no_starstar && pattern + 1 < pattern_end &&
+ pattern[1] == CHAR_ASTERISK)
+ is_start = FALSE;
+ }
+
+if (is_start)
+ {
+ out.out_str[0] = CHAR_BACKSLASH;
+ out.out_str[1] = CHAR_A;
+ convert_glob_write_str(&out, 2);
+ }
+
+while (pattern < pattern_end)
+ {
+ c = *pattern++;
+
+ if (c == CHAR_ASTERISK)
+ {
+ is_start = pattern == pattern_start + 1;
+
+ if (in_atomic)
+ {
+ convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS);
+ in_atomic = FALSE;
+ }
+
+ if (!no_starstar && pattern < pattern_end && *pattern == CHAR_ASTERISK)
+ {
+ after_separator = is_start || (pattern[-2] == separator);
+
+ do pattern++; while (pattern < pattern_end &&
+ *pattern == CHAR_ASTERISK);
+
+ if (pattern >= pattern_end)
+ {
+ no_slash_z = TRUE;
+ break;
+ }
+
+ after_starstar = TRUE;
+
+ if (after_separator && escape != 0 && *pattern == escape &&
+ pattern + 1 < pattern_end && pattern[1] == separator)
+ pattern++;
+
+ if (is_start)
+ {
+ if (*pattern != separator) continue;
+
+ out.out_str[0] = CHAR_LEFT_PARENTHESIS;
+ out.out_str[1] = CHAR_QUESTION_MARK;
+ out.out_str[2] = CHAR_COLON;
+ out.out_str[3] = CHAR_BACKSLASH;
+ out.out_str[4] = CHAR_A;
+ out.out_str[5] = CHAR_VERTICAL_LINE;
+ convert_glob_write_str(&out, 6);
+
+ convert_glob_print_separator(&out, separator, with_escape);
+ convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS);
+
+ pattern++;
+ continue;
+ }
+
+ convert_glob_print_commit(&out);
+
+ if (!after_separator || *pattern != separator)
+ {
+ out.out_str[0] = CHAR_DOT;
+ out.out_str[1] = CHAR_ASTERISK;
+ out.out_str[2] = CHAR_QUESTION_MARK;
+ convert_glob_write_str(&out, 3);
+ continue;
+ }
+
+ out.out_str[0] = CHAR_LEFT_PARENTHESIS;
+ out.out_str[1] = CHAR_QUESTION_MARK;
+ out.out_str[2] = CHAR_COLON;
+ out.out_str[3] = CHAR_DOT;
+ out.out_str[4] = CHAR_ASTERISK;
+ out.out_str[5] = CHAR_QUESTION_MARK;
+
+ convert_glob_write_str(&out, 6);
+
+ convert_glob_print_separator(&out, separator, with_escape);
+
+ out.out_str[0] = CHAR_RIGHT_PARENTHESIS;
+ out.out_str[1] = CHAR_QUESTION_MARK;
+ out.out_str[2] = CHAR_QUESTION_MARK;
+ convert_glob_write_str(&out, 3);
+
+ pattern++;
+ continue;
+ }
+
+ if (pattern < pattern_end && *pattern == CHAR_ASTERISK)
+ {
+ do pattern++; while (pattern < pattern_end &&
+ *pattern == CHAR_ASTERISK);
+ }
+
+ if (no_wildsep)
+ {
+ if (pattern >= pattern_end)
+ {
+ no_slash_z = TRUE;
+ break;
+ }
+
+ /* Start check must be after the end check. */
+ if (is_start) continue;
+ }
+
+ if (!is_start)
+ {
+ if (after_starstar)
+ {
+ out.out_str[0] = CHAR_LEFT_PARENTHESIS;
+ out.out_str[1] = CHAR_QUESTION_MARK;
+ out.out_str[2] = CHAR_GREATER_THAN_SIGN;
+ convert_glob_write_str(&out, 3);
+ in_atomic = TRUE;
+ }
+ else
+ convert_glob_print_commit(&out);
+ }
+
+ if (no_wildsep)
+ convert_glob_write(&out, CHAR_DOT);
+ else
+ convert_glob_print_wildcard(&out, separator, with_escape);
+
+ out.out_str[0] = CHAR_ASTERISK;
+ out.out_str[1] = CHAR_QUESTION_MARK;
+ if (pattern >= pattern_end)
+ out.out_str[1] = CHAR_PLUS;
+ convert_glob_write_str(&out, 2);
+ continue;
+ }
+
+ if (c == CHAR_QUESTION_MARK)
+ {
+ if (no_wildsep)
+ convert_glob_write(&out, CHAR_DOT);
+ else
+ convert_glob_print_wildcard(&out, separator, with_escape);
+ continue;
+ }
+
+ if (c == CHAR_LEFT_SQUARE_BRACKET)
+ {
+ result = convert_glob_parse_range(&pattern, pattern_end,
+ &out, utf, separator, with_escape, escape, no_wildsep);
+ if (result != 0) break;
+ continue;
+ }
+
+ if (escape != 0 && c == escape)
+ {
+ if (pattern >= pattern_end)
+ {
+ result = PCRE2_ERROR_CONVERT_SYNTAX;
+ break;
+ }
+ c = *pattern++;
+ }
+
+ if (c < 128 && strchr(pcre2_escaped_literals, c) != NULL)
+ convert_glob_write(&out, CHAR_BACKSLASH);
+
+ convert_glob_write(&out, c);
+ }
+
+if (result == 0)
+ {
+ if (!no_slash_z)
+ {
+ out.out_str[0] = CHAR_BACKSLASH;
+ out.out_str[1] = CHAR_z;
+ convert_glob_write_str(&out, 2);
+ }
+
+ if (in_atomic)
+ convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS);
+
+ convert_glob_write(&out, CHAR_NUL);
+
+ if (!dummyrun && out.output_size != (PCRE2_SIZE) (out.output - use_buffer))
+ result = PCRE2_ERROR_NOMEMORY;
+ }
+
+if (result != 0)
+ {
+ *bufflenptr = pattern - pattern_start;
+ return result;
+ }
+
+*bufflenptr = out.output_size - 1;
+return 0;
+}
+
+
+/*************************************************
+* Convert pattern *
+*************************************************/
+
+/* This is the external-facing function for converting other forms of pattern
+into PCRE2 regular expression patterns. On error, the bufflenptr argument is
+used to return an offset in the original pattern.
+
+Arguments:
+ pattern the input pattern
+ plength length of input, or PCRE2_ZERO_TERMINATED
+ options options bits
+ buffptr pointer to pointer to output buffer
+ bufflenptr pointer to length of output buffer
+ ccontext convert context or NULL
+
+Returns: 0 for success, else an error code (+ve or -ve)
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_pattern_convert(PCRE2_SPTR pattern, PCRE2_SIZE plength, uint32_t options,
+ PCRE2_UCHAR **buffptr, PCRE2_SIZE *bufflenptr,
+ pcre2_convert_context *ccontext)
+{
+int i, rc;
+PCRE2_UCHAR dummy_buffer[DUMMY_BUFFER_SIZE];
+PCRE2_UCHAR *use_buffer = dummy_buffer;
+PCRE2_SIZE use_length = DUMMY_BUFFER_SIZE;
+BOOL utf = (options & PCRE2_CONVERT_UTF) != 0;
+uint32_t pattype = options & TYPE_OPTIONS;
+
+if (pattern == NULL || bufflenptr == NULL) return PCRE2_ERROR_NULL;
+
+if ((options & ~ALL_OPTIONS) != 0 || /* Undefined bit set */
+ (pattype & (~pattype+1)) != pattype || /* More than one type set */
+ pattype == 0) /* No type set */
+ {
+ *bufflenptr = 0; /* Error offset */
+ return PCRE2_ERROR_BADOPTION;
+ }
+
+if (plength == PCRE2_ZERO_TERMINATED) plength = PRIV(strlen)(pattern);
+if (ccontext == NULL) ccontext =
+ (pcre2_convert_context *)(&PRIV(default_convert_context));
+
+/* Check UTF if required. */
+
+#ifndef SUPPORT_UNICODE
+if (utf)
+ {
+ *bufflenptr = 0; /* Error offset */
+ return PCRE2_ERROR_UNICODE_NOT_SUPPORTED;
+ }
+#else
+if (utf && (options & PCRE2_CONVERT_NO_UTF_CHECK) == 0)
+ {
+ PCRE2_SIZE erroroffset;
+ rc = PRIV(valid_utf)(pattern, plength, &erroroffset);
+ if (rc != 0)
+ {
+ *bufflenptr = erroroffset;
+ return rc;
+ }
+ }
+#endif
+
+/* If buffptr is not NULL, and what it points to is not NULL, we are being
+provided with a buffer and a length, so set them as the buffer to use. */
+
+if (buffptr != NULL && *buffptr != NULL)
+ {
+ use_buffer = *buffptr;
+ use_length = *bufflenptr;
+ }
+
+/* Call an individual converter, either just once (if a buffer was provided or
+just the length is needed), or twice (if a memory allocation is required). */
+
+for (i = 0; i < 2; i++)
+ {
+ PCRE2_UCHAR *allocated;
+ BOOL dummyrun = buffptr == NULL || *buffptr == NULL;
+
+ switch(pattype)
+ {
+ case PCRE2_CONVERT_GLOB:
+ rc = convert_glob(options & ~PCRE2_CONVERT_GLOB, pattern, plength, utf,
+ use_buffer, use_length, bufflenptr, dummyrun, ccontext);
+ break;
+
+ case PCRE2_CONVERT_POSIX_BASIC:
+ case PCRE2_CONVERT_POSIX_EXTENDED:
+ rc = convert_posix(pattype, pattern, plength, utf, use_buffer, use_length,
+ bufflenptr, dummyrun, ccontext);
+ break;
+
+ default:
+ *bufflenptr = 0; /* Error offset */
+ return PCRE2_ERROR_INTERNAL;
+ }
+
+ if (rc != 0 || /* Error */
+ buffptr == NULL || /* Just the length is required */
+ *buffptr != NULL) /* Buffer was provided or allocated */
+ return rc;
+
+ /* Allocate memory for the buffer, with hidden space for an allocator at
+ the start. The next time round the loop runs the conversion for real. */
+
+ allocated = PRIV(memctl_malloc)(sizeof(pcre2_memctl) +
+ (*bufflenptr + 1)*PCRE2_CODE_UNIT_WIDTH, (pcre2_memctl *)ccontext);
+ if (allocated == NULL) return PCRE2_ERROR_NOMEMORY;
+ *buffptr = (PCRE2_UCHAR *)(((char *)allocated) + sizeof(pcre2_memctl));
+
+ use_buffer = *buffptr;
+ use_length = *bufflenptr + 1;
+ }
+
+/* Control should never get here. */
+
+return PCRE2_ERROR_INTERNAL;
+}
+
+
+/*************************************************
+* Free converted pattern *
+*************************************************/
+
+/* This frees a converted pattern that was put in newly-allocated memory.
+
+Argument: the converted pattern
+Returns: nothing
+*/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_converted_pattern_free(PCRE2_UCHAR *converted)
+{
+if (converted != NULL)
+ {
+ pcre2_memctl *memctl =
+ (pcre2_memctl *)((char *)converted - sizeof(pcre2_memctl));
+ memctl->free(memctl, memctl->memory_data);
+ }
+}
+
+/* End of pcre2_convert.c */
diff --git a/contrib/libs/pcre2/src/pcre2_dfa_match.c b/contrib/libs/pcre2/src/pcre2_dfa_match.c
new file mode 100644
index 00000000000..faa3db8b57e
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_dfa_match.c
@@ -0,0 +1,4066 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This module contains the external function pcre2_dfa_match(), which is an
+alternative matching function that uses a sort of DFA algorithm (not a true
+FSM). This is NOT Perl-compatible, but it has advantages in certain
+applications. */
+
+
+/* NOTE ABOUT PERFORMANCE: A user of this function sent some code that improved
+the performance of his patterns greatly. I could not use it as it stood, as it
+was not thread safe, and made assumptions about pattern sizes. Also, it caused
+test 7 to loop, and test 9 to crash with a segfault.
+
+The issue is the check for duplicate states, which is done by a simple linear
+search up the state list. (Grep for "duplicate" below to find the code.) For
+many patterns, there will never be many states active at one time, so a simple
+linear search is fine. In patterns that have many active states, it might be a
+bottleneck. The suggested code used an indexing scheme to remember which states
+had previously been used for each character, and avoided the linear search when
+it knew there was no chance of a duplicate. This was implemented when adding
+states to the state lists.
+
+I wrote some thread-safe, not-limited code to try something similar at the time
+of checking for duplicates (instead of when adding states), using index vectors
+on the stack. It did give a 13% improvement with one specially constructed
+pattern for certain subject strings, but on other strings and on many of the
+simpler patterns in the test suite it did worse. The major problem, I think,
+was the extra time to initialize the index. This had to be done for each call
+of internal_dfa_match(). (The supplied patch used a static vector, initialized
+only once - I suspect this was the cause of the problems with the tests.)
+
+Overall, I concluded that the gains in some cases did not outweigh the losses
+in others, so I abandoned this code. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#define NLBLOCK mb /* Block containing newline information */
+#define PSSTART start_subject /* Field containing processed string start */
+#define PSEND end_subject /* Field containing processed string end */
+
+#include "pcre2_internal.h"
+
+#define PUBLIC_DFA_MATCH_OPTIONS \
+ (PCRE2_ANCHORED|PCRE2_ENDANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \
+ PCRE2_NOTEMPTY_ATSTART|PCRE2_NO_UTF_CHECK|PCRE2_PARTIAL_HARD| \
+ PCRE2_PARTIAL_SOFT|PCRE2_DFA_SHORTEST|PCRE2_DFA_RESTART| \
+ PCRE2_COPY_MATCHED_SUBJECT)
+
+
+/*************************************************
+* Code parameters and static tables *
+*************************************************/
+
+/* These are offsets that are used to turn the OP_TYPESTAR and friends opcodes
+into others, under special conditions. A gap of 20 between the blocks should be
+enough. The resulting opcodes don't have to be less than 256 because they are
+never stored, so we push them well clear of the normal opcodes. */
+
+#define OP_PROP_EXTRA 300
+#define OP_EXTUNI_EXTRA 320
+#define OP_ANYNL_EXTRA 340
+#define OP_HSPACE_EXTRA 360
+#define OP_VSPACE_EXTRA 380
+
+
+/* This table identifies those opcodes that are followed immediately by a
+character that is to be tested in some way. This makes it possible to
+centralize the loading of these characters. In the case of Type * etc, the
+"character" is the opcode for \D, \d, \S, \s, \W, or \w, which will always be a
+small value. Non-zero values in the table are the offsets from the opcode where
+the character is to be found. ***NOTE*** If the start of this table is
+modified, the three tables that follow must also be modified. */
+
+static const uint8_t coptable[] = {
+ 0, /* End */
+ 0, 0, 0, 0, 0, /* \A, \G, \K, \B, \b */
+ 0, 0, 0, 0, 0, 0, /* \D, \d, \S, \s, \W, \w */
+ 0, 0, 0, /* Any, AllAny, Anybyte */
+ 0, 0, /* \P, \p */
+ 0, 0, 0, 0, 0, /* \R, \H, \h, \V, \v */
+ 0, /* \X */
+ 0, 0, 0, 0, 0, 0, /* \Z, \z, $, $M, ^, ^M */
+ 1, /* Char */
+ 1, /* Chari */
+ 1, /* not */
+ 1, /* noti */
+ /* Positive single-char repeats */
+ 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */
+ 1+IMM2_SIZE, 1+IMM2_SIZE, /* upto, minupto */
+ 1+IMM2_SIZE, /* exact */
+ 1, 1, 1, 1+IMM2_SIZE, /* *+, ++, ?+, upto+ */
+ 1, 1, 1, 1, 1, 1, /* *I, *?I, +I, +?I, ?I, ??I */
+ 1+IMM2_SIZE, 1+IMM2_SIZE, /* upto I, minupto I */
+ 1+IMM2_SIZE, /* exact I */
+ 1, 1, 1, 1+IMM2_SIZE, /* *+I, ++I, ?+I, upto+I */
+ /* Negative single-char repeats - only for chars < 256 */
+ 1, 1, 1, 1, 1, 1, /* NOT *, *?, +, +?, ?, ?? */
+ 1+IMM2_SIZE, 1+IMM2_SIZE, /* NOT upto, minupto */
+ 1+IMM2_SIZE, /* NOT exact */
+ 1, 1, 1, 1+IMM2_SIZE, /* NOT *+, ++, ?+, upto+ */
+ 1, 1, 1, 1, 1, 1, /* NOT *I, *?I, +I, +?I, ?I, ??I */
+ 1+IMM2_SIZE, 1+IMM2_SIZE, /* NOT upto I, minupto I */
+ 1+IMM2_SIZE, /* NOT exact I */
+ 1, 1, 1, 1+IMM2_SIZE, /* NOT *+I, ++I, ?+I, upto+I */
+ /* Positive type repeats */
+ 1, 1, 1, 1, 1, 1, /* Type *, *?, +, +?, ?, ?? */
+ 1+IMM2_SIZE, 1+IMM2_SIZE, /* Type upto, minupto */
+ 1+IMM2_SIZE, /* Type exact */
+ 1, 1, 1, 1+IMM2_SIZE, /* Type *+, ++, ?+, upto+ */
+ /* Character class & ref repeats */
+ 0, 0, 0, 0, 0, 0, /* *, *?, +, +?, ?, ?? */
+ 0, 0, /* CRRANGE, CRMINRANGE */
+ 0, 0, 0, 0, /* Possessive *+, ++, ?+, CRPOSRANGE */
+ 0, /* CLASS */
+ 0, /* NCLASS */
+ 0, /* XCLASS - variable length */
+ 0, /* REF */
+ 0, /* REFI */
+ 0, /* DNREF */
+ 0, /* DNREFI */
+ 0, /* RECURSE */
+ 0, /* CALLOUT */
+ 0, /* CALLOUT_STR */
+ 0, /* Alt */
+ 0, /* Ket */
+ 0, /* KetRmax */
+ 0, /* KetRmin */
+ 0, /* KetRpos */
+ 0, /* Reverse */
+ 0, /* Assert */
+ 0, /* Assert not */
+ 0, /* Assert behind */
+ 0, /* Assert behind not */
+ 0, /* NA assert */
+ 0, /* NA assert behind */
+ 0, /* ONCE */
+ 0, /* SCRIPT_RUN */
+ 0, 0, 0, 0, 0, /* BRA, BRAPOS, CBRA, CBRAPOS, COND */
+ 0, 0, 0, 0, 0, /* SBRA, SBRAPOS, SCBRA, SCBRAPOS, SCOND */
+ 0, 0, /* CREF, DNCREF */
+ 0, 0, /* RREF, DNRREF */
+ 0, 0, /* FALSE, TRUE */
+ 0, 0, 0, /* BRAZERO, BRAMINZERO, BRAPOSZERO */
+ 0, 0, 0, /* MARK, PRUNE, PRUNE_ARG */
+ 0, 0, 0, 0, /* SKIP, SKIP_ARG, THEN, THEN_ARG */
+ 0, 0, /* COMMIT, COMMIT_ARG */
+ 0, 0, 0, /* FAIL, ACCEPT, ASSERT_ACCEPT */
+ 0, 0, 0 /* CLOSE, SKIPZERO, DEFINE */
+};
+
+/* This table identifies those opcodes that inspect a character. It is used to
+remember the fact that a character could have been inspected when the end of
+the subject is reached. ***NOTE*** If the start of this table is modified, the
+two tables that follow must also be modified. */
+
+static const uint8_t poptable[] = {
+ 0, /* End */
+ 0, 0, 0, 1, 1, /* \A, \G, \K, \B, \b */
+ 1, 1, 1, 1, 1, 1, /* \D, \d, \S, \s, \W, \w */
+ 1, 1, 1, /* Any, AllAny, Anybyte */
+ 1, 1, /* \P, \p */
+ 1, 1, 1, 1, 1, /* \R, \H, \h, \V, \v */
+ 1, /* \X */
+ 0, 0, 0, 0, 0, 0, /* \Z, \z, $, $M, ^, ^M */
+ 1, /* Char */
+ 1, /* Chari */
+ 1, /* not */
+ 1, /* noti */
+ /* Positive single-char repeats */
+ 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */
+ 1, 1, 1, /* upto, minupto, exact */
+ 1, 1, 1, 1, /* *+, ++, ?+, upto+ */
+ 1, 1, 1, 1, 1, 1, /* *I, *?I, +I, +?I, ?I, ??I */
+ 1, 1, 1, /* upto I, minupto I, exact I */
+ 1, 1, 1, 1, /* *+I, ++I, ?+I, upto+I */
+ /* Negative single-char repeats - only for chars < 256 */
+ 1, 1, 1, 1, 1, 1, /* NOT *, *?, +, +?, ?, ?? */
+ 1, 1, 1, /* NOT upto, minupto, exact */
+ 1, 1, 1, 1, /* NOT *+, ++, ?+, upto+ */
+ 1, 1, 1, 1, 1, 1, /* NOT *I, *?I, +I, +?I, ?I, ??I */
+ 1, 1, 1, /* NOT upto I, minupto I, exact I */
+ 1, 1, 1, 1, /* NOT *+I, ++I, ?+I, upto+I */
+ /* Positive type repeats */
+ 1, 1, 1, 1, 1, 1, /* Type *, *?, +, +?, ?, ?? */
+ 1, 1, 1, /* Type upto, minupto, exact */
+ 1, 1, 1, 1, /* Type *+, ++, ?+, upto+ */
+ /* Character class & ref repeats */
+ 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */
+ 1, 1, /* CRRANGE, CRMINRANGE */
+ 1, 1, 1, 1, /* Possessive *+, ++, ?+, CRPOSRANGE */
+ 1, /* CLASS */
+ 1, /* NCLASS */
+ 1, /* XCLASS - variable length */
+ 0, /* REF */
+ 0, /* REFI */
+ 0, /* DNREF */
+ 0, /* DNREFI */
+ 0, /* RECURSE */
+ 0, /* CALLOUT */
+ 0, /* CALLOUT_STR */
+ 0, /* Alt */
+ 0, /* Ket */
+ 0, /* KetRmax */
+ 0, /* KetRmin */
+ 0, /* KetRpos */
+ 0, /* Reverse */
+ 0, /* Assert */
+ 0, /* Assert not */
+ 0, /* Assert behind */
+ 0, /* Assert behind not */
+ 0, /* NA assert */
+ 0, /* NA assert behind */
+ 0, /* ONCE */
+ 0, /* SCRIPT_RUN */
+ 0, 0, 0, 0, 0, /* BRA, BRAPOS, CBRA, CBRAPOS, COND */
+ 0, 0, 0, 0, 0, /* SBRA, SBRAPOS, SCBRA, SCBRAPOS, SCOND */
+ 0, 0, /* CREF, DNCREF */
+ 0, 0, /* RREF, DNRREF */
+ 0, 0, /* FALSE, TRUE */
+ 0, 0, 0, /* BRAZERO, BRAMINZERO, BRAPOSZERO */
+ 0, 0, 0, /* MARK, PRUNE, PRUNE_ARG */
+ 0, 0, 0, 0, /* SKIP, SKIP_ARG, THEN, THEN_ARG */
+ 0, 0, /* COMMIT, COMMIT_ARG */
+ 0, 0, 0, /* FAIL, ACCEPT, ASSERT_ACCEPT */
+ 0, 0, 0 /* CLOSE, SKIPZERO, DEFINE */
+};
+
+/* These 2 tables allow for compact code for testing for \D, \d, \S, \s, \W,
+and \w */
+
+static const uint8_t toptable1[] = {
+ 0, 0, 0, 0, 0, 0,
+ ctype_digit, ctype_digit,
+ ctype_space, ctype_space,
+ ctype_word, ctype_word,
+ 0, 0 /* OP_ANY, OP_ALLANY */
+};
+
+static const uint8_t toptable2[] = {
+ 0, 0, 0, 0, 0, 0,
+ ctype_digit, 0,
+ ctype_space, 0,
+ ctype_word, 0,
+ 1, 1 /* OP_ANY, OP_ALLANY */
+};
+
+
+/* Structure for holding data about a particular state, which is in effect the
+current data for an active path through the match tree. It must consist
+entirely of ints because the working vector we are passed, and which we put
+these structures in, is a vector of ints. */
+
+typedef struct stateblock {
+ int offset; /* Offset to opcode (-ve has meaning) */
+ int count; /* Count for repeats */
+ int data; /* Some use extra data */
+} stateblock;
+
+#define INTS_PER_STATEBLOCK (int)(sizeof(stateblock)/sizeof(int))
+
+
+/* Before version 10.32 the recursive calls of internal_dfa_match() were passed
+local working space and output vectors that were created on the stack. This has
+caused issues for some patterns, especially in small-stack environments such as
+Windows. A new scheme is now in use which sets up a vector on the stack, but if
+this is too small, heap memory is used, up to the heap_limit. The main
+parameters are all numbers of ints because the workspace is a vector of ints.
+
+The size of the starting stack vector, DFA_START_RWS_SIZE, is in bytes, and is
+defined in pcre2_internal.h so as to be available to pcre2test when it is
+finding the minimum heap requirement for a match. */
+
+#define OVEC_UNIT (sizeof(PCRE2_SIZE)/sizeof(int))
+
+#define RWS_BASE_SIZE (DFA_START_RWS_SIZE/sizeof(int)) /* Stack vector */
+#define RWS_RSIZE 1000 /* Work size for recursion */
+#define RWS_OVEC_RSIZE (1000*OVEC_UNIT) /* Ovector for recursion */
+#define RWS_OVEC_OSIZE (2*OVEC_UNIT) /* Ovector in other cases */
+
+/* This structure is at the start of each workspace block. */
+
+typedef struct RWS_anchor {
+ struct RWS_anchor *next;
+ uint32_t size; /* Number of ints */
+ uint32_t free; /* Number of ints */
+} RWS_anchor;
+
+#define RWS_ANCHOR_SIZE (sizeof(RWS_anchor)/sizeof(int))
+
+
+
+/*************************************************
+* Process a callout *
+*************************************************/
+
+/* This function is called to perform a callout.
+
+Arguments:
+ code current code pointer
+ offsets points to current capture offsets
+ current_subject start of current subject match
+ ptr current position in subject
+ mb the match block
+ extracode extra code offset when called from condition
+ lengthptr where to return the callout length
+
+Returns: the return from the callout
+*/
+
+static int
+do_callout_dfa(PCRE2_SPTR code, PCRE2_SIZE *offsets, PCRE2_SPTR current_subject,
+ PCRE2_SPTR ptr, dfa_match_block *mb, PCRE2_SIZE extracode,
+ PCRE2_SIZE *lengthptr)
+{
+pcre2_callout_block *cb = mb->cb;
+
+*lengthptr = (code[extracode] == OP_CALLOUT)?
+ (PCRE2_SIZE)PRIV(OP_lengths)[OP_CALLOUT] :
+ (PCRE2_SIZE)GET(code, 1 + 2*LINK_SIZE + extracode);
+
+if (mb->callout == NULL) return 0; /* No callout provided */
+
+/* Fixed fields in the callout block are set once and for all at the start of
+matching. */
+
+cb->offset_vector = offsets;
+cb->start_match = (PCRE2_SIZE)(current_subject - mb->start_subject);
+cb->current_position = (PCRE2_SIZE)(ptr - mb->start_subject);
+cb->pattern_position = GET(code, 1 + extracode);
+cb->next_item_length = GET(code, 1 + LINK_SIZE + extracode);
+
+if (code[extracode] == OP_CALLOUT)
+ {
+ cb->callout_number = code[1 + 2*LINK_SIZE + extracode];
+ cb->callout_string_offset = 0;
+ cb->callout_string = NULL;
+ cb->callout_string_length = 0;
+ }
+else
+ {
+ cb->callout_number = 0;
+ cb->callout_string_offset = GET(code, 1 + 3*LINK_SIZE + extracode);
+ cb->callout_string = code + (1 + 4*LINK_SIZE + extracode) + 1;
+ cb->callout_string_length = *lengthptr - (1 + 4*LINK_SIZE) - 2;
+ }
+
+return (mb->callout)(cb, mb->callout_data);
+}
+
+
+
+/*************************************************
+* Expand local workspace memory *
+*************************************************/
+
+/* This function is called when internal_dfa_match() is about to be called
+recursively and there is insufficient working space left in the current
+workspace block. If there's an existing next block, use it; otherwise get a new
+block unless the heap limit is reached.
+
+Arguments:
+ rwsptr pointer to block pointer (updated)
+ ovecsize space needed for an ovector
+ mb the match block
+
+Returns: 0 rwsptr has been updated
+ !0 an error code
+*/
+
+static int
+more_workspace(RWS_anchor **rwsptr, unsigned int ovecsize, dfa_match_block *mb)
+{
+RWS_anchor *rws = *rwsptr;
+RWS_anchor *new;
+
+if (rws->next != NULL)
+ {
+ new = rws->next;
+ }
+
+/* Sizes in the RWS_anchor blocks are in units of sizeof(int), but
+mb->heap_limit and mb->heap_used are in kibibytes. Play carefully, to avoid
+overflow. */
+
+else
+ {
+ uint32_t newsize = (rws->size >= UINT32_MAX/2)? UINT32_MAX/2 : rws->size * 2;
+ uint32_t newsizeK = newsize/(1024/sizeof(int));
+
+ if (newsizeK + mb->heap_used > mb->heap_limit)
+ newsizeK = (uint32_t)(mb->heap_limit - mb->heap_used);
+ newsize = newsizeK*(1024/sizeof(int));
+
+ if (newsize < RWS_RSIZE + ovecsize + RWS_ANCHOR_SIZE)
+ return PCRE2_ERROR_HEAPLIMIT;
+ new = mb->memctl.malloc(newsize*sizeof(int), mb->memctl.memory_data);
+ if (new == NULL) return PCRE2_ERROR_NOMEMORY;
+ mb->heap_used += newsizeK;
+ new->next = NULL;
+ new->size = newsize;
+ rws->next = new;
+ }
+
+new->free = new->size - RWS_ANCHOR_SIZE;
+*rwsptr = new;
+return 0;
+}
+
+
+
+/*************************************************
+* Match a Regular Expression - DFA engine *
+*************************************************/
+
+/* This internal function applies a compiled pattern to a subject string,
+starting at a given point, using a DFA engine. This function is called from the
+external one, possibly multiple times if the pattern is not anchored. The
+function calls itself recursively for some kinds of subpattern.
+
+Arguments:
+ mb the match_data block with fixed information
+ this_start_code the opening bracket of this subexpression's code
+ current_subject where we currently are in the subject string
+ start_offset start offset in the subject string
+ offsets vector to contain the matching string offsets
+ offsetcount size of same
+ workspace vector of workspace
+ wscount size of same
+ rlevel function call recursion level
+
+Returns: > 0 => number of match offset pairs placed in offsets
+ = 0 => offsets overflowed; longest matches are present
+ -1 => failed to match
+ < -1 => some kind of unexpected problem
+
+The following macros are used for adding states to the two state vectors (one
+for the current character, one for the following character). */
+
+#define ADD_ACTIVE(x,y) \
+ if (active_count++ < wscount) \
+ { \
+ next_active_state->offset = (x); \
+ next_active_state->count = (y); \
+ next_active_state++; \
+ } \
+ else return PCRE2_ERROR_DFA_WSSIZE
+
+#define ADD_ACTIVE_DATA(x,y,z) \
+ if (active_count++ < wscount) \
+ { \
+ next_active_state->offset = (x); \
+ next_active_state->count = (y); \
+ next_active_state->data = (z); \
+ next_active_state++; \
+ } \
+ else return PCRE2_ERROR_DFA_WSSIZE
+
+#define ADD_NEW(x,y) \
+ if (new_count++ < wscount) \
+ { \
+ next_new_state->offset = (x); \
+ next_new_state->count = (y); \
+ next_new_state++; \
+ } \
+ else return PCRE2_ERROR_DFA_WSSIZE
+
+#define ADD_NEW_DATA(x,y,z) \
+ if (new_count++ < wscount) \
+ { \
+ next_new_state->offset = (x); \
+ next_new_state->count = (y); \
+ next_new_state->data = (z); \
+ next_new_state++; \
+ } \
+ else return PCRE2_ERROR_DFA_WSSIZE
+
+/* And now, here is the code */
+
+static int
+internal_dfa_match(
+ dfa_match_block *mb,
+ PCRE2_SPTR this_start_code,
+ PCRE2_SPTR current_subject,
+ PCRE2_SIZE start_offset,
+ PCRE2_SIZE *offsets,
+ uint32_t offsetcount,
+ int *workspace,
+ int wscount,
+ uint32_t rlevel,
+ int *RWS)
+{
+stateblock *active_states, *new_states, *temp_states;
+stateblock *next_active_state, *next_new_state;
+const uint8_t *ctypes, *lcc, *fcc;
+PCRE2_SPTR ptr;
+PCRE2_SPTR end_code;
+dfa_recursion_info new_recursive;
+int active_count, new_count, match_count;
+
+/* Some fields in the mb block are frequently referenced, so we load them into
+independent variables in the hope that this will perform better. */
+
+PCRE2_SPTR start_subject = mb->start_subject;
+PCRE2_SPTR end_subject = mb->end_subject;
+PCRE2_SPTR start_code = mb->start_code;
+
+#ifdef SUPPORT_UNICODE
+BOOL utf = (mb->poptions & PCRE2_UTF) != 0;
+BOOL utf_or_ucp = utf || (mb->poptions & PCRE2_UCP) != 0;
+#else
+BOOL utf = FALSE;
+#endif
+
+BOOL reset_could_continue = FALSE;
+
+if (mb->match_call_count++ >= mb->match_limit) return PCRE2_ERROR_MATCHLIMIT;
+if (rlevel++ > mb->match_limit_depth) return PCRE2_ERROR_DEPTHLIMIT;
+offsetcount &= (uint32_t)(-2); /* Round down */
+
+wscount -= 2;
+wscount = (wscount - (wscount % (INTS_PER_STATEBLOCK * 2))) /
+ (2 * INTS_PER_STATEBLOCK);
+
+ctypes = mb->tables + ctypes_offset;
+lcc = mb->tables + lcc_offset;
+fcc = mb->tables + fcc_offset;
+
+match_count = PCRE2_ERROR_NOMATCH; /* A negative number */
+
+active_states = (stateblock *)(workspace + 2);
+next_new_state = new_states = active_states + wscount;
+new_count = 0;
+
+/* The first thing in any (sub) pattern is a bracket of some sort. Push all
+the alternative states onto the list, and find out where the end is. This
+makes is possible to use this function recursively, when we want to stop at a
+matching internal ket rather than at the end.
+
+If we are dealing with a backward assertion we have to find out the maximum
+amount to move back, and set up each alternative appropriately. */
+
+if (*this_start_code == OP_ASSERTBACK || *this_start_code == OP_ASSERTBACK_NOT)
+ {
+ size_t max_back = 0;
+ size_t gone_back;
+
+ end_code = this_start_code;
+ do
+ {
+ size_t back = (size_t)GET(end_code, 2+LINK_SIZE);
+ if (back > max_back) max_back = back;
+ end_code += GET(end_code, 1);
+ }
+ while (*end_code == OP_ALT);
+
+ /* If we can't go back the amount required for the longest lookbehind
+ pattern, go back as far as we can; some alternatives may still be viable. */
+
+#ifdef SUPPORT_UNICODE
+ /* In character mode we have to step back character by character */
+
+ if (utf)
+ {
+ for (gone_back = 0; gone_back < max_back; gone_back++)
+ {
+ if (current_subject <= start_subject) break;
+ current_subject--;
+ ACROSSCHAR(current_subject > start_subject, current_subject,
+ current_subject--);
+ }
+ }
+ else
+#endif
+
+ /* In byte-mode we can do this quickly. */
+
+ {
+ size_t current_offset = (size_t)(current_subject - start_subject);
+ gone_back = (current_offset < max_back)? current_offset : max_back;
+ current_subject -= gone_back;
+ }
+
+ /* Save the earliest consulted character */
+
+ if (current_subject < mb->start_used_ptr)
+ mb->start_used_ptr = current_subject;
+
+ /* Now we can process the individual branches. There will be an OP_REVERSE at
+ the start of each branch, except when the length of the branch is zero. */
+
+ end_code = this_start_code;
+ do
+ {
+ uint32_t revlen = (end_code[1+LINK_SIZE] == OP_REVERSE)? 1 + LINK_SIZE : 0;
+ size_t back = (revlen == 0)? 0 : (size_t)GET(end_code, 2+LINK_SIZE);
+ if (back <= gone_back)
+ {
+ int bstate = (int)(end_code - start_code + 1 + LINK_SIZE + revlen);
+ ADD_NEW_DATA(-bstate, 0, (int)(gone_back - back));
+ }
+ end_code += GET(end_code, 1);
+ }
+ while (*end_code == OP_ALT);
+ }
+
+/* This is the code for a "normal" subpattern (not a backward assertion). The
+start of a whole pattern is always one of these. If we are at the top level,
+we may be asked to restart matching from the same point that we reached for a
+previous partial match. We still have to scan through the top-level branches to
+find the end state. */
+
+else
+ {
+ end_code = this_start_code;
+
+ /* Restarting */
+
+ if (rlevel == 1 && (mb->moptions & PCRE2_DFA_RESTART) != 0)
+ {
+ do { end_code += GET(end_code, 1); } while (*end_code == OP_ALT);
+ new_count = workspace[1];
+ if (!workspace[0])
+ memcpy(new_states, active_states, (size_t)new_count * sizeof(stateblock));
+ }
+
+ /* Not restarting */
+
+ else
+ {
+ int length = 1 + LINK_SIZE +
+ ((*this_start_code == OP_CBRA || *this_start_code == OP_SCBRA ||
+ *this_start_code == OP_CBRAPOS || *this_start_code == OP_SCBRAPOS)
+ ? IMM2_SIZE:0);
+ do
+ {
+ ADD_NEW((int)(end_code - start_code + length), 0);
+ end_code += GET(end_code, 1);
+ length = 1 + LINK_SIZE;
+ }
+ while (*end_code == OP_ALT);
+ }
+ }
+
+workspace[0] = 0; /* Bit indicating which vector is current */
+
+/* Loop for scanning the subject */
+
+ptr = current_subject;
+for (;;)
+ {
+ int i, j;
+ int clen, dlen;
+ uint32_t c, d;
+ int forced_fail = 0;
+ BOOL partial_newline = FALSE;
+ BOOL could_continue = reset_could_continue;
+ reset_could_continue = FALSE;
+
+ if (ptr > mb->last_used_ptr) mb->last_used_ptr = ptr;
+
+ /* Make the new state list into the active state list and empty the
+ new state list. */
+
+ temp_states = active_states;
+ active_states = new_states;
+ new_states = temp_states;
+ active_count = new_count;
+ new_count = 0;
+
+ workspace[0] ^= 1; /* Remember for the restarting feature */
+ workspace[1] = active_count;
+
+ /* Set the pointers for adding new states */
+
+ next_active_state = active_states + active_count;
+ next_new_state = new_states;
+
+ /* Load the current character from the subject outside the loop, as many
+ different states may want to look at it, and we assume that at least one
+ will. */
+
+ if (ptr < end_subject)
+ {
+ clen = 1; /* Number of data items in the character */
+#ifdef SUPPORT_UNICODE
+ GETCHARLENTEST(c, ptr, clen);
+#else
+ c = *ptr;
+#endif /* SUPPORT_UNICODE */
+ }
+ else
+ {
+ clen = 0; /* This indicates the end of the subject */
+ c = NOTACHAR; /* This value should never actually be used */
+ }
+
+ /* Scan up the active states and act on each one. The result of an action
+ may be to add more states to the currently active list (e.g. on hitting a
+ parenthesis) or it may be to put states on the new list, for considering
+ when we move the character pointer on. */
+
+ for (i = 0; i < active_count; i++)
+ {
+ stateblock *current_state = active_states + i;
+ BOOL caseless = FALSE;
+ PCRE2_SPTR code;
+ uint32_t codevalue;
+ int state_offset = current_state->offset;
+ int rrc;
+ int count;
+
+ /* A negative offset is a special case meaning "hold off going to this
+ (negated) state until the number of characters in the data field have
+ been skipped". If the could_continue flag was passed over from a previous
+ state, arrange for it to passed on. */
+
+ if (state_offset < 0)
+ {
+ if (current_state->data > 0)
+ {
+ ADD_NEW_DATA(state_offset, current_state->count,
+ current_state->data - 1);
+ if (could_continue) reset_could_continue = TRUE;
+ continue;
+ }
+ else
+ {
+ current_state->offset = state_offset = -state_offset;
+ }
+ }
+
+ /* Check for a duplicate state with the same count, and skip if found.
+ See the note at the head of this module about the possibility of improving
+ performance here. */
+
+ for (j = 0; j < i; j++)
+ {
+ if (active_states[j].offset == state_offset &&
+ active_states[j].count == current_state->count)
+ goto NEXT_ACTIVE_STATE;
+ }
+
+ /* The state offset is the offset to the opcode */
+
+ code = start_code + state_offset;
+ codevalue = *code;
+
+ /* If this opcode inspects a character, but we are at the end of the
+ subject, remember the fact for use when testing for a partial match. */
+
+ if (clen == 0 && poptable[codevalue] != 0)
+ could_continue = TRUE;
+
+ /* If this opcode is followed by an inline character, load it. It is
+ tempting to test for the presence of a subject character here, but that
+ is wrong, because sometimes zero repetitions of the subject are
+ permitted.
+
+ We also use this mechanism for opcodes such as OP_TYPEPLUS that take an
+ argument that is not a data character - but is always one byte long because
+ the values are small. We have to take special action to deal with \P, \p,
+ \H, \h, \V, \v and \X in this case. To keep the other cases fast, convert
+ these ones to new opcodes. */
+
+ if (coptable[codevalue] > 0)
+ {
+ dlen = 1;
+#ifdef SUPPORT_UNICODE
+ if (utf) { GETCHARLEN(d, (code + coptable[codevalue]), dlen); } else
+#endif /* SUPPORT_UNICODE */
+ d = code[coptable[codevalue]];
+ if (codevalue >= OP_TYPESTAR)
+ {
+ switch(d)
+ {
+ case OP_ANYBYTE: return PCRE2_ERROR_DFA_UITEM;
+ case OP_NOTPROP:
+ case OP_PROP: codevalue += OP_PROP_EXTRA; break;
+ case OP_ANYNL: codevalue += OP_ANYNL_EXTRA; break;
+ case OP_EXTUNI: codevalue += OP_EXTUNI_EXTRA; break;
+ case OP_NOT_HSPACE:
+ case OP_HSPACE: codevalue += OP_HSPACE_EXTRA; break;
+ case OP_NOT_VSPACE:
+ case OP_VSPACE: codevalue += OP_VSPACE_EXTRA; break;
+ default: break;
+ }
+ }
+ }
+ else
+ {
+ dlen = 0; /* Not strictly necessary, but compilers moan */
+ d = NOTACHAR; /* if these variables are not set. */
+ }
+
+
+ /* Now process the individual opcodes */
+
+ switch (codevalue)
+ {
+/* ========================================================================== */
+ /* These cases are never obeyed. This is a fudge that causes a compile-
+ time error if the vectors coptable or poptable, which are indexed by
+ opcode, are not the correct length. It seems to be the only way to do
+ such a check at compile time, as the sizeof() operator does not work
+ in the C preprocessor. */
+
+ case OP_TABLE_LENGTH:
+ case OP_TABLE_LENGTH +
+ ((sizeof(coptable) == OP_TABLE_LENGTH) &&
+ (sizeof(poptable) == OP_TABLE_LENGTH)):
+ return 0;
+
+/* ========================================================================== */
+ /* Reached a closing bracket. If not at the end of the pattern, carry
+ on with the next opcode. For repeating opcodes, also add the repeat
+ state. Note that KETRPOS will always be encountered at the end of the
+ subpattern, because the possessive subpattern repeats are always handled
+ using recursive calls. Thus, it never adds any new states.
+
+ At the end of the (sub)pattern, unless we have an empty string and
+ PCRE2_NOTEMPTY is set, or PCRE2_NOTEMPTY_ATSTART is set and we are at the
+ start of the subject, save the match data, shifting up all previous
+ matches so we always have the longest first. */
+
+ case OP_KET:
+ case OP_KETRMIN:
+ case OP_KETRMAX:
+ case OP_KETRPOS:
+ if (code != end_code)
+ {
+ ADD_ACTIVE(state_offset + 1 + LINK_SIZE, 0);
+ if (codevalue != OP_KET)
+ {
+ ADD_ACTIVE(state_offset - (int)GET(code, 1), 0);
+ }
+ }
+ else
+ {
+ if (ptr > current_subject ||
+ ((mb->moptions & PCRE2_NOTEMPTY) == 0 &&
+ ((mb->moptions & PCRE2_NOTEMPTY_ATSTART) == 0 ||
+ current_subject > start_subject + mb->start_offset)))
+ {
+ if (match_count < 0) match_count = (offsetcount >= 2)? 1 : 0;
+ else if (match_count > 0 && ++match_count * 2 > (int)offsetcount)
+ match_count = 0;
+ count = ((match_count == 0)? (int)offsetcount : match_count * 2) - 2;
+ if (count > 0) (void)memmove(offsets + 2, offsets,
+ (size_t)count * sizeof(PCRE2_SIZE));
+ if (offsetcount >= 2)
+ {
+ offsets[0] = (PCRE2_SIZE)(current_subject - start_subject);
+ offsets[1] = (PCRE2_SIZE)(ptr - start_subject);
+ }
+ if ((mb->moptions & PCRE2_DFA_SHORTEST) != 0) return match_count;
+ }
+ }
+ break;
+
+/* ========================================================================== */
+ /* These opcodes add to the current list of states without looking
+ at the current character. */
+
+ /*-----------------------------------------------------------------*/
+ case OP_ALT:
+ do { code += GET(code, 1); } while (*code == OP_ALT);
+ ADD_ACTIVE((int)(code - start_code), 0);
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_BRA:
+ case OP_SBRA:
+ do
+ {
+ ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0);
+ code += GET(code, 1);
+ }
+ while (*code == OP_ALT);
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_CBRA:
+ case OP_SCBRA:
+ ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE + IMM2_SIZE), 0);
+ code += GET(code, 1);
+ while (*code == OP_ALT)
+ {
+ ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0);
+ code += GET(code, 1);
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_BRAZERO:
+ case OP_BRAMINZERO:
+ ADD_ACTIVE(state_offset + 1, 0);
+ code += 1 + GET(code, 2);
+ while (*code == OP_ALT) code += GET(code, 1);
+ ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0);
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_SKIPZERO:
+ code += 1 + GET(code, 2);
+ while (*code == OP_ALT) code += GET(code, 1);
+ ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0);
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_CIRC:
+ if (ptr == start_subject && (mb->moptions & PCRE2_NOTBOL) == 0)
+ { ADD_ACTIVE(state_offset + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_CIRCM:
+ if ((ptr == start_subject && (mb->moptions & PCRE2_NOTBOL) == 0) ||
+ ((ptr != end_subject || (mb->poptions & PCRE2_ALT_CIRCUMFLEX) != 0 )
+ && WAS_NEWLINE(ptr)))
+ { ADD_ACTIVE(state_offset + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_EOD:
+ if (ptr >= end_subject)
+ {
+ if ((mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ return PCRE2_ERROR_PARTIAL;
+ else { ADD_ACTIVE(state_offset + 1, 0); }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_SOD:
+ if (ptr == start_subject) { ADD_ACTIVE(state_offset + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_SOM:
+ if (ptr == start_subject + start_offset) { ADD_ACTIVE(state_offset + 1, 0); }
+ break;
+
+
+/* ========================================================================== */
+ /* These opcodes inspect the next subject character, and sometimes
+ the previous one as well, but do not have an argument. The variable
+ clen contains the length of the current character and is zero if we are
+ at the end of the subject. */
+
+ /*-----------------------------------------------------------------*/
+ case OP_ANY:
+ if (clen > 0 && !IS_NEWLINE(ptr))
+ {
+ if (ptr + 1 >= mb->end_subject &&
+ (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ c == NLBLOCK->nl[0])
+ {
+ could_continue = partial_newline = TRUE;
+ }
+ else
+ {
+ ADD_NEW(state_offset + 1, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_ALLANY:
+ if (clen > 0)
+ { ADD_NEW(state_offset + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_EODN:
+ if (clen == 0 || (IS_NEWLINE(ptr) && ptr == end_subject - mb->nllen))
+ {
+ if ((mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ return PCRE2_ERROR_PARTIAL;
+ ADD_ACTIVE(state_offset + 1, 0);
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_DOLL:
+ if ((mb->moptions & PCRE2_NOTEOL) == 0)
+ {
+ if (clen == 0 && (mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ could_continue = TRUE;
+ else if (clen == 0 ||
+ ((mb->poptions & PCRE2_DOLLAR_ENDONLY) == 0 && IS_NEWLINE(ptr) &&
+ (ptr == end_subject - mb->nllen)
+ ))
+ { ADD_ACTIVE(state_offset + 1, 0); }
+ else if (ptr + 1 >= mb->end_subject &&
+ (mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ c == NLBLOCK->nl[0])
+ {
+ if ((mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ {
+ reset_could_continue = TRUE;
+ ADD_NEW_DATA(-(state_offset + 1), 0, 1);
+ }
+ else could_continue = partial_newline = TRUE;
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_DOLLM:
+ if ((mb->moptions & PCRE2_NOTEOL) == 0)
+ {
+ if (clen == 0 && (mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ could_continue = TRUE;
+ else if (clen == 0 ||
+ ((mb->poptions & PCRE2_DOLLAR_ENDONLY) == 0 && IS_NEWLINE(ptr)))
+ { ADD_ACTIVE(state_offset + 1, 0); }
+ else if (ptr + 1 >= mb->end_subject &&
+ (mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ c == NLBLOCK->nl[0])
+ {
+ if ((mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ {
+ reset_could_continue = TRUE;
+ ADD_NEW_DATA(-(state_offset + 1), 0, 1);
+ }
+ else could_continue = partial_newline = TRUE;
+ }
+ }
+ else if (IS_NEWLINE(ptr))
+ { ADD_ACTIVE(state_offset + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+
+ case OP_DIGIT:
+ case OP_WHITESPACE:
+ case OP_WORDCHAR:
+ if (clen > 0 && c < 256 &&
+ ((ctypes[c] & toptable1[codevalue]) ^ toptable2[codevalue]) != 0)
+ { ADD_NEW(state_offset + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_NOT_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ if (clen > 0 && (c >= 256 ||
+ ((ctypes[c] & toptable1[codevalue]) ^ toptable2[codevalue]) != 0))
+ { ADD_NEW(state_offset + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_WORD_BOUNDARY:
+ case OP_NOT_WORD_BOUNDARY:
+ {
+ int left_word, right_word;
+
+ if (ptr > start_subject)
+ {
+ PCRE2_SPTR temp = ptr - 1;
+ if (temp < mb->start_used_ptr) mb->start_used_ptr = temp;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (utf) { BACKCHAR(temp); }
+#endif
+ GETCHARTEST(d, temp);
+#ifdef SUPPORT_UNICODE
+ if ((mb->poptions & PCRE2_UCP) != 0)
+ {
+ if (d == '_') left_word = TRUE; else
+ {
+ uint32_t cat = UCD_CATEGORY(d);
+ left_word = (cat == ucp_L || cat == ucp_N);
+ }
+ }
+ else
+#endif
+ left_word = d < 256 && (ctypes[d] & ctype_word) != 0;
+ }
+ else left_word = FALSE;
+
+ if (clen > 0)
+ {
+ if (ptr >= mb->last_used_ptr)
+ {
+ PCRE2_SPTR temp = ptr + 1;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (utf) { FORWARDCHARTEST(temp, mb->end_subject); }
+#endif
+ mb->last_used_ptr = temp;
+ }
+#ifdef SUPPORT_UNICODE
+ if ((mb->poptions & PCRE2_UCP) != 0)
+ {
+ if (c == '_') right_word = TRUE; else
+ {
+ uint32_t cat = UCD_CATEGORY(c);
+ right_word = (cat == ucp_L || cat == ucp_N);
+ }
+ }
+ else
+#endif
+ right_word = c < 256 && (ctypes[c] & ctype_word) != 0;
+ }
+ else right_word = FALSE;
+
+ if ((left_word == right_word) == (codevalue == OP_NOT_WORD_BOUNDARY))
+ { ADD_ACTIVE(state_offset + 1, 0); }
+ }
+ break;
+
+
+ /*-----------------------------------------------------------------*/
+ /* Check the next character by Unicode property. We will get here only
+ if the support is in the binary; otherwise a compile-time error occurs.
+ */
+
+#ifdef SUPPORT_UNICODE
+ case OP_PROP:
+ case OP_NOTPROP:
+ if (clen > 0)
+ {
+ BOOL OK;
+ const uint32_t *cp;
+ const ucd_record * prop = GET_UCD(c);
+ switch(code[1])
+ {
+ case PT_ANY:
+ OK = TRUE;
+ break;
+
+ case PT_LAMP:
+ OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll ||
+ prop->chartype == ucp_Lt;
+ break;
+
+ case PT_GC:
+ OK = PRIV(ucp_gentype)[prop->chartype] == code[2];
+ break;
+
+ case PT_PC:
+ OK = prop->chartype == code[2];
+ break;
+
+ case PT_SC:
+ OK = prop->script == code[2];
+ break;
+
+ case PT_SCX:
+ OK = (prop->script == code[2] ||
+ MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), code[2]) != 0);
+ break;
+
+ /* These are specials for combination cases. */
+
+ case PT_ALNUM:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N;
+ break;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ switch(c)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z;
+ break;
+ }
+ break;
+
+ case PT_WORD:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N ||
+ c == CHAR_UNDERSCORE;
+ break;
+
+ case PT_CLIST:
+ cp = PRIV(ucd_caseless_sets) + code[2];
+ for (;;)
+ {
+ if (c < *cp) { OK = FALSE; break; }
+ if (c == *cp++) { OK = TRUE; break; }
+ }
+ break;
+
+ case PT_UCNC:
+ OK = c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT ||
+ c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) ||
+ c >= 0xe000;
+ break;
+
+ case PT_BIDICL:
+ OK = UCD_BIDICLASS(c) == code[2];
+ break;
+
+ case PT_BOOL:
+ OK = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), code[2]) != 0;
+ break;
+
+ /* Should never occur, but keep compilers from grumbling. */
+
+ default:
+ OK = codevalue != OP_PROP;
+ break;
+ }
+
+ if (OK == (codevalue == OP_PROP)) { ADD_NEW(state_offset + 3, 0); }
+ }
+ break;
+#endif
+
+
+
+/* ========================================================================== */
+ /* These opcodes likewise inspect the subject character, but have an
+ argument that is not a data character. It is one of these opcodes:
+ OP_ANY, OP_ALLANY, OP_DIGIT, OP_NOT_DIGIT, OP_WHITESPACE, OP_NOT_SPACE,
+ OP_WORDCHAR, OP_NOT_WORDCHAR. The value is loaded into d. */
+
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEPOSPLUS:
+ count = current_state->count; /* Already matched */
+ if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); }
+ if (clen > 0)
+ {
+ if (d == OP_ANY && ptr + 1 >= mb->end_subject &&
+ (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ c == NLBLOCK->nl[0])
+ {
+ could_continue = partial_newline = TRUE;
+ }
+ else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) ||
+ (c < 256 &&
+ (d != OP_ANY || !IS_NEWLINE(ptr)) &&
+ ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0))
+ {
+ if (count > 0 && codevalue == OP_TYPEPOSPLUS)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ count++;
+ ADD_NEW(state_offset, count);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEPOSQUERY:
+ ADD_ACTIVE(state_offset + 2, 0);
+ if (clen > 0)
+ {
+ if (d == OP_ANY && ptr + 1 >= mb->end_subject &&
+ (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ c == NLBLOCK->nl[0])
+ {
+ could_continue = partial_newline = TRUE;
+ }
+ else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) ||
+ (c < 256 &&
+ (d != OP_ANY || !IS_NEWLINE(ptr)) &&
+ ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0))
+ {
+ if (codevalue == OP_TYPEPOSQUERY)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW(state_offset + 2, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPOSSTAR:
+ ADD_ACTIVE(state_offset + 2, 0);
+ if (clen > 0)
+ {
+ if (d == OP_ANY && ptr + 1 >= mb->end_subject &&
+ (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ c == NLBLOCK->nl[0])
+ {
+ could_continue = partial_newline = TRUE;
+ }
+ else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) ||
+ (c < 256 &&
+ (d != OP_ANY || !IS_NEWLINE(ptr)) &&
+ ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0))
+ {
+ if (codevalue == OP_TYPEPOSSTAR)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW(state_offset, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_TYPEEXACT:
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ if (d == OP_ANY && ptr + 1 >= mb->end_subject &&
+ (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ c == NLBLOCK->nl[0])
+ {
+ could_continue = partial_newline = TRUE;
+ }
+ else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) ||
+ (c < 256 &&
+ (d != OP_ANY || !IS_NEWLINE(ptr)) &&
+ ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0))
+ {
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW(state_offset + 1 + IMM2_SIZE + 1, 0); }
+ else
+ { ADD_NEW(state_offset, count); }
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEPOSUPTO:
+ ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0);
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ if (d == OP_ANY && ptr + 1 >= mb->end_subject &&
+ (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ c == NLBLOCK->nl[0])
+ {
+ could_continue = partial_newline = TRUE;
+ }
+ else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) ||
+ (c < 256 &&
+ (d != OP_ANY || !IS_NEWLINE(ptr)) &&
+ ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0))
+ {
+ if (codevalue == OP_TYPEPOSUPTO)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW(state_offset + 2 + IMM2_SIZE, 0); }
+ else
+ { ADD_NEW(state_offset, count); }
+ }
+ }
+ break;
+
+/* ========================================================================== */
+ /* These are virtual opcodes that are used when something like
+ OP_TYPEPLUS has OP_PROP, OP_NOTPROP, OP_ANYNL, or OP_EXTUNI as its
+ argument. It keeps the code above fast for the other cases. The argument
+ is in the d variable. */
+
+#ifdef SUPPORT_UNICODE
+ case OP_PROP_EXTRA + OP_TYPEPLUS:
+ case OP_PROP_EXTRA + OP_TYPEMINPLUS:
+ case OP_PROP_EXTRA + OP_TYPEPOSPLUS:
+ count = current_state->count; /* Already matched */
+ if (count > 0) { ADD_ACTIVE(state_offset + 4, 0); }
+ if (clen > 0)
+ {
+ BOOL OK;
+ const uint32_t *cp;
+ const ucd_record * prop = GET_UCD(c);
+ switch(code[2])
+ {
+ case PT_ANY:
+ OK = TRUE;
+ break;
+
+ case PT_LAMP:
+ OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll ||
+ prop->chartype == ucp_Lt;
+ break;
+
+ case PT_GC:
+ OK = PRIV(ucp_gentype)[prop->chartype] == code[3];
+ break;
+
+ case PT_PC:
+ OK = prop->chartype == code[3];
+ break;
+
+ case PT_SC:
+ OK = prop->script == code[3];
+ break;
+
+ case PT_SCX:
+ OK = (prop->script == code[3] ||
+ MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), code[3]) != 0);
+ break;
+
+ /* These are specials for combination cases. */
+
+ case PT_ALNUM:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N;
+ break;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ switch(c)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z;
+ break;
+ }
+ break;
+
+ case PT_WORD:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N ||
+ c == CHAR_UNDERSCORE;
+ break;
+
+ case PT_CLIST:
+ cp = PRIV(ucd_caseless_sets) + code[3];
+ for (;;)
+ {
+ if (c < *cp) { OK = FALSE; break; }
+ if (c == *cp++) { OK = TRUE; break; }
+ }
+ break;
+
+ case PT_UCNC:
+ OK = c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT ||
+ c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) ||
+ c >= 0xe000;
+ break;
+
+ case PT_BIDICL:
+ OK = UCD_BIDICLASS(c) == code[3];
+ break;
+
+ case PT_BOOL:
+ OK = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), code[3]) != 0;
+ break;
+
+ /* Should never occur, but keep compilers from grumbling. */
+
+ default:
+ OK = codevalue != OP_PROP;
+ break;
+ }
+
+ if (OK == (d == OP_PROP))
+ {
+ if (count > 0 && codevalue == OP_PROP_EXTRA + OP_TYPEPOSPLUS)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ count++;
+ ADD_NEW(state_offset, count);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_EXTUNI_EXTRA + OP_TYPEPLUS:
+ case OP_EXTUNI_EXTRA + OP_TYPEMINPLUS:
+ case OP_EXTUNI_EXTRA + OP_TYPEPOSPLUS:
+ count = current_state->count; /* Already matched */
+ if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); }
+ if (clen > 0)
+ {
+ int ncount = 0;
+ if (count > 0 && codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSPLUS)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ (void)PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf,
+ &ncount);
+ count++;
+ ADD_NEW_DATA(-state_offset, count, ncount);
+ }
+ break;
+#endif
+
+ /*-----------------------------------------------------------------*/
+ case OP_ANYNL_EXTRA + OP_TYPEPLUS:
+ case OP_ANYNL_EXTRA + OP_TYPEMINPLUS:
+ case OP_ANYNL_EXTRA + OP_TYPEPOSPLUS:
+ count = current_state->count; /* Already matched */
+ if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); }
+ if (clen > 0)
+ {
+ int ncount = 0;
+ switch (c)
+ {
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#ifndef EBCDIC
+ case 0x2028:
+ case 0x2029:
+#endif /* Not EBCDIC */
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) break;
+ goto ANYNL01;
+
+ case CHAR_CR:
+ if (ptr + 1 < end_subject && UCHAR21TEST(ptr + 1) == CHAR_LF) ncount = 1;
+ /* Fall through */
+
+ ANYNL01:
+ case CHAR_LF:
+ if (count > 0 && codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSPLUS)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ count++;
+ ADD_NEW_DATA(-state_offset, count, ncount);
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_VSPACE_EXTRA + OP_TYPEPLUS:
+ case OP_VSPACE_EXTRA + OP_TYPEMINPLUS:
+ case OP_VSPACE_EXTRA + OP_TYPEPOSPLUS:
+ count = current_state->count; /* Already matched */
+ if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); }
+ if (clen > 0)
+ {
+ BOOL OK;
+ switch (c)
+ {
+ VSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = FALSE;
+ break;
+ }
+
+ if (OK == (d == OP_VSPACE))
+ {
+ if (count > 0 && codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSPLUS)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ count++;
+ ADD_NEW_DATA(-state_offset, count, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_HSPACE_EXTRA + OP_TYPEPLUS:
+ case OP_HSPACE_EXTRA + OP_TYPEMINPLUS:
+ case OP_HSPACE_EXTRA + OP_TYPEPOSPLUS:
+ count = current_state->count; /* Already matched */
+ if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); }
+ if (clen > 0)
+ {
+ BOOL OK;
+ switch (c)
+ {
+ HSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = FALSE;
+ break;
+ }
+
+ if (OK == (d == OP_HSPACE))
+ {
+ if (count > 0 && codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSPLUS)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ count++;
+ ADD_NEW_DATA(-state_offset, count, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+#ifdef SUPPORT_UNICODE
+ case OP_PROP_EXTRA + OP_TYPEQUERY:
+ case OP_PROP_EXTRA + OP_TYPEMINQUERY:
+ case OP_PROP_EXTRA + OP_TYPEPOSQUERY:
+ count = 4;
+ goto QS1;
+
+ case OP_PROP_EXTRA + OP_TYPESTAR:
+ case OP_PROP_EXTRA + OP_TYPEMINSTAR:
+ case OP_PROP_EXTRA + OP_TYPEPOSSTAR:
+ count = 0;
+
+ QS1:
+
+ ADD_ACTIVE(state_offset + 4, 0);
+ if (clen > 0)
+ {
+ BOOL OK;
+ const uint32_t *cp;
+ const ucd_record * prop = GET_UCD(c);
+ switch(code[2])
+ {
+ case PT_ANY:
+ OK = TRUE;
+ break;
+
+ case PT_LAMP:
+ OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll ||
+ prop->chartype == ucp_Lt;
+ break;
+
+ case PT_GC:
+ OK = PRIV(ucp_gentype)[prop->chartype] == code[3];
+ break;
+
+ case PT_PC:
+ OK = prop->chartype == code[3];
+ break;
+
+ case PT_SC:
+ OK = prop->script == code[3];
+ break;
+
+ case PT_SCX:
+ OK = (prop->script == code[3] ||
+ MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), code[3]) != 0);
+ break;
+
+ /* These are specials for combination cases. */
+
+ case PT_ALNUM:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N;
+ break;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ switch(c)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z;
+ break;
+ }
+ break;
+
+ case PT_WORD:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N ||
+ c == CHAR_UNDERSCORE;
+ break;
+
+ case PT_CLIST:
+ cp = PRIV(ucd_caseless_sets) + code[3];
+ for (;;)
+ {
+ if (c < *cp) { OK = FALSE; break; }
+ if (c == *cp++) { OK = TRUE; break; }
+ }
+ break;
+
+ case PT_UCNC:
+ OK = c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT ||
+ c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) ||
+ c >= 0xe000;
+ break;
+
+ case PT_BIDICL:
+ OK = UCD_BIDICLASS(c) == code[3];
+ break;
+
+ case PT_BOOL:
+ OK = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), code[3]) != 0;
+ break;
+
+ /* Should never occur, but keep compilers from grumbling. */
+
+ default:
+ OK = codevalue != OP_PROP;
+ break;
+ }
+
+ if (OK == (d == OP_PROP))
+ {
+ if (codevalue == OP_PROP_EXTRA + OP_TYPEPOSSTAR ||
+ codevalue == OP_PROP_EXTRA + OP_TYPEPOSQUERY)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW(state_offset + count, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_EXTUNI_EXTRA + OP_TYPEQUERY:
+ case OP_EXTUNI_EXTRA + OP_TYPEMINQUERY:
+ case OP_EXTUNI_EXTRA + OP_TYPEPOSQUERY:
+ count = 2;
+ goto QS2;
+
+ case OP_EXTUNI_EXTRA + OP_TYPESTAR:
+ case OP_EXTUNI_EXTRA + OP_TYPEMINSTAR:
+ case OP_EXTUNI_EXTRA + OP_TYPEPOSSTAR:
+ count = 0;
+
+ QS2:
+
+ ADD_ACTIVE(state_offset + 2, 0);
+ if (clen > 0)
+ {
+ int ncount = 0;
+ if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSSTAR ||
+ codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSQUERY)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ (void)PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf,
+ &ncount);
+ ADD_NEW_DATA(-(state_offset + count), 0, ncount);
+ }
+ break;
+#endif
+
+ /*-----------------------------------------------------------------*/
+ case OP_ANYNL_EXTRA + OP_TYPEQUERY:
+ case OP_ANYNL_EXTRA + OP_TYPEMINQUERY:
+ case OP_ANYNL_EXTRA + OP_TYPEPOSQUERY:
+ count = 2;
+ goto QS3;
+
+ case OP_ANYNL_EXTRA + OP_TYPESTAR:
+ case OP_ANYNL_EXTRA + OP_TYPEMINSTAR:
+ case OP_ANYNL_EXTRA + OP_TYPEPOSSTAR:
+ count = 0;
+
+ QS3:
+ ADD_ACTIVE(state_offset + 2, 0);
+ if (clen > 0)
+ {
+ int ncount = 0;
+ switch (c)
+ {
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#ifndef EBCDIC
+ case 0x2028:
+ case 0x2029:
+#endif /* Not EBCDIC */
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) break;
+ goto ANYNL02;
+
+ case CHAR_CR:
+ if (ptr + 1 < end_subject && UCHAR21TEST(ptr + 1) == CHAR_LF) ncount = 1;
+ /* Fall through */
+
+ ANYNL02:
+ case CHAR_LF:
+ if (codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSSTAR ||
+ codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSQUERY)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW_DATA(-(state_offset + (int)count), 0, ncount);
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_VSPACE_EXTRA + OP_TYPEQUERY:
+ case OP_VSPACE_EXTRA + OP_TYPEMINQUERY:
+ case OP_VSPACE_EXTRA + OP_TYPEPOSQUERY:
+ count = 2;
+ goto QS4;
+
+ case OP_VSPACE_EXTRA + OP_TYPESTAR:
+ case OP_VSPACE_EXTRA + OP_TYPEMINSTAR:
+ case OP_VSPACE_EXTRA + OP_TYPEPOSSTAR:
+ count = 0;
+
+ QS4:
+ ADD_ACTIVE(state_offset + 2, 0);
+ if (clen > 0)
+ {
+ BOOL OK;
+ switch (c)
+ {
+ VSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = FALSE;
+ break;
+ }
+ if (OK == (d == OP_VSPACE))
+ {
+ if (codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSSTAR ||
+ codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSQUERY)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW_DATA(-(state_offset + (int)count), 0, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_HSPACE_EXTRA + OP_TYPEQUERY:
+ case OP_HSPACE_EXTRA + OP_TYPEMINQUERY:
+ case OP_HSPACE_EXTRA + OP_TYPEPOSQUERY:
+ count = 2;
+ goto QS5;
+
+ case OP_HSPACE_EXTRA + OP_TYPESTAR:
+ case OP_HSPACE_EXTRA + OP_TYPEMINSTAR:
+ case OP_HSPACE_EXTRA + OP_TYPEPOSSTAR:
+ count = 0;
+
+ QS5:
+ ADD_ACTIVE(state_offset + 2, 0);
+ if (clen > 0)
+ {
+ BOOL OK;
+ switch (c)
+ {
+ HSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = FALSE;
+ break;
+ }
+
+ if (OK == (d == OP_HSPACE))
+ {
+ if (codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSSTAR ||
+ codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSQUERY)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW_DATA(-(state_offset + (int)count), 0, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+#ifdef SUPPORT_UNICODE
+ case OP_PROP_EXTRA + OP_TYPEEXACT:
+ case OP_PROP_EXTRA + OP_TYPEUPTO:
+ case OP_PROP_EXTRA + OP_TYPEMINUPTO:
+ case OP_PROP_EXTRA + OP_TYPEPOSUPTO:
+ if (codevalue != OP_PROP_EXTRA + OP_TYPEEXACT)
+ { ADD_ACTIVE(state_offset + 1 + IMM2_SIZE + 3, 0); }
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ BOOL OK;
+ const uint32_t *cp;
+ const ucd_record * prop = GET_UCD(c);
+ switch(code[1 + IMM2_SIZE + 1])
+ {
+ case PT_ANY:
+ OK = TRUE;
+ break;
+
+ case PT_LAMP:
+ OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll ||
+ prop->chartype == ucp_Lt;
+ break;
+
+ case PT_GC:
+ OK = PRIV(ucp_gentype)[prop->chartype] == code[1 + IMM2_SIZE + 2];
+ break;
+
+ case PT_PC:
+ OK = prop->chartype == code[1 + IMM2_SIZE + 2];
+ break;
+
+ case PT_SC:
+ OK = prop->script == code[1 + IMM2_SIZE + 2];
+ break;
+
+ case PT_SCX:
+ OK = (prop->script == code[1 + IMM2_SIZE + 2] ||
+ MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop),
+ code[1 + IMM2_SIZE + 2]) != 0);
+ break;
+
+ /* These are specials for combination cases. */
+
+ case PT_ALNUM:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N;
+ break;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ switch(c)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z;
+ break;
+ }
+ break;
+
+ case PT_WORD:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N ||
+ c == CHAR_UNDERSCORE;
+ break;
+
+ case PT_CLIST:
+ cp = PRIV(ucd_caseless_sets) + code[1 + IMM2_SIZE + 2];
+ for (;;)
+ {
+ if (c < *cp) { OK = FALSE; break; }
+ if (c == *cp++) { OK = TRUE; break; }
+ }
+ break;
+
+ case PT_UCNC:
+ OK = c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT ||
+ c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) ||
+ c >= 0xe000;
+ break;
+
+ case PT_BIDICL:
+ OK = UCD_BIDICLASS(c) == code[1 + IMM2_SIZE + 2];
+ break;
+
+ case PT_BOOL:
+ OK = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), code[1 + IMM2_SIZE + 2]) != 0;
+ break;
+
+ /* Should never occur, but keep compilers from grumbling. */
+
+ default:
+ OK = codevalue != OP_PROP;
+ break;
+ }
+
+ if (OK == (d == OP_PROP))
+ {
+ if (codevalue == OP_PROP_EXTRA + OP_TYPEPOSUPTO)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW(state_offset + 1 + IMM2_SIZE + 3, 0); }
+ else
+ { ADD_NEW(state_offset, count); }
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_EXTUNI_EXTRA + OP_TYPEEXACT:
+ case OP_EXTUNI_EXTRA + OP_TYPEUPTO:
+ case OP_EXTUNI_EXTRA + OP_TYPEMINUPTO:
+ case OP_EXTUNI_EXTRA + OP_TYPEPOSUPTO:
+ if (codevalue != OP_EXTUNI_EXTRA + OP_TYPEEXACT)
+ { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); }
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ PCRE2_SPTR nptr;
+ int ncount = 0;
+ if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSUPTO)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ nptr = PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf,
+ &ncount);
+ if (nptr >= end_subject && (mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ reset_could_continue = TRUE;
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, ncount); }
+ else
+ { ADD_NEW_DATA(-state_offset, count, ncount); }
+ }
+ break;
+#endif
+
+ /*-----------------------------------------------------------------*/
+ case OP_ANYNL_EXTRA + OP_TYPEEXACT:
+ case OP_ANYNL_EXTRA + OP_TYPEUPTO:
+ case OP_ANYNL_EXTRA + OP_TYPEMINUPTO:
+ case OP_ANYNL_EXTRA + OP_TYPEPOSUPTO:
+ if (codevalue != OP_ANYNL_EXTRA + OP_TYPEEXACT)
+ { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); }
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ int ncount = 0;
+ switch (c)
+ {
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#ifndef EBCDIC
+ case 0x2028:
+ case 0x2029:
+#endif /* Not EBCDIC */
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) break;
+ goto ANYNL03;
+
+ case CHAR_CR:
+ if (ptr + 1 < end_subject && UCHAR21TEST(ptr + 1) == CHAR_LF) ncount = 1;
+ /* Fall through */
+
+ ANYNL03:
+ case CHAR_LF:
+ if (codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSUPTO)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, ncount); }
+ else
+ { ADD_NEW_DATA(-state_offset, count, ncount); }
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_VSPACE_EXTRA + OP_TYPEEXACT:
+ case OP_VSPACE_EXTRA + OP_TYPEUPTO:
+ case OP_VSPACE_EXTRA + OP_TYPEMINUPTO:
+ case OP_VSPACE_EXTRA + OP_TYPEPOSUPTO:
+ if (codevalue != OP_VSPACE_EXTRA + OP_TYPEEXACT)
+ { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); }
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ BOOL OK;
+ switch (c)
+ {
+ VSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = FALSE;
+ }
+
+ if (OK == (d == OP_VSPACE))
+ {
+ if (codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSUPTO)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, 0); }
+ else
+ { ADD_NEW_DATA(-state_offset, count, 0); }
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_HSPACE_EXTRA + OP_TYPEEXACT:
+ case OP_HSPACE_EXTRA + OP_TYPEUPTO:
+ case OP_HSPACE_EXTRA + OP_TYPEMINUPTO:
+ case OP_HSPACE_EXTRA + OP_TYPEPOSUPTO:
+ if (codevalue != OP_HSPACE_EXTRA + OP_TYPEEXACT)
+ { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); }
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ BOOL OK;
+ switch (c)
+ {
+ HSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = FALSE;
+ break;
+ }
+
+ if (OK == (d == OP_HSPACE))
+ {
+ if (codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSUPTO)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, 0); }
+ else
+ { ADD_NEW_DATA(-state_offset, count, 0); }
+ }
+ }
+ break;
+
+/* ========================================================================== */
+ /* These opcodes are followed by a character that is usually compared
+ to the current subject character; it is loaded into d. We still get
+ here even if there is no subject character, because in some cases zero
+ repetitions are permitted. */
+
+ /*-----------------------------------------------------------------*/
+ case OP_CHAR:
+ if (clen > 0 && c == d) { ADD_NEW(state_offset + dlen + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_CHARI:
+ if (clen == 0) break;
+
+#ifdef SUPPORT_UNICODE
+ if (utf_or_ucp)
+ {
+ if (c == d) { ADD_NEW(state_offset + dlen + 1, 0); } else
+ {
+ unsigned int othercase;
+ if (c < 128)
+ othercase = fcc[c];
+ else
+ othercase = UCD_OTHERCASE(c);
+ if (d == othercase) { ADD_NEW(state_offset + dlen + 1, 0); }
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+ /* Not UTF or UCP mode */
+ {
+ if (TABLE_GET(c, lcc, c) == TABLE_GET(d, lcc, d))
+ { ADD_NEW(state_offset + 2, 0); }
+ }
+ break;
+
+
+#ifdef SUPPORT_UNICODE
+ /*-----------------------------------------------------------------*/
+ /* This is a tricky one because it can match more than one character.
+ Find out how many characters to skip, and then set up a negative state
+ to wait for them to pass before continuing. */
+
+ case OP_EXTUNI:
+ if (clen > 0)
+ {
+ int ncount = 0;
+ PCRE2_SPTR nptr = PRIV(extuni)(c, ptr + clen, mb->start_subject,
+ end_subject, utf, &ncount);
+ if (nptr >= end_subject && (mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ reset_could_continue = TRUE;
+ ADD_NEW_DATA(-(state_offset + 1), 0, ncount);
+ }
+ break;
+#endif
+
+ /*-----------------------------------------------------------------*/
+ /* This is a tricky like EXTUNI because it too can match more than one
+ character (when CR is followed by LF). In this case, set up a negative
+ state to wait for one character to pass before continuing. */
+
+ case OP_ANYNL:
+ if (clen > 0) switch(c)
+ {
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#ifndef EBCDIC
+ case 0x2028:
+ case 0x2029:
+#endif /* Not EBCDIC */
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) break;
+ /* Fall through */
+
+ case CHAR_LF:
+ ADD_NEW(state_offset + 1, 0);
+ break;
+
+ case CHAR_CR:
+ if (ptr + 1 >= end_subject)
+ {
+ ADD_NEW(state_offset + 1, 0);
+ if ((mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ reset_could_continue = TRUE;
+ }
+ else if (UCHAR21TEST(ptr + 1) == CHAR_LF)
+ {
+ ADD_NEW_DATA(-(state_offset + 1), 0, 1);
+ }
+ else
+ {
+ ADD_NEW(state_offset + 1, 0);
+ }
+ break;
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_NOT_VSPACE:
+ if (clen > 0) switch(c)
+ {
+ VSPACE_CASES:
+ break;
+
+ default:
+ ADD_NEW(state_offset + 1, 0);
+ break;
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_VSPACE:
+ if (clen > 0) switch(c)
+ {
+ VSPACE_CASES:
+ ADD_NEW(state_offset + 1, 0);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_NOT_HSPACE:
+ if (clen > 0) switch(c)
+ {
+ HSPACE_CASES:
+ break;
+
+ default:
+ ADD_NEW(state_offset + 1, 0);
+ break;
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_HSPACE:
+ if (clen > 0) switch(c)
+ {
+ HSPACE_CASES:
+ ADD_NEW(state_offset + 1, 0);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ /* Match a negated single character casefully. */
+
+ case OP_NOT:
+ if (clen > 0 && c != d) { ADD_NEW(state_offset + dlen + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ /* Match a negated single character caselessly. */
+
+ case OP_NOTI:
+ if (clen > 0)
+ {
+ uint32_t otherd;
+#ifdef SUPPORT_UNICODE
+ if (utf_or_ucp && d >= 128)
+ otherd = UCD_OTHERCASE(d);
+ else
+#endif /* SUPPORT_UNICODE */
+ otherd = TABLE_GET(d, fcc, d);
+ if (c != d && c != otherd)
+ { ADD_NEW(state_offset + dlen + 1, 0); }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_POSPLUSI:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUSI:
+ case OP_NOTPOSPLUSI:
+ caseless = TRUE;
+ codevalue -= OP_STARI - OP_STAR;
+
+ /* Fall through */
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_POSPLUS:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTPOSPLUS:
+ count = current_state->count; /* Already matched */
+ if (count > 0) { ADD_ACTIVE(state_offset + dlen + 1, 0); }
+ if (clen > 0)
+ {
+ uint32_t otherd = NOTACHAR;
+ if (caseless)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf_or_ucp && d >= 128)
+ otherd = UCD_OTHERCASE(d);
+ else
+#endif /* SUPPORT_UNICODE */
+ otherd = TABLE_GET(d, fcc, d);
+ }
+ if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR))
+ {
+ if (count > 0 &&
+ (codevalue == OP_POSPLUS || codevalue == OP_NOTPOSPLUS))
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ count++;
+ ADD_NEW(state_offset, count);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_POSQUERYI:
+ case OP_NOTQUERYI:
+ case OP_NOTMINQUERYI:
+ case OP_NOTPOSQUERYI:
+ caseless = TRUE;
+ codevalue -= OP_STARI - OP_STAR;
+ /* Fall through */
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_POSQUERY:
+ case OP_NOTQUERY:
+ case OP_NOTMINQUERY:
+ case OP_NOTPOSQUERY:
+ ADD_ACTIVE(state_offset + dlen + 1, 0);
+ if (clen > 0)
+ {
+ uint32_t otherd = NOTACHAR;
+ if (caseless)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf_or_ucp && d >= 128)
+ otherd = UCD_OTHERCASE(d);
+ else
+#endif /* SUPPORT_UNICODE */
+ otherd = TABLE_GET(d, fcc, d);
+ }
+ if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR))
+ {
+ if (codevalue == OP_POSQUERY || codevalue == OP_NOTPOSQUERY)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW(state_offset + dlen + 1, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_STARI:
+ case OP_MINSTARI:
+ case OP_POSSTARI:
+ case OP_NOTSTARI:
+ case OP_NOTMINSTARI:
+ case OP_NOTPOSSTARI:
+ caseless = TRUE;
+ codevalue -= OP_STARI - OP_STAR;
+ /* Fall through */
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_POSSTAR:
+ case OP_NOTSTAR:
+ case OP_NOTMINSTAR:
+ case OP_NOTPOSSTAR:
+ ADD_ACTIVE(state_offset + dlen + 1, 0);
+ if (clen > 0)
+ {
+ uint32_t otherd = NOTACHAR;
+ if (caseless)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf_or_ucp && d >= 128)
+ otherd = UCD_OTHERCASE(d);
+ else
+#endif /* SUPPORT_UNICODE */
+ otherd = TABLE_GET(d, fcc, d);
+ }
+ if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR))
+ {
+ if (codevalue == OP_POSSTAR || codevalue == OP_NOTPOSSTAR)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW(state_offset, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_EXACTI:
+ case OP_NOTEXACTI:
+ caseless = TRUE;
+ codevalue -= OP_STARI - OP_STAR;
+ /* Fall through */
+ case OP_EXACT:
+ case OP_NOTEXACT:
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ uint32_t otherd = NOTACHAR;
+ if (caseless)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf_or_ucp && d >= 128)
+ otherd = UCD_OTHERCASE(d);
+ else
+#endif /* SUPPORT_UNICODE */
+ otherd = TABLE_GET(d, fcc, d);
+ }
+ if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR))
+ {
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW(state_offset + dlen + 1 + IMM2_SIZE, 0); }
+ else
+ { ADD_NEW(state_offset, count); }
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_UPTOI:
+ case OP_MINUPTOI:
+ case OP_POSUPTOI:
+ case OP_NOTUPTOI:
+ case OP_NOTMINUPTOI:
+ case OP_NOTPOSUPTOI:
+ caseless = TRUE;
+ codevalue -= OP_STARI - OP_STAR;
+ /* Fall through */
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_POSUPTO:
+ case OP_NOTUPTO:
+ case OP_NOTMINUPTO:
+ case OP_NOTPOSUPTO:
+ ADD_ACTIVE(state_offset + dlen + 1 + IMM2_SIZE, 0);
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ uint32_t otherd = NOTACHAR;
+ if (caseless)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf_or_ucp && d >= 128)
+ otherd = UCD_OTHERCASE(d);
+ else
+#endif /* SUPPORT_UNICODE */
+ otherd = TABLE_GET(d, fcc, d);
+ }
+ if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR))
+ {
+ if (codevalue == OP_POSUPTO || codevalue == OP_NOTPOSUPTO)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW(state_offset + dlen + 1 + IMM2_SIZE, 0); }
+ else
+ { ADD_NEW(state_offset, count); }
+ }
+ }
+ break;
+
+
+/* ========================================================================== */
+ /* These are the class-handling opcodes */
+
+ case OP_CLASS:
+ case OP_NCLASS:
+ case OP_XCLASS:
+ {
+ BOOL isinclass = FALSE;
+ int next_state_offset;
+ PCRE2_SPTR ecode;
+
+ /* For a simple class, there is always just a 32-byte table, and we
+ can set isinclass from it. */
+
+ if (codevalue != OP_XCLASS)
+ {
+ ecode = code + 1 + (32 / sizeof(PCRE2_UCHAR));
+ if (clen > 0)
+ {
+ isinclass = (c > 255)? (codevalue == OP_NCLASS) :
+ ((((uint8_t *)(code + 1))[c/8] & (1u << (c&7))) != 0);
+ }
+ }
+
+ /* An extended class may have a table or a list of single characters,
+ ranges, or both, and it may be positive or negative. There's a
+ function that sorts all this out. */
+
+ else
+ {
+ ecode = code + GET(code, 1);
+ if (clen > 0) isinclass = PRIV(xclass)(c, code + 1 + LINK_SIZE, utf);
+ }
+
+ /* At this point, isinclass is set for all kinds of class, and ecode
+ points to the byte after the end of the class. If there is a
+ quantifier, this is where it will be. */
+
+ next_state_offset = (int)(ecode - start_code);
+
+ switch (*ecode)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPOSSTAR:
+ ADD_ACTIVE(next_state_offset + 1, 0);
+ if (isinclass)
+ {
+ if (*ecode == OP_CRPOSSTAR)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW(state_offset, 0);
+ }
+ break;
+
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRPOSPLUS:
+ count = current_state->count; /* Already matched */
+ if (count > 0) { ADD_ACTIVE(next_state_offset + 1, 0); }
+ if (isinclass)
+ {
+ if (count > 0 && *ecode == OP_CRPOSPLUS)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ count++;
+ ADD_NEW(state_offset, count);
+ }
+ break;
+
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSQUERY:
+ ADD_ACTIVE(next_state_offset + 1, 0);
+ if (isinclass)
+ {
+ if (*ecode == OP_CRPOSQUERY)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW(next_state_offset + 1, 0);
+ }
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ count = current_state->count; /* Already matched */
+ if (count >= (int)GET2(ecode, 1))
+ { ADD_ACTIVE(next_state_offset + 1 + 2 * IMM2_SIZE, 0); }
+ if (isinclass)
+ {
+ int max = (int)GET2(ecode, 1 + IMM2_SIZE);
+
+ if (*ecode == OP_CRPOSRANGE && count >= (int)GET2(ecode, 1))
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+
+ if (++count >= max && max != 0) /* Max 0 => no limit */
+ { ADD_NEW(next_state_offset + 1 + 2 * IMM2_SIZE, 0); }
+ else
+ { ADD_NEW(state_offset, count); }
+ }
+ break;
+
+ default:
+ if (isinclass) { ADD_NEW(next_state_offset, 0); }
+ break;
+ }
+ }
+ break;
+
+/* ========================================================================== */
+ /* These are the opcodes for fancy brackets of various kinds. We have
+ to use recursion in order to handle them. The "always failing" assertion
+ (?!) is optimised to OP_FAIL when compiling, so we have to support that,
+ though the other "backtracking verbs" are not supported. */
+
+ case OP_FAIL:
+ forced_fail++; /* Count FAILs for multiple states */
+ break;
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ {
+ int rc;
+ int *local_workspace;
+ PCRE2_SIZE *local_offsets;
+ PCRE2_SPTR endasscode = code + GET(code, 1);
+ RWS_anchor *rws = (RWS_anchor *)RWS;
+
+ if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE)
+ {
+ rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb);
+ if (rc != 0) return rc;
+ RWS = (int *)rws;
+ }
+
+ local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free);
+ local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE;
+ rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE;
+
+ while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1);
+
+ rc = internal_dfa_match(
+ mb, /* static match data */
+ code, /* this subexpression's code */
+ ptr, /* where we currently are */
+ (PCRE2_SIZE)(ptr - start_subject), /* start offset */
+ local_offsets, /* offset vector */
+ RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */
+ local_workspace, /* workspace vector */
+ RWS_RSIZE, /* size of same */
+ rlevel, /* function recursion level */
+ RWS); /* recursion workspace */
+
+ rws->free += RWS_RSIZE + RWS_OVEC_OSIZE;
+
+ if (rc < 0 && rc != PCRE2_ERROR_NOMATCH) return rc;
+ if ((rc >= 0) == (codevalue == OP_ASSERT || codevalue == OP_ASSERTBACK))
+ { ADD_ACTIVE((int)(endasscode + LINK_SIZE + 1 - start_code), 0); }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_COND:
+ case OP_SCOND:
+ {
+ int codelink = (int)GET(code, 1);
+ PCRE2_UCHAR condcode;
+
+ /* Because of the way auto-callout works during compile, a callout item
+ is inserted between OP_COND and an assertion condition. This does not
+ happen for the other conditions. */
+
+ if (code[LINK_SIZE + 1] == OP_CALLOUT
+ || code[LINK_SIZE + 1] == OP_CALLOUT_STR)
+ {
+ PCRE2_SIZE callout_length;
+ rrc = do_callout_dfa(code, offsets, current_subject, ptr, mb,
+ 1 + LINK_SIZE, &callout_length);
+ if (rrc < 0) return rrc; /* Abandon */
+ if (rrc > 0) break; /* Fail this thread */
+ code += callout_length; /* Skip callout data */
+ }
+
+ condcode = code[LINK_SIZE+1];
+
+ /* Back reference conditions and duplicate named recursion conditions
+ are not supported */
+
+ if (condcode == OP_CREF || condcode == OP_DNCREF ||
+ condcode == OP_DNRREF)
+ return PCRE2_ERROR_DFA_UCOND;
+
+ /* The DEFINE condition is always false, and the assertion (?!) is
+ converted to OP_FAIL. */
+
+ if (condcode == OP_FALSE || condcode == OP_FAIL)
+ { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); }
+
+ /* There is also an always-true condition */
+
+ else if (condcode == OP_TRUE)
+ { ADD_ACTIVE(state_offset + LINK_SIZE + 2, 0); }
+
+ /* The only supported version of OP_RREF is for the value RREF_ANY,
+ which means "test if in any recursion". We can't test for specifically
+ recursed groups. */
+
+ else if (condcode == OP_RREF)
+ {
+ unsigned int value = GET2(code, LINK_SIZE + 2);
+ if (value != RREF_ANY) return PCRE2_ERROR_DFA_UCOND;
+ if (mb->recursive != NULL)
+ { ADD_ACTIVE(state_offset + LINK_SIZE + 2 + IMM2_SIZE, 0); }
+ else { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); }
+ }
+
+ /* Otherwise, the condition is an assertion */
+
+ else
+ {
+ int rc;
+ int *local_workspace;
+ PCRE2_SIZE *local_offsets;
+ PCRE2_SPTR asscode = code + LINK_SIZE + 1;
+ PCRE2_SPTR endasscode = asscode + GET(asscode, 1);
+ RWS_anchor *rws = (RWS_anchor *)RWS;
+
+ if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE)
+ {
+ rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb);
+ if (rc != 0) return rc;
+ RWS = (int *)rws;
+ }
+
+ local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free);
+ local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE;
+ rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE;
+
+ while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1);
+
+ rc = internal_dfa_match(
+ mb, /* fixed match data */
+ asscode, /* this subexpression's code */
+ ptr, /* where we currently are */
+ (PCRE2_SIZE)(ptr - start_subject), /* start offset */
+ local_offsets, /* offset vector */
+ RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */
+ local_workspace, /* workspace vector */
+ RWS_RSIZE, /* size of same */
+ rlevel, /* function recursion level */
+ RWS); /* recursion workspace */
+
+ rws->free += RWS_RSIZE + RWS_OVEC_OSIZE;
+
+ if (rc < 0 && rc != PCRE2_ERROR_NOMATCH) return rc;
+ if ((rc >= 0) ==
+ (condcode == OP_ASSERT || condcode == OP_ASSERTBACK))
+ { ADD_ACTIVE((int)(endasscode + LINK_SIZE + 1 - start_code), 0); }
+ else
+ { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); }
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_RECURSE:
+ {
+ int rc;
+ int *local_workspace;
+ PCRE2_SIZE *local_offsets;
+ RWS_anchor *rws = (RWS_anchor *)RWS;
+ dfa_recursion_info *ri;
+ PCRE2_SPTR callpat = start_code + GET(code, 1);
+ uint32_t recno = (callpat == mb->start_code)? 0 :
+ GET2(callpat, 1 + LINK_SIZE);
+
+ if (rws->free < RWS_RSIZE + RWS_OVEC_RSIZE)
+ {
+ rc = more_workspace(&rws, RWS_OVEC_RSIZE, mb);
+ if (rc != 0) return rc;
+ RWS = (int *)rws;
+ }
+
+ local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free);
+ local_workspace = ((int *)local_offsets) + RWS_OVEC_RSIZE;
+ rws->free -= RWS_RSIZE + RWS_OVEC_RSIZE;
+
+ /* Check for repeating a recursion without advancing the subject
+ pointer. This should catch convoluted mutual recursions. (Some simple
+ cases are caught at compile time.) */
+
+ for (ri = mb->recursive; ri != NULL; ri = ri->prevrec)
+ if (recno == ri->group_num && ptr == ri->subject_position)
+ return PCRE2_ERROR_RECURSELOOP;
+
+ /* Remember this recursion and where we started it so as to
+ catch infinite loops. */
+
+ new_recursive.group_num = recno;
+ new_recursive.subject_position = ptr;
+ new_recursive.prevrec = mb->recursive;
+ mb->recursive = &new_recursive;
+
+ rc = internal_dfa_match(
+ mb, /* fixed match data */
+ callpat, /* this subexpression's code */
+ ptr, /* where we currently are */
+ (PCRE2_SIZE)(ptr - start_subject), /* start offset */
+ local_offsets, /* offset vector */
+ RWS_OVEC_RSIZE/OVEC_UNIT, /* size of same */
+ local_workspace, /* workspace vector */
+ RWS_RSIZE, /* size of same */
+ rlevel, /* function recursion level */
+ RWS); /* recursion workspace */
+
+ rws->free += RWS_RSIZE + RWS_OVEC_RSIZE;
+ mb->recursive = new_recursive.prevrec; /* Done this recursion */
+
+ /* Ran out of internal offsets */
+
+ if (rc == 0) return PCRE2_ERROR_DFA_RECURSE;
+
+ /* For each successful matched substring, set up the next state with a
+ count of characters to skip before trying it. Note that the count is in
+ characters, not bytes. */
+
+ if (rc > 0)
+ {
+ for (rc = rc*2 - 2; rc >= 0; rc -= 2)
+ {
+ PCRE2_SIZE charcount = local_offsets[rc+1] - local_offsets[rc];
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (utf)
+ {
+ PCRE2_SPTR p = start_subject + local_offsets[rc];
+ PCRE2_SPTR pp = start_subject + local_offsets[rc+1];
+ while (p < pp) if (NOT_FIRSTCU(*p++)) charcount--;
+ }
+#endif
+ if (charcount > 0)
+ {
+ ADD_NEW_DATA(-(state_offset + LINK_SIZE + 1), 0,
+ (int)(charcount - 1));
+ }
+ else
+ {
+ ADD_ACTIVE(state_offset + LINK_SIZE + 1, 0);
+ }
+ }
+ }
+ else if (rc != PCRE2_ERROR_NOMATCH) return rc;
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_BRAPOS:
+ case OP_SBRAPOS:
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ case OP_BRAPOSZERO:
+ {
+ int rc;
+ int *local_workspace;
+ PCRE2_SIZE *local_offsets;
+ PCRE2_SIZE charcount, matched_count;
+ PCRE2_SPTR local_ptr = ptr;
+ RWS_anchor *rws = (RWS_anchor *)RWS;
+ BOOL allow_zero;
+
+ if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE)
+ {
+ rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb);
+ if (rc != 0) return rc;
+ RWS = (int *)rws;
+ }
+
+ local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free);
+ local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE;
+ rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE;
+
+ if (codevalue == OP_BRAPOSZERO)
+ {
+ allow_zero = TRUE;
+ codevalue = *(++code); /* Codevalue will be one of above BRAs */
+ }
+ else allow_zero = FALSE;
+
+ /* Loop to match the subpattern as many times as possible as if it were
+ a complete pattern. */
+
+ for (matched_count = 0;; matched_count++)
+ {
+ rc = internal_dfa_match(
+ mb, /* fixed match data */
+ code, /* this subexpression's code */
+ local_ptr, /* where we currently are */
+ (PCRE2_SIZE)(ptr - start_subject), /* start offset */
+ local_offsets, /* offset vector */
+ RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */
+ local_workspace, /* workspace vector */
+ RWS_RSIZE, /* size of same */
+ rlevel, /* function recursion level */
+ RWS); /* recursion workspace */
+
+ /* Failed to match */
+
+ if (rc < 0)
+ {
+ if (rc != PCRE2_ERROR_NOMATCH) return rc;
+ break;
+ }
+
+ /* Matched: break the loop if zero characters matched. */
+
+ charcount = local_offsets[1] - local_offsets[0];
+ if (charcount == 0) break;
+ local_ptr += charcount; /* Advance temporary position ptr */
+ }
+
+ rws->free += RWS_RSIZE + RWS_OVEC_OSIZE;
+
+ /* At this point we have matched the subpattern matched_count
+ times, and local_ptr is pointing to the character after the end of the
+ last match. */
+
+ if (matched_count > 0 || allow_zero)
+ {
+ PCRE2_SPTR end_subpattern = code;
+ int next_state_offset;
+
+ do { end_subpattern += GET(end_subpattern, 1); }
+ while (*end_subpattern == OP_ALT);
+ next_state_offset =
+ (int)(end_subpattern - start_code + LINK_SIZE + 1);
+
+ /* Optimization: if there are no more active states, and there
+ are no new states yet set up, then skip over the subject string
+ right here, to save looping. Otherwise, set up the new state to swing
+ into action when the end of the matched substring is reached. */
+
+ if (i + 1 >= active_count && new_count == 0)
+ {
+ ptr = local_ptr;
+ clen = 0;
+ ADD_NEW(next_state_offset, 0);
+ }
+ else
+ {
+ PCRE2_SPTR p = ptr;
+ PCRE2_SPTR pp = local_ptr;
+ charcount = (PCRE2_SIZE)(pp - p);
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (utf) while (p < pp) if (NOT_FIRSTCU(*p++)) charcount--;
+#endif
+ ADD_NEW_DATA(-next_state_offset, 0, (int)(charcount - 1));
+ }
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_ONCE:
+ {
+ int rc;
+ int *local_workspace;
+ PCRE2_SIZE *local_offsets;
+ RWS_anchor *rws = (RWS_anchor *)RWS;
+
+ if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE)
+ {
+ rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb);
+ if (rc != 0) return rc;
+ RWS = (int *)rws;
+ }
+
+ local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free);
+ local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE;
+ rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE;
+
+ rc = internal_dfa_match(
+ mb, /* fixed match data */
+ code, /* this subexpression's code */
+ ptr, /* where we currently are */
+ (PCRE2_SIZE)(ptr - start_subject), /* start offset */
+ local_offsets, /* offset vector */
+ RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */
+ local_workspace, /* workspace vector */
+ RWS_RSIZE, /* size of same */
+ rlevel, /* function recursion level */
+ RWS); /* recursion workspace */
+
+ rws->free += RWS_RSIZE + RWS_OVEC_OSIZE;
+
+ if (rc >= 0)
+ {
+ PCRE2_SPTR end_subpattern = code;
+ PCRE2_SIZE charcount = local_offsets[1] - local_offsets[0];
+ int next_state_offset, repeat_state_offset;
+
+ do { end_subpattern += GET(end_subpattern, 1); }
+ while (*end_subpattern == OP_ALT);
+ next_state_offset =
+ (int)(end_subpattern - start_code + LINK_SIZE + 1);
+
+ /* If the end of this subpattern is KETRMAX or KETRMIN, we must
+ arrange for the repeat state also to be added to the relevant list.
+ Calculate the offset, or set -1 for no repeat. */
+
+ repeat_state_offset = (*end_subpattern == OP_KETRMAX ||
+ *end_subpattern == OP_KETRMIN)?
+ (int)(end_subpattern - start_code - GET(end_subpattern, 1)) : -1;
+
+ /* If we have matched an empty string, add the next state at the
+ current character pointer. This is important so that the duplicate
+ checking kicks in, which is what breaks infinite loops that match an
+ empty string. */
+
+ if (charcount == 0)
+ {
+ ADD_ACTIVE(next_state_offset, 0);
+ }
+
+ /* Optimization: if there are no more active states, and there
+ are no new states yet set up, then skip over the subject string
+ right here, to save looping. Otherwise, set up the new state to swing
+ into action when the end of the matched substring is reached. */
+
+ else if (i + 1 >= active_count && new_count == 0)
+ {
+ ptr += charcount;
+ clen = 0;
+ ADD_NEW(next_state_offset, 0);
+
+ /* If we are adding a repeat state at the new character position,
+ we must fudge things so that it is the only current state.
+ Otherwise, it might be a duplicate of one we processed before, and
+ that would cause it to be skipped. */
+
+ if (repeat_state_offset >= 0)
+ {
+ next_active_state = active_states;
+ active_count = 0;
+ i = -1;
+ ADD_ACTIVE(repeat_state_offset, 0);
+ }
+ }
+ else
+ {
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (utf)
+ {
+ PCRE2_SPTR p = start_subject + local_offsets[0];
+ PCRE2_SPTR pp = start_subject + local_offsets[1];
+ while (p < pp) if (NOT_FIRSTCU(*p++)) charcount--;
+ }
+#endif
+ ADD_NEW_DATA(-next_state_offset, 0, (int)(charcount - 1));
+ if (repeat_state_offset >= 0)
+ { ADD_NEW_DATA(-repeat_state_offset, 0, (int)(charcount - 1)); }
+ }
+ }
+ else if (rc != PCRE2_ERROR_NOMATCH) return rc;
+ }
+ break;
+
+
+/* ========================================================================== */
+ /* Handle callouts */
+
+ case OP_CALLOUT:
+ case OP_CALLOUT_STR:
+ {
+ PCRE2_SIZE callout_length;
+ rrc = do_callout_dfa(code, offsets, current_subject, ptr, mb, 0,
+ &callout_length);
+ if (rrc < 0) return rrc; /* Abandon */
+ if (rrc == 0)
+ { ADD_ACTIVE(state_offset + (int)callout_length, 0); }
+ }
+ break;
+
+
+/* ========================================================================== */
+ default: /* Unsupported opcode */
+ return PCRE2_ERROR_DFA_UITEM;
+ }
+
+ NEXT_ACTIVE_STATE: continue;
+
+ } /* End of loop scanning active states */
+
+ /* We have finished the processing at the current subject character. If no
+ new states have been set for the next character, we have found all the
+ matches that we are going to find. If partial matching has been requested,
+ check for appropriate conditions.
+
+ The "forced_ fail" variable counts the number of (*F) encountered for the
+ character. If it is equal to the original active_count (saved in
+ workspace[1]) it means that (*F) was found on every active state. In this
+ case we don't want to give a partial match.
+
+ The "could_continue" variable is true if a state could have continued but
+ for the fact that the end of the subject was reached. */
+
+ if (new_count <= 0)
+ {
+ if (could_continue && /* Some could go on, and */
+ forced_fail != workspace[1] && /* Not all forced fail & */
+ ( /* either... */
+ (mb->moptions & PCRE2_PARTIAL_HARD) != 0 /* Hard partial */
+ || /* or... */
+ ((mb->moptions & PCRE2_PARTIAL_SOFT) != 0 && /* Soft partial and */
+ match_count < 0) /* no matches */
+ ) && /* And... */
+ (
+ partial_newline || /* Either partial NL */
+ ( /* or ... */
+ ptr >= end_subject && /* End of subject and */
+ ( /* either */
+ ptr > mb->start_used_ptr || /* Inspected non-empty string */
+ mb->allowemptypartial /* or pattern has lookbehind */
+ ) /* or could match empty */
+ )
+ ))
+ match_count = PCRE2_ERROR_PARTIAL;
+ break; /* Exit from loop along the subject string */
+ }
+
+ /* One or more states are active for the next character. */
+
+ ptr += clen; /* Advance to next subject character */
+ } /* Loop to move along the subject string */
+
+/* Control gets here from "break" a few lines above. If we have a match and
+PCRE2_ENDANCHORED is set, the match fails. */
+
+if (match_count >= 0 &&
+ ((mb->moptions | mb->poptions) & PCRE2_ENDANCHORED) != 0 &&
+ ptr < end_subject)
+ match_count = PCRE2_ERROR_NOMATCH;
+
+return match_count;
+}
+
+
+
+/*************************************************
+* Match a pattern using the DFA algorithm *
+*************************************************/
+
+/* This function matches a compiled pattern to a subject string, using the
+alternate matching algorithm that finds all matches at once.
+
+Arguments:
+ code points to the compiled pattern
+ subject subject string
+ length length of subject string
+ startoffset where to start matching in the subject
+ options option bits
+ match_data points to a match data structure
+ gcontext points to a match context
+ workspace pointer to workspace
+ wscount size of workspace
+
+Returns: > 0 => number of match offset pairs placed in offsets
+ = 0 => offsets overflowed; longest matches are present
+ -1 => failed to match
+ < -1 => some kind of unexpected problem
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_dfa_match(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length,
+ PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data,
+ pcre2_match_context *mcontext, int *workspace, PCRE2_SIZE wscount)
+{
+int rc;
+int was_zero_terminated = 0;
+
+const pcre2_real_code *re = (const pcre2_real_code *)code;
+
+PCRE2_SPTR start_match;
+PCRE2_SPTR end_subject;
+PCRE2_SPTR bumpalong_limit;
+PCRE2_SPTR req_cu_ptr;
+
+BOOL utf, anchored, startline, firstline;
+BOOL has_first_cu = FALSE;
+BOOL has_req_cu = FALSE;
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+PCRE2_SPTR memchr_found_first_cu = NULL;
+PCRE2_SPTR memchr_found_first_cu2 = NULL;
+#endif
+
+PCRE2_UCHAR first_cu = 0;
+PCRE2_UCHAR first_cu2 = 0;
+PCRE2_UCHAR req_cu = 0;
+PCRE2_UCHAR req_cu2 = 0;
+
+const uint8_t *start_bits = NULL;
+
+/* We need to have mb pointing to a match block, because the IS_NEWLINE macro
+is used below, and it expects NLBLOCK to be defined as a pointer. */
+
+pcre2_callout_block cb;
+dfa_match_block actual_match_block;
+dfa_match_block *mb = &actual_match_block;
+
+/* Set up a starting block of memory for use during recursive calls to
+internal_dfa_match(). By putting this on the stack, it minimizes resource use
+in the case when it is not needed. If this is too small, more memory is
+obtained from the heap. At the start of each block is an anchor structure.*/
+
+int base_recursion_workspace[RWS_BASE_SIZE];
+RWS_anchor *rws = (RWS_anchor *)base_recursion_workspace;
+rws->next = NULL;
+rws->size = RWS_BASE_SIZE;
+rws->free = RWS_BASE_SIZE - RWS_ANCHOR_SIZE;
+
+/* Recognize NULL, length 0 as an empty string. */
+
+if (subject == NULL && length == 0) subject = (PCRE2_SPTR)"";
+
+/* Plausibility checks */
+
+if ((options & ~PUBLIC_DFA_MATCH_OPTIONS) != 0) return PCRE2_ERROR_BADOPTION;
+if (re == NULL || subject == NULL || workspace == NULL || match_data == NULL)
+ return PCRE2_ERROR_NULL;
+
+if (length == PCRE2_ZERO_TERMINATED)
+ {
+ length = PRIV(strlen)(subject);
+ was_zero_terminated = 1;
+ }
+
+if (wscount < 20) return PCRE2_ERROR_DFA_WSSIZE;
+if (start_offset > length) return PCRE2_ERROR_BADOFFSET;
+
+/* Partial matching and PCRE2_ENDANCHORED are currently not allowed at the same
+time. */
+
+if ((options & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0 &&
+ ((re->overall_options | options) & PCRE2_ENDANCHORED) != 0)
+ return PCRE2_ERROR_BADOPTION;
+
+/* Invalid UTF support is not available for DFA matching. */
+
+if ((re->overall_options & PCRE2_MATCH_INVALID_UTF) != 0)
+ return PCRE2_ERROR_DFA_UINVALID_UTF;
+
+/* Check that the first field in the block is the magic number. If it is not,
+return with PCRE2_ERROR_BADMAGIC. */
+
+if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC;
+
+/* Check the code unit width. */
+
+if ((re->flags & PCRE2_MODE_MASK) != PCRE2_CODE_UNIT_WIDTH/8)
+ return PCRE2_ERROR_BADMODE;
+
+/* PCRE2_NOTEMPTY and PCRE2_NOTEMPTY_ATSTART are match-time flags in the
+options variable for this function. Users of PCRE2 who are not calling the
+function directly would like to have a way of setting these flags, in the same
+way that they can set pcre2_compile() flags like PCRE2_NO_AUTOPOSSESS with
+constructions like (*NO_AUTOPOSSESS). To enable this, (*NOTEMPTY) and
+(*NOTEMPTY_ATSTART) set bits in the pattern's "flag" function which can now be
+transferred to the options for this function. The bits are guaranteed to be
+adjacent, but do not have the same values. This bit of Boolean trickery assumes
+that the match-time bits are not more significant than the flag bits. If by
+accident this is not the case, a compile-time division by zero error will
+occur. */
+
+#define FF (PCRE2_NOTEMPTY_SET|PCRE2_NE_ATST_SET)
+#define OO (PCRE2_NOTEMPTY|PCRE2_NOTEMPTY_ATSTART)
+options |= (re->flags & FF) / ((FF & (~FF+1)) / (OO & (~OO+1)));
+#undef FF
+#undef OO
+
+/* If restarting after a partial match, do some sanity checks on the contents
+of the workspace. */
+
+if ((options & PCRE2_DFA_RESTART) != 0)
+ {
+ if ((workspace[0] & (-2)) != 0 || workspace[1] < 1 ||
+ workspace[1] > (int)((wscount - 2)/INTS_PER_STATEBLOCK))
+ return PCRE2_ERROR_DFA_BADRESTART;
+ }
+
+/* Set some local values */
+
+utf = (re->overall_options & PCRE2_UTF) != 0;
+start_match = subject + start_offset;
+end_subject = subject + length;
+req_cu_ptr = start_match - 1;
+anchored = (options & (PCRE2_ANCHORED|PCRE2_DFA_RESTART)) != 0 ||
+ (re->overall_options & PCRE2_ANCHORED) != 0;
+
+/* The "must be at the start of a line" flags are used in a loop when finding
+where to start. */
+
+startline = (re->flags & PCRE2_STARTLINE) != 0;
+firstline = (re->overall_options & PCRE2_FIRSTLINE) != 0;
+bumpalong_limit = end_subject;
+
+/* Initialize and set up the fixed fields in the callout block, with a pointer
+in the match block. */
+
+mb->cb = &cb;
+cb.version = 2;
+cb.subject = subject;
+cb.subject_length = (PCRE2_SIZE)(end_subject - subject);
+cb.callout_flags = 0;
+cb.capture_top = 1; /* No capture support */
+cb.capture_last = 0;
+cb.mark = NULL; /* No (*MARK) support */
+
+/* Get data from the match context, if present, and fill in the remaining
+fields in the match block. It is an error to set an offset limit without
+setting the flag at compile time. */
+
+if (mcontext == NULL)
+ {
+ mb->callout = NULL;
+ mb->memctl = re->memctl;
+ mb->match_limit = PRIV(default_match_context).match_limit;
+ mb->match_limit_depth = PRIV(default_match_context).depth_limit;
+ mb->heap_limit = PRIV(default_match_context).heap_limit;
+ }
+else
+ {
+ if (mcontext->offset_limit != PCRE2_UNSET)
+ {
+ if ((re->overall_options & PCRE2_USE_OFFSET_LIMIT) == 0)
+ return PCRE2_ERROR_BADOFFSETLIMIT;
+ bumpalong_limit = subject + mcontext->offset_limit;
+ }
+ mb->callout = mcontext->callout;
+ mb->callout_data = mcontext->callout_data;
+ mb->memctl = mcontext->memctl;
+ mb->match_limit = mcontext->match_limit;
+ mb->match_limit_depth = mcontext->depth_limit;
+ mb->heap_limit = mcontext->heap_limit;
+ }
+
+if (mb->match_limit > re->limit_match)
+ mb->match_limit = re->limit_match;
+
+if (mb->match_limit_depth > re->limit_depth)
+ mb->match_limit_depth = re->limit_depth;
+
+if (mb->heap_limit > re->limit_heap)
+ mb->heap_limit = re->limit_heap;
+
+mb->start_code = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)) +
+ re->name_count * re->name_entry_size;
+mb->tables = re->tables;
+mb->start_subject = subject;
+mb->end_subject = end_subject;
+mb->start_offset = start_offset;
+mb->allowemptypartial = (re->max_lookbehind > 0) ||
+ (re->flags & PCRE2_MATCH_EMPTY) != 0;
+mb->moptions = options;
+mb->poptions = re->overall_options;
+mb->match_call_count = 0;
+mb->heap_used = 0;
+
+/* Process the \R and newline settings. */
+
+mb->bsr_convention = re->bsr_convention;
+mb->nltype = NLTYPE_FIXED;
+switch(re->newline_convention)
+ {
+ case PCRE2_NEWLINE_CR:
+ mb->nllen = 1;
+ mb->nl[0] = CHAR_CR;
+ break;
+
+ case PCRE2_NEWLINE_LF:
+ mb->nllen = 1;
+ mb->nl[0] = CHAR_NL;
+ break;
+
+ case PCRE2_NEWLINE_NUL:
+ mb->nllen = 1;
+ mb->nl[0] = CHAR_NUL;
+ break;
+
+ case PCRE2_NEWLINE_CRLF:
+ mb->nllen = 2;
+ mb->nl[0] = CHAR_CR;
+ mb->nl[1] = CHAR_NL;
+ break;
+
+ case PCRE2_NEWLINE_ANY:
+ mb->nltype = NLTYPE_ANY;
+ break;
+
+ case PCRE2_NEWLINE_ANYCRLF:
+ mb->nltype = NLTYPE_ANYCRLF;
+ break;
+
+ default: return PCRE2_ERROR_INTERNAL;
+ }
+
+/* Check a UTF string for validity if required. For 8-bit and 16-bit strings,
+we must also check that a starting offset does not point into the middle of a
+multiunit character. We check only the portion of the subject that is going to
+be inspected during matching - from the offset minus the maximum back reference
+to the given length. This saves time when a small part of a large subject is
+being matched by the use of a starting offset. Note that the maximum lookbehind
+is a number of characters, not code units. */
+
+#ifdef SUPPORT_UNICODE
+if (utf && (options & PCRE2_NO_UTF_CHECK) == 0)
+ {
+ PCRE2_SPTR check_subject = start_match; /* start_match includes offset */
+
+ if (start_offset > 0)
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ unsigned int i;
+ if (start_match < end_subject && NOT_FIRSTCU(*start_match))
+ return PCRE2_ERROR_BADUTFOFFSET;
+ for (i = re->max_lookbehind; i > 0 && check_subject > subject; i--)
+ {
+ check_subject--;
+ while (check_subject > subject &&
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ (*check_subject & 0xc0) == 0x80)
+#else /* 16-bit */
+ (*check_subject & 0xfc00) == 0xdc00)
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ check_subject--;
+ }
+#else /* In the 32-bit library, one code unit equals one character. */
+ check_subject -= re->max_lookbehind;
+ if (check_subject < subject) check_subject = subject;
+#endif /* PCRE2_CODE_UNIT_WIDTH != 32 */
+ }
+
+ /* Validate the relevant portion of the subject. After an error, adjust the
+ offset to be an absolute offset in the whole string. */
+
+ match_data->rc = PRIV(valid_utf)(check_subject,
+ length - (PCRE2_SIZE)(check_subject - subject), &(match_data->startchar));
+ if (match_data->rc != 0)
+ {
+ match_data->startchar += (PCRE2_SIZE)(check_subject - subject);
+ return match_data->rc;
+ }
+ }
+#endif /* SUPPORT_UNICODE */
+
+/* Set up the first code unit to match, if available. If there's no first code
+unit there may be a bitmap of possible first characters. */
+
+if ((re->flags & PCRE2_FIRSTSET) != 0)
+ {
+ has_first_cu = TRUE;
+ first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit);
+ if ((re->flags & PCRE2_FIRSTCASELESS) != 0)
+ {
+ first_cu2 = TABLE_GET(first_cu, mb->tables + fcc_offset, first_cu);
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (first_cu > 127 && !utf && (re->overall_options & PCRE2_UCP) != 0)
+ first_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(first_cu);
+#else
+ if (first_cu > 127 && (utf || (re->overall_options & PCRE2_UCP) != 0))
+ first_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(first_cu);
+#endif
+#endif /* SUPPORT_UNICODE */
+ }
+ }
+else
+ if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0)
+ start_bits = re->start_bitmap;
+
+/* There may be a "last known required code unit" set. */
+
+if ((re->flags & PCRE2_LASTSET) != 0)
+ {
+ has_req_cu = TRUE;
+ req_cu = req_cu2 = (PCRE2_UCHAR)(re->last_codeunit);
+ if ((re->flags & PCRE2_LASTCASELESS) != 0)
+ {
+ req_cu2 = TABLE_GET(req_cu, mb->tables + fcc_offset, req_cu);
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (req_cu > 127 && !utf && (re->overall_options & PCRE2_UCP) != 0)
+ req_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(req_cu);
+#else
+ if (req_cu > 127 && (utf || (re->overall_options & PCRE2_UCP) != 0))
+ req_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(req_cu);
+#endif
+#endif /* SUPPORT_UNICODE */
+ }
+ }
+
+/* If the match data block was previously used with PCRE2_COPY_MATCHED_SUBJECT,
+free the memory that was obtained. */
+
+if ((match_data->flags & PCRE2_MD_COPIED_SUBJECT) != 0)
+ {
+ match_data->memctl.free((void *)match_data->subject,
+ match_data->memctl.memory_data);
+ match_data->flags &= ~PCRE2_MD_COPIED_SUBJECT;
+ }
+
+/* Fill in fields that are always returned in the match data. */
+
+match_data->code = re;
+match_data->subject = NULL; /* Default for no match */
+match_data->mark = NULL;
+match_data->matchedby = PCRE2_MATCHEDBY_DFA_INTERPRETER;
+
+/* Call the main matching function, looping for a non-anchored regex after a
+failed match. If not restarting, perform certain optimizations at the start of
+a match. */
+
+for (;;)
+ {
+ /* ----------------- Start of match optimizations ---------------- */
+
+ /* There are some optimizations that avoid running the match if a known
+ starting point is not found, or if a known later code unit is not present.
+ However, there is an option (settable at compile time) that disables
+ these, for testing and for ensuring that all callouts do actually occur.
+ The optimizations must also be avoided when restarting a DFA match. */
+
+ if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0 &&
+ (options & PCRE2_DFA_RESTART) == 0)
+ {
+ /* If firstline is TRUE, the start of the match is constrained to the first
+ line of a multiline string. That is, the match must be before or at the
+ first newline following the start of matching. Temporarily adjust
+ end_subject so that we stop the optimization scans for a first code unit
+ immediately after the first character of a newline (the first code unit can
+ legitimately be a newline). If the match fails at the newline, later code
+ breaks this loop. */
+
+ if (firstline)
+ {
+ PCRE2_SPTR t = start_match;
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ while (t < end_subject && !IS_NEWLINE(t))
+ {
+ t++;
+ ACROSSCHAR(t < end_subject, t, t++);
+ }
+ }
+ else
+#endif
+ while (t < end_subject && !IS_NEWLINE(t)) t++;
+ end_subject = t;
+ }
+
+ /* Anchored: check the first code unit if one is recorded. This may seem
+ pointless but it can help in detecting a no match case without scanning for
+ the required code unit. */
+
+ if (anchored)
+ {
+ if (has_first_cu || start_bits != NULL)
+ {
+ BOOL ok = start_match < end_subject;
+ if (ok)
+ {
+ PCRE2_UCHAR c = UCHAR21TEST(start_match);
+ ok = has_first_cu && (c == first_cu || c == first_cu2);
+ if (!ok && start_bits != NULL)
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (c > 255) c = 255;
+#endif
+ ok = (start_bits[c/8] & (1u << (c&7))) != 0;
+ }
+ }
+ if (!ok) break;
+ }
+ }
+
+ /* Not anchored. Advance to a unique first code unit if there is one. */
+
+ else
+ {
+ if (has_first_cu)
+ {
+ if (first_cu != first_cu2) /* Caseless */
+ {
+ /* In 16-bit and 32_bit modes we have to do our own search, so can
+ look for both cases at once. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ PCRE2_UCHAR smc;
+ while (start_match < end_subject &&
+ (smc = UCHAR21TEST(start_match)) != first_cu &&
+ smc != first_cu2)
+ start_match++;
+#else
+ /* In 8-bit mode, the use of memchr() gives a big speed up, even
+ though we have to call it twice in order to find the earliest
+ occurrence of the code unit in either of its cases. Caching is used
+ to remember the positions of previously found code units. This can
+ make a huge difference when the strings are very long and only one
+ case is actually present. */
+
+ PCRE2_SPTR pp1 = NULL;
+ PCRE2_SPTR pp2 = NULL;
+ PCRE2_SIZE searchlength = end_subject - start_match;
+
+ /* If we haven't got a previously found position for first_cu, or if
+ the current starting position is later, we need to do a search. If
+ the code unit is not found, set it to the end. */
+
+ if (memchr_found_first_cu == NULL ||
+ start_match > memchr_found_first_cu)
+ {
+ pp1 = memchr(start_match, first_cu, searchlength);
+ memchr_found_first_cu = (pp1 == NULL)? end_subject : pp1;
+ }
+
+ /* If the start is before a previously found position, use the
+ previous position, or NULL if a previous search failed. */
+
+ else pp1 = (memchr_found_first_cu == end_subject)? NULL :
+ memchr_found_first_cu;
+
+ /* Do the same thing for the other case. */
+
+ if (memchr_found_first_cu2 == NULL ||
+ start_match > memchr_found_first_cu2)
+ {
+ pp2 = memchr(start_match, first_cu2, searchlength);
+ memchr_found_first_cu2 = (pp2 == NULL)? end_subject : pp2;
+ }
+
+ else pp2 = (memchr_found_first_cu2 == end_subject)? NULL :
+ memchr_found_first_cu2;
+
+ /* Set the start to the end of the subject if neither case was found.
+ Otherwise, use the earlier found point. */
+
+ if (pp1 == NULL)
+ start_match = (pp2 == NULL)? end_subject : pp2;
+ else
+ start_match = (pp2 == NULL || pp1 < pp2)? pp1 : pp2;
+
+#endif /* 8-bit handling */
+ }
+
+ /* The caseful case is much simpler. */
+
+ else
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ while (start_match < end_subject && UCHAR21TEST(start_match) !=
+ first_cu)
+ start_match++;
+#else /* 8-bit code units */
+ start_match = memchr(start_match, first_cu, end_subject - start_match);
+ if (start_match == NULL) start_match = end_subject;
+#endif
+ }
+
+ /* If we can't find the required code unit, having reached the true end
+ of the subject, break the bumpalong loop, to force a match failure,
+ except when doing partial matching, when we let the next cycle run at
+ the end of the subject. To see why, consider the pattern /(?<=abc)def/,
+ which partially matches "abc", even though the string does not contain
+ the starting character "d". If we have not reached the true end of the
+ subject (PCRE2_FIRSTLINE caused end_subject to be temporarily modified)
+ we also let the cycle run, because the matching string is legitimately
+ allowed to start with the first code unit of a newline. */
+
+ if ((mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) == 0 &&
+ start_match >= mb->end_subject)
+ break;
+ }
+
+ /* If there's no first code unit, advance to just after a linebreak for a
+ multiline match if required. */
+
+ else if (startline)
+ {
+ if (start_match > mb->start_subject + start_offset)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ while (start_match < end_subject && !WAS_NEWLINE(start_match))
+ {
+ start_match++;
+ ACROSSCHAR(start_match < end_subject, start_match, start_match++);
+ }
+ }
+ else
+#endif
+ while (start_match < end_subject && !WAS_NEWLINE(start_match))
+ start_match++;
+
+ /* If we have just passed a CR and the newline option is ANY or
+ ANYCRLF, and we are now at a LF, advance the match position by one
+ more code unit. */
+
+ if (start_match[-1] == CHAR_CR &&
+ (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) &&
+ start_match < end_subject &&
+ UCHAR21TEST(start_match) == CHAR_NL)
+ start_match++;
+ }
+ }
+
+ /* If there's no first code unit or a requirement for a multiline line
+ start, advance to a non-unique first code unit if any have been
+ identified. The bitmap contains only 256 bits. When code units are 16 or
+ 32 bits wide, all code units greater than 254 set the 255 bit. */
+
+ else if (start_bits != NULL)
+ {
+ while (start_match < end_subject)
+ {
+ uint32_t c = UCHAR21TEST(start_match);
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (c > 255) c = 255;
+#endif
+ if ((start_bits[c/8] & (1u << (c&7))) != 0) break;
+ start_match++;
+ }
+
+ /* See comment above in first_cu checking about the next line. */
+
+ if ((mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) == 0 &&
+ start_match >= mb->end_subject)
+ break;
+ }
+ } /* End of first code unit handling */
+
+ /* Restore fudged end_subject */
+
+ end_subject = mb->end_subject;
+
+ /* The following two optimizations are disabled for partial matching. */
+
+ if ((mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) == 0)
+ {
+ PCRE2_SPTR p;
+
+ /* The minimum matching length is a lower bound; no actual string of that
+ length may actually match the pattern. Although the value is, strictly,
+ in characters, we treat it as code units to avoid spending too much time
+ in this optimization. */
+
+ if (end_subject - start_match < re->minlength) goto NOMATCH_EXIT;
+
+ /* If req_cu is set, we know that that code unit must appear in the
+ subject for the match to succeed. If the first code unit is set, req_cu
+ must be later in the subject; otherwise the test starts at the match
+ point. This optimization can save a huge amount of backtracking in
+ patterns with nested unlimited repeats that aren't going to match.
+ Writing separate code for cased/caseless versions makes it go faster, as
+ does using an autoincrement and backing off on a match. As in the case of
+ the first code unit, using memchr() in the 8-bit library gives a big
+ speed up. Unlike the first_cu check above, we do not need to call
+ memchr() twice in the caseless case because we only need to check for the
+ presence of the character in either case, not find the first occurrence.
+
+ The search can be skipped if the code unit was found later than the
+ current starting point in a previous iteration of the bumpalong loop.
+
+ HOWEVER: when the subject string is very, very long, searching to its end
+ can take a long time, and give bad performance on quite ordinary
+ patterns. This showed up when somebody was matching something like
+ /^\d+C/ on a 32-megabyte string... so we don't do this when the string is
+ sufficiently long, but it's worth searching a lot more for unanchored
+ patterns. */
+
+ p = start_match + (has_first_cu? 1:0);
+ if (has_req_cu && p > req_cu_ptr)
+ {
+ PCRE2_SIZE check_length = end_subject - start_match;
+
+ if (check_length < REQ_CU_MAX ||
+ (!anchored && check_length < REQ_CU_MAX * 1000))
+ {
+ if (req_cu != req_cu2) /* Caseless */
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ while (p < end_subject)
+ {
+ uint32_t pp = UCHAR21INCTEST(p);
+ if (pp == req_cu || pp == req_cu2) { p--; break; }
+ }
+#else /* 8-bit code units */
+ PCRE2_SPTR pp = p;
+ p = memchr(pp, req_cu, end_subject - pp);
+ if (p == NULL)
+ {
+ p = memchr(pp, req_cu2, end_subject - pp);
+ if (p == NULL) p = end_subject;
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH != 8 */
+ }
+
+ /* The caseful case */
+
+ else
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ while (p < end_subject)
+ {
+ if (UCHAR21INCTEST(p) == req_cu) { p--; break; }
+ }
+
+#else /* 8-bit code units */
+ p = memchr(p, req_cu, end_subject - p);
+ if (p == NULL) p = end_subject;
+#endif
+ }
+
+ /* If we can't find the required code unit, break the matching loop,
+ forcing a match failure. */
+
+ if (p >= end_subject) break;
+
+ /* If we have found the required code unit, save the point where we
+ found it, so that we don't search again next time round the loop if
+ the start hasn't passed this code unit yet. */
+
+ req_cu_ptr = p;
+ }
+ }
+ }
+ }
+
+ /* ------------ End of start of match optimizations ------------ */
+
+ /* Give no match if we have passed the bumpalong limit. */
+
+ if (start_match > bumpalong_limit) break;
+
+ /* OK, now we can do the business */
+
+ mb->start_used_ptr = start_match;
+ mb->last_used_ptr = start_match;
+ mb->recursive = NULL;
+
+ rc = internal_dfa_match(
+ mb, /* fixed match data */
+ mb->start_code, /* this subexpression's code */
+ start_match, /* where we currently are */
+ start_offset, /* start offset in subject */
+ match_data->ovector, /* offset vector */
+ (uint32_t)match_data->oveccount * 2, /* actual size of same */
+ workspace, /* workspace vector */
+ (int)wscount, /* size of same */
+ 0, /* function recurse level */
+ base_recursion_workspace); /* initial workspace for recursion */
+
+ /* Anything other than "no match" means we are done, always; otherwise, carry
+ on only if not anchored. */
+
+ if (rc != PCRE2_ERROR_NOMATCH || anchored)
+ {
+ if (rc == PCRE2_ERROR_PARTIAL && match_data->oveccount > 0)
+ {
+ match_data->ovector[0] = (PCRE2_SIZE)(start_match - subject);
+ match_data->ovector[1] = (PCRE2_SIZE)(end_subject - subject);
+ }
+ match_data->leftchar = (PCRE2_SIZE)(mb->start_used_ptr - subject);
+ match_data->rightchar = (PCRE2_SIZE)( mb->last_used_ptr - subject);
+ match_data->startchar = (PCRE2_SIZE)(start_match - subject);
+ match_data->rc = rc;
+
+ if (rc >= 0 &&(options & PCRE2_COPY_MATCHED_SUBJECT) != 0)
+ {
+ length = CU2BYTES(length + was_zero_terminated);
+ match_data->subject = match_data->memctl.malloc(length,
+ match_data->memctl.memory_data);
+ if (match_data->subject == NULL) return PCRE2_ERROR_NOMEMORY;
+ memcpy((void *)match_data->subject, subject, length);
+ match_data->flags |= PCRE2_MD_COPIED_SUBJECT;
+ }
+ else
+ {
+ if (rc >= 0 || rc == PCRE2_ERROR_PARTIAL) match_data->subject = subject;
+ }
+ goto EXIT;
+ }
+
+ /* Advance to the next subject character unless we are at the end of a line
+ and firstline is set. */
+
+ if (firstline && IS_NEWLINE(start_match)) break;
+ start_match++;
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ ACROSSCHAR(start_match < end_subject, start_match, start_match++);
+ }
+#endif
+ if (start_match > end_subject) break;
+
+ /* If we have just passed a CR and we are now at a LF, and the pattern does
+ not contain any explicit matches for \r or \n, and the newline option is CRLF
+ or ANY or ANYCRLF, advance the match position by one more character. */
+
+ if (UCHAR21TEST(start_match - 1) == CHAR_CR &&
+ start_match < end_subject &&
+ UCHAR21TEST(start_match) == CHAR_NL &&
+ (re->flags & PCRE2_HASCRORLF) == 0 &&
+ (mb->nltype == NLTYPE_ANY ||
+ mb->nltype == NLTYPE_ANYCRLF ||
+ mb->nllen == 2))
+ start_match++;
+
+ } /* "Bumpalong" loop */
+
+NOMATCH_EXIT:
+rc = PCRE2_ERROR_NOMATCH;
+
+EXIT:
+while (rws->next != NULL)
+ {
+ RWS_anchor *next = rws->next;
+ rws->next = next->next;
+ mb->memctl.free(next, mb->memctl.memory_data);
+ }
+
+return rc;
+}
+
+/* These #undefs are here to enable unity builds with CMake. */
+
+#undef NLBLOCK /* Block containing newline information */
+#undef PSSTART /* Field containing processed string start */
+#undef PSEND /* Field containing processed string end */
+
+/* End of pcre2_dfa_match.c */
diff --git a/contrib/libs/pcre2/src/pcre2_error.c b/contrib/libs/pcre2/src/pcre2_error.c
new file mode 100644
index 00000000000..cbb9629674f
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_error.c
@@ -0,0 +1,341 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2021 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+#define STRING(a) # a
+#define XSTRING(s) STRING(s)
+
+/* The texts of compile-time error messages. Compile-time error numbers start
+at COMPILE_ERROR_BASE (100).
+
+This used to be a table of strings, but in order to reduce the number of
+relocations needed when a shared library is loaded dynamically, it is now one
+long string. We cannot use a table of offsets, because the lengths of inserts
+such as XSTRING(MAX_NAME_SIZE) are not known. Instead,
+pcre2_get_error_message() counts through to the one it wants - this isn't a
+performance issue because these strings are used only when there is an error.
+
+Each substring ends with \0 to insert a null character. This includes the final
+substring, so that the whole string ends with \0\0, which can be detected when
+counting through. */
+
+static const unsigned char compile_error_texts[] =
+ "no error\0"
+ "\\ at end of pattern\0"
+ "\\c at end of pattern\0"
+ "unrecognized character follows \\\0"
+ "numbers out of order in {} quantifier\0"
+ /* 5 */
+ "number too big in {} quantifier\0"
+ "missing terminating ] for character class\0"
+ "escape sequence is invalid in character class\0"
+ "range out of order in character class\0"
+ "quantifier does not follow a repeatable item\0"
+ /* 10 */
+ "internal error: unexpected repeat\0"
+ "unrecognized character after (? or (?-\0"
+ "POSIX named classes are supported only within a class\0"
+ "POSIX collating elements are not supported\0"
+ "missing closing parenthesis\0"
+ /* 15 */
+ "reference to non-existent subpattern\0"
+ "pattern passed as NULL\0"
+ "unrecognised compile-time option bit(s)\0"
+ "missing ) after (?# comment\0"
+ "parentheses are too deeply nested\0"
+ /* 20 */
+ "regular expression is too large\0"
+ "failed to allocate heap memory\0"
+ "unmatched closing parenthesis\0"
+ "internal error: code overflow\0"
+ "missing closing parenthesis for condition\0"
+ /* 25 */
+ "lookbehind assertion is not fixed length\0"
+ "a relative value of zero is not allowed\0"
+ "conditional subpattern contains more than two branches\0"
+ "assertion expected after (?( or (?(?C)\0"
+ "digit expected after (?+ or (?-\0"
+ /* 30 */
+ "unknown POSIX class name\0"
+ "internal error in pcre2_study(): should not occur\0"
+ "this version of PCRE2 does not have Unicode support\0"
+ "parentheses are too deeply nested (stack check)\0"
+ "character code point value in \\x{} or \\o{} is too large\0"
+ /* 35 */
+ "lookbehind is too complicated\0"
+ "\\C is not allowed in a lookbehind assertion in UTF-" XSTRING(PCRE2_CODE_UNIT_WIDTH) " mode\0"
+ "PCRE2 does not support \\F, \\L, \\l, \\N{name}, \\U, or \\u\0"
+ "number after (?C is greater than 255\0"
+ "closing parenthesis for (?C expected\0"
+ /* 40 */
+ "invalid escape sequence in (*VERB) name\0"
+ "unrecognized character after (?P\0"
+ "syntax error in subpattern name (missing terminator?)\0"
+ "two named subpatterns have the same name (PCRE2_DUPNAMES not set)\0"
+ "subpattern name must start with a non-digit\0"
+ /* 45 */
+ "this version of PCRE2 does not have support for \\P, \\p, or \\X\0"
+ "malformed \\P or \\p sequence\0"
+ "unknown property after \\P or \\p\0"
+ "subpattern name is too long (maximum " XSTRING(MAX_NAME_SIZE) " code units)\0"
+ "too many named subpatterns (maximum " XSTRING(MAX_NAME_COUNT) ")\0"
+ /* 50 */
+ "invalid range in character class\0"
+ "octal value is greater than \\377 in 8-bit non-UTF-8 mode\0"
+ "internal error: overran compiling workspace\0"
+ "internal error: previously-checked referenced subpattern not found\0"
+ "DEFINE subpattern contains more than one branch\0"
+ /* 55 */
+ "missing opening brace after \\o\0"
+ "internal error: unknown newline setting\0"
+ "\\g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain number\0"
+ "(?R (recursive pattern call) must be followed by a closing parenthesis\0"
+ /* "an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT)\0" */
+ "obsolete error (should not occur)\0" /* Was the above */
+ /* 60 */
+ "(*VERB) not recognized or malformed\0"
+ "subpattern number is too big\0"
+ "subpattern name expected\0"
+ "internal error: parsed pattern overflow\0"
+ "non-octal character in \\o{} (closing brace missing?)\0"
+ /* 65 */
+ "different names for subpatterns of the same number are not allowed\0"
+ "(*MARK) must have an argument\0"
+ "non-hex character in \\x{} (closing brace missing?)\0"
+#ifndef EBCDIC
+ "\\c must be followed by a printable ASCII character\0"
+#else
+ "\\c must be followed by a letter or one of [\\]^_?\0"
+#endif
+ "\\k is not followed by a braced, angle-bracketed, or quoted name\0"
+ /* 70 */
+ "internal error: unknown meta code in check_lookbehinds()\0"
+ "\\N is not supported in a class\0"
+ "callout string is too long\0"
+ "disallowed Unicode code point (>= 0xd800 && <= 0xdfff)\0"
+ "using UTF is disabled by the application\0"
+ /* 75 */
+ "using UCP is disabled by the application\0"
+ "name is too long in (*MARK), (*PRUNE), (*SKIP), or (*THEN)\0"
+ "character code point value in \\u.... sequence is too large\0"
+ "digits missing in \\x{} or \\o{} or \\N{U+}\0"
+ "syntax error or number too big in (?(VERSION condition\0"
+ /* 80 */
+ "internal error: unknown opcode in auto_possessify()\0"
+ "missing terminating delimiter for callout with string argument\0"
+ "unrecognized string delimiter follows (?C\0"
+ "using \\C is disabled by the application\0"
+ "(?| and/or (?J: or (?x: parentheses are too deeply nested\0"
+ /* 85 */
+ "using \\C is disabled in this PCRE2 library\0"
+ "regular expression is too complicated\0"
+ "lookbehind assertion is too long\0"
+ "pattern string is longer than the limit set by the application\0"
+ "internal error: unknown code in parsed pattern\0"
+ /* 90 */
+ "internal error: bad code value in parsed_skip()\0"
+ "PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES is not allowed in UTF-16 mode\0"
+ "invalid option bits with PCRE2_LITERAL\0"
+ "\\N{U+dddd} is supported only in Unicode (UTF) mode\0"
+ "invalid hyphen in option setting\0"
+ /* 95 */
+ "(*alpha_assertion) not recognized\0"
+ "script runs require Unicode support, which this version of PCRE2 does not have\0"
+ "too many capturing groups (maximum 65535)\0"
+ "atomic assertion expected after (?( or (?(?C)\0"
+ "\\K is not allowed in lookarounds (but see PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK)\0"
+ ;
+
+/* Match-time and UTF error texts are in the same format. */
+
+static const unsigned char match_error_texts[] =
+ "no error\0"
+ "no match\0"
+ "partial match\0"
+ "UTF-8 error: 1 byte missing at end\0"
+ "UTF-8 error: 2 bytes missing at end\0"
+ /* 5 */
+ "UTF-8 error: 3 bytes missing at end\0"
+ "UTF-8 error: 4 bytes missing at end\0"
+ "UTF-8 error: 5 bytes missing at end\0"
+ "UTF-8 error: byte 2 top bits not 0x80\0"
+ "UTF-8 error: byte 3 top bits not 0x80\0"
+ /* 10 */
+ "UTF-8 error: byte 4 top bits not 0x80\0"
+ "UTF-8 error: byte 5 top bits not 0x80\0"
+ "UTF-8 error: byte 6 top bits not 0x80\0"
+ "UTF-8 error: 5-byte character is not allowed (RFC 3629)\0"
+ "UTF-8 error: 6-byte character is not allowed (RFC 3629)\0"
+ /* 15 */
+ "UTF-8 error: code points greater than 0x10ffff are not defined\0"
+ "UTF-8 error: code points 0xd800-0xdfff are not defined\0"
+ "UTF-8 error: overlong 2-byte sequence\0"
+ "UTF-8 error: overlong 3-byte sequence\0"
+ "UTF-8 error: overlong 4-byte sequence\0"
+ /* 20 */
+ "UTF-8 error: overlong 5-byte sequence\0"
+ "UTF-8 error: overlong 6-byte sequence\0"
+ "UTF-8 error: isolated byte with 0x80 bit set\0"
+ "UTF-8 error: illegal byte (0xfe or 0xff)\0"
+ "UTF-16 error: missing low surrogate at end\0"
+ /* 25 */
+ "UTF-16 error: invalid low surrogate\0"
+ "UTF-16 error: isolated low surrogate\0"
+ "UTF-32 error: code points 0xd800-0xdfff are not defined\0"
+ "UTF-32 error: code points greater than 0x10ffff are not defined\0"
+ "bad data value\0"
+ /* 30 */
+ "patterns do not all use the same character tables\0"
+ "magic number missing\0"
+ "pattern compiled in wrong mode: 8/16/32-bit error\0"
+ "bad offset value\0"
+ "bad option value\0"
+ /* 35 */
+ "invalid replacement string\0"
+ "bad offset into UTF string\0"
+ "callout error code\0" /* Never returned by PCRE2 itself */
+ "invalid data in workspace for DFA restart\0"
+ "too much recursion for DFA matching\0"
+ /* 40 */
+ "backreference condition or recursion test is not supported for DFA matching\0"
+ "function is not supported for DFA matching\0"
+ "pattern contains an item that is not supported for DFA matching\0"
+ "workspace size exceeded in DFA matching\0"
+ "internal error - pattern overwritten?\0"
+ /* 45 */
+ "bad JIT option\0"
+ "JIT stack limit reached\0"
+ "match limit exceeded\0"
+ "no more memory\0"
+ "unknown substring\0"
+ /* 50 */
+ "non-unique substring name\0"
+ "NULL argument passed with non-zero length\0"
+ "nested recursion at the same subject position\0"
+ "matching depth limit exceeded\0"
+ "requested value is not available\0"
+ /* 55 */
+ "requested value is not set\0"
+ "offset limit set without PCRE2_USE_OFFSET_LIMIT\0"
+ "bad escape sequence in replacement string\0"
+ "expected closing curly bracket in replacement string\0"
+ "bad substitution in replacement string\0"
+ /* 60 */
+ "match with end before start or start moved backwards is not supported\0"
+ "too many replacements (more than INT_MAX)\0"
+ "bad serialized data\0"
+ "heap limit exceeded\0"
+ "invalid syntax\0"
+ /* 65 */
+ "internal error - duplicate substitution match\0"
+ "PCRE2_MATCH_INVALID_UTF is not supported for DFA matching\0"
+ ;
+
+
+/*************************************************
+* Return error message *
+*************************************************/
+
+/* This function copies an error message into a buffer whose units are of an
+appropriate width. Error numbers are positive for compile-time errors, and
+negative for match-time errors (except for UTF errors), but the numbers are all
+distinct.
+
+Arguments:
+ enumber error number
+ buffer where to put the message (zero terminated)
+ size size of the buffer in code units
+
+Returns: length of message if all is well
+ negative on error
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_get_error_message(int enumber, PCRE2_UCHAR *buffer, PCRE2_SIZE size)
+{
+const unsigned char *message;
+PCRE2_SIZE i;
+int n;
+
+if (size == 0) return PCRE2_ERROR_NOMEMORY;
+
+if (enumber >= COMPILE_ERROR_BASE) /* Compile error */
+ {
+ message = compile_error_texts;
+ n = enumber - COMPILE_ERROR_BASE;
+ }
+else if (enumber < 0) /* Match or UTF error */
+ {
+ message = match_error_texts;
+ n = -enumber;
+ }
+else /* Invalid error number */
+ {
+ message = (unsigned char *)"\0"; /* Empty message list */
+ n = 1;
+ }
+
+for (; n > 0; n--)
+ {
+ while (*message++ != CHAR_NUL) {};
+ if (*message == CHAR_NUL) return PCRE2_ERROR_BADDATA;
+ }
+
+for (i = 0; *message != 0; i++)
+ {
+ if (i >= size - 1)
+ {
+ buffer[i] = 0; /* Terminate partial message */
+ return PCRE2_ERROR_NOMEMORY;
+ }
+ buffer[i] = *message++;
+ }
+
+buffer[i] = 0;
+return (int)i;
+}
+
+/* End of pcre2_error.c */
diff --git a/contrib/libs/pcre2/src/pcre2_extuni.c b/contrib/libs/pcre2/src/pcre2_extuni.c
new file mode 100644
index 00000000000..eb2e4706bbe
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_extuni.c
@@ -0,0 +1,148 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2021 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains an internal function that is used to match a Unicode
+extended grapheme sequence. It is used by both pcre2_match() and
+pcre2_def_match(). However, it is called only when Unicode support is being
+compiled. Nevertheless, we provide a dummy function when there is no Unicode
+support, because some compilers do not like functionless source files. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+
+#include "pcre2_internal.h"
+
+
+/* Dummy function */
+
+#ifndef SUPPORT_UNICODE
+PCRE2_SPTR
+PRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR start_subject,
+ PCRE2_SPTR end_subject, BOOL utf, int *xcount)
+{
+(void)c;
+(void)eptr;
+(void)start_subject;
+(void)end_subject;
+(void)utf;
+(void)xcount;
+return NULL;
+}
+#else
+
+
+/*************************************************
+* Match an extended grapheme sequence *
+*************************************************/
+
+/*
+Arguments:
+ c the first character
+ eptr pointer to next character
+ start_subject pointer to start of subject
+ end_subject pointer to end of subject
+ utf TRUE if in UTF mode
+ xcount pointer to count of additional characters,
+ or NULL if count not needed
+
+Returns: pointer after the end of the sequence
+*/
+
+PCRE2_SPTR
+PRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR start_subject,
+ PCRE2_SPTR end_subject, BOOL utf, int *xcount)
+{
+int lgb = UCD_GRAPHBREAK(c);
+
+while (eptr < end_subject)
+ {
+ int rgb;
+ int len = 1;
+ if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); }
+ rgb = UCD_GRAPHBREAK(c);
+ if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break;
+
+ /* Not breaking between Regional Indicators is allowed only if there
+ are an even number of preceding RIs. */
+
+ if (lgb == ucp_gbRegional_Indicator && rgb == ucp_gbRegional_Indicator)
+ {
+ int ricount = 0;
+ PCRE2_SPTR bptr = eptr - 1;
+ if (utf) BACKCHAR(bptr);
+
+ /* bptr is pointing to the left-hand character */
+
+ while (bptr > start_subject)
+ {
+ bptr--;
+ if (utf)
+ {
+ BACKCHAR(bptr);
+ GETCHAR(c, bptr);
+ }
+ else
+ c = *bptr;
+ if (UCD_GRAPHBREAK(c) != ucp_gbRegional_Indicator) break;
+ ricount++;
+ }
+ if ((ricount & 1) != 0) break; /* Grapheme break required */
+ }
+
+ /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this
+ allows any number of them before a following Extended_Pictographic. */
+
+ if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) ||
+ lgb != ucp_gbExtended_Pictographic)
+ lgb = rgb;
+
+ eptr += len;
+ if (xcount != NULL) *xcount += 1;
+ }
+
+return eptr;
+}
+
+#endif /* SUPPORT_UNICODE */
+
+/* End of pcre2_extuni.c */
diff --git a/contrib/libs/pcre2/src/pcre2_find_bracket.c b/contrib/libs/pcre2/src/pcre2_find_bracket.c
new file mode 100644
index 00000000000..f80503a3f11
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_find_bracket.c
@@ -0,0 +1,219 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2018 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This module contains a single function that scans through a compiled pattern
+until it finds a capturing bracket with the given number, or, if the number is
+negative, an instance of OP_REVERSE for a lookbehind. The function is called
+from pcre2_compile.c and also from pcre2_study.c when finding the minimum
+matching length. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+/*************************************************
+* Scan compiled regex for specific bracket *
+*************************************************/
+
+/*
+Arguments:
+ code points to start of expression
+ utf TRUE in UTF mode
+ number the required bracket number or negative to find a lookbehind
+
+Returns: pointer to the opcode for the bracket, or NULL if not found
+*/
+
+PCRE2_SPTR
+PRIV(find_bracket)(PCRE2_SPTR code, BOOL utf, int number)
+{
+for (;;)
+ {
+ PCRE2_UCHAR c = *code;
+
+ if (c == OP_END) return NULL;
+
+ /* XCLASS is used for classes that cannot be represented just by a bit map.
+ This includes negated single high-valued characters. CALLOUT_STR is used for
+ callouts with string arguments. In both cases the length in the table is
+ zero; the actual length is stored in the compiled code. */
+
+ if (c == OP_XCLASS) code += GET(code, 1);
+ else if (c == OP_CALLOUT_STR) code += GET(code, 1 + 2*LINK_SIZE);
+
+ /* Handle lookbehind */
+
+ else if (c == OP_REVERSE)
+ {
+ if (number < 0) return (PCRE2_UCHAR *)code;
+ code += PRIV(OP_lengths)[c];
+ }
+
+ /* Handle capturing bracket */
+
+ else if (c == OP_CBRA || c == OP_SCBRA ||
+ c == OP_CBRAPOS || c == OP_SCBRAPOS)
+ {
+ int n = (int)GET2(code, 1+LINK_SIZE);
+ if (n == number) return (PCRE2_UCHAR *)code;
+ code += PRIV(OP_lengths)[c];
+ }
+
+ /* Otherwise, we can get the item's length from the table, except that for
+ repeated character types, we have to test for \p and \P, which have an extra
+ two bytes of parameters, and for MARK/PRUNE/SKIP/THEN with an argument, we
+ must add in its length. */
+
+ else
+ {
+ switch(c)
+ {
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ case OP_TYPEPOSQUERY:
+ if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2;
+ break;
+
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEEXACT:
+ case OP_TYPEPOSUPTO:
+ if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP)
+ code += 2;
+ break;
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_SKIP_ARG:
+ case OP_THEN_ARG:
+ code += code[1];
+ break;
+ }
+
+ /* Add in the fixed length from the table */
+
+ code += PRIV(OP_lengths)[c];
+
+ /* In UTF-8 and UTF-16 modes, opcodes that are followed by a character may be
+ followed by a multi-byte character. The length in the table is a minimum, so
+ we have to arrange to skip the extra bytes. */
+
+#ifdef MAYBE_UTF_MULTI
+ if (utf) switch(c)
+ {
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ case OP_EXACT:
+ case OP_EXACTI:
+ case OP_NOTEXACT:
+ case OP_NOTEXACTI:
+ case OP_UPTO:
+ case OP_UPTOI:
+ case OP_NOTUPTO:
+ case OP_NOTUPTOI:
+ case OP_MINUPTO:
+ case OP_MINUPTOI:
+ case OP_NOTMINUPTO:
+ case OP_NOTMINUPTOI:
+ case OP_POSUPTO:
+ case OP_POSUPTOI:
+ case OP_NOTPOSUPTO:
+ case OP_NOTPOSUPTOI:
+ case OP_STAR:
+ case OP_STARI:
+ case OP_NOTSTAR:
+ case OP_NOTSTARI:
+ case OP_MINSTAR:
+ case OP_MINSTARI:
+ case OP_NOTMINSTAR:
+ case OP_NOTMINSTARI:
+ case OP_POSSTAR:
+ case OP_POSSTARI:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSSTARI:
+ case OP_PLUS:
+ case OP_PLUSI:
+ case OP_NOTPLUS:
+ case OP_NOTPLUSI:
+ case OP_MINPLUS:
+ case OP_MINPLUSI:
+ case OP_NOTMINPLUS:
+ case OP_NOTMINPLUSI:
+ case OP_POSPLUS:
+ case OP_POSPLUSI:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSPLUSI:
+ case OP_QUERY:
+ case OP_QUERYI:
+ case OP_NOTQUERY:
+ case OP_NOTQUERYI:
+ case OP_MINQUERY:
+ case OP_MINQUERYI:
+ case OP_NOTMINQUERY:
+ case OP_NOTMINQUERYI:
+ case OP_POSQUERY:
+ case OP_POSQUERYI:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSQUERYI:
+ if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]);
+ break;
+ }
+#else
+ (void)(utf); /* Keep compiler happy by referencing function argument */
+#endif /* MAYBE_UTF_MULTI */
+ }
+ }
+}
+
+/* End of pcre2_find_bracket.c */
diff --git a/contrib/libs/pcre2/src/pcre2_internal.h b/contrib/libs/pcre2/src/pcre2_internal.h
new file mode 100644
index 00000000000..92dd3138d4a
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_internal.h
@@ -0,0 +1,2047 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE2 is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#ifndef PCRE2_INTERNAL_H_IDEMPOTENT_GUARD
+#define PCRE2_INTERNAL_H_IDEMPOTENT_GUARD
+
+/* We do not support both EBCDIC and Unicode at the same time. The "configure"
+script prevents both being selected, but not everybody uses "configure". EBCDIC
+is only supported for the 8-bit library, but the check for this has to be later
+in this file, because the first part is not width-dependent, and is included by
+pcre2test.c with CODE_UNIT_WIDTH == 0. */
+
+#if defined EBCDIC && defined SUPPORT_UNICODE
+#error The use of both EBCDIC and SUPPORT_UNICODE is not supported.
+#endif
+
+/* Standard C headers */
+
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Macros to make boolean values more obvious. The #ifndef is to pacify
+compiler warnings in environments where these macros are defined elsewhere.
+Unfortunately, there is no way to do the same for the typedef. */
+
+typedef int BOOL;
+#ifndef FALSE
+#define FALSE 0
+#define TRUE 1
+#endif
+
+/* Valgrind (memcheck) support */
+
+#ifdef SUPPORT_VALGRIND
+#include <valgrind/memcheck.h>
+#endif
+
+/* -ftrivial-auto-var-init support supports initializing all local variables
+to avoid some classes of bug, but this can cause an unacceptable slowdown
+for large on-stack arrays in hot functions. This macro lets us annotate
+such arrays. */
+
+#ifdef HAVE_ATTRIBUTE_UNINITIALIZED
+#define PCRE2_KEEP_UNINITIALIZED __attribute__((uninitialized))
+#else
+#define PCRE2_KEEP_UNINITIALIZED
+#endif
+
+/* Older versions of MSVC lack snprintf(). This define allows for
+warning/error-free compilation and testing with MSVC compilers back to at least
+MSVC 10/2010. Except for VC6 (which is missing some fundamentals and fails). */
+
+#if defined(_MSC_VER) && (_MSC_VER < 1900)
+#define snprintf _snprintf
+#endif
+
+/* When compiling a DLL for Windows, the exported symbols have to be declared
+using some MS magic. I found some useful information on this web page:
+http://msdn2.microsoft.com/en-us/library/y4h7bcy6(VS.80).aspx. According to the
+information there, using __declspec(dllexport) without "extern" we have a
+definition; with "extern" we have a declaration. The settings here override the
+setting in pcre2.h (which is included below); it defines only PCRE2_EXP_DECL,
+which is all that is needed for applications (they just import the symbols). We
+use:
+
+ PCRE2_EXP_DECL for declarations
+ PCRE2_EXP_DEFN for definitions
+
+The reason for wrapping this in #ifndef PCRE2_EXP_DECL is so that pcre2test,
+which is an application, but needs to import this file in order to "peek" at
+internals, can #include pcre2.h first to get an application's-eye view.
+
+In principle, people compiling for non-Windows, non-Unix-like (i.e. uncommon,
+special-purpose environments) might want to stick other stuff in front of
+exported symbols. That's why, in the non-Windows case, we set PCRE2_EXP_DEFN
+only if it is not already set. */
+
+#ifndef PCRE2_EXP_DECL
+# ifdef _WIN32
+# ifndef PCRE2_STATIC
+# define PCRE2_EXP_DECL extern __declspec(dllexport)
+# define PCRE2_EXP_DEFN __declspec(dllexport)
+# else
+# define PCRE2_EXP_DECL extern
+# define PCRE2_EXP_DEFN
+# endif
+# else
+# ifdef __cplusplus
+# define PCRE2_EXP_DECL extern "C"
+# else
+# define PCRE2_EXP_DECL extern
+# endif
+# ifndef PCRE2_EXP_DEFN
+# define PCRE2_EXP_DEFN PCRE2_EXP_DECL
+# endif
+# endif
+#endif
+
+/* Include the public PCRE2 header and the definitions of UCP character
+property values. This must follow the setting of PCRE2_EXP_DECL above. */
+
+#include "pcre2.h"
+#include "pcre2_ucp.h"
+
+/* When PCRE2 is compiled as a C++ library, the subject pointer can be replaced
+with a custom type. This makes it possible, for example, to allow pcre2_match()
+to process subject strings that are discontinuous by using a smart pointer
+class. It must always be possible to inspect all of the subject string in
+pcre2_match() because of the way it backtracks. */
+
+/* WARNING: This is as yet untested for PCRE2. */
+
+#ifdef CUSTOM_SUBJECT_PTR
+#undef PCRE2_SPTR
+#define PCRE2_SPTR CUSTOM_SUBJECT_PTR
+#endif
+
+/* When checking for integer overflow in pcre2_compile(), we need to handle
+large integers. If a 64-bit integer type is available, we can use that.
+Otherwise we have to cast to double, which of course requires floating point
+arithmetic. Handle this by defining a macro for the appropriate type. */
+
+#if defined INT64_MAX || defined int64_t
+#define INT64_OR_DOUBLE int64_t
+#else
+#define INT64_OR_DOUBLE double
+#endif
+
+/* External (in the C sense) functions and tables that are private to the
+libraries are always referenced using the PRIV macro. This makes it possible
+for pcre2test.c to include some of the source files from the libraries using a
+different PRIV definition to avoid name clashes. It also makes it clear in the
+code that a non-static object is being referenced. */
+
+#ifndef PRIV
+#define PRIV(name) _pcre2_##name
+#endif
+
+/* When compiling for use with the Virtual Pascal compiler, these functions
+need to have their names changed. PCRE2 must be compiled with the -DVPCOMPAT
+option on the command line. */
+
+#ifdef VPCOMPAT
+#define strlen(s) _strlen(s)
+#define strncmp(s1,s2,m) _strncmp(s1,s2,m)
+#define memcmp(s,c,n) _memcmp(s,c,n)
+#define memcpy(d,s,n) _memcpy(d,s,n)
+#define memmove(d,s,n) _memmove(d,s,n)
+#define memset(s,c,n) _memset(s,c,n)
+#else /* VPCOMPAT */
+
+/* Otherwise, to cope with SunOS4 and other systems that lack memmove(), define
+a macro that calls an emulating function. */
+
+#ifndef HAVE_MEMMOVE
+#undef memmove /* Some systems may have a macro */
+#define memmove(a, b, c) PRIV(memmove)(a, b, c)
+#endif /* not HAVE_MEMMOVE */
+#endif /* not VPCOMPAT */
+
+/* This is an unsigned int value that no UTF character can ever have, as
+Unicode doesn't go beyond 0x0010ffff. */
+
+#define NOTACHAR 0xffffffff
+
+/* This is the largest valid UTF/Unicode code point. */
+
+#define MAX_UTF_CODE_POINT 0x10ffff
+
+/* Compile-time positive error numbers (all except UTF errors, which are
+negative) start at this value. It should probably never be changed, in case
+some application is checking for specific numbers. There is a copy of this
+#define in pcre2posix.c (which now no longer includes this file). Ideally, a
+way of having a single definition should be found, but as the number is
+unlikely to change, this is not a pressing issue. The original reason for
+having a base other than 0 was to keep the absolute values of compile-time and
+run-time error numbers numerically different, but in the event the code does
+not rely on this. */
+
+#define COMPILE_ERROR_BASE 100
+
+/* The initial frames vector for remembering pcre2_match() backtracking points
+is allocated on the heap, of this size (bytes) or ten times the frame size if
+larger, unless the heap limit is smaller. Typical frame sizes are a few hundred
+bytes (it depends on the number of capturing parentheses) so 20KiB handles
+quite a few frames. A larger vector on the heap is obtained for matches that
+need more frames, subject to the heap limit. */
+
+#define START_FRAMES_SIZE 20480
+
+/* For DFA matching, an initial internal workspace vector is allocated on the
+stack. The heap is used only if this turns out to be too small. */
+
+#define DFA_START_RWS_SIZE 30720
+
+/* Define the default BSR convention. */
+
+#ifdef BSR_ANYCRLF
+#define BSR_DEFAULT PCRE2_BSR_ANYCRLF
+#else
+#define BSR_DEFAULT PCRE2_BSR_UNICODE
+#endif
+
+
+/* ---------------- Basic UTF-8 macros ---------------- */
+
+/* These UTF-8 macros are always defined because they are used in pcre2test for
+handling wide characters in 16-bit and 32-bit modes, even if an 8-bit library
+is not supported. */
+
+/* Tests whether a UTF-8 code point needs extra bytes to decode. */
+
+#define HASUTF8EXTRALEN(c) ((c) >= 0xc0)
+
+/* The following macros were originally written in the form of loops that used
+data from the tables whose names start with PRIV(utf8_table). They were
+rewritten by a user so as not to use loops, because in some environments this
+gives a significant performance advantage, and it seems never to do any harm.
+*/
+
+/* Base macro to pick up the remaining bytes of a UTF-8 character, not
+advancing the pointer. */
+
+#define GETUTF8(c, eptr) \
+ { \
+ if ((c & 0x20u) == 0) \
+ c = ((c & 0x1fu) << 6) | (eptr[1] & 0x3fu); \
+ else if ((c & 0x10u) == 0) \
+ c = ((c & 0x0fu) << 12) | ((eptr[1] & 0x3fu) << 6) | (eptr[2] & 0x3fu); \
+ else if ((c & 0x08u) == 0) \
+ c = ((c & 0x07u) << 18) | ((eptr[1] & 0x3fu) << 12) | \
+ ((eptr[2] & 0x3fu) << 6) | (eptr[3] & 0x3fu); \
+ else if ((c & 0x04u) == 0) \
+ c = ((c & 0x03u) << 24) | ((eptr[1] & 0x3fu) << 18) | \
+ ((eptr[2] & 0x3fu) << 12) | ((eptr[3] & 0x3fu) << 6) | \
+ (eptr[4] & 0x3fu); \
+ else \
+ c = ((c & 0x01u) << 30) | ((eptr[1] & 0x3fu) << 24) | \
+ ((eptr[2] & 0x3fu) << 18) | ((eptr[3] & 0x3fu) << 12) | \
+ ((eptr[4] & 0x3fu) << 6) | (eptr[5] & 0x3fu); \
+ }
+
+/* Base macro to pick up the remaining bytes of a UTF-8 character, advancing
+the pointer. */
+
+#define GETUTF8INC(c, eptr) \
+ { \
+ if ((c & 0x20u) == 0) \
+ c = ((c & 0x1fu) << 6) | (*eptr++ & 0x3fu); \
+ else if ((c & 0x10u) == 0) \
+ { \
+ c = ((c & 0x0fu) << 12) | ((*eptr & 0x3fu) << 6) | (eptr[1] & 0x3fu); \
+ eptr += 2; \
+ } \
+ else if ((c & 0x08u) == 0) \
+ { \
+ c = ((c & 0x07u) << 18) | ((*eptr & 0x3fu) << 12) | \
+ ((eptr[1] & 0x3fu) << 6) | (eptr[2] & 0x3fu); \
+ eptr += 3; \
+ } \
+ else if ((c & 0x04u) == 0) \
+ { \
+ c = ((c & 0x03u) << 24) | ((*eptr & 0x3fu) << 18) | \
+ ((eptr[1] & 0x3fu) << 12) | ((eptr[2] & 0x3fu) << 6) | \
+ (eptr[3] & 0x3fu); \
+ eptr += 4; \
+ } \
+ else \
+ { \
+ c = ((c & 0x01u) << 30) | ((*eptr & 0x3fu) << 24) | \
+ ((eptr[1] & 0x3fu) << 18) | ((eptr[2] & 0x3fu) << 12) | \
+ ((eptr[3] & 0x3fu) << 6) | (eptr[4] & 0x3fu); \
+ eptr += 5; \
+ } \
+ }
+
+/* Base macro to pick up the remaining bytes of a UTF-8 character, not
+advancing the pointer, incrementing the length. */
+
+#define GETUTF8LEN(c, eptr, len) \
+ { \
+ if ((c & 0x20u) == 0) \
+ { \
+ c = ((c & 0x1fu) << 6) | (eptr[1] & 0x3fu); \
+ len++; \
+ } \
+ else if ((c & 0x10u) == 0) \
+ { \
+ c = ((c & 0x0fu) << 12) | ((eptr[1] & 0x3fu) << 6) | (eptr[2] & 0x3fu); \
+ len += 2; \
+ } \
+ else if ((c & 0x08u) == 0) \
+ {\
+ c = ((c & 0x07u) << 18) | ((eptr[1] & 0x3fu) << 12) | \
+ ((eptr[2] & 0x3fu) << 6) | (eptr[3] & 0x3fu); \
+ len += 3; \
+ } \
+ else if ((c & 0x04u) == 0) \
+ { \
+ c = ((c & 0x03u) << 24) | ((eptr[1] & 0x3fu) << 18) | \
+ ((eptr[2] & 0x3fu) << 12) | ((eptr[3] & 0x3fu) << 6) | \
+ (eptr[4] & 0x3fu); \
+ len += 4; \
+ } \
+ else \
+ {\
+ c = ((c & 0x01u) << 30) | ((eptr[1] & 0x3fu) << 24) | \
+ ((eptr[2] & 0x3fu) << 18) | ((eptr[3] & 0x3fu) << 12) | \
+ ((eptr[4] & 0x3fu) << 6) | (eptr[5] & 0x3fu); \
+ len += 5; \
+ } \
+ }
+
+/* --------------- Whitespace macros ---------------- */
+
+/* Tests for Unicode horizontal and vertical whitespace characters must check a
+number of different values. Using a switch statement for this generates the
+fastest code (no loop, no memory access), and there are several places in the
+interpreter code where this happens. In order to ensure that all the case lists
+remain in step, we use macros so that there is only one place where the lists
+are defined.
+
+These values are also required as lists in pcre2_compile.c when processing \h,
+\H, \v and \V in a character class. The lists are defined in pcre2_tables.c,
+but macros that define the values are here so that all the definitions are
+together. The lists must be in ascending character order, terminated by
+NOTACHAR (which is 0xffffffff).
+
+Any changes should ensure that the various macros are kept in step with each
+other. NOTE: The values also appear in pcre2_jit_compile.c. */
+
+/* -------------- ASCII/Unicode environments -------------- */
+
+#ifndef EBCDIC
+
+/* Character U+180E (Mongolian Vowel Separator) is not included in the list of
+spaces in the Unicode file PropList.txt, and Perl does not recognize it as a
+space. However, in many other sources it is listed as a space and has been in
+PCRE (both APIs) for a long time. */
+
+#define HSPACE_LIST \
+ CHAR_HT, CHAR_SPACE, CHAR_NBSP, \
+ 0x1680, 0x180e, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, \
+ 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202f, 0x205f, 0x3000, \
+ NOTACHAR
+
+#define HSPACE_MULTIBYTE_CASES \
+ case 0x1680: /* OGHAM SPACE MARK */ \
+ case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ \
+ case 0x2000: /* EN QUAD */ \
+ case 0x2001: /* EM QUAD */ \
+ case 0x2002: /* EN SPACE */ \
+ case 0x2003: /* EM SPACE */ \
+ case 0x2004: /* THREE-PER-EM SPACE */ \
+ case 0x2005: /* FOUR-PER-EM SPACE */ \
+ case 0x2006: /* SIX-PER-EM SPACE */ \
+ case 0x2007: /* FIGURE SPACE */ \
+ case 0x2008: /* PUNCTUATION SPACE */ \
+ case 0x2009: /* THIN SPACE */ \
+ case 0x200A: /* HAIR SPACE */ \
+ case 0x202f: /* NARROW NO-BREAK SPACE */ \
+ case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ \
+ case 0x3000 /* IDEOGRAPHIC SPACE */
+
+#define HSPACE_BYTE_CASES \
+ case CHAR_HT: \
+ case CHAR_SPACE: \
+ case CHAR_NBSP
+
+#define HSPACE_CASES \
+ HSPACE_BYTE_CASES: \
+ HSPACE_MULTIBYTE_CASES
+
+#define VSPACE_LIST \
+ CHAR_LF, CHAR_VT, CHAR_FF, CHAR_CR, CHAR_NEL, 0x2028, 0x2029, NOTACHAR
+
+#define VSPACE_MULTIBYTE_CASES \
+ case 0x2028: /* LINE SEPARATOR */ \
+ case 0x2029 /* PARAGRAPH SEPARATOR */
+
+#define VSPACE_BYTE_CASES \
+ case CHAR_LF: \
+ case CHAR_VT: \
+ case CHAR_FF: \
+ case CHAR_CR: \
+ case CHAR_NEL
+
+#define VSPACE_CASES \
+ VSPACE_BYTE_CASES: \
+ VSPACE_MULTIBYTE_CASES
+
+/* -------------- EBCDIC environments -------------- */
+
+#else
+#define HSPACE_LIST CHAR_HT, CHAR_SPACE, CHAR_NBSP, NOTACHAR
+
+#define HSPACE_BYTE_CASES \
+ case CHAR_HT: \
+ case CHAR_SPACE: \
+ case CHAR_NBSP
+
+#define HSPACE_CASES HSPACE_BYTE_CASES
+
+#ifdef EBCDIC_NL25
+#define VSPACE_LIST \
+ CHAR_VT, CHAR_FF, CHAR_CR, CHAR_NEL, CHAR_LF, NOTACHAR
+#else
+#define VSPACE_LIST \
+ CHAR_VT, CHAR_FF, CHAR_CR, CHAR_LF, CHAR_NEL, NOTACHAR
+#endif
+
+#define VSPACE_BYTE_CASES \
+ case CHAR_LF: \
+ case CHAR_VT: \
+ case CHAR_FF: \
+ case CHAR_CR: \
+ case CHAR_NEL
+
+#define VSPACE_CASES VSPACE_BYTE_CASES
+#endif /* EBCDIC */
+
+/* -------------- End of whitespace macros -------------- */
+
+
+/* PCRE2 is able to support several different kinds of newline (CR, LF, CRLF,
+"any" and "anycrlf" at present). The following macros are used to package up
+testing for newlines. NLBLOCK, PSSTART, and PSEND are defined in the various
+modules to indicate in which datablock the parameters exist, and what the
+start/end of string field names are. */
+
+#define NLTYPE_FIXED 0 /* Newline is a fixed length string */
+#define NLTYPE_ANY 1 /* Newline is any Unicode line ending */
+#define NLTYPE_ANYCRLF 2 /* Newline is CR, LF, or CRLF */
+
+/* This macro checks for a newline at the given position */
+
+#define IS_NEWLINE(p) \
+ ((NLBLOCK->nltype != NLTYPE_FIXED)? \
+ ((p) < NLBLOCK->PSEND && \
+ PRIV(is_newline)((p), NLBLOCK->nltype, NLBLOCK->PSEND, \
+ &(NLBLOCK->nllen), utf)) \
+ : \
+ ((p) <= NLBLOCK->PSEND - NLBLOCK->nllen && \
+ UCHAR21TEST(p) == NLBLOCK->nl[0] && \
+ (NLBLOCK->nllen == 1 || UCHAR21TEST(p+1) == NLBLOCK->nl[1]) \
+ ) \
+ )
+
+/* This macro checks for a newline immediately preceding the given position */
+
+#define WAS_NEWLINE(p) \
+ ((NLBLOCK->nltype != NLTYPE_FIXED)? \
+ ((p) > NLBLOCK->PSSTART && \
+ PRIV(was_newline)((p), NLBLOCK->nltype, NLBLOCK->PSSTART, \
+ &(NLBLOCK->nllen), utf)) \
+ : \
+ ((p) >= NLBLOCK->PSSTART + NLBLOCK->nllen && \
+ UCHAR21TEST(p - NLBLOCK->nllen) == NLBLOCK->nl[0] && \
+ (NLBLOCK->nllen == 1 || UCHAR21TEST(p - NLBLOCK->nllen + 1) == NLBLOCK->nl[1]) \
+ ) \
+ )
+
+/* Private flags containing information about the compiled pattern. The first
+three must not be changed, because whichever is set is actually the number of
+bytes in a code unit in that mode. */
+
+#define PCRE2_MODE8 0x00000001 /* compiled in 8 bit mode */
+#define PCRE2_MODE16 0x00000002 /* compiled in 16 bit mode */
+#define PCRE2_MODE32 0x00000004 /* compiled in 32 bit mode */
+#define PCRE2_FIRSTSET 0x00000010 /* first_code unit is set */
+#define PCRE2_FIRSTCASELESS 0x00000020 /* caseless first code unit */
+#define PCRE2_FIRSTMAPSET 0x00000040 /* bitmap of first code units is set */
+#define PCRE2_LASTSET 0x00000080 /* last code unit is set */
+#define PCRE2_LASTCASELESS 0x00000100 /* caseless last code unit */
+#define PCRE2_STARTLINE 0x00000200 /* start after \n for multiline */
+#define PCRE2_JCHANGED 0x00000400 /* j option used in pattern */
+#define PCRE2_HASCRORLF 0x00000800 /* explicit \r or \n in pattern */
+#define PCRE2_HASTHEN 0x00001000 /* pattern contains (*THEN) */
+#define PCRE2_MATCH_EMPTY 0x00002000 /* pattern can match empty string */
+#define PCRE2_BSR_SET 0x00004000 /* BSR was set in the pattern */
+#define PCRE2_NL_SET 0x00008000 /* newline was set in the pattern */
+#define PCRE2_NOTEMPTY_SET 0x00010000 /* (*NOTEMPTY) used ) keep */
+#define PCRE2_NE_ATST_SET 0x00020000 /* (*NOTEMPTY_ATSTART) used) together */
+#define PCRE2_DEREF_TABLES 0x00040000 /* release character tables */
+#define PCRE2_NOJIT 0x00080000 /* (*NOJIT) used */
+#define PCRE2_HASBKPORX 0x00100000 /* contains \P, \p, or \X */
+#define PCRE2_DUPCAPUSED 0x00200000 /* contains (?| */
+#define PCRE2_HASBKC 0x00400000 /* contains \C */
+#define PCRE2_HASACCEPT 0x00800000 /* contains (*ACCEPT) */
+
+#define PCRE2_MODE_MASK (PCRE2_MODE8 | PCRE2_MODE16 | PCRE2_MODE32)
+
+/* Values for the matchedby field in a match data block. */
+
+enum { PCRE2_MATCHEDBY_INTERPRETER, /* pcre2_match() */
+ PCRE2_MATCHEDBY_DFA_INTERPRETER, /* pcre2_dfa_match() */
+ PCRE2_MATCHEDBY_JIT }; /* pcre2_jit_match() */
+
+/* Values for the flags field in a match data block. */
+
+#define PCRE2_MD_COPIED_SUBJECT 0x01u
+
+/* Magic number to provide a small check against being handed junk. */
+
+#define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */
+
+/* The maximum remaining length of subject we are prepared to search for a
+req_unit match from an anchored pattern. In 8-bit mode, memchr() is used and is
+much faster than the search loop that has to be used in 16-bit and 32-bit
+modes. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#define REQ_CU_MAX 5000
+#else
+#define REQ_CU_MAX 2000
+#endif
+
+/* Offsets for the bitmap tables in the cbits set of tables. Each table
+contains a set of bits for a class map. Some classes are built by combining
+these tables. */
+
+#define cbit_space 0 /* [:space:] or \s */
+#define cbit_xdigit 32 /* [:xdigit:] */
+#define cbit_digit 64 /* [:digit:] or \d */
+#define cbit_upper 96 /* [:upper:] */
+#define cbit_lower 128 /* [:lower:] */
+#define cbit_word 160 /* [:word:] or \w */
+#define cbit_graph 192 /* [:graph:] */
+#define cbit_print 224 /* [:print:] */
+#define cbit_punct 256 /* [:punct:] */
+#define cbit_cntrl 288 /* [:cntrl:] */
+#define cbit_length 320 /* Length of the cbits table */
+
+/* Bit definitions for entries in the ctypes table. Do not change these values
+without checking pcre2_jit_compile.c, which has an assertion to ensure that
+ctype_word has the value 16. */
+
+#define ctype_space 0x01
+#define ctype_letter 0x02
+#define ctype_lcletter 0x04
+#define ctype_digit 0x08
+#define ctype_word 0x10 /* alphanumeric or '_' */
+
+/* Offsets of the various tables from the base tables pointer, and
+total length of the tables. */
+
+#define lcc_offset 0 /* Lower case */
+#define fcc_offset 256 /* Flip case */
+#define cbits_offset 512 /* Character classes */
+#define ctypes_offset (cbits_offset + cbit_length) /* Character types */
+#define TABLES_LENGTH (ctypes_offset + 256)
+
+
+/* -------------------- Character and string names ------------------------ */
+
+/* If PCRE2 is to support UTF-8 on EBCDIC platforms, we cannot use normal
+character constants like '*' because the compiler would emit their EBCDIC code,
+which is different from their ASCII/UTF-8 code. Instead we define macros for
+the characters so that they always use the ASCII/UTF-8 code when UTF-8 support
+is enabled. When UTF-8 support is not enabled, the definitions use character
+literals. Both character and string versions of each character are needed, and
+there are some longer strings as well.
+
+This means that, on EBCDIC platforms, the PCRE2 library can handle either
+EBCDIC, or UTF-8, but not both. To support both in the same compiled library
+would need different lookups depending on whether PCRE2_UTF was set or not.
+This would make it impossible to use characters in switch/case statements,
+which would reduce performance. For a theoretical use (which nobody has asked
+for) in a minority area (EBCDIC platforms), this is not sensible. Any
+application that did need both could compile two versions of the library, using
+macros to give the functions distinct names. */
+
+#ifndef SUPPORT_UNICODE
+
+/* UTF-8 support is not enabled; use the platform-dependent character literals
+so that PCRE2 works in both ASCII and EBCDIC environments, but only in non-UTF
+mode. Newline characters are problematic in EBCDIC. Though it has CR and LF
+characters, a common practice has been to use its NL (0x15) character as the
+line terminator in C-like processing environments. However, sometimes the LF
+(0x25) character is used instead, according to this Unicode document:
+
+http://unicode.org/standard/reports/tr13/tr13-5.html
+
+PCRE2 defaults EBCDIC NL to 0x15, but has a build-time option to select 0x25
+instead. Whichever is *not* chosen is defined as NEL.
+
+In both ASCII and EBCDIC environments, CHAR_NL and CHAR_LF are synonyms for the
+same code point. */
+
+#ifdef EBCDIC
+
+#ifndef EBCDIC_NL25
+#define CHAR_NL '\x15'
+#define CHAR_NEL '\x25'
+#define STR_NL "\x15"
+#define STR_NEL "\x25"
+#else
+#define CHAR_NL '\x25'
+#define CHAR_NEL '\x15'
+#define STR_NL "\x25"
+#define STR_NEL "\x15"
+#endif
+
+#define CHAR_LF CHAR_NL
+#define STR_LF STR_NL
+
+#define CHAR_ESC '\047'
+#define CHAR_DEL '\007'
+#define CHAR_NBSP ((unsigned char)'\x41')
+#define STR_ESC "\047"
+#define STR_DEL "\007"
+
+#else /* Not EBCDIC */
+
+/* In ASCII/Unicode, linefeed is '\n' and we equate this to NL for
+compatibility. NEL is the Unicode newline character; make sure it is
+a positive value. */
+
+#define CHAR_LF '\n'
+#define CHAR_NL CHAR_LF
+#define CHAR_NEL ((unsigned char)'\x85')
+#define CHAR_ESC '\033'
+#define CHAR_DEL '\177'
+#define CHAR_NBSP ((unsigned char)'\xa0')
+
+#define STR_LF "\n"
+#define STR_NL STR_LF
+#define STR_NEL "\x85"
+#define STR_ESC "\033"
+#define STR_DEL "\177"
+
+#endif /* EBCDIC */
+
+/* The remaining definitions work in both environments. */
+
+#define CHAR_NUL '\0'
+#define CHAR_HT '\t'
+#define CHAR_VT '\v'
+#define CHAR_FF '\f'
+#define CHAR_CR '\r'
+#define CHAR_BS '\b'
+#define CHAR_BEL '\a'
+
+#define CHAR_SPACE ' '
+#define CHAR_EXCLAMATION_MARK '!'
+#define CHAR_QUOTATION_MARK '"'
+#define CHAR_NUMBER_SIGN '#'
+#define CHAR_DOLLAR_SIGN '$'
+#define CHAR_PERCENT_SIGN '%'
+#define CHAR_AMPERSAND '&'
+#define CHAR_APOSTROPHE '\''
+#define CHAR_LEFT_PARENTHESIS '('
+#define CHAR_RIGHT_PARENTHESIS ')'
+#define CHAR_ASTERISK '*'
+#define CHAR_PLUS '+'
+#define CHAR_COMMA ','
+#define CHAR_MINUS '-'
+#define CHAR_DOT '.'
+#define CHAR_SLASH '/'
+#define CHAR_0 '0'
+#define CHAR_1 '1'
+#define CHAR_2 '2'
+#define CHAR_3 '3'
+#define CHAR_4 '4'
+#define CHAR_5 '5'
+#define CHAR_6 '6'
+#define CHAR_7 '7'
+#define CHAR_8 '8'
+#define CHAR_9 '9'
+#define CHAR_COLON ':'
+#define CHAR_SEMICOLON ';'
+#define CHAR_LESS_THAN_SIGN '<'
+#define CHAR_EQUALS_SIGN '='
+#define CHAR_GREATER_THAN_SIGN '>'
+#define CHAR_QUESTION_MARK '?'
+#define CHAR_COMMERCIAL_AT '@'
+#define CHAR_A 'A'
+#define CHAR_B 'B'
+#define CHAR_C 'C'
+#define CHAR_D 'D'
+#define CHAR_E 'E'
+#define CHAR_F 'F'
+#define CHAR_G 'G'
+#define CHAR_H 'H'
+#define CHAR_I 'I'
+#define CHAR_J 'J'
+#define CHAR_K 'K'
+#define CHAR_L 'L'
+#define CHAR_M 'M'
+#define CHAR_N 'N'
+#define CHAR_O 'O'
+#define CHAR_P 'P'
+#define CHAR_Q 'Q'
+#define CHAR_R 'R'
+#define CHAR_S 'S'
+#define CHAR_T 'T'
+#define CHAR_U 'U'
+#define CHAR_V 'V'
+#define CHAR_W 'W'
+#define CHAR_X 'X'
+#define CHAR_Y 'Y'
+#define CHAR_Z 'Z'
+#define CHAR_LEFT_SQUARE_BRACKET '['
+#define CHAR_BACKSLASH '\\'
+#define CHAR_RIGHT_SQUARE_BRACKET ']'
+#define CHAR_CIRCUMFLEX_ACCENT '^'
+#define CHAR_UNDERSCORE '_'
+#define CHAR_GRAVE_ACCENT '`'
+#define CHAR_a 'a'
+#define CHAR_b 'b'
+#define CHAR_c 'c'
+#define CHAR_d 'd'
+#define CHAR_e 'e'
+#define CHAR_f 'f'
+#define CHAR_g 'g'
+#define CHAR_h 'h'
+#define CHAR_i 'i'
+#define CHAR_j 'j'
+#define CHAR_k 'k'
+#define CHAR_l 'l'
+#define CHAR_m 'm'
+#define CHAR_n 'n'
+#define CHAR_o 'o'
+#define CHAR_p 'p'
+#define CHAR_q 'q'
+#define CHAR_r 'r'
+#define CHAR_s 's'
+#define CHAR_t 't'
+#define CHAR_u 'u'
+#define CHAR_v 'v'
+#define CHAR_w 'w'
+#define CHAR_x 'x'
+#define CHAR_y 'y'
+#define CHAR_z 'z'
+#define CHAR_LEFT_CURLY_BRACKET '{'
+#define CHAR_VERTICAL_LINE '|'
+#define CHAR_RIGHT_CURLY_BRACKET '}'
+#define CHAR_TILDE '~'
+
+#define STR_HT "\t"
+#define STR_VT "\v"
+#define STR_FF "\f"
+#define STR_CR "\r"
+#define STR_BS "\b"
+#define STR_BEL "\a"
+
+#define STR_SPACE " "
+#define STR_EXCLAMATION_MARK "!"
+#define STR_QUOTATION_MARK "\""
+#define STR_NUMBER_SIGN "#"
+#define STR_DOLLAR_SIGN "$"
+#define STR_PERCENT_SIGN "%"
+#define STR_AMPERSAND "&"
+#define STR_APOSTROPHE "'"
+#define STR_LEFT_PARENTHESIS "("
+#define STR_RIGHT_PARENTHESIS ")"
+#define STR_ASTERISK "*"
+#define STR_PLUS "+"
+#define STR_COMMA ","
+#define STR_MINUS "-"
+#define STR_DOT "."
+#define STR_SLASH "/"
+#define STR_0 "0"
+#define STR_1 "1"
+#define STR_2 "2"
+#define STR_3 "3"
+#define STR_4 "4"
+#define STR_5 "5"
+#define STR_6 "6"
+#define STR_7 "7"
+#define STR_8 "8"
+#define STR_9 "9"
+#define STR_COLON ":"
+#define STR_SEMICOLON ";"
+#define STR_LESS_THAN_SIGN "<"
+#define STR_EQUALS_SIGN "="
+#define STR_GREATER_THAN_SIGN ">"
+#define STR_QUESTION_MARK "?"
+#define STR_COMMERCIAL_AT "@"
+#define STR_A "A"
+#define STR_B "B"
+#define STR_C "C"
+#define STR_D "D"
+#define STR_E "E"
+#define STR_F "F"
+#define STR_G "G"
+#define STR_H "H"
+#define STR_I "I"
+#define STR_J "J"
+#define STR_K "K"
+#define STR_L "L"
+#define STR_M "M"
+#define STR_N "N"
+#define STR_O "O"
+#define STR_P "P"
+#define STR_Q "Q"
+#define STR_R "R"
+#define STR_S "S"
+#define STR_T "T"
+#define STR_U "U"
+#define STR_V "V"
+#define STR_W "W"
+#define STR_X "X"
+#define STR_Y "Y"
+#define STR_Z "Z"
+#define STR_LEFT_SQUARE_BRACKET "["
+#define STR_BACKSLASH "\\"
+#define STR_RIGHT_SQUARE_BRACKET "]"
+#define STR_CIRCUMFLEX_ACCENT "^"
+#define STR_UNDERSCORE "_"
+#define STR_GRAVE_ACCENT "`"
+#define STR_a "a"
+#define STR_b "b"
+#define STR_c "c"
+#define STR_d "d"
+#define STR_e "e"
+#define STR_f "f"
+#define STR_g "g"
+#define STR_h "h"
+#define STR_i "i"
+#define STR_j "j"
+#define STR_k "k"
+#define STR_l "l"
+#define STR_m "m"
+#define STR_n "n"
+#define STR_o "o"
+#define STR_p "p"
+#define STR_q "q"
+#define STR_r "r"
+#define STR_s "s"
+#define STR_t "t"
+#define STR_u "u"
+#define STR_v "v"
+#define STR_w "w"
+#define STR_x "x"
+#define STR_y "y"
+#define STR_z "z"
+#define STR_LEFT_CURLY_BRACKET "{"
+#define STR_VERTICAL_LINE "|"
+#define STR_RIGHT_CURLY_BRACKET "}"
+#define STR_TILDE "~"
+
+#define STRING_ACCEPT0 "ACCEPT\0"
+#define STRING_COMMIT0 "COMMIT\0"
+#define STRING_F0 "F\0"
+#define STRING_FAIL0 "FAIL\0"
+#define STRING_MARK0 "MARK\0"
+#define STRING_PRUNE0 "PRUNE\0"
+#define STRING_SKIP0 "SKIP\0"
+#define STRING_THEN "THEN"
+
+#define STRING_atomic0 "atomic\0"
+#define STRING_pla0 "pla\0"
+#define STRING_plb0 "plb\0"
+#define STRING_napla0 "napla\0"
+#define STRING_naplb0 "naplb\0"
+#define STRING_nla0 "nla\0"
+#define STRING_nlb0 "nlb\0"
+#define STRING_sr0 "sr\0"
+#define STRING_asr0 "asr\0"
+#define STRING_positive_lookahead0 "positive_lookahead\0"
+#define STRING_positive_lookbehind0 "positive_lookbehind\0"
+#define STRING_non_atomic_positive_lookahead0 "non_atomic_positive_lookahead\0"
+#define STRING_non_atomic_positive_lookbehind0 "non_atomic_positive_lookbehind\0"
+#define STRING_negative_lookahead0 "negative_lookahead\0"
+#define STRING_negative_lookbehind0 "negative_lookbehind\0"
+#define STRING_script_run0 "script_run\0"
+#define STRING_atomic_script_run "atomic_script_run"
+
+#define STRING_alpha0 "alpha\0"
+#define STRING_lower0 "lower\0"
+#define STRING_upper0 "upper\0"
+#define STRING_alnum0 "alnum\0"
+#define STRING_ascii0 "ascii\0"
+#define STRING_blank0 "blank\0"
+#define STRING_cntrl0 "cntrl\0"
+#define STRING_digit0 "digit\0"
+#define STRING_graph0 "graph\0"
+#define STRING_print0 "print\0"
+#define STRING_punct0 "punct\0"
+#define STRING_space0 "space\0"
+#define STRING_word0 "word\0"
+#define STRING_xdigit "xdigit"
+
+#define STRING_DEFINE "DEFINE"
+#define STRING_VERSION "VERSION"
+#define STRING_WEIRD_STARTWORD "[:<:]]"
+#define STRING_WEIRD_ENDWORD "[:>:]]"
+
+#define STRING_CR_RIGHTPAR "CR)"
+#define STRING_LF_RIGHTPAR "LF)"
+#define STRING_CRLF_RIGHTPAR "CRLF)"
+#define STRING_ANY_RIGHTPAR "ANY)"
+#define STRING_ANYCRLF_RIGHTPAR "ANYCRLF)"
+#define STRING_NUL_RIGHTPAR "NUL)"
+#define STRING_BSR_ANYCRLF_RIGHTPAR "BSR_ANYCRLF)"
+#define STRING_BSR_UNICODE_RIGHTPAR "BSR_UNICODE)"
+#define STRING_UTF8_RIGHTPAR "UTF8)"
+#define STRING_UTF16_RIGHTPAR "UTF16)"
+#define STRING_UTF32_RIGHTPAR "UTF32)"
+#define STRING_UTF_RIGHTPAR "UTF)"
+#define STRING_UCP_RIGHTPAR "UCP)"
+#define STRING_NO_AUTO_POSSESS_RIGHTPAR "NO_AUTO_POSSESS)"
+#define STRING_NO_DOTSTAR_ANCHOR_RIGHTPAR "NO_DOTSTAR_ANCHOR)"
+#define STRING_NO_JIT_RIGHTPAR "NO_JIT)"
+#define STRING_NO_START_OPT_RIGHTPAR "NO_START_OPT)"
+#define STRING_NOTEMPTY_RIGHTPAR "NOTEMPTY)"
+#define STRING_NOTEMPTY_ATSTART_RIGHTPAR "NOTEMPTY_ATSTART)"
+#define STRING_LIMIT_HEAP_EQ "LIMIT_HEAP="
+#define STRING_LIMIT_MATCH_EQ "LIMIT_MATCH="
+#define STRING_LIMIT_DEPTH_EQ "LIMIT_DEPTH="
+#define STRING_LIMIT_RECURSION_EQ "LIMIT_RECURSION="
+#define STRING_MARK "MARK"
+
+#define STRING_bc "bc"
+#define STRING_bidiclass "bidiclass"
+#define STRING_sc "sc"
+#define STRING_script "script"
+#define STRING_scriptextensions "scriptextensions"
+#define STRING_scx "scx"
+
+#else /* SUPPORT_UNICODE */
+
+/* UTF-8 support is enabled; always use UTF-8 (=ASCII) character codes. This
+works in both modes non-EBCDIC platforms, and on EBCDIC platforms in UTF-8 mode
+only. */
+
+#define CHAR_HT '\011'
+#define CHAR_VT '\013'
+#define CHAR_FF '\014'
+#define CHAR_CR '\015'
+#define CHAR_LF '\012'
+#define CHAR_NL CHAR_LF
+#define CHAR_NEL ((unsigned char)'\x85')
+#define CHAR_BS '\010'
+#define CHAR_BEL '\007'
+#define CHAR_ESC '\033'
+#define CHAR_DEL '\177'
+
+#define CHAR_NUL '\0'
+#define CHAR_SPACE '\040'
+#define CHAR_EXCLAMATION_MARK '\041'
+#define CHAR_QUOTATION_MARK '\042'
+#define CHAR_NUMBER_SIGN '\043'
+#define CHAR_DOLLAR_SIGN '\044'
+#define CHAR_PERCENT_SIGN '\045'
+#define CHAR_AMPERSAND '\046'
+#define CHAR_APOSTROPHE '\047'
+#define CHAR_LEFT_PARENTHESIS '\050'
+#define CHAR_RIGHT_PARENTHESIS '\051'
+#define CHAR_ASTERISK '\052'
+#define CHAR_PLUS '\053'
+#define CHAR_COMMA '\054'
+#define CHAR_MINUS '\055'
+#define CHAR_DOT '\056'
+#define CHAR_SLASH '\057'
+#define CHAR_0 '\060'
+#define CHAR_1 '\061'
+#define CHAR_2 '\062'
+#define CHAR_3 '\063'
+#define CHAR_4 '\064'
+#define CHAR_5 '\065'
+#define CHAR_6 '\066'
+#define CHAR_7 '\067'
+#define CHAR_8 '\070'
+#define CHAR_9 '\071'
+#define CHAR_COLON '\072'
+#define CHAR_SEMICOLON '\073'
+#define CHAR_LESS_THAN_SIGN '\074'
+#define CHAR_EQUALS_SIGN '\075'
+#define CHAR_GREATER_THAN_SIGN '\076'
+#define CHAR_QUESTION_MARK '\077'
+#define CHAR_COMMERCIAL_AT '\100'
+#define CHAR_A '\101'
+#define CHAR_B '\102'
+#define CHAR_C '\103'
+#define CHAR_D '\104'
+#define CHAR_E '\105'
+#define CHAR_F '\106'
+#define CHAR_G '\107'
+#define CHAR_H '\110'
+#define CHAR_I '\111'
+#define CHAR_J '\112'
+#define CHAR_K '\113'
+#define CHAR_L '\114'
+#define CHAR_M '\115'
+#define CHAR_N '\116'
+#define CHAR_O '\117'
+#define CHAR_P '\120'
+#define CHAR_Q '\121'
+#define CHAR_R '\122'
+#define CHAR_S '\123'
+#define CHAR_T '\124'
+#define CHAR_U '\125'
+#define CHAR_V '\126'
+#define CHAR_W '\127'
+#define CHAR_X '\130'
+#define CHAR_Y '\131'
+#define CHAR_Z '\132'
+#define CHAR_LEFT_SQUARE_BRACKET '\133'
+#define CHAR_BACKSLASH '\134'
+#define CHAR_RIGHT_SQUARE_BRACKET '\135'
+#define CHAR_CIRCUMFLEX_ACCENT '\136'
+#define CHAR_UNDERSCORE '\137'
+#define CHAR_GRAVE_ACCENT '\140'
+#define CHAR_a '\141'
+#define CHAR_b '\142'
+#define CHAR_c '\143'
+#define CHAR_d '\144'
+#define CHAR_e '\145'
+#define CHAR_f '\146'
+#define CHAR_g '\147'
+#define CHAR_h '\150'
+#define CHAR_i '\151'
+#define CHAR_j '\152'
+#define CHAR_k '\153'
+#define CHAR_l '\154'
+#define CHAR_m '\155'
+#define CHAR_n '\156'
+#define CHAR_o '\157'
+#define CHAR_p '\160'
+#define CHAR_q '\161'
+#define CHAR_r '\162'
+#define CHAR_s '\163'
+#define CHAR_t '\164'
+#define CHAR_u '\165'
+#define CHAR_v '\166'
+#define CHAR_w '\167'
+#define CHAR_x '\170'
+#define CHAR_y '\171'
+#define CHAR_z '\172'
+#define CHAR_LEFT_CURLY_BRACKET '\173'
+#define CHAR_VERTICAL_LINE '\174'
+#define CHAR_RIGHT_CURLY_BRACKET '\175'
+#define CHAR_TILDE '\176'
+#define CHAR_NBSP ((unsigned char)'\xa0')
+
+#define STR_HT "\011"
+#define STR_VT "\013"
+#define STR_FF "\014"
+#define STR_CR "\015"
+#define STR_NL "\012"
+#define STR_BS "\010"
+#define STR_BEL "\007"
+#define STR_ESC "\033"
+#define STR_DEL "\177"
+
+#define STR_SPACE "\040"
+#define STR_EXCLAMATION_MARK "\041"
+#define STR_QUOTATION_MARK "\042"
+#define STR_NUMBER_SIGN "\043"
+#define STR_DOLLAR_SIGN "\044"
+#define STR_PERCENT_SIGN "\045"
+#define STR_AMPERSAND "\046"
+#define STR_APOSTROPHE "\047"
+#define STR_LEFT_PARENTHESIS "\050"
+#define STR_RIGHT_PARENTHESIS "\051"
+#define STR_ASTERISK "\052"
+#define STR_PLUS "\053"
+#define STR_COMMA "\054"
+#define STR_MINUS "\055"
+#define STR_DOT "\056"
+#define STR_SLASH "\057"
+#define STR_0 "\060"
+#define STR_1 "\061"
+#define STR_2 "\062"
+#define STR_3 "\063"
+#define STR_4 "\064"
+#define STR_5 "\065"
+#define STR_6 "\066"
+#define STR_7 "\067"
+#define STR_8 "\070"
+#define STR_9 "\071"
+#define STR_COLON "\072"
+#define STR_SEMICOLON "\073"
+#define STR_LESS_THAN_SIGN "\074"
+#define STR_EQUALS_SIGN "\075"
+#define STR_GREATER_THAN_SIGN "\076"
+#define STR_QUESTION_MARK "\077"
+#define STR_COMMERCIAL_AT "\100"
+#define STR_A "\101"
+#define STR_B "\102"
+#define STR_C "\103"
+#define STR_D "\104"
+#define STR_E "\105"
+#define STR_F "\106"
+#define STR_G "\107"
+#define STR_H "\110"
+#define STR_I "\111"
+#define STR_J "\112"
+#define STR_K "\113"
+#define STR_L "\114"
+#define STR_M "\115"
+#define STR_N "\116"
+#define STR_O "\117"
+#define STR_P "\120"
+#define STR_Q "\121"
+#define STR_R "\122"
+#define STR_S "\123"
+#define STR_T "\124"
+#define STR_U "\125"
+#define STR_V "\126"
+#define STR_W "\127"
+#define STR_X "\130"
+#define STR_Y "\131"
+#define STR_Z "\132"
+#define STR_LEFT_SQUARE_BRACKET "\133"
+#define STR_BACKSLASH "\134"
+#define STR_RIGHT_SQUARE_BRACKET "\135"
+#define STR_CIRCUMFLEX_ACCENT "\136"
+#define STR_UNDERSCORE "\137"
+#define STR_GRAVE_ACCENT "\140"
+#define STR_a "\141"
+#define STR_b "\142"
+#define STR_c "\143"
+#define STR_d "\144"
+#define STR_e "\145"
+#define STR_f "\146"
+#define STR_g "\147"
+#define STR_h "\150"
+#define STR_i "\151"
+#define STR_j "\152"
+#define STR_k "\153"
+#define STR_l "\154"
+#define STR_m "\155"
+#define STR_n "\156"
+#define STR_o "\157"
+#define STR_p "\160"
+#define STR_q "\161"
+#define STR_r "\162"
+#define STR_s "\163"
+#define STR_t "\164"
+#define STR_u "\165"
+#define STR_v "\166"
+#define STR_w "\167"
+#define STR_x "\170"
+#define STR_y "\171"
+#define STR_z "\172"
+#define STR_LEFT_CURLY_BRACKET "\173"
+#define STR_VERTICAL_LINE "\174"
+#define STR_RIGHT_CURLY_BRACKET "\175"
+#define STR_TILDE "\176"
+
+#define STRING_ACCEPT0 STR_A STR_C STR_C STR_E STR_P STR_T "\0"
+#define STRING_COMMIT0 STR_C STR_O STR_M STR_M STR_I STR_T "\0"
+#define STRING_F0 STR_F "\0"
+#define STRING_FAIL0 STR_F STR_A STR_I STR_L "\0"
+#define STRING_MARK0 STR_M STR_A STR_R STR_K "\0"
+#define STRING_PRUNE0 STR_P STR_R STR_U STR_N STR_E "\0"
+#define STRING_SKIP0 STR_S STR_K STR_I STR_P "\0"
+#define STRING_THEN STR_T STR_H STR_E STR_N
+
+#define STRING_atomic0 STR_a STR_t STR_o STR_m STR_i STR_c "\0"
+#define STRING_pla0 STR_p STR_l STR_a "\0"
+#define STRING_plb0 STR_p STR_l STR_b "\0"
+#define STRING_napla0 STR_n STR_a STR_p STR_l STR_a "\0"
+#define STRING_naplb0 STR_n STR_a STR_p STR_l STR_b "\0"
+#define STRING_nla0 STR_n STR_l STR_a "\0"
+#define STRING_nlb0 STR_n STR_l STR_b "\0"
+#define STRING_sr0 STR_s STR_r "\0"
+#define STRING_asr0 STR_a STR_s STR_r "\0"
+#define STRING_positive_lookahead0 STR_p STR_o STR_s STR_i STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_a STR_h STR_e STR_a STR_d "\0"
+#define STRING_positive_lookbehind0 STR_p STR_o STR_s STR_i STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_b STR_e STR_h STR_i STR_n STR_d "\0"
+#define STRING_non_atomic_positive_lookahead0 STR_n STR_o STR_n STR_UNDERSCORE STR_a STR_t STR_o STR_m STR_i STR_c STR_UNDERSCORE STR_p STR_o STR_s STR_i STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_a STR_h STR_e STR_a STR_d "\0"
+#define STRING_non_atomic_positive_lookbehind0 STR_n STR_o STR_n STR_UNDERSCORE STR_a STR_t STR_o STR_m STR_i STR_c STR_UNDERSCORE STR_p STR_o STR_s STR_i STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_b STR_e STR_h STR_i STR_n STR_d "\0"
+#define STRING_negative_lookahead0 STR_n STR_e STR_g STR_a STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_a STR_h STR_e STR_a STR_d "\0"
+#define STRING_negative_lookbehind0 STR_n STR_e STR_g STR_a STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_b STR_e STR_h STR_i STR_n STR_d "\0"
+#define STRING_script_run0 STR_s STR_c STR_r STR_i STR_p STR_t STR_UNDERSCORE STR_r STR_u STR_n "\0"
+#define STRING_atomic_script_run STR_a STR_t STR_o STR_m STR_i STR_c STR_UNDERSCORE STR_s STR_c STR_r STR_i STR_p STR_t STR_UNDERSCORE STR_r STR_u STR_n
+
+#define STRING_alpha0 STR_a STR_l STR_p STR_h STR_a "\0"
+#define STRING_lower0 STR_l STR_o STR_w STR_e STR_r "\0"
+#define STRING_upper0 STR_u STR_p STR_p STR_e STR_r "\0"
+#define STRING_alnum0 STR_a STR_l STR_n STR_u STR_m "\0"
+#define STRING_ascii0 STR_a STR_s STR_c STR_i STR_i "\0"
+#define STRING_blank0 STR_b STR_l STR_a STR_n STR_k "\0"
+#define STRING_cntrl0 STR_c STR_n STR_t STR_r STR_l "\0"
+#define STRING_digit0 STR_d STR_i STR_g STR_i STR_t "\0"
+#define STRING_graph0 STR_g STR_r STR_a STR_p STR_h "\0"
+#define STRING_print0 STR_p STR_r STR_i STR_n STR_t "\0"
+#define STRING_punct0 STR_p STR_u STR_n STR_c STR_t "\0"
+#define STRING_space0 STR_s STR_p STR_a STR_c STR_e "\0"
+#define STRING_word0 STR_w STR_o STR_r STR_d "\0"
+#define STRING_xdigit STR_x STR_d STR_i STR_g STR_i STR_t
+
+#define STRING_DEFINE STR_D STR_E STR_F STR_I STR_N STR_E
+#define STRING_VERSION STR_V STR_E STR_R STR_S STR_I STR_O STR_N
+#define STRING_WEIRD_STARTWORD STR_LEFT_SQUARE_BRACKET STR_COLON STR_LESS_THAN_SIGN STR_COLON STR_RIGHT_SQUARE_BRACKET STR_RIGHT_SQUARE_BRACKET
+#define STRING_WEIRD_ENDWORD STR_LEFT_SQUARE_BRACKET STR_COLON STR_GREATER_THAN_SIGN STR_COLON STR_RIGHT_SQUARE_BRACKET STR_RIGHT_SQUARE_BRACKET
+
+#define STRING_CR_RIGHTPAR STR_C STR_R STR_RIGHT_PARENTHESIS
+#define STRING_LF_RIGHTPAR STR_L STR_F STR_RIGHT_PARENTHESIS
+#define STRING_CRLF_RIGHTPAR STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS
+#define STRING_ANY_RIGHTPAR STR_A STR_N STR_Y STR_RIGHT_PARENTHESIS
+#define STRING_ANYCRLF_RIGHTPAR STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS
+#define STRING_NUL_RIGHTPAR STR_N STR_U STR_L STR_RIGHT_PARENTHESIS
+#define STRING_BSR_ANYCRLF_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS
+#define STRING_BSR_UNICODE_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_U STR_N STR_I STR_C STR_O STR_D STR_E STR_RIGHT_PARENTHESIS
+#define STRING_UTF8_RIGHTPAR STR_U STR_T STR_F STR_8 STR_RIGHT_PARENTHESIS
+#define STRING_UTF16_RIGHTPAR STR_U STR_T STR_F STR_1 STR_6 STR_RIGHT_PARENTHESIS
+#define STRING_UTF32_RIGHTPAR STR_U STR_T STR_F STR_3 STR_2 STR_RIGHT_PARENTHESIS
+#define STRING_UTF_RIGHTPAR STR_U STR_T STR_F STR_RIGHT_PARENTHESIS
+#define STRING_UCP_RIGHTPAR STR_U STR_C STR_P STR_RIGHT_PARENTHESIS
+#define STRING_NO_AUTO_POSSESS_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_A STR_U STR_T STR_O STR_UNDERSCORE STR_P STR_O STR_S STR_S STR_E STR_S STR_S STR_RIGHT_PARENTHESIS
+#define STRING_NO_DOTSTAR_ANCHOR_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_D STR_O STR_T STR_S STR_T STR_A STR_R STR_UNDERSCORE STR_A STR_N STR_C STR_H STR_O STR_R STR_RIGHT_PARENTHESIS
+#define STRING_NO_JIT_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_J STR_I STR_T STR_RIGHT_PARENTHESIS
+#define STRING_NO_START_OPT_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_S STR_T STR_A STR_R STR_T STR_UNDERSCORE STR_O STR_P STR_T STR_RIGHT_PARENTHESIS
+#define STRING_NOTEMPTY_RIGHTPAR STR_N STR_O STR_T STR_E STR_M STR_P STR_T STR_Y STR_RIGHT_PARENTHESIS
+#define STRING_NOTEMPTY_ATSTART_RIGHTPAR STR_N STR_O STR_T STR_E STR_M STR_P STR_T STR_Y STR_UNDERSCORE STR_A STR_T STR_S STR_T STR_A STR_R STR_T STR_RIGHT_PARENTHESIS
+#define STRING_LIMIT_HEAP_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_H STR_E STR_A STR_P STR_EQUALS_SIGN
+#define STRING_LIMIT_MATCH_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_M STR_A STR_T STR_C STR_H STR_EQUALS_SIGN
+#define STRING_LIMIT_DEPTH_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_D STR_E STR_P STR_T STR_H STR_EQUALS_SIGN
+#define STRING_LIMIT_RECURSION_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_R STR_E STR_C STR_U STR_R STR_S STR_I STR_O STR_N STR_EQUALS_SIGN
+#define STRING_MARK STR_M STR_A STR_R STR_K
+
+#define STRING_bc STR_b STR_c
+#define STRING_bidiclass STR_b STR_i STR_d STR_i STR_c STR_l STR_a STR_s STR_s
+#define STRING_sc STR_s STR_c
+#define STRING_script STR_s STR_c STR_r STR_i STR_p STR_t
+#define STRING_scriptextensions STR_s STR_c STR_r STR_i STR_p STR_t STR_e STR_x STR_t STR_e STR_n STR_s STR_i STR_o STR_n STR_s
+#define STRING_scx STR_s STR_c STR_x
+
+
+#endif /* SUPPORT_UNICODE */
+
+/* -------------------- End of character and string names -------------------*/
+
+/* -------------------- Definitions for compiled patterns -------------------*/
+
+/* Codes for different types of Unicode property. If these definitions are
+changed, the autopossessifying table in pcre2_auto_possess.c must be updated to
+match. */
+
+#define PT_ANY 0 /* Any property - matches all chars */
+#define PT_LAMP 1 /* L& - the union of Lu, Ll, Lt */
+#define PT_GC 2 /* Specified general characteristic (e.g. L) */
+#define PT_PC 3 /* Specified particular characteristic (e.g. Lu) */
+#define PT_SC 4 /* Script only (e.g. Han) */
+#define PT_SCX 5 /* Script extensions (includes SC) */
+#define PT_ALNUM 6 /* Alphanumeric - the union of L and N */
+#define PT_SPACE 7 /* Perl space - general category Z plus 9,10,12,13 */
+#define PT_PXSPACE 8 /* POSIX space - Z plus 9,10,11,12,13 */
+#define PT_WORD 9 /* Word - L plus N plus underscore */
+#define PT_CLIST 10 /* Pseudo-property: match character list */
+#define PT_UCNC 11 /* Universal Character nameable character */
+#define PT_BIDICL 12 /* Specified bidi class */
+#define PT_BOOL 13 /* Boolean property */
+#define PT_TABSIZE 14 /* Size of square table for autopossessify tests */
+
+/* The following special properties are used only in XCLASS items, when POSIX
+classes are specified and PCRE2_UCP is set - in other words, for Unicode
+handling of these classes. They are not available via the \p or \P escapes like
+those in the above list, and so they do not take part in the autopossessifying
+table. */
+
+#define PT_PXGRAPH 14 /* [:graph:] - characters that mark the paper */
+#define PT_PXPRINT 15 /* [:print:] - [:graph:] plus non-control spaces */
+#define PT_PXPUNCT 16 /* [:punct:] - punctuation characters */
+
+/* This value is used when parsing \p and \P escapes to indicate that neither
+\p{script:...} nor \p{scx:...} has been encountered. */
+
+#define PT_NOTSCRIPT 255
+
+/* Flag bits and data types for the extended class (OP_XCLASS) for classes that
+contain characters with values greater than 255. */
+
+#define XCL_NOT 0x01 /* Flag: this is a negative class */
+#define XCL_MAP 0x02 /* Flag: a 32-byte map is present */
+#define XCL_HASPROP 0x04 /* Flag: property checks are present. */
+
+#define XCL_END 0 /* Marks end of individual items */
+#define XCL_SINGLE 1 /* Single item (one multibyte char) follows */
+#define XCL_RANGE 2 /* A range (two multibyte chars) follows */
+#define XCL_PROP 3 /* Unicode property (2-byte property code follows) */
+#define XCL_NOTPROP 4 /* Unicode inverted property (ditto) */
+
+/* These are escaped items that aren't just an encoding of a particular data
+value such as \n. They must have non-zero values, as check_escape() returns 0
+for a data character. In the escapes[] table in pcre2_compile.c their values
+are negated in order to distinguish them from data values.
+
+They must appear here in the same order as in the opcode definitions below, up
+to ESC_z. There's a dummy for OP_ALLANY because it corresponds to "." in DOTALL
+mode rather than an escape sequence. It is also used for [^] in JavaScript
+compatibility mode, and for \C in non-utf mode. In non-DOTALL mode, "." behaves
+like \N.
+
+Negative numbers are used to encode a backreference (\1, \2, \3, etc.) in
+check_escape(). There are tests in the code for an escape greater than ESC_b
+and less than ESC_Z to detect the types that may be repeated. These are the
+types that consume characters. If any new escapes are put in between that don't
+consume a character, that code will have to change. */
+
+enum { ESC_A = 1, ESC_G, ESC_K, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s,
+ ESC_W, ESC_w, ESC_N, ESC_dum, ESC_C, ESC_P, ESC_p, ESC_R, ESC_H,
+ ESC_h, ESC_V, ESC_v, ESC_X, ESC_Z, ESC_z,
+ ESC_E, ESC_Q, ESC_g, ESC_k };
+
+
+/********************** Opcode definitions ******************/
+
+/****** NOTE NOTE NOTE ******
+
+Starting from 1 (i.e. after OP_END), the values up to OP_EOD must correspond in
+order to the list of escapes immediately above. Furthermore, values up to
+OP_DOLLM must not be changed without adjusting the table called autoposstab in
+pcre2_auto_possess.c.
+
+Whenever this list is updated, the two macro definitions that follow must be
+updated to match. The possessification table called "opcode_possessify" in
+pcre2_compile.c must also be updated, and also the tables called "coptable"
+and "poptable" in pcre2_dfa_match.c.
+
+****** NOTE NOTE NOTE ******/
+
+
+/* The values between FIRST_AUTOTAB_OP and LAST_AUTOTAB_RIGHT_OP, inclusive,
+are used in a table for deciding whether a repeated character type can be
+auto-possessified. */
+
+#define FIRST_AUTOTAB_OP OP_NOT_DIGIT
+#define LAST_AUTOTAB_LEFT_OP OP_EXTUNI
+#define LAST_AUTOTAB_RIGHT_OP OP_DOLLM
+
+enum {
+ OP_END, /* 0 End of pattern */
+
+ /* Values corresponding to backslashed metacharacters */
+
+ OP_SOD, /* 1 Start of data: \A */
+ OP_SOM, /* 2 Start of match (subject + offset): \G */
+ OP_SET_SOM, /* 3 Set start of match (\K) */
+ OP_NOT_WORD_BOUNDARY, /* 4 \B */
+ OP_WORD_BOUNDARY, /* 5 \b */
+ OP_NOT_DIGIT, /* 6 \D */
+ OP_DIGIT, /* 7 \d */
+ OP_NOT_WHITESPACE, /* 8 \S */
+ OP_WHITESPACE, /* 9 \s */
+ OP_NOT_WORDCHAR, /* 10 \W */
+ OP_WORDCHAR, /* 11 \w */
+
+ OP_ANY, /* 12 Match any character except newline (\N) */
+ OP_ALLANY, /* 13 Match any character */
+ OP_ANYBYTE, /* 14 Match any byte (\C); different to OP_ANY for UTF-8 */
+ OP_NOTPROP, /* 15 \P (not Unicode property) */
+ OP_PROP, /* 16 \p (Unicode property) */
+ OP_ANYNL, /* 17 \R (any newline sequence) */
+ OP_NOT_HSPACE, /* 18 \H (not horizontal whitespace) */
+ OP_HSPACE, /* 19 \h (horizontal whitespace) */
+ OP_NOT_VSPACE, /* 20 \V (not vertical whitespace) */
+ OP_VSPACE, /* 21 \v (vertical whitespace) */
+ OP_EXTUNI, /* 22 \X (extended Unicode sequence */
+ OP_EODN, /* 23 End of data or \n at end of data (\Z) */
+ OP_EOD, /* 24 End of data (\z) */
+
+ /* Line end assertions */
+
+ OP_DOLL, /* 25 End of line - not multiline */
+ OP_DOLLM, /* 26 End of line - multiline */
+ OP_CIRC, /* 27 Start of line - not multiline */
+ OP_CIRCM, /* 28 Start of line - multiline */
+
+ /* Single characters; caseful must precede the caseless ones, and these
+ must remain in this order, and adjacent. */
+
+ OP_CHAR, /* 29 Match one character, casefully */
+ OP_CHARI, /* 30 Match one character, caselessly */
+ OP_NOT, /* 31 Match one character, not the given one, casefully */
+ OP_NOTI, /* 32 Match one character, not the given one, caselessly */
+
+ /* The following sets of 13 opcodes must always be kept in step because
+ the offset from the first one is used to generate the others. */
+
+ /* Repeated characters; caseful must precede the caseless ones */
+
+ OP_STAR, /* 33 The maximizing and minimizing versions of */
+ OP_MINSTAR, /* 34 these six opcodes must come in pairs, with */
+ OP_PLUS, /* 35 the minimizing one second. */
+ OP_MINPLUS, /* 36 */
+ OP_QUERY, /* 37 */
+ OP_MINQUERY, /* 38 */
+
+ OP_UPTO, /* 39 From 0 to n matches of one character, caseful*/
+ OP_MINUPTO, /* 40 */
+ OP_EXACT, /* 41 Exactly n matches */
+
+ OP_POSSTAR, /* 42 Possessified star, caseful */
+ OP_POSPLUS, /* 43 Possessified plus, caseful */
+ OP_POSQUERY, /* 44 Posesssified query, caseful */
+ OP_POSUPTO, /* 45 Possessified upto, caseful */
+
+ /* Repeated characters; caseless must follow the caseful ones */
+
+ OP_STARI, /* 46 */
+ OP_MINSTARI, /* 47 */
+ OP_PLUSI, /* 48 */
+ OP_MINPLUSI, /* 49 */
+ OP_QUERYI, /* 50 */
+ OP_MINQUERYI, /* 51 */
+
+ OP_UPTOI, /* 52 From 0 to n matches of one character, caseless */
+ OP_MINUPTOI, /* 53 */
+ OP_EXACTI, /* 54 */
+
+ OP_POSSTARI, /* 55 Possessified star, caseless */
+ OP_POSPLUSI, /* 56 Possessified plus, caseless */
+ OP_POSQUERYI, /* 57 Posesssified query, caseless */
+ OP_POSUPTOI, /* 58 Possessified upto, caseless */
+
+ /* The negated ones must follow the non-negated ones, and match them */
+ /* Negated repeated character, caseful; must precede the caseless ones */
+
+ OP_NOTSTAR, /* 59 The maximizing and minimizing versions of */
+ OP_NOTMINSTAR, /* 60 these six opcodes must come in pairs, with */
+ OP_NOTPLUS, /* 61 the minimizing one second. They must be in */
+ OP_NOTMINPLUS, /* 62 exactly the same order as those above. */
+ OP_NOTQUERY, /* 63 */
+ OP_NOTMINQUERY, /* 64 */
+
+ OP_NOTUPTO, /* 65 From 0 to n matches, caseful */
+ OP_NOTMINUPTO, /* 66 */
+ OP_NOTEXACT, /* 67 Exactly n matches */
+
+ OP_NOTPOSSTAR, /* 68 Possessified versions, caseful */
+ OP_NOTPOSPLUS, /* 69 */
+ OP_NOTPOSQUERY, /* 70 */
+ OP_NOTPOSUPTO, /* 71 */
+
+ /* Negated repeated character, caseless; must follow the caseful ones */
+
+ OP_NOTSTARI, /* 72 */
+ OP_NOTMINSTARI, /* 73 */
+ OP_NOTPLUSI, /* 74 */
+ OP_NOTMINPLUSI, /* 75 */
+ OP_NOTQUERYI, /* 76 */
+ OP_NOTMINQUERYI, /* 77 */
+
+ OP_NOTUPTOI, /* 78 From 0 to n matches, caseless */
+ OP_NOTMINUPTOI, /* 79 */
+ OP_NOTEXACTI, /* 80 Exactly n matches */
+
+ OP_NOTPOSSTARI, /* 81 Possessified versions, caseless */
+ OP_NOTPOSPLUSI, /* 82 */
+ OP_NOTPOSQUERYI, /* 83 */
+ OP_NOTPOSUPTOI, /* 84 */
+
+ /* Character types */
+
+ OP_TYPESTAR, /* 85 The maximizing and minimizing versions of */
+ OP_TYPEMINSTAR, /* 86 these six opcodes must come in pairs, with */
+ OP_TYPEPLUS, /* 87 the minimizing one second. These codes must */
+ OP_TYPEMINPLUS, /* 88 be in exactly the same order as those above. */
+ OP_TYPEQUERY, /* 89 */
+ OP_TYPEMINQUERY, /* 90 */
+
+ OP_TYPEUPTO, /* 91 From 0 to n matches */
+ OP_TYPEMINUPTO, /* 92 */
+ OP_TYPEEXACT, /* 93 Exactly n matches */
+
+ OP_TYPEPOSSTAR, /* 94 Possessified versions */
+ OP_TYPEPOSPLUS, /* 95 */
+ OP_TYPEPOSQUERY, /* 96 */
+ OP_TYPEPOSUPTO, /* 97 */
+
+ /* These are used for character classes and back references; only the
+ first six are the same as the sets above. */
+
+ OP_CRSTAR, /* 98 The maximizing and minimizing versions of */
+ OP_CRMINSTAR, /* 99 all these opcodes must come in pairs, with */
+ OP_CRPLUS, /* 100 the minimizing one second. These codes must */
+ OP_CRMINPLUS, /* 101 be in exactly the same order as those above. */
+ OP_CRQUERY, /* 102 */
+ OP_CRMINQUERY, /* 103 */
+
+ OP_CRRANGE, /* 104 These are different to the three sets above. */
+ OP_CRMINRANGE, /* 105 */
+
+ OP_CRPOSSTAR, /* 106 Possessified versions */
+ OP_CRPOSPLUS, /* 107 */
+ OP_CRPOSQUERY, /* 108 */
+ OP_CRPOSRANGE, /* 109 */
+
+ /* End of quantifier opcodes */
+
+ OP_CLASS, /* 110 Match a character class, chars < 256 only */
+ OP_NCLASS, /* 111 Same, but the bitmap was created from a negative
+ class - the difference is relevant only when a
+ character > 255 is encountered. */
+ OP_XCLASS, /* 112 Extended class for handling > 255 chars within the
+ class. This does both positive and negative. */
+ OP_REF, /* 113 Match a back reference, casefully */
+ OP_REFI, /* 114 Match a back reference, caselessly */
+ OP_DNREF, /* 115 Match a duplicate name backref, casefully */
+ OP_DNREFI, /* 116 Match a duplicate name backref, caselessly */
+ OP_RECURSE, /* 117 Match a numbered subpattern (possibly recursive) */
+ OP_CALLOUT, /* 118 Call out to external function if provided */
+ OP_CALLOUT_STR, /* 119 Call out with string argument */
+
+ OP_ALT, /* 120 Start of alternation */
+ OP_KET, /* 121 End of group that doesn't have an unbounded repeat */
+ OP_KETRMAX, /* 122 These two must remain together and in this */
+ OP_KETRMIN, /* 123 order. They are for groups the repeat for ever. */
+ OP_KETRPOS, /* 124 Possessive unlimited repeat. */
+
+ /* The assertions must come before BRA, CBRA, ONCE, and COND. */
+
+ OP_REVERSE, /* 125 Move pointer back - used in lookbehind assertions */
+ OP_ASSERT, /* 126 Positive lookahead */
+ OP_ASSERT_NOT, /* 127 Negative lookahead */
+ OP_ASSERTBACK, /* 128 Positive lookbehind */
+ OP_ASSERTBACK_NOT, /* 129 Negative lookbehind */
+ OP_ASSERT_NA, /* 130 Positive non-atomic lookahead */
+ OP_ASSERTBACK_NA, /* 131 Positive non-atomic lookbehind */
+
+ /* ONCE, SCRIPT_RUN, BRA, BRAPOS, CBRA, CBRAPOS, and COND must come
+ immediately after the assertions, with ONCE first, as there's a test for >=
+ ONCE for a subpattern that isn't an assertion. The POS versions must
+ immediately follow the non-POS versions in each case. */
+
+ OP_ONCE, /* 132 Atomic group, contains captures */
+ OP_SCRIPT_RUN, /* 133 Non-capture, but check characters' scripts */
+ OP_BRA, /* 134 Start of non-capturing bracket */
+ OP_BRAPOS, /* 135 Ditto, with unlimited, possessive repeat */
+ OP_CBRA, /* 136 Start of capturing bracket */
+ OP_CBRAPOS, /* 137 Ditto, with unlimited, possessive repeat */
+ OP_COND, /* 138 Conditional group */
+
+ /* These five must follow the previous five, in the same order. There's a
+ check for >= SBRA to distinguish the two sets. */
+
+ OP_SBRA, /* 139 Start of non-capturing bracket, check empty */
+ OP_SBRAPOS, /* 149 Ditto, with unlimited, possessive repeat */
+ OP_SCBRA, /* 141 Start of capturing bracket, check empty */
+ OP_SCBRAPOS, /* 142 Ditto, with unlimited, possessive repeat */
+ OP_SCOND, /* 143 Conditional group, check empty */
+
+ /* The next two pairs must (respectively) be kept together. */
+
+ OP_CREF, /* 144 Used to hold a capture number as condition */
+ OP_DNCREF, /* 145 Used to point to duplicate names as a condition */
+ OP_RREF, /* 146 Used to hold a recursion number as condition */
+ OP_DNRREF, /* 147 Used to point to duplicate names as a condition */
+ OP_FALSE, /* 148 Always false (used by DEFINE and VERSION) */
+ OP_TRUE, /* 149 Always true (used by VERSION) */
+
+ OP_BRAZERO, /* 150 These two must remain together and in this */
+ OP_BRAMINZERO, /* 151 order. */
+ OP_BRAPOSZERO, /* 152 */
+
+ /* These are backtracking control verbs */
+
+ OP_MARK, /* 153 always has an argument */
+ OP_PRUNE, /* 154 */
+ OP_PRUNE_ARG, /* 155 same, but with argument */
+ OP_SKIP, /* 156 */
+ OP_SKIP_ARG, /* 157 same, but with argument */
+ OP_THEN, /* 158 */
+ OP_THEN_ARG, /* 159 same, but with argument */
+ OP_COMMIT, /* 160 */
+ OP_COMMIT_ARG, /* 161 same, but with argument */
+
+ /* These are forced failure and success verbs. FAIL and ACCEPT do accept an
+ argument, but these cases can be compiled as, for example, (*MARK:X)(*FAIL)
+ without the need for a special opcode. */
+
+ OP_FAIL, /* 162 */
+ OP_ACCEPT, /* 163 */
+ OP_ASSERT_ACCEPT, /* 164 Used inside assertions */
+ OP_CLOSE, /* 165 Used before OP_ACCEPT to close open captures */
+
+ /* This is used to skip a subpattern with a {0} quantifier */
+
+ OP_SKIPZERO, /* 166 */
+
+ /* This is used to identify a DEFINE group during compilation so that it can
+ be checked for having only one branch. It is changed to OP_FALSE before
+ compilation finishes. */
+
+ OP_DEFINE, /* 167 */
+
+ /* This is not an opcode, but is used to check that tables indexed by opcode
+ are the correct length, in order to catch updating errors - there have been
+ some in the past. */
+
+ OP_TABLE_LENGTH
+
+};
+
+/* *** NOTE NOTE NOTE *** Whenever the list above is updated, the two macro
+definitions that follow must also be updated to match. There are also tables
+called "opcode_possessify" in pcre2_compile.c and "coptable" and "poptable" in
+pcre2_dfa_match.c that must be updated. */
+
+
+/* This macro defines textual names for all the opcodes. These are used only
+for debugging, and some of them are only partial names. The macro is referenced
+only in pcre2_printint.c, which fills out the full names in many cases (and in
+some cases doesn't actually use these names at all). */
+
+#define OP_NAME_LIST \
+ "End", "\\A", "\\G", "\\K", "\\B", "\\b", "\\D", "\\d", \
+ "\\S", "\\s", "\\W", "\\w", "Any", "AllAny", "Anybyte", \
+ "notprop", "prop", "\\R", "\\H", "\\h", "\\V", "\\v", \
+ "extuni", "\\Z", "\\z", \
+ "$", "$", "^", "^", "char", "chari", "not", "noti", \
+ "*", "*?", "+", "+?", "?", "??", \
+ "{", "{", "{", \
+ "*+","++", "?+", "{", \
+ "*", "*?", "+", "+?", "?", "??", \
+ "{", "{", "{", \
+ "*+","++", "?+", "{", \
+ "*", "*?", "+", "+?", "?", "??", \
+ "{", "{", "{", \
+ "*+","++", "?+", "{", \
+ "*", "*?", "+", "+?", "?", "??", \
+ "{", "{", "{", \
+ "*+","++", "?+", "{", \
+ "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \
+ "*+","++", "?+", "{", \
+ "*", "*?", "+", "+?", "?", "??", "{", "{", \
+ "*+","++", "?+", "{", \
+ "class", "nclass", "xclass", "Ref", "Refi", "DnRef", "DnRefi", \
+ "Recurse", "Callout", "CalloutStr", \
+ "Alt", "Ket", "KetRmax", "KetRmin", "KetRpos", \
+ "Reverse", "Assert", "Assert not", \
+ "Assert back", "Assert back not", \
+ "Non-atomic assert", "Non-atomic assert back", \
+ "Once", \
+ "Script run", \
+ "Bra", "BraPos", "CBra", "CBraPos", \
+ "Cond", \
+ "SBra", "SBraPos", "SCBra", "SCBraPos", \
+ "SCond", \
+ "Cond ref", "Cond dnref", "Cond rec", "Cond dnrec", \
+ "Cond false", "Cond true", \
+ "Brazero", "Braminzero", "Braposzero", \
+ "*MARK", "*PRUNE", "*PRUNE", "*SKIP", "*SKIP", \
+ "*THEN", "*THEN", "*COMMIT", "*COMMIT", "*FAIL", \
+ "*ACCEPT", "*ASSERT_ACCEPT", \
+ "Close", "Skip zero", "Define"
+
+
+/* This macro defines the length of fixed length operations in the compiled
+regex. The lengths are used when searching for specific things, and also in the
+debugging printing of a compiled regex. We use a macro so that it can be
+defined close to the definitions of the opcodes themselves.
+
+As things have been extended, some of these are no longer fixed lenths, but are
+minima instead. For example, the length of a single-character repeat may vary
+in UTF-8 mode. The code that uses this table must know about such things. */
+
+#define OP_LENGTHS \
+ 1, /* End */ \
+ 1, 1, 1, 1, 1, /* \A, \G, \K, \B, \b */ \
+ 1, 1, 1, 1, 1, 1, /* \D, \d, \S, \s, \W, \w */ \
+ 1, 1, 1, /* Any, AllAny, Anybyte */ \
+ 3, 3, /* \P, \p */ \
+ 1, 1, 1, 1, 1, /* \R, \H, \h, \V, \v */ \
+ 1, /* \X */ \
+ 1, 1, 1, 1, 1, 1, /* \Z, \z, $, $M ^, ^M */ \
+ 2, /* Char - the minimum length */ \
+ 2, /* Chari - the minimum length */ \
+ 2, /* not */ \
+ 2, /* noti */ \
+ /* Positive single-char repeats ** These are */ \
+ 2, 2, 2, 2, 2, 2, /* *, *?, +, +?, ?, ?? ** minima in */ \
+ 2+IMM2_SIZE, 2+IMM2_SIZE, /* upto, minupto ** mode */ \
+ 2+IMM2_SIZE, /* exact */ \
+ 2, 2, 2, 2+IMM2_SIZE, /* *+, ++, ?+, upto+ */ \
+ 2, 2, 2, 2, 2, 2, /* *I, *?I, +I, +?I, ?I, ??I ** UTF-8 */ \
+ 2+IMM2_SIZE, 2+IMM2_SIZE, /* upto I, minupto I */ \
+ 2+IMM2_SIZE, /* exact I */ \
+ 2, 2, 2, 2+IMM2_SIZE, /* *+I, ++I, ?+I, upto+I */ \
+ /* Negative single-char repeats - only for chars < 256 */ \
+ 2, 2, 2, 2, 2, 2, /* NOT *, *?, +, +?, ?, ?? */ \
+ 2+IMM2_SIZE, 2+IMM2_SIZE, /* NOT upto, minupto */ \
+ 2+IMM2_SIZE, /* NOT exact */ \
+ 2, 2, 2, 2+IMM2_SIZE, /* Possessive NOT *, +, ?, upto */ \
+ 2, 2, 2, 2, 2, 2, /* NOT *I, *?I, +I, +?I, ?I, ??I */ \
+ 2+IMM2_SIZE, 2+IMM2_SIZE, /* NOT upto I, minupto I */ \
+ 2+IMM2_SIZE, /* NOT exact I */ \
+ 2, 2, 2, 2+IMM2_SIZE, /* Possessive NOT *I, +I, ?I, upto I */ \
+ /* Positive type repeats */ \
+ 2, 2, 2, 2, 2, 2, /* Type *, *?, +, +?, ?, ?? */ \
+ 2+IMM2_SIZE, 2+IMM2_SIZE, /* Type upto, minupto */ \
+ 2+IMM2_SIZE, /* Type exact */ \
+ 2, 2, 2, 2+IMM2_SIZE, /* Possessive *+, ++, ?+, upto+ */ \
+ /* Character class & ref repeats */ \
+ 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ \
+ 1+2*IMM2_SIZE, 1+2*IMM2_SIZE, /* CRRANGE, CRMINRANGE */ \
+ 1, 1, 1, 1+2*IMM2_SIZE, /* Possessive *+, ++, ?+, CRPOSRANGE */ \
+ 1+(32/sizeof(PCRE2_UCHAR)), /* CLASS */ \
+ 1+(32/sizeof(PCRE2_UCHAR)), /* NCLASS */ \
+ 0, /* XCLASS - variable length */ \
+ 1+IMM2_SIZE, /* REF */ \
+ 1+IMM2_SIZE, /* REFI */ \
+ 1+2*IMM2_SIZE, /* DNREF */ \
+ 1+2*IMM2_SIZE, /* DNREFI */ \
+ 1+LINK_SIZE, /* RECURSE */ \
+ 1+2*LINK_SIZE+1, /* CALLOUT */ \
+ 0, /* CALLOUT_STR - variable length */ \
+ 1+LINK_SIZE, /* Alt */ \
+ 1+LINK_SIZE, /* Ket */ \
+ 1+LINK_SIZE, /* KetRmax */ \
+ 1+LINK_SIZE, /* KetRmin */ \
+ 1+LINK_SIZE, /* KetRpos */ \
+ 1+LINK_SIZE, /* Reverse */ \
+ 1+LINK_SIZE, /* Assert */ \
+ 1+LINK_SIZE, /* Assert not */ \
+ 1+LINK_SIZE, /* Assert behind */ \
+ 1+LINK_SIZE, /* Assert behind not */ \
+ 1+LINK_SIZE, /* NA Assert */ \
+ 1+LINK_SIZE, /* NA Assert behind */ \
+ 1+LINK_SIZE, /* ONCE */ \
+ 1+LINK_SIZE, /* SCRIPT_RUN */ \
+ 1+LINK_SIZE, /* BRA */ \
+ 1+LINK_SIZE, /* BRAPOS */ \
+ 1+LINK_SIZE+IMM2_SIZE, /* CBRA */ \
+ 1+LINK_SIZE+IMM2_SIZE, /* CBRAPOS */ \
+ 1+LINK_SIZE, /* COND */ \
+ 1+LINK_SIZE, /* SBRA */ \
+ 1+LINK_SIZE, /* SBRAPOS */ \
+ 1+LINK_SIZE+IMM2_SIZE, /* SCBRA */ \
+ 1+LINK_SIZE+IMM2_SIZE, /* SCBRAPOS */ \
+ 1+LINK_SIZE, /* SCOND */ \
+ 1+IMM2_SIZE, 1+2*IMM2_SIZE, /* CREF, DNCREF */ \
+ 1+IMM2_SIZE, 1+2*IMM2_SIZE, /* RREF, DNRREF */ \
+ 1, 1, /* FALSE, TRUE */ \
+ 1, 1, 1, /* BRAZERO, BRAMINZERO, BRAPOSZERO */ \
+ 3, 1, 3, /* MARK, PRUNE, PRUNE_ARG */ \
+ 1, 3, /* SKIP, SKIP_ARG */ \
+ 1, 3, /* THEN, THEN_ARG */ \
+ 1, 3, /* COMMIT, COMMIT_ARG */ \
+ 1, 1, 1, /* FAIL, ACCEPT, ASSERT_ACCEPT */ \
+ 1+IMM2_SIZE, 1, /* CLOSE, SKIPZERO */ \
+ 1 /* DEFINE */
+
+/* A magic value for OP_RREF to indicate the "any recursion" condition. */
+
+#define RREF_ANY 0xffff
+
+
+/* ---------- Private structures that are mode-independent. ---------- */
+
+/* Structure to hold data for custom memory management. */
+
+typedef struct pcre2_memctl {
+ void * (*malloc)(size_t, void *);
+ void (*free)(void *, void *);
+ void *memory_data;
+} pcre2_memctl;
+
+/* Structure for building a chain of open capturing subpatterns during
+compiling, so that instructions to close them can be compiled when (*ACCEPT) is
+encountered. */
+
+typedef struct open_capitem {
+ struct open_capitem *next; /* Chain link */
+ uint16_t number; /* Capture number */
+ uint16_t assert_depth; /* Assertion depth when opened */
+} open_capitem;
+
+/* Layout of the UCP type table that translates property names into types and
+codes. Each entry used to point directly to a name, but to reduce the number of
+relocations in shared libraries, it now has an offset into a single string
+instead. */
+
+typedef struct {
+ uint16_t name_offset;
+ uint16_t type;
+ uint16_t value;
+} ucp_type_table;
+
+/* Unicode character database (UCD) record format */
+
+typedef struct {
+ uint8_t script; /* ucp_Arabic, etc. */
+ uint8_t chartype; /* ucp_Cc, etc. (general categories) */
+ uint8_t gbprop; /* ucp_gbControl, etc. (grapheme break property) */
+ uint8_t caseset; /* offset to multichar other cases or zero */
+ int32_t other_case; /* offset to other case, or zero if none */
+ uint16_t scriptx_bidiclass; /* script extension (11 bit) and bidi class (5 bit) values */
+ uint16_t bprops; /* binary properties offset */
+} ucd_record;
+
+/* UCD access macros */
+
+#define UCD_BLOCK_SIZE 128
+#define REAL_GET_UCD(ch) (PRIV(ucd_records) + \
+ PRIV(ucd_stage2)[PRIV(ucd_stage1)[(int)(ch) / UCD_BLOCK_SIZE] * \
+ UCD_BLOCK_SIZE + (int)(ch) % UCD_BLOCK_SIZE])
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+#define GET_UCD(ch) ((ch > MAX_UTF_CODE_POINT)? \
+ PRIV(dummy_ucd_record) : REAL_GET_UCD(ch))
+#else
+#define GET_UCD(ch) REAL_GET_UCD(ch)
+#endif
+
+#define UCD_SCRIPTX_MASK 0x3ff
+#define UCD_BIDICLASS_SHIFT 11
+#define UCD_BPROPS_MASK 0xfff
+
+#define UCD_SCRIPTX_PROP(prop) ((prop)->scriptx_bidiclass & UCD_SCRIPTX_MASK)
+#define UCD_BIDICLASS_PROP(prop) ((prop)->scriptx_bidiclass >> UCD_BIDICLASS_SHIFT)
+#define UCD_BPROPS_PROP(prop) ((prop)->bprops & UCD_BPROPS_MASK)
+
+#define UCD_CHARTYPE(ch) GET_UCD(ch)->chartype
+#define UCD_SCRIPT(ch) GET_UCD(ch)->script
+#define UCD_CATEGORY(ch) PRIV(ucp_gentype)[UCD_CHARTYPE(ch)]
+#define UCD_GRAPHBREAK(ch) GET_UCD(ch)->gbprop
+#define UCD_CASESET(ch) GET_UCD(ch)->caseset
+#define UCD_OTHERCASE(ch) ((uint32_t)((int)ch + (int)(GET_UCD(ch)->other_case)))
+#define UCD_SCRIPTX(ch) UCD_SCRIPTX_PROP(GET_UCD(ch))
+#define UCD_BPROPS(ch) UCD_BPROPS_PROP(GET_UCD(ch))
+#define UCD_BIDICLASS(ch) UCD_BIDICLASS_PROP(GET_UCD(ch))
+
+/* The "scriptx" and bprops fields contain offsets into vectors of 32-bit words
+that form a bitmap representing a list of scripts or boolean properties. These
+macros test or set a bit in the map by number. */
+
+#define MAPBIT(map,n) ((map)[(n)/32]&(1u<<((n)%32)))
+#define MAPSET(map,n) ((map)[(n)/32]|=(1u<<((n)%32)))
+
+/* Header for serialized pcre2 codes. */
+
+typedef struct pcre2_serialized_data {
+ uint32_t magic;
+ uint32_t version;
+ uint32_t config;
+ int32_t number_of_codes;
+} pcre2_serialized_data;
+
+
+
+/* ----------------- Items that need PCRE2_CODE_UNIT_WIDTH ----------------- */
+
+/* When this file is included by pcre2test, PCRE2_CODE_UNIT_WIDTH is defined as
+0, so the following items are omitted. */
+
+#if defined PCRE2_CODE_UNIT_WIDTH && PCRE2_CODE_UNIT_WIDTH != 0
+
+/* EBCDIC is supported only for the 8-bit library. */
+
+#if defined EBCDIC && PCRE2_CODE_UNIT_WIDTH != 8
+#error EBCDIC is not supported for the 16-bit or 32-bit libraries
+#endif
+
+/* This is the largest non-UTF code point. */
+
+#define MAX_NON_UTF_CHAR (0xffffffffU >> (32 - PCRE2_CODE_UNIT_WIDTH))
+
+/* Internal shared data tables and variables. These are used by more than one
+of the exported public functions. They have to be "external" in the C sense,
+but are not part of the PCRE2 public API. Although the data for some of them is
+identical in all libraries, they must have different names so that multiple
+libraries can be simultaneously linked to a single application. However, UTF-8
+tables are needed only when compiling the 8-bit library. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+extern const int PRIV(utf8_table1)[];
+extern const int PRIV(utf8_table1_size);
+extern const int PRIV(utf8_table2)[];
+extern const int PRIV(utf8_table3)[];
+extern const uint8_t PRIV(utf8_table4)[];
+#endif
+
+#define _pcre2_OP_lengths PCRE2_SUFFIX(_pcre2_OP_lengths_)
+#define _pcre2_callout_end_delims PCRE2_SUFFIX(_pcre2_callout_end_delims_)
+#define _pcre2_callout_start_delims PCRE2_SUFFIX(_pcre2_callout_start_delims_)
+#define _pcre2_default_compile_context PCRE2_SUFFIX(_pcre2_default_compile_context_)
+#define _pcre2_default_convert_context PCRE2_SUFFIX(_pcre2_default_convert_context_)
+#define _pcre2_default_match_context PCRE2_SUFFIX(_pcre2_default_match_context_)
+#define _pcre2_default_tables PCRE2_SUFFIX(_pcre2_default_tables_)
+#if PCRE2_CODE_UNIT_WIDTH == 32
+#define _pcre2_dummy_ucd_record PCRE2_SUFFIX(_pcre2_dummy_ucd_record_)
+#endif
+#define _pcre2_hspace_list PCRE2_SUFFIX(_pcre2_hspace_list_)
+#define _pcre2_vspace_list PCRE2_SUFFIX(_pcre2_vspace_list_)
+#define _pcre2_ucd_boolprop_sets PCRE2_SUFFIX(_pcre2_ucd_boolprop_sets_)
+#define _pcre2_ucd_caseless_sets PCRE2_SUFFIX(_pcre2_ucd_caseless_sets_)
+#define _pcre2_ucd_digit_sets PCRE2_SUFFIX(_pcre2_ucd_digit_sets_)
+#define _pcre2_ucd_script_sets PCRE2_SUFFIX(_pcre2_ucd_script_sets_)
+#define _pcre2_ucd_records PCRE2_SUFFIX(_pcre2_ucd_records_)
+#define _pcre2_ucd_stage1 PCRE2_SUFFIX(_pcre2_ucd_stage1_)
+#define _pcre2_ucd_stage2 PCRE2_SUFFIX(_pcre2_ucd_stage2_)
+#define _pcre2_ucp_gbtable PCRE2_SUFFIX(_pcre2_ucp_gbtable_)
+#define _pcre2_ucp_gentype PCRE2_SUFFIX(_pcre2_ucp_gentype_)
+#define _pcre2_ucp_typerange PCRE2_SUFFIX(_pcre2_ucp_typerange_)
+#define _pcre2_unicode_version PCRE2_SUFFIX(_pcre2_unicode_version_)
+#define _pcre2_utt PCRE2_SUFFIX(_pcre2_utt_)
+#define _pcre2_utt_names PCRE2_SUFFIX(_pcre2_utt_names_)
+#define _pcre2_utt_size PCRE2_SUFFIX(_pcre2_utt_size_)
+
+extern const uint8_t PRIV(OP_lengths)[];
+extern const uint32_t PRIV(callout_end_delims)[];
+extern const uint32_t PRIV(callout_start_delims)[];
+extern const pcre2_compile_context PRIV(default_compile_context);
+extern const pcre2_convert_context PRIV(default_convert_context);
+extern const pcre2_match_context PRIV(default_match_context);
+extern const uint8_t PRIV(default_tables)[];
+extern const uint32_t PRIV(hspace_list)[];
+extern const uint32_t PRIV(vspace_list)[];
+extern const uint32_t PRIV(ucd_boolprop_sets)[];
+extern const uint32_t PRIV(ucd_caseless_sets)[];
+extern const uint32_t PRIV(ucd_digit_sets)[];
+extern const uint32_t PRIV(ucd_script_sets)[];
+extern const ucd_record PRIV(ucd_records)[];
+#if PCRE2_CODE_UNIT_WIDTH == 32
+extern const ucd_record PRIV(dummy_ucd_record)[];
+#endif
+extern const uint16_t PRIV(ucd_stage1)[];
+extern const uint16_t PRIV(ucd_stage2)[];
+extern const uint32_t PRIV(ucp_gbtable)[];
+extern const uint32_t PRIV(ucp_gentype)[];
+#ifdef SUPPORT_JIT
+extern const int PRIV(ucp_typerange)[];
+#endif
+extern const char *PRIV(unicode_version);
+extern const ucp_type_table PRIV(utt)[];
+extern const char PRIV(utt_names)[];
+extern const size_t PRIV(utt_size);
+
+/* Mode-dependent macros and hidden and private structures are defined in a
+separate file so that pcre2test can include them at all supported widths. When
+compiling the library, PCRE2_CODE_UNIT_WIDTH will be defined, and we can
+include them at the appropriate width, after setting up suffix macros for the
+private structures. */
+
+#define branch_chain PCRE2_SUFFIX(branch_chain_)
+#define compile_block PCRE2_SUFFIX(compile_block_)
+#define dfa_match_block PCRE2_SUFFIX(dfa_match_block_)
+#define match_block PCRE2_SUFFIX(match_block_)
+#define named_group PCRE2_SUFFIX(named_group_)
+
+#include "pcre2_intmodedep.h"
+
+/* Private "external" functions. These are internal functions that are called
+from modules other than the one in which they are defined. They have to be
+"external" in the C sense, but are not part of the PCRE2 public API. They are
+not referenced from pcre2test, and must not be defined when no code unit width
+is available. */
+
+#define _pcre2_auto_possessify PCRE2_SUFFIX(_pcre2_auto_possessify_)
+#define _pcre2_check_escape PCRE2_SUFFIX(_pcre2_check_escape_)
+#define _pcre2_extuni PCRE2_SUFFIX(_pcre2_extuni_)
+#define _pcre2_find_bracket PCRE2_SUFFIX(_pcre2_find_bracket_)
+#define _pcre2_is_newline PCRE2_SUFFIX(_pcre2_is_newline_)
+#define _pcre2_jit_free_rodata PCRE2_SUFFIX(_pcre2_jit_free_rodata_)
+#define _pcre2_jit_free PCRE2_SUFFIX(_pcre2_jit_free_)
+#define _pcre2_jit_get_size PCRE2_SUFFIX(_pcre2_jit_get_size_)
+#define _pcre2_jit_get_target PCRE2_SUFFIX(_pcre2_jit_get_target_)
+#define _pcre2_memctl_malloc PCRE2_SUFFIX(_pcre2_memctl_malloc_)
+#define _pcre2_ord2utf PCRE2_SUFFIX(_pcre2_ord2utf_)
+#define _pcre2_script_run PCRE2_SUFFIX(_pcre2_script_run_)
+#define _pcre2_strcmp PCRE2_SUFFIX(_pcre2_strcmp_)
+#define _pcre2_strcmp_c8 PCRE2_SUFFIX(_pcre2_strcmp_c8_)
+#define _pcre2_strcpy_c8 PCRE2_SUFFIX(_pcre2_strcpy_c8_)
+#define _pcre2_strlen PCRE2_SUFFIX(_pcre2_strlen_)
+#define _pcre2_strncmp PCRE2_SUFFIX(_pcre2_strncmp_)
+#define _pcre2_strncmp_c8 PCRE2_SUFFIX(_pcre2_strncmp_c8_)
+#define _pcre2_study PCRE2_SUFFIX(_pcre2_study_)
+#define _pcre2_valid_utf PCRE2_SUFFIX(_pcre2_valid_utf_)
+#define _pcre2_was_newline PCRE2_SUFFIX(_pcre2_was_newline_)
+#define _pcre2_xclass PCRE2_SUFFIX(_pcre2_xclass_)
+
+extern int _pcre2_auto_possessify(PCRE2_UCHAR *,
+ const compile_block *);
+extern int _pcre2_check_escape(PCRE2_SPTR *, PCRE2_SPTR, uint32_t *,
+ int *, uint32_t, uint32_t, BOOL, compile_block *);
+extern PCRE2_SPTR _pcre2_extuni(uint32_t, PCRE2_SPTR, PCRE2_SPTR, PCRE2_SPTR,
+ BOOL, int *);
+extern PCRE2_SPTR _pcre2_find_bracket(PCRE2_SPTR, BOOL, int);
+extern BOOL _pcre2_is_newline(PCRE2_SPTR, uint32_t, PCRE2_SPTR,
+ uint32_t *, BOOL);
+extern void _pcre2_jit_free_rodata(void *, void *);
+extern void _pcre2_jit_free(void *, pcre2_memctl *);
+extern size_t _pcre2_jit_get_size(void *);
+const char * _pcre2_jit_get_target(void);
+extern void * _pcre2_memctl_malloc(size_t, pcre2_memctl *);
+extern unsigned int _pcre2_ord2utf(uint32_t, PCRE2_UCHAR *);
+extern BOOL _pcre2_script_run(PCRE2_SPTR, PCRE2_SPTR, BOOL);
+extern int _pcre2_strcmp(PCRE2_SPTR, PCRE2_SPTR);
+extern int _pcre2_strcmp_c8(PCRE2_SPTR, const char *);
+extern PCRE2_SIZE _pcre2_strcpy_c8(PCRE2_UCHAR *, const char *);
+extern PCRE2_SIZE _pcre2_strlen(PCRE2_SPTR);
+extern int _pcre2_strncmp(PCRE2_SPTR, PCRE2_SPTR, size_t);
+extern int _pcre2_strncmp_c8(PCRE2_SPTR, const char *, size_t);
+extern int _pcre2_study(pcre2_real_code *);
+extern int _pcre2_valid_utf(PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE *);
+extern BOOL _pcre2_was_newline(PCRE2_SPTR, uint32_t, PCRE2_SPTR,
+ uint32_t *, BOOL);
+extern BOOL _pcre2_xclass(uint32_t, PCRE2_SPTR, BOOL);
+
+/* This function is needed only when memmove() is not available. */
+
+#if !defined(VPCOMPAT) && !defined(HAVE_MEMMOVE)
+#define _pcre2_memmove PCRE2_SUFFIX(_pcre2_memmove)
+extern void * _pcre2_memmove(void *, const void *, size_t);
+#endif
+
+#endif /* PCRE2_CODE_UNIT_WIDTH */
+#endif /* PCRE2_INTERNAL_H_IDEMPOTENT_GUARD */
+
+/* End of pcre2_internal.h */
diff --git a/contrib/libs/pcre2/src/pcre2_intmodedep.h b/contrib/libs/pcre2/src/pcre2_intmodedep.h
new file mode 100644
index 00000000000..390e737a6ef
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_intmodedep.h
@@ -0,0 +1,934 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This module contains mode-dependent macro and structure definitions. The
+file is #included by pcre2_internal.h if PCRE2_CODE_UNIT_WIDTH is defined.
+These mode-dependent items are kept in a separate file so that they can also be
+#included multiple times for different code unit widths by pcre2test in order
+to have access to the hidden structures at all supported widths.
+
+Some of the mode-dependent macros are required at different widths for
+different parts of the pcre2test code (in particular, the included
+pcre_printint.c file). We undefine them here so that they can be re-defined for
+multiple inclusions. Not all of these are used in pcre2test, but it's easier
+just to undefine them all. */
+
+#undef ACROSSCHAR
+#undef BACKCHAR
+#undef BYTES2CU
+#undef CHMAX_255
+#undef CU2BYTES
+#undef FORWARDCHAR
+#undef FORWARDCHARTEST
+#undef GET
+#undef GET2
+#undef GETCHAR
+#undef GETCHARINC
+#undef GETCHARINCTEST
+#undef GETCHARLEN
+#undef GETCHARLENTEST
+#undef GETCHARTEST
+#undef GET_EXTRALEN
+#undef HAS_EXTRALEN
+#undef IMM2_SIZE
+#undef MAX_255
+#undef MAX_MARK
+#undef MAX_PATTERN_SIZE
+#undef MAX_UTF_SINGLE_CU
+#undef NOT_FIRSTCU
+#undef PUT
+#undef PUT2
+#undef PUT2INC
+#undef PUTCHAR
+#undef PUTINC
+#undef TABLE_GET
+
+
+
+/* -------------------------- MACROS ----------------------------- */
+
+/* PCRE keeps offsets in its compiled code as at least 16-bit quantities
+(always stored in big-endian order in 8-bit mode) by default. These are used,
+for example, to link from the start of a subpattern to its alternatives and its
+end. The use of 16 bits per offset limits the size of an 8-bit compiled regex
+to around 64K, which is big enough for almost everybody. However, I received a
+request for an even bigger limit. For this reason, and also to make the code
+easier to maintain, the storing and loading of offsets from the compiled code
+unit string is now handled by the macros that are defined here.
+
+The macros are controlled by the value of LINK_SIZE. This defaults to 2, but
+values of 3 or 4 are also supported. */
+
+/* ------------------- 8-bit support ------------------ */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+
+#if LINK_SIZE == 2
+#define PUT(a,n,d) \
+ (a[n] = (PCRE2_UCHAR)((d) >> 8)), \
+ (a[(n)+1] = (PCRE2_UCHAR)((d) & 255))
+#define GET(a,n) \
+ (unsigned int)(((a)[n] << 8) | (a)[(n)+1])
+#define MAX_PATTERN_SIZE (1 << 16)
+
+#elif LINK_SIZE == 3
+#define PUT(a,n,d) \
+ (a[n] = (PCRE2_UCHAR)((d) >> 16)), \
+ (a[(n)+1] = (PCRE2_UCHAR)((d) >> 8)), \
+ (a[(n)+2] = (PCRE2_UCHAR)((d) & 255))
+#define GET(a,n) \
+ (unsigned int)(((a)[n] << 16) | ((a)[(n)+1] << 8) | (a)[(n)+2])
+#define MAX_PATTERN_SIZE (1 << 24)
+
+#elif LINK_SIZE == 4
+#define PUT(a,n,d) \
+ (a[n] = (PCRE2_UCHAR)((d) >> 24)), \
+ (a[(n)+1] = (PCRE2_UCHAR)((d) >> 16)), \
+ (a[(n)+2] = (PCRE2_UCHAR)((d) >> 8)), \
+ (a[(n)+3] = (PCRE2_UCHAR)((d) & 255))
+#define GET(a,n) \
+ (unsigned int)(((a)[n] << 24) | ((a)[(n)+1] << 16) | ((a)[(n)+2] << 8) | (a)[(n)+3])
+#define MAX_PATTERN_SIZE (1 << 30) /* Keep it positive */
+
+#else
+#error LINK_SIZE must be 2, 3, or 4
+#endif
+
+
+/* ------------------- 16-bit support ------------------ */
+
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+
+#if LINK_SIZE == 2
+#undef LINK_SIZE
+#define LINK_SIZE 1
+#define PUT(a,n,d) \
+ (a[n] = (PCRE2_UCHAR)(d))
+#define GET(a,n) \
+ (a[n])
+#define MAX_PATTERN_SIZE (1 << 16)
+
+#elif LINK_SIZE == 3 || LINK_SIZE == 4
+#undef LINK_SIZE
+#define LINK_SIZE 2
+#define PUT(a,n,d) \
+ (a[n] = (PCRE2_UCHAR)((d) >> 16)), \
+ (a[(n)+1] = (PCRE2_UCHAR)((d) & 65535))
+#define GET(a,n) \
+ (unsigned int)(((a)[n] << 16) | (a)[(n)+1])
+#define MAX_PATTERN_SIZE (1 << 30) /* Keep it positive */
+
+#else
+#error LINK_SIZE must be 2, 3, or 4
+#endif
+
+
+/* ------------------- 32-bit support ------------------ */
+
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+#undef LINK_SIZE
+#define LINK_SIZE 1
+#define PUT(a,n,d) \
+ (a[n] = (d))
+#define GET(a,n) \
+ (a[n])
+#define MAX_PATTERN_SIZE (1 << 30) /* Keep it positive */
+
+#else
+#error Unsupported compiling mode
+#endif
+
+
+/* --------------- Other mode-specific macros ----------------- */
+
+/* PCRE uses some other (at least) 16-bit quantities that do not change when
+the size of offsets changes. There are used for repeat counts and for other
+things such as capturing parenthesis numbers in back references.
+
+Define the number of code units required to hold a 16-bit count/offset, and
+macros to load and store such a value. For reasons that I do not understand,
+the expression in the 8-bit GET2 macro is treated by gcc as a signed
+expression, even when a is declared as unsigned. It seems that any kind of
+arithmetic results in a signed value. Hence the cast. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#define IMM2_SIZE 2
+#define GET2(a,n) (unsigned int)(((a)[n] << 8) | (a)[(n)+1])
+#define PUT2(a,n,d) a[n] = (d) >> 8, a[(n)+1] = (d) & 255
+
+#else /* Code units are 16 or 32 bits */
+#define IMM2_SIZE 1
+#define GET2(a,n) a[n]
+#define PUT2(a,n,d) a[n] = d
+#endif
+
+/* Other macros that are different for 8-bit mode. The MAX_255 macro checks
+whether its argument, which is assumed to be one code unit, is less than 256.
+The CHMAX_255 macro does not assume one code unit. The maximum length of a MARK
+name must fit in one code unit; currently it is set to 255 or 65535. The
+TABLE_GET macro is used to access elements of tables containing exactly 256
+items. Its argument is a code unit. When code points can be greater than 255, a
+check is needed before accessing these tables. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#define MAX_255(c) TRUE
+#define MAX_MARK ((1u << 8) - 1)
+#define TABLE_GET(c, table, default) ((table)[c])
+#ifdef SUPPORT_UNICODE
+#define SUPPORT_WIDE_CHARS
+#define CHMAX_255(c) ((c) <= 255u)
+#else
+#define CHMAX_255(c) TRUE
+#endif /* SUPPORT_UNICODE */
+
+#else /* Code units are 16 or 32 bits */
+#define CHMAX_255(c) ((c) <= 255u)
+#define MAX_255(c) ((c) <= 255u)
+#define MAX_MARK ((1u << 16) - 1)
+#define SUPPORT_WIDE_CHARS
+#define TABLE_GET(c, table, default) (MAX_255(c)? ((table)[c]):(default))
+#endif
+
+
+/* ----------------- Character-handling macros ----------------- */
+
+/* There is a proposed future special "UTF-21" mode, in which only the lowest
+21 bits of a 32-bit character are interpreted as UTF, with the remaining 11
+high-order bits available to the application for other uses. In preparation for
+the future implementation of this mode, there are macros that load a data item
+and, if in this special mode, mask it to 21 bits. These macros all have names
+starting with UCHAR21. In all other modes, including the normal 32-bit
+library, the macros all have the same simple definitions. When the new mode is
+implemented, it is expected that these definitions will be varied appropriately
+using #ifdef when compiling the library that supports the special mode. */
+
+#define UCHAR21(eptr) (*(eptr))
+#define UCHAR21TEST(eptr) (*(eptr))
+#define UCHAR21INC(eptr) (*(eptr)++)
+#define UCHAR21INCTEST(eptr) (*(eptr)++)
+
+/* When UTF encoding is being used, a character is no longer just a single
+byte in 8-bit mode or a single short in 16-bit mode. The macros for character
+handling generate simple sequences when used in the basic mode, and more
+complicated ones for UTF characters. GETCHARLENTEST and other macros are not
+used when UTF is not supported. To make sure they can never even appear when
+UTF support is omitted, we don't even define them. */
+
+#ifndef SUPPORT_UNICODE
+
+/* #define MAX_UTF_SINGLE_CU */
+/* #define HAS_EXTRALEN(c) */
+/* #define GET_EXTRALEN(c) */
+/* #define NOT_FIRSTCU(c) */
+#define GETCHAR(c, eptr) c = *eptr;
+#define GETCHARTEST(c, eptr) c = *eptr;
+#define GETCHARINC(c, eptr) c = *eptr++;
+#define GETCHARINCTEST(c, eptr) c = *eptr++;
+#define GETCHARLEN(c, eptr, len) c = *eptr;
+#define PUTCHAR(c, p) (*p = c, 1)
+/* #define GETCHARLENTEST(c, eptr, len) */
+/* #define BACKCHAR(eptr) */
+/* #define FORWARDCHAR(eptr) */
+/* #define FORWARCCHARTEST(eptr,end) */
+/* #define ACROSSCHAR(condition, eptr, action) */
+
+#else /* SUPPORT_UNICODE */
+
+/* ------------------- 8-bit support ------------------ */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#define MAYBE_UTF_MULTI /* UTF chars may use multiple code units */
+
+/* The largest UTF code point that can be encoded as a single code unit. */
+
+#define MAX_UTF_SINGLE_CU 127
+
+/* Tests whether the code point needs extra characters to decode. */
+
+#define HAS_EXTRALEN(c) HASUTF8EXTRALEN(c)
+
+/* Returns with the additional number of characters if IS_MULTICHAR(c) is TRUE.
+Otherwise it has an undefined behaviour. */
+
+#define GET_EXTRALEN(c) (PRIV(utf8_table4)[(c) & 0x3fu])
+
+/* Returns TRUE, if the given value is not the first code unit of a UTF
+sequence. */
+
+#define NOT_FIRSTCU(c) (((c) & 0xc0u) == 0x80u)
+
+/* Get the next UTF-8 character, not advancing the pointer. This is called when
+we know we are in UTF-8 mode. */
+
+#define GETCHAR(c, eptr) \
+ c = *eptr; \
+ if (c >= 0xc0u) GETUTF8(c, eptr);
+
+/* Get the next UTF-8 character, testing for UTF-8 mode, and not advancing the
+pointer. */
+
+#define GETCHARTEST(c, eptr) \
+ c = *eptr; \
+ if (utf && c >= 0xc0u) GETUTF8(c, eptr);
+
+/* Get the next UTF-8 character, advancing the pointer. This is called when we
+know we are in UTF-8 mode. */
+
+#define GETCHARINC(c, eptr) \
+ c = *eptr++; \
+ if (c >= 0xc0u) GETUTF8INC(c, eptr);
+
+/* Get the next character, testing for UTF-8 mode, and advancing the pointer.
+This is called when we don't know if we are in UTF-8 mode. */
+
+#define GETCHARINCTEST(c, eptr) \
+ c = *eptr++; \
+ if (utf && c >= 0xc0u) GETUTF8INC(c, eptr);
+
+/* Get the next UTF-8 character, not advancing the pointer, incrementing length
+if there are extra bytes. This is called when we know we are in UTF-8 mode. */
+
+#define GETCHARLEN(c, eptr, len) \
+ c = *eptr; \
+ if (c >= 0xc0u) GETUTF8LEN(c, eptr, len);
+
+/* Get the next UTF-8 character, testing for UTF-8 mode, not advancing the
+pointer, incrementing length if there are extra bytes. This is called when we
+do not know if we are in UTF-8 mode. */
+
+#define GETCHARLENTEST(c, eptr, len) \
+ c = *eptr; \
+ if (utf && c >= 0xc0u) GETUTF8LEN(c, eptr, len);
+
+/* If the pointer is not at the start of a character, move it back until
+it is. This is called only in UTF-8 mode - we don't put a test within the macro
+because almost all calls are already within a block of UTF-8 only code. */
+
+#define BACKCHAR(eptr) while((*eptr & 0xc0u) == 0x80u) eptr--
+
+/* Same as above, just in the other direction. */
+#define FORWARDCHAR(eptr) while((*eptr & 0xc0u) == 0x80u) eptr++
+#define FORWARDCHARTEST(eptr,end) while(eptr < end && (*eptr & 0xc0u) == 0x80u) eptr++
+
+/* Same as above, but it allows a fully customizable form. */
+#define ACROSSCHAR(condition, eptr, action) \
+ while((condition) && ((*eptr) & 0xc0u) == 0x80u) action
+
+/* Deposit a character into memory, returning the number of code units. */
+
+#define PUTCHAR(c, p) ((utf && c > MAX_UTF_SINGLE_CU)? \
+ PRIV(ord2utf)(c,p) : (*p = c, 1))
+
+
+/* ------------------- 16-bit support ------------------ */
+
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+#define MAYBE_UTF_MULTI /* UTF chars may use multiple code units */
+
+/* The largest UTF code point that can be encoded as a single code unit. */
+
+#define MAX_UTF_SINGLE_CU 65535
+
+/* Tests whether the code point needs extra characters to decode. */
+
+#define HAS_EXTRALEN(c) (((c) & 0xfc00u) == 0xd800u)
+
+/* Returns with the additional number of characters if IS_MULTICHAR(c) is TRUE.
+Otherwise it has an undefined behaviour. */
+
+#define GET_EXTRALEN(c) 1
+
+/* Returns TRUE, if the given value is not the first code unit of a UTF
+sequence. */
+
+#define NOT_FIRSTCU(c) (((c) & 0xfc00u) == 0xdc00u)
+
+/* Base macro to pick up the low surrogate of a UTF-16 character, not
+advancing the pointer. */
+
+#define GETUTF16(c, eptr) \
+ { c = (((c & 0x3ffu) << 10) | (eptr[1] & 0x3ffu)) + 0x10000u; }
+
+/* Get the next UTF-16 character, not advancing the pointer. This is called when
+we know we are in UTF-16 mode. */
+
+#define GETCHAR(c, eptr) \
+ c = *eptr; \
+ if ((c & 0xfc00u) == 0xd800u) GETUTF16(c, eptr);
+
+/* Get the next UTF-16 character, testing for UTF-16 mode, and not advancing the
+pointer. */
+
+#define GETCHARTEST(c, eptr) \
+ c = *eptr; \
+ if (utf && (c & 0xfc00u) == 0xd800u) GETUTF16(c, eptr);
+
+/* Base macro to pick up the low surrogate of a UTF-16 character, advancing
+the pointer. */
+
+#define GETUTF16INC(c, eptr) \
+ { c = (((c & 0x3ffu) << 10) | (*eptr++ & 0x3ffu)) + 0x10000u; }
+
+/* Get the next UTF-16 character, advancing the pointer. This is called when we
+know we are in UTF-16 mode. */
+
+#define GETCHARINC(c, eptr) \
+ c = *eptr++; \
+ if ((c & 0xfc00u) == 0xd800u) GETUTF16INC(c, eptr);
+
+/* Get the next character, testing for UTF-16 mode, and advancing the pointer.
+This is called when we don't know if we are in UTF-16 mode. */
+
+#define GETCHARINCTEST(c, eptr) \
+ c = *eptr++; \
+ if (utf && (c & 0xfc00u) == 0xd800u) GETUTF16INC(c, eptr);
+
+/* Base macro to pick up the low surrogate of a UTF-16 character, not
+advancing the pointer, incrementing the length. */
+
+#define GETUTF16LEN(c, eptr, len) \
+ { c = (((c & 0x3ffu) << 10) | (eptr[1] & 0x3ffu)) + 0x10000u; len++; }
+
+/* Get the next UTF-16 character, not advancing the pointer, incrementing
+length if there is a low surrogate. This is called when we know we are in
+UTF-16 mode. */
+
+#define GETCHARLEN(c, eptr, len) \
+ c = *eptr; \
+ if ((c & 0xfc00u) == 0xd800u) GETUTF16LEN(c, eptr, len);
+
+/* Get the next UTF-816character, testing for UTF-16 mode, not advancing the
+pointer, incrementing length if there is a low surrogate. This is called when
+we do not know if we are in UTF-16 mode. */
+
+#define GETCHARLENTEST(c, eptr, len) \
+ c = *eptr; \
+ if (utf && (c & 0xfc00u) == 0xd800u) GETUTF16LEN(c, eptr, len);
+
+/* If the pointer is not at the start of a character, move it back until
+it is. This is called only in UTF-16 mode - we don't put a test within the
+macro because almost all calls are already within a block of UTF-16 only
+code. */
+
+#define BACKCHAR(eptr) if ((*eptr & 0xfc00u) == 0xdc00u) eptr--
+
+/* Same as above, just in the other direction. */
+#define FORWARDCHAR(eptr) if ((*eptr & 0xfc00u) == 0xdc00u) eptr++
+#define FORWARDCHARTEST(eptr,end) if (eptr < end && (*eptr & 0xfc00u) == 0xdc00u) eptr++
+
+/* Same as above, but it allows a fully customizable form. */
+#define ACROSSCHAR(condition, eptr, action) \
+ if ((condition) && ((*eptr) & 0xfc00u) == 0xdc00u) action
+
+/* Deposit a character into memory, returning the number of code units. */
+
+#define PUTCHAR(c, p) ((utf && c > MAX_UTF_SINGLE_CU)? \
+ PRIV(ord2utf)(c,p) : (*p = c, 1))
+
+
+/* ------------------- 32-bit support ------------------ */
+
+#else
+
+/* These are trivial for the 32-bit library, since all UTF-32 characters fit
+into one PCRE2_UCHAR unit. */
+
+#define MAX_UTF_SINGLE_CU (0x10ffffu)
+#define HAS_EXTRALEN(c) (0)
+#define GET_EXTRALEN(c) (0)
+#define NOT_FIRSTCU(c) (0)
+
+/* Get the next UTF-32 character, not advancing the pointer. This is called when
+we know we are in UTF-32 mode. */
+
+#define GETCHAR(c, eptr) \
+ c = *(eptr);
+
+/* Get the next UTF-32 character, testing for UTF-32 mode, and not advancing the
+pointer. */
+
+#define GETCHARTEST(c, eptr) \
+ c = *(eptr);
+
+/* Get the next UTF-32 character, advancing the pointer. This is called when we
+know we are in UTF-32 mode. */
+
+#define GETCHARINC(c, eptr) \
+ c = *((eptr)++);
+
+/* Get the next character, testing for UTF-32 mode, and advancing the pointer.
+This is called when we don't know if we are in UTF-32 mode. */
+
+#define GETCHARINCTEST(c, eptr) \
+ c = *((eptr)++);
+
+/* Get the next UTF-32 character, not advancing the pointer, not incrementing
+length (since all UTF-32 is of length 1). This is called when we know we are in
+UTF-32 mode. */
+
+#define GETCHARLEN(c, eptr, len) \
+ GETCHAR(c, eptr)
+
+/* Get the next UTF-32character, testing for UTF-32 mode, not advancing the
+pointer, not incrementing the length (since all UTF-32 is of length 1).
+This is called when we do not know if we are in UTF-32 mode. */
+
+#define GETCHARLENTEST(c, eptr, len) \
+ GETCHARTEST(c, eptr)
+
+/* If the pointer is not at the start of a character, move it back until
+it is. This is called only in UTF-32 mode - we don't put a test within the
+macro because almost all calls are already within a block of UTF-32 only
+code.
+
+These are all no-ops since all UTF-32 characters fit into one PCRE2_UCHAR. */
+
+#define BACKCHAR(eptr) do { } while (0)
+
+/* Same as above, just in the other direction. */
+
+#define FORWARDCHAR(eptr) do { } while (0)
+#define FORWARDCHARTEST(eptr,end) do { } while (0)
+
+/* Same as above, but it allows a fully customizable form. */
+
+#define ACROSSCHAR(condition, eptr, action) do { } while (0)
+
+/* Deposit a character into memory, returning the number of code units. */
+
+#define PUTCHAR(c, p) (*p = c, 1)
+
+#endif /* UTF-32 character handling */
+#endif /* SUPPORT_UNICODE */
+
+
+/* Mode-dependent macros that have the same definition in all modes. */
+
+#define CU2BYTES(x) ((x)*((PCRE2_CODE_UNIT_WIDTH/8)))
+#define BYTES2CU(x) ((x)/((PCRE2_CODE_UNIT_WIDTH/8)))
+#define PUTINC(a,n,d) PUT(a,n,d), a += LINK_SIZE
+#define PUT2INC(a,n,d) PUT2(a,n,d), a += IMM2_SIZE
+
+
+/* ----------------------- HIDDEN STRUCTURES ----------------------------- */
+
+/* NOTE: All these structures *must* start with a pcre2_memctl structure. The
+code that uses them is simpler because it assumes this. */
+
+/* The real general context structure. At present it holds only data for custom
+memory control. */
+
+typedef struct pcre2_real_general_context {
+ pcre2_memctl memctl;
+} pcre2_real_general_context;
+
+/* The real compile context structure */
+
+typedef struct pcre2_real_compile_context {
+ pcre2_memctl memctl;
+ int (*stack_guard)(uint32_t, void *);
+ void *stack_guard_data;
+ const uint8_t *tables;
+ PCRE2_SIZE max_pattern_length;
+ uint16_t bsr_convention;
+ uint16_t newline_convention;
+ uint32_t parens_nest_limit;
+ uint32_t extra_options;
+} pcre2_real_compile_context;
+
+/* The real match context structure. */
+
+typedef struct pcre2_real_match_context {
+ pcre2_memctl memctl;
+#ifdef SUPPORT_JIT
+ pcre2_jit_callback jit_callback;
+ void *jit_callback_data;
+#endif
+ int (*callout)(pcre2_callout_block *, void *);
+ void *callout_data;
+ int (*substitute_callout)(pcre2_substitute_callout_block *, void *);
+ void *substitute_callout_data;
+ PCRE2_SIZE offset_limit;
+ uint32_t heap_limit;
+ uint32_t match_limit;
+ uint32_t depth_limit;
+} pcre2_real_match_context;
+
+/* The real convert context structure. */
+
+typedef struct pcre2_real_convert_context {
+ pcre2_memctl memctl;
+ uint32_t glob_separator;
+ uint32_t glob_escape;
+} pcre2_real_convert_context;
+
+/* The real compiled code structure. The type for the blocksize field is
+defined specially because it is required in pcre2_serialize_decode() when
+copying the size from possibly unaligned memory into a variable of the same
+type. Use a macro rather than a typedef to avoid compiler warnings when this
+file is included multiple times by pcre2test. LOOKBEHIND_MAX specifies the
+largest lookbehind that is supported. (OP_REVERSE in a pattern has a 16-bit
+argument in 8-bit and 16-bit modes, so we need no more than a 16-bit field
+here.) */
+
+#undef CODE_BLOCKSIZE_TYPE
+#define CODE_BLOCKSIZE_TYPE size_t
+
+#undef LOOKBEHIND_MAX
+#define LOOKBEHIND_MAX UINT16_MAX
+
+typedef struct pcre2_real_code {
+ pcre2_memctl memctl; /* Memory control fields */
+ const uint8_t *tables; /* The character tables */
+ void *executable_jit; /* Pointer to JIT code */
+ uint8_t start_bitmap[32]; /* Bitmap for starting code unit < 256 */
+ CODE_BLOCKSIZE_TYPE blocksize; /* Total (bytes) that was malloc-ed */
+ uint32_t magic_number; /* Paranoid and endianness check */
+ uint32_t compile_options; /* Options passed to pcre2_compile() */
+ uint32_t overall_options; /* Options after processing the pattern */
+ uint32_t extra_options; /* Taken from compile_context */
+ uint32_t flags; /* Various state flags */
+ uint32_t limit_heap; /* Limit set in the pattern */
+ uint32_t limit_match; /* Limit set in the pattern */
+ uint32_t limit_depth; /* Limit set in the pattern */
+ uint32_t first_codeunit; /* Starting code unit */
+ uint32_t last_codeunit; /* This codeunit must be seen */
+ uint16_t bsr_convention; /* What \R matches */
+ uint16_t newline_convention; /* What is a newline? */
+ uint16_t max_lookbehind; /* Longest lookbehind (characters) */
+ uint16_t minlength; /* Minimum length of match */
+ uint16_t top_bracket; /* Highest numbered group */
+ uint16_t top_backref; /* Highest numbered back reference */
+ uint16_t name_entry_size; /* Size (code units) of table entries */
+ uint16_t name_count; /* Number of name entries in the table */
+} pcre2_real_code;
+
+/* The real match data structure. Define ovector as large as it can ever
+actually be so that array bound checkers don't grumble. Memory for this
+structure is obtained by calling pcre2_match_data_create(), which sets the size
+as the offset of ovector plus a pair of elements for each capturable string, so
+the size varies from call to call. As the maximum number of capturing
+subpatterns is 65535 we must allow for 65536 strings to include the overall
+match. (See also the heapframe structure below.) */
+
+struct heapframe; /* Forward reference */
+
+typedef struct pcre2_real_match_data {
+ pcre2_memctl memctl; /* Memory control fields */
+ const pcre2_real_code *code; /* The pattern used for the match */
+ PCRE2_SPTR subject; /* The subject that was matched */
+ PCRE2_SPTR mark; /* Pointer to last mark */
+ struct heapframe *heapframes; /* Backtracking frames heap memory */
+ PCRE2_SIZE heapframes_size; /* Malloc-ed size */
+ PCRE2_SIZE leftchar; /* Offset to leftmost code unit */
+ PCRE2_SIZE rightchar; /* Offset to rightmost code unit */
+ PCRE2_SIZE startchar; /* Offset to starting code unit */
+ uint8_t matchedby; /* Type of match (normal, JIT, DFA) */
+ uint8_t flags; /* Various flags */
+ uint16_t oveccount; /* Number of pairs */
+ int rc; /* The return code from the match */
+ PCRE2_SIZE ovector[131072]; /* Must be last in the structure */
+} pcre2_real_match_data;
+
+
+/* ----------------------- PRIVATE STRUCTURES ----------------------------- */
+
+/* These structures are not needed for pcre2test. */
+
+#ifndef PCRE2_PCRE2TEST
+
+/* Structures for checking for mutual recursion when scanning compiled or
+parsed code. */
+
+typedef struct recurse_check {
+ struct recurse_check *prev;
+ PCRE2_SPTR group;
+} recurse_check;
+
+typedef struct parsed_recurse_check {
+ struct parsed_recurse_check *prev;
+ uint32_t *groupptr;
+} parsed_recurse_check;
+
+/* Structure for building a cache when filling in recursion offsets. */
+
+typedef struct recurse_cache {
+ PCRE2_SPTR group;
+ int groupnumber;
+} recurse_cache;
+
+/* Structure for maintaining a chain of pointers to the currently incomplete
+branches, for testing for left recursion while compiling. */
+
+typedef struct branch_chain {
+ struct branch_chain *outer;
+ PCRE2_UCHAR *current_branch;
+} branch_chain;
+
+/* Structure for building a list of named groups during the first pass of
+compiling. */
+
+typedef struct named_group {
+ PCRE2_SPTR name; /* Points to the name in the pattern */
+ uint32_t number; /* Group number */
+ uint16_t length; /* Length of the name */
+ uint16_t isdup; /* TRUE if a duplicate */
+} named_group;
+
+/* Structure for passing "static" information around between the functions
+doing the compiling, so that they are thread-safe. */
+
+typedef struct compile_block {
+ pcre2_real_compile_context *cx; /* Points to the compile context */
+ const uint8_t *lcc; /* Points to lower casing table */
+ const uint8_t *fcc; /* Points to case-flipping table */
+ const uint8_t *cbits; /* Points to character type table */
+ const uint8_t *ctypes; /* Points to table of type maps */
+ PCRE2_SPTR start_workspace; /* The start of working space */
+ PCRE2_SPTR start_code; /* The start of the compiled code */
+ PCRE2_SPTR start_pattern; /* The start of the pattern */
+ PCRE2_SPTR end_pattern; /* The end of the pattern */
+ PCRE2_UCHAR *name_table; /* The name/number table */
+ PCRE2_SIZE workspace_size; /* Size of workspace */
+ PCRE2_SIZE small_ref_offset[10]; /* Offsets for \1 to \9 */
+ PCRE2_SIZE erroroffset; /* Offset of error in pattern */
+ uint16_t names_found; /* Number of entries so far */
+ uint16_t name_entry_size; /* Size of each entry */
+ uint16_t parens_depth; /* Depth of nested parentheses */
+ uint16_t assert_depth; /* Depth of nested assertions */
+ open_capitem *open_caps; /* Chain of open capture items */
+ named_group *named_groups; /* Points to vector in pre-compile */
+ uint32_t named_group_list_size; /* Number of entries in the list */
+ uint32_t external_options; /* External (initial) options */
+ uint32_t external_flags; /* External flag bits to be set */
+ uint32_t bracount; /* Count of capturing parentheses */
+ uint32_t lastcapture; /* Last capture encountered */
+ uint32_t *parsed_pattern; /* Parsed pattern buffer */
+ uint32_t *parsed_pattern_end; /* Parsed pattern should not get here */
+ uint32_t *groupinfo; /* Group info vector */
+ uint32_t top_backref; /* Maximum back reference */
+ uint32_t backref_map; /* Bitmap of low back refs */
+ uint32_t nltype; /* Newline type */
+ uint32_t nllen; /* Newline string length */
+ uint32_t class_range_start; /* Overall class range start */
+ uint32_t class_range_end; /* Overall class range end */
+ PCRE2_UCHAR nl[4]; /* Newline string when fixed length */
+ uint32_t req_varyopt; /* "After variable item" flag for reqbyte */
+ int max_lookbehind; /* Maximum lookbehind (characters) */
+ BOOL had_accept; /* (*ACCEPT) encountered */
+ BOOL had_pruneorskip; /* (*PRUNE) or (*SKIP) encountered */
+ BOOL had_recurse; /* Had a recursion or subroutine call */
+ BOOL dupnames; /* Duplicate names exist */
+} compile_block;
+
+/* Structure for keeping the properties of the in-memory stack used
+by the JIT matcher. */
+
+typedef struct pcre2_real_jit_stack {
+ pcre2_memctl memctl;
+ void* stack;
+} pcre2_real_jit_stack;
+
+/* Structure for items in a linked list that represents an explicit recursive
+call within the pattern when running pcre2_dfa_match(). */
+
+typedef struct dfa_recursion_info {
+ struct dfa_recursion_info *prevrec;
+ PCRE2_SPTR subject_position;
+ uint32_t group_num;
+} dfa_recursion_info;
+
+/* Structure for "stack" frames that are used for remembering backtracking
+positions during matching. As these are used in a vector, with the ovector item
+being extended, the size of the structure must be a multiple of PCRE2_SIZE. The
+only way to check this at compile time is to force an error by generating an
+array with a negative size. By putting this in a typedef (which is never used),
+we don't generate any code when all is well. */
+
+typedef struct heapframe {
+
+ /* The first set of fields are variables that have to be preserved over calls
+ to RRMATCH(), but which do not need to be copied to new frames. */
+
+ PCRE2_SPTR ecode; /* The current position in the pattern */
+ PCRE2_SPTR temp_sptr[2]; /* Used for short-term PCRE_SPTR values */
+ PCRE2_SIZE length; /* Used for character, string, or code lengths */
+ PCRE2_SIZE back_frame; /* Amount to subtract on RRETURN */
+ PCRE2_SIZE temp_size; /* Used for short-term PCRE2_SIZE values */
+ uint32_t rdepth; /* "Recursion" depth */
+ uint32_t group_frame_type; /* Type information for group frames */
+ uint32_t temp_32[4]; /* Used for short-term 32-bit or BOOL values */
+ uint8_t return_id; /* Where to go on in internal "return" */
+ uint8_t op; /* Processing opcode */
+
+ /* At this point, the structure is 16-bit aligned. On most architectures
+ the alignment requirement for a pointer will ensure that the eptr field below
+ is 32-bit or 64-bit aligned. However, on m68k it is fine to have a pointer
+ that is 16-bit aligned. We must therefore ensure that what comes between here
+ and eptr is an odd multiple of 16 bits so as to get back into 32-bit
+ alignment. This happens naturally when PCRE2_UCHAR is 8 bits wide, but needs
+ fudges in the other cases. In the 32-bit case the padding comes first so that
+ the occu field itself is 32-bit aligned. Without the padding, this structure
+ is no longer a multiple of PCRE2_SIZE on m68k, and the check below fails. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ PCRE2_UCHAR occu[6]; /* Used for other case code units */
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+ PCRE2_UCHAR occu[2]; /* Used for other case code units */
+ uint8_t unused[2]; /* Ensure 32-bit alignment (see above) */
+#else
+ uint8_t unused[2]; /* Ensure 32-bit alignment (see above) */
+ PCRE2_UCHAR occu[1]; /* Used for other case code units */
+#endif
+
+ /* The rest have to be copied from the previous frame whenever a new frame
+ becomes current. The final field is specified as a large vector so that
+ runtime array bound checks don't catch references to it. However, for any
+ specific call to pcre2_match() the memory allocated for each frame structure
+ allows for exactly the right size ovector for the number of capturing
+ parentheses. (See also the comment for pcre2_real_match_data above.) */
+
+ PCRE2_SPTR eptr; /* MUST BE FIRST */
+ PCRE2_SPTR start_match; /* Can be adjusted by \K */
+ PCRE2_SPTR mark; /* Most recent mark on the success path */
+ uint32_t current_recurse; /* Current (deepest) recursion number */
+ uint32_t capture_last; /* Most recent capture */
+ PCRE2_SIZE last_group_offset; /* Saved offset to most recent group frame */
+ PCRE2_SIZE offset_top; /* Offset after highest capture */
+ PCRE2_SIZE ovector[131072]; /* Must be last in the structure */
+} heapframe;
+
+/* This typedef is a check that the size of the heapframe structure is a
+multiple of PCRE2_SIZE. See various comments above. */
+
+typedef char check_heapframe_size[
+ ((sizeof(heapframe) % sizeof(PCRE2_SIZE)) == 0)? (+1):(-1)];
+
+/* Structure for computing the alignment of heapframe. */
+
+typedef struct heapframe_align {
+ char unalign; /* Completely unalign the current offset */
+ heapframe frame; /* Offset is its alignment */
+} heapframe_align;
+
+/* This define is the minimum alignment required for a heapframe, in bytes. */
+
+#define HEAPFRAME_ALIGNMENT offsetof(heapframe_align, frame)
+
+/* Structure for passing "static" information around between the functions
+doing traditional NFA matching (pcre2_match() and friends). */
+
+typedef struct match_block {
+ pcre2_memctl memctl; /* For general use */
+ PCRE2_SIZE heap_limit; /* As it says */
+ uint32_t match_limit; /* As it says */
+ uint32_t match_limit_depth; /* As it says */
+ uint32_t match_call_count; /* Number of times a new frame is created */
+ BOOL hitend; /* Hit the end of the subject at some point */
+ BOOL hasthen; /* Pattern contains (*THEN) */
+ BOOL allowemptypartial; /* Allow empty hard partial */
+ const uint8_t *lcc; /* Points to lower casing table */
+ const uint8_t *fcc; /* Points to case-flipping table */
+ const uint8_t *ctypes; /* Points to table of type maps */
+ PCRE2_SIZE start_offset; /* The start offset value */
+ PCRE2_SIZE end_offset_top; /* Highwater mark at end of match */
+ uint16_t partial; /* PARTIAL options */
+ uint16_t bsr_convention; /* \R interpretation */
+ uint16_t name_count; /* Number of names in name table */
+ uint16_t name_entry_size; /* Size of entry in names table */
+ PCRE2_SPTR name_table; /* Table of group names */
+ PCRE2_SPTR start_code; /* For use when recursing */
+ PCRE2_SPTR start_subject; /* Start of the subject string */
+ PCRE2_SPTR check_subject; /* Where UTF-checked from */
+ PCRE2_SPTR end_subject; /* End of the subject string */
+ PCRE2_SPTR end_match_ptr; /* Subject position at end match */
+ PCRE2_SPTR start_used_ptr; /* Earliest consulted character */
+ PCRE2_SPTR last_used_ptr; /* Latest consulted character */
+ PCRE2_SPTR mark; /* Mark pointer to pass back on success */
+ PCRE2_SPTR nomatch_mark; /* Mark pointer to pass back on failure */
+ PCRE2_SPTR verb_ecode_ptr; /* For passing back info */
+ PCRE2_SPTR verb_skip_ptr; /* For passing back a (*SKIP) name */
+ uint32_t verb_current_recurse; /* Current recurse when (*VERB) happens */
+ uint32_t moptions; /* Match options */
+ uint32_t poptions; /* Pattern options */
+ uint32_t skip_arg_count; /* For counting SKIP_ARGs */
+ uint32_t ignore_skip_arg; /* For re-run when SKIP arg name not found */
+ uint32_t nltype; /* Newline type */
+ uint32_t nllen; /* Newline string length */
+ PCRE2_UCHAR nl[4]; /* Newline string when fixed */
+ pcre2_callout_block *cb; /* Points to a callout block */
+ void *callout_data; /* To pass back to callouts */
+ int (*callout)(pcre2_callout_block *,void *); /* Callout function or NULL */
+} match_block;
+
+/* A similar structure is used for the same purpose by the DFA matching
+functions. */
+
+typedef struct dfa_match_block {
+ pcre2_memctl memctl; /* For general use */
+ PCRE2_SPTR start_code; /* Start of the compiled pattern */
+ PCRE2_SPTR start_subject ; /* Start of the subject string */
+ PCRE2_SPTR end_subject; /* End of subject string */
+ PCRE2_SPTR start_used_ptr; /* Earliest consulted character */
+ PCRE2_SPTR last_used_ptr; /* Latest consulted character */
+ const uint8_t *tables; /* Character tables */
+ PCRE2_SIZE start_offset; /* The start offset value */
+ PCRE2_SIZE heap_limit; /* As it says */
+ PCRE2_SIZE heap_used; /* As it says */
+ uint32_t match_limit; /* As it says */
+ uint32_t match_limit_depth; /* As it says */
+ uint32_t match_call_count; /* Number of calls of internal function */
+ uint32_t moptions; /* Match options */
+ uint32_t poptions; /* Pattern options */
+ uint32_t nltype; /* Newline type */
+ uint32_t nllen; /* Newline string length */
+ BOOL allowemptypartial; /* Allow empty hard partial */
+ PCRE2_UCHAR nl[4]; /* Newline string when fixed */
+ uint16_t bsr_convention; /* \R interpretation */
+ pcre2_callout_block *cb; /* Points to a callout block */
+ void *callout_data; /* To pass back to callouts */
+ int (*callout)(pcre2_callout_block *,void *); /* Callout function or NULL */
+ dfa_recursion_info *recursive; /* Linked list of recursion data */
+} dfa_match_block;
+
+#endif /* PCRE2_PCRE2TEST */
+
+/* End of pcre2_intmodedep.h */
diff --git a/contrib/libs/pcre2/src/pcre2_jit_compile.c b/contrib/libs/pcre2/src/pcre2_jit_compile.c
new file mode 100644
index 00000000000..3fb93ff6590
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_jit_compile.c
@@ -0,0 +1,14507 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ This module by Zoltan Herczeg
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2021 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+#ifdef SUPPORT_JIT
+
+/* All-in-one: Since we use the JIT compiler only from here,
+we just include it. This way we don't need to touch the build
+system files. */
+
+#define SLJIT_CONFIG_AUTO 1
+#define SLJIT_CONFIG_STATIC 1
+#define SLJIT_VERBOSE 0
+
+#ifdef PCRE2_DEBUG
+#define SLJIT_DEBUG 1
+#else
+#define SLJIT_DEBUG 0
+#endif
+
+#define SLJIT_MALLOC(size, allocator_data) pcre2_jit_malloc(size, allocator_data)
+#define SLJIT_FREE(ptr, allocator_data) pcre2_jit_free(ptr, allocator_data)
+
+static void * pcre2_jit_malloc(size_t size, void *allocator_data)
+{
+pcre2_memctl *allocator = ((pcre2_memctl*)allocator_data);
+return allocator->malloc(size, allocator->memory_data);
+}
+
+static void pcre2_jit_free(void *ptr, void *allocator_data)
+{
+pcre2_memctl *allocator = ((pcre2_memctl*)allocator_data);
+allocator->free(ptr, allocator->memory_data);
+}
+
+#include "sljit/sljitLir.c"
+
+#if defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED
+#error Unsupported architecture
+#endif
+
+/* Defines for debugging purposes. */
+
+/* 1 - Use unoptimized capturing brackets.
+ 2 - Enable capture_last_ptr (includes option 1). */
+/* #define DEBUG_FORCE_UNOPTIMIZED_CBRAS 2 */
+
+/* 1 - Always have a control head. */
+/* #define DEBUG_FORCE_CONTROL_HEAD 1 */
+
+/* Allocate memory for the regex stack on the real machine stack.
+Fast, but limited size. */
+#define MACHINE_STACK_SIZE 32768
+
+/* Growth rate for stack allocated by the OS. Should be the multiply
+of page size. */
+#define STACK_GROWTH_RATE 8192
+
+/* Enable to check that the allocation could destroy temporaries. */
+#if defined SLJIT_DEBUG && SLJIT_DEBUG
+#define DESTROY_REGISTERS 1
+#endif
+
+/*
+Short summary about the backtracking mechanism empolyed by the jit code generator:
+
+The code generator follows the recursive nature of the PERL compatible regular
+expressions. The basic blocks of regular expressions are condition checkers
+whose execute different commands depending on the result of the condition check.
+The relationship between the operators can be horizontal (concatenation) and
+vertical (sub-expression) (See struct backtrack_common for more details).
+
+ 'ab' - 'a' and 'b' regexps are concatenated
+ 'a+' - 'a' is the sub-expression of the '+' operator
+
+The condition checkers are boolean (true/false) checkers. Machine code is generated
+for the checker itself and for the actions depending on the result of the checker.
+The 'true' case is called as the matching path (expected path), and the other is called as
+the 'backtrack' path. Branch instructions are expesive for all CPUs, so we avoid taken
+branches on the matching path.
+
+ Greedy star operator (*) :
+ Matching path: match happens.
+ Backtrack path: match failed.
+ Non-greedy star operator (*?) :
+ Matching path: no need to perform a match.
+ Backtrack path: match is required.
+
+The following example shows how the code generated for a capturing bracket
+with two alternatives. Let A, B, C, D are arbirary regular expressions, and
+we have the following regular expression:
+
+ A(B|C)D
+
+The generated code will be the following:
+
+ A matching path
+ '(' matching path (pushing arguments to the stack)
+ B matching path
+ ')' matching path (pushing arguments to the stack)
+ D matching path
+ return with successful match
+
+ D backtrack path
+ ')' backtrack path (If we arrived from "C" jump to the backtrack of "C")
+ B backtrack path
+ C expected path
+ jump to D matching path
+ C backtrack path
+ A backtrack path
+
+ Notice, that the order of backtrack code paths are the opposite of the fast
+ code paths. In this way the topmost value on the stack is always belong
+ to the current backtrack code path. The backtrack path must check
+ whether there is a next alternative. If so, it needs to jump back to
+ the matching path eventually. Otherwise it needs to clear out its own stack
+ frame and continue the execution on the backtrack code paths.
+*/
+
+/*
+Saved stack frames:
+
+Atomic blocks and asserts require reloading the values of private data
+when the backtrack mechanism performed. Because of OP_RECURSE, the data
+are not necessarly known in compile time, thus we need a dynamic restore
+mechanism.
+
+The stack frames are stored in a chain list, and have the following format:
+([ capturing bracket offset ][ start value ][ end value ])+ ... [ 0 ] [ previous head ]
+
+Thus we can restore the private data to a particular point in the stack.
+*/
+
+typedef struct jit_arguments {
+ /* Pointers first. */
+ struct sljit_stack *stack;
+ PCRE2_SPTR str;
+ PCRE2_SPTR begin;
+ PCRE2_SPTR end;
+ pcre2_match_data *match_data;
+ PCRE2_SPTR startchar_ptr;
+ PCRE2_UCHAR *mark_ptr;
+ int (*callout)(pcre2_callout_block *, void *);
+ void *callout_data;
+ /* Everything else after. */
+ sljit_uw offset_limit;
+ sljit_u32 limit_match;
+ sljit_u32 oveccount;
+ sljit_u32 options;
+} jit_arguments;
+
+#define JIT_NUMBER_OF_COMPILE_MODES 3
+
+typedef struct executable_functions {
+ void *executable_funcs[JIT_NUMBER_OF_COMPILE_MODES];
+ void *read_only_data_heads[JIT_NUMBER_OF_COMPILE_MODES];
+ sljit_uw executable_sizes[JIT_NUMBER_OF_COMPILE_MODES];
+ sljit_u32 top_bracket;
+ sljit_u32 limit_match;
+} executable_functions;
+
+typedef struct jump_list {
+ struct sljit_jump *jump;
+ struct jump_list *next;
+} jump_list;
+
+typedef struct stub_list {
+ struct sljit_jump *start;
+ struct sljit_label *quit;
+ struct stub_list *next;
+} stub_list;
+
+enum frame_types {
+ no_frame = -1,
+ no_stack = -2
+};
+
+enum control_types {
+ type_mark = 0,
+ type_then_trap = 1
+};
+
+enum early_fail_types {
+ type_skip = 0,
+ type_fail = 1,
+ type_fail_range = 2
+};
+
+typedef int (SLJIT_FUNC *jit_function)(jit_arguments *args);
+
+/* The following structure is the key data type for the recursive
+code generator. It is allocated by compile_matchingpath, and contains
+the arguments for compile_backtrackingpath. Must be the first member
+of its descendants. */
+typedef struct backtrack_common {
+ /* Concatenation stack. */
+ struct backtrack_common *prev;
+ jump_list *nextbacktracks;
+ /* Internal stack (for component operators). */
+ struct backtrack_common *top;
+ jump_list *topbacktracks;
+ /* Opcode pointer. */
+ PCRE2_SPTR cc;
+} backtrack_common;
+
+typedef struct assert_backtrack {
+ backtrack_common common;
+ jump_list *condfailed;
+ /* Less than 0 if a frame is not needed. */
+ int framesize;
+ /* Points to our private memory word on the stack. */
+ int private_data_ptr;
+ /* For iterators. */
+ struct sljit_label *matchingpath;
+} assert_backtrack;
+
+typedef struct bracket_backtrack {
+ backtrack_common common;
+ /* Where to coninue if an alternative is successfully matched. */
+ struct sljit_label *alternative_matchingpath;
+ /* For rmin and rmax iterators. */
+ struct sljit_label *recursive_matchingpath;
+ /* For greedy ? operator. */
+ struct sljit_label *zero_matchingpath;
+ /* Contains the branches of a failed condition. */
+ union {
+ /* Both for OP_COND, OP_SCOND. */
+ jump_list *condfailed;
+ assert_backtrack *assert;
+ /* For OP_ONCE. Less than 0 if not needed. */
+ int framesize;
+ /* For brackets with >3 alternatives. */
+ struct sljit_put_label *matching_put_label;
+ } u;
+ /* Points to our private memory word on the stack. */
+ int private_data_ptr;
+} bracket_backtrack;
+
+typedef struct bracketpos_backtrack {
+ backtrack_common common;
+ /* Points to our private memory word on the stack. */
+ int private_data_ptr;
+ /* Reverting stack is needed. */
+ int framesize;
+ /* Allocated stack size. */
+ int stacksize;
+} bracketpos_backtrack;
+
+typedef struct braminzero_backtrack {
+ backtrack_common common;
+ struct sljit_label *matchingpath;
+} braminzero_backtrack;
+
+typedef struct char_iterator_backtrack {
+ backtrack_common common;
+ /* Next iteration. */
+ struct sljit_label *matchingpath;
+ union {
+ jump_list *backtracks;
+ struct {
+ unsigned int othercasebit;
+ PCRE2_UCHAR chr;
+ BOOL enabled;
+ } charpos;
+ } u;
+} char_iterator_backtrack;
+
+typedef struct ref_iterator_backtrack {
+ backtrack_common common;
+ /* Next iteration. */
+ struct sljit_label *matchingpath;
+} ref_iterator_backtrack;
+
+typedef struct recurse_entry {
+ struct recurse_entry *next;
+ /* Contains the function entry label. */
+ struct sljit_label *entry_label;
+ /* Contains the function entry label. */
+ struct sljit_label *backtrack_label;
+ /* Collects the entry calls until the function is not created. */
+ jump_list *entry_calls;
+ /* Collects the backtrack calls until the function is not created. */
+ jump_list *backtrack_calls;
+ /* Points to the starting opcode. */
+ sljit_sw start;
+} recurse_entry;
+
+typedef struct recurse_backtrack {
+ backtrack_common common;
+ /* Return to the matching path. */
+ struct sljit_label *matchingpath;
+ /* Recursive pattern. */
+ recurse_entry *entry;
+ /* Pattern is inlined. */
+ BOOL inlined_pattern;
+} recurse_backtrack;
+
+#define OP_THEN_TRAP OP_TABLE_LENGTH
+
+typedef struct then_trap_backtrack {
+ backtrack_common common;
+ /* If then_trap is not NULL, this structure contains the real
+ then_trap for the backtracking path. */
+ struct then_trap_backtrack *then_trap;
+ /* Points to the starting opcode. */
+ sljit_sw start;
+ /* Exit point for the then opcodes of this alternative. */
+ jump_list *quit;
+ /* Frame size of the current alternative. */
+ int framesize;
+} then_trap_backtrack;
+
+#define MAX_N_CHARS 12
+#define MAX_DIFF_CHARS 5
+
+typedef struct fast_forward_char_data {
+ /* Number of characters in the chars array, 255 for any character. */
+ sljit_u8 count;
+ /* Number of last UTF-8 characters in the chars array. */
+ sljit_u8 last_count;
+ /* Available characters in the current position. */
+ PCRE2_UCHAR chars[MAX_DIFF_CHARS];
+} fast_forward_char_data;
+
+#define MAX_CLASS_RANGE_SIZE 4
+#define MAX_CLASS_CHARS_SIZE 3
+
+typedef struct compiler_common {
+ /* The sljit ceneric compiler. */
+ struct sljit_compiler *compiler;
+ /* Compiled regular expression. */
+ pcre2_real_code *re;
+ /* First byte code. */
+ PCRE2_SPTR start;
+ /* Maps private data offset to each opcode. */
+ sljit_s32 *private_data_ptrs;
+ /* Chain list of read-only data ptrs. */
+ void *read_only_data_head;
+ /* Tells whether the capturing bracket is optimized. */
+ sljit_u8 *optimized_cbracket;
+ /* Tells whether the starting offset is a target of then. */
+ sljit_u8 *then_offsets;
+ /* Current position where a THEN must jump. */
+ then_trap_backtrack *then_trap;
+ /* Starting offset of private data for capturing brackets. */
+ sljit_s32 cbra_ptr;
+ /* Output vector starting point. Must be divisible by 2. */
+ sljit_s32 ovector_start;
+ /* Points to the starting character of the current match. */
+ sljit_s32 start_ptr;
+ /* Last known position of the requested byte. */
+ sljit_s32 req_char_ptr;
+ /* Head of the last recursion. */
+ sljit_s32 recursive_head_ptr;
+ /* First inspected character for partial matching.
+ (Needed for avoiding zero length partial matches.) */
+ sljit_s32 start_used_ptr;
+ /* Starting pointer for partial soft matches. */
+ sljit_s32 hit_start;
+ /* Pointer of the match end position. */
+ sljit_s32 match_end_ptr;
+ /* Points to the marked string. */
+ sljit_s32 mark_ptr;
+ /* Recursive control verb management chain. */
+ sljit_s32 control_head_ptr;
+ /* Points to the last matched capture block index. */
+ sljit_s32 capture_last_ptr;
+ /* Fast forward skipping byte code pointer. */
+ PCRE2_SPTR fast_forward_bc_ptr;
+ /* Locals used by fast fail optimization. */
+ sljit_s32 early_fail_start_ptr;
+ sljit_s32 early_fail_end_ptr;
+ /* Variables used by recursive call generator. */
+ sljit_s32 recurse_bitset_size;
+ uint8_t *recurse_bitset;
+
+ /* Flipped and lower case tables. */
+ const sljit_u8 *fcc;
+ sljit_sw lcc;
+ /* Mode can be PCRE2_JIT_COMPLETE and others. */
+ int mode;
+ /* TRUE, when empty match is accepted for partial matching. */
+ BOOL allow_empty_partial;
+ /* TRUE, when minlength is greater than 0. */
+ BOOL might_be_empty;
+ /* \K is found in the pattern. */
+ BOOL has_set_som;
+ /* (*SKIP:arg) is found in the pattern. */
+ BOOL has_skip_arg;
+ /* (*THEN) is found in the pattern. */
+ BOOL has_then;
+ /* (*SKIP) or (*SKIP:arg) is found in lookbehind assertion. */
+ BOOL has_skip_in_assert_back;
+ /* Quit is redirected by recurse, negative assertion, or positive assertion in conditional block. */
+ BOOL local_quit_available;
+ /* Currently in a positive assertion. */
+ BOOL in_positive_assertion;
+ /* Newline control. */
+ int nltype;
+ sljit_u32 nlmax;
+ sljit_u32 nlmin;
+ int newline;
+ int bsr_nltype;
+ sljit_u32 bsr_nlmax;
+ sljit_u32 bsr_nlmin;
+ /* Dollar endonly. */
+ int endonly;
+ /* Tables. */
+ sljit_sw ctypes;
+ /* Named capturing brackets. */
+ PCRE2_SPTR name_table;
+ sljit_sw name_count;
+ sljit_sw name_entry_size;
+
+ /* Labels and jump lists. */
+ struct sljit_label *partialmatchlabel;
+ struct sljit_label *quit_label;
+ struct sljit_label *abort_label;
+ struct sljit_label *accept_label;
+ struct sljit_label *ff_newline_shortcut;
+ stub_list *stubs;
+ recurse_entry *entries;
+ recurse_entry *currententry;
+ jump_list *partialmatch;
+ jump_list *quit;
+ jump_list *positive_assertion_quit;
+ jump_list *abort;
+ jump_list *failed_match;
+ jump_list *accept;
+ jump_list *calllimit;
+ jump_list *stackalloc;
+ jump_list *revertframes;
+ jump_list *wordboundary;
+ jump_list *anynewline;
+ jump_list *hspace;
+ jump_list *vspace;
+ jump_list *casefulcmp;
+ jump_list *caselesscmp;
+ jump_list *reset_match;
+ BOOL unset_backref;
+ BOOL alt_circumflex;
+#ifdef SUPPORT_UNICODE
+ BOOL utf;
+ BOOL invalid_utf;
+ BOOL ucp;
+ /* Points to saving area for iref. */
+ sljit_s32 iref_ptr;
+ jump_list *getucd;
+ jump_list *getucdtype;
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ jump_list *utfreadchar;
+ jump_list *utfreadtype8;
+ jump_list *utfpeakcharback;
+#endif
+#if PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16
+ jump_list *utfreadchar_invalid;
+ jump_list *utfreadnewline_invalid;
+ jump_list *utfmoveback_invalid;
+ jump_list *utfpeakcharback_invalid;
+#endif
+#endif /* SUPPORT_UNICODE */
+} compiler_common;
+
+/* For byte_sequence_compare. */
+
+typedef struct compare_context {
+ int length;
+ int sourcereg;
+#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED
+ int ucharptr;
+ union {
+ sljit_s32 asint;
+ sljit_u16 asushort;
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ sljit_u8 asbyte;
+ sljit_u8 asuchars[4];
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+ sljit_u16 asuchars[2];
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+ sljit_u32 asuchars[1];
+#endif
+ } c;
+ union {
+ sljit_s32 asint;
+ sljit_u16 asushort;
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ sljit_u8 asbyte;
+ sljit_u8 asuchars[4];
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+ sljit_u16 asuchars[2];
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+ sljit_u32 asuchars[1];
+#endif
+ } oc;
+#endif
+} compare_context;
+
+/* Undefine sljit macros. */
+#undef CMP
+
+/* Used for accessing the elements of the stack. */
+#define STACK(i) ((i) * SSIZE_OF(sw))
+
+#ifdef SLJIT_PREF_SHIFT_REG
+#if SLJIT_PREF_SHIFT_REG == SLJIT_R2
+/* Nothing. */
+#elif SLJIT_PREF_SHIFT_REG == SLJIT_R3
+#define SHIFT_REG_IS_R3
+#else
+#error "Unsupported shift register"
+#endif
+#endif
+
+#define TMP1 SLJIT_R0
+#ifdef SHIFT_REG_IS_R3
+#define TMP2 SLJIT_R3
+#define TMP3 SLJIT_R2
+#else
+#define TMP2 SLJIT_R2
+#define TMP3 SLJIT_R3
+#endif
+#define STR_PTR SLJIT_R1
+#define STR_END SLJIT_S0
+#define STACK_TOP SLJIT_S1
+#define STACK_LIMIT SLJIT_S2
+#define COUNT_MATCH SLJIT_S3
+#define ARGUMENTS SLJIT_S4
+#define RETURN_ADDR SLJIT_R4
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+#define HAS_VIRTUAL_REGISTERS 1
+#else
+#define HAS_VIRTUAL_REGISTERS 0
+#endif
+
+/* Local space layout. */
+/* These two locals can be used by the current opcode. */
+#define LOCALS0 (0 * sizeof(sljit_sw))
+#define LOCALS1 (1 * sizeof(sljit_sw))
+/* Two local variables for possessive quantifiers (char1 cannot use them). */
+#define POSSESSIVE0 (2 * sizeof(sljit_sw))
+#define POSSESSIVE1 (3 * sizeof(sljit_sw))
+/* Max limit of recursions. */
+#define LIMIT_MATCH (4 * sizeof(sljit_sw))
+/* The output vector is stored on the stack, and contains pointers
+to characters. The vector data is divided into two groups: the first
+group contains the start / end character pointers, and the second is
+the start pointers when the end of the capturing group has not yet reached. */
+#define OVECTOR_START (common->ovector_start)
+#define OVECTOR(i) (OVECTOR_START + (i) * SSIZE_OF(sw))
+#define OVECTOR_PRIV(i) (common->cbra_ptr + (i) * SSIZE_OF(sw))
+#define PRIVATE_DATA(cc) (common->private_data_ptrs[(cc) - common->start])
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#define MOV_UCHAR SLJIT_MOV_U8
+#define IN_UCHARS(x) (x)
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+#define MOV_UCHAR SLJIT_MOV_U16
+#define UCHAR_SHIFT (1)
+#define IN_UCHARS(x) ((x) * 2)
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+#define MOV_UCHAR SLJIT_MOV_U32
+#define UCHAR_SHIFT (2)
+#define IN_UCHARS(x) ((x) * 4)
+#else
+#error Unsupported compiling mode
+#endif
+
+/* Shortcuts. */
+#define DEFINE_COMPILER \
+ struct sljit_compiler *compiler = common->compiler
+#define OP1(op, dst, dstw, src, srcw) \
+ sljit_emit_op1(compiler, (op), (dst), (dstw), (src), (srcw))
+#define OP2(op, dst, dstw, src1, src1w, src2, src2w) \
+ sljit_emit_op2(compiler, (op), (dst), (dstw), (src1), (src1w), (src2), (src2w))
+#define OP2U(op, src1, src1w, src2, src2w) \
+ sljit_emit_op2u(compiler, (op), (src1), (src1w), (src2), (src2w))
+#define OP_SRC(op, src, srcw) \
+ sljit_emit_op_src(compiler, (op), (src), (srcw))
+#define LABEL() \
+ sljit_emit_label(compiler)
+#define JUMP(type) \
+ sljit_emit_jump(compiler, (type))
+#define JUMPTO(type, label) \
+ sljit_set_label(sljit_emit_jump(compiler, (type)), (label))
+#define JUMPHERE(jump) \
+ sljit_set_label((jump), sljit_emit_label(compiler))
+#define SET_LABEL(jump, label) \
+ sljit_set_label((jump), (label))
+#define CMP(type, src1, src1w, src2, src2w) \
+ sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w))
+#define CMPTO(type, src1, src1w, src2, src2w, label) \
+ sljit_set_label(sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w)), (label))
+#define OP_FLAGS(op, dst, dstw, type) \
+ sljit_emit_op_flags(compiler, (op), (dst), (dstw), (type))
+#define CMOV(type, dst_reg, src, srcw) \
+ sljit_emit_cmov(compiler, (type), (dst_reg), (src), (srcw))
+#define GET_LOCAL_BASE(dst, dstw, offset) \
+ sljit_get_local_base(compiler, (dst), (dstw), (offset))
+
+#define READ_CHAR_MAX 0x7fffffff
+
+#define INVALID_UTF_CHAR -1
+#define UNASSIGNED_UTF_CHAR 888
+
+#if defined SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+
+#define GETCHARINC_INVALID(c, ptr, end, invalid_action) \
+ { \
+ if (ptr[0] <= 0x7f) \
+ c = *ptr++; \
+ else if (ptr + 1 < end && ptr[1] >= 0x80 && ptr[1] < 0xc0) \
+ { \
+ c = ptr[1] - 0x80; \
+ \
+ if (ptr[0] >= 0xc2 && ptr[0] <= 0xdf) \
+ { \
+ c |= (ptr[0] - 0xc0) << 6; \
+ ptr += 2; \
+ } \
+ else if (ptr + 2 < end && ptr[2] >= 0x80 && ptr[2] < 0xc0) \
+ { \
+ c = c << 6 | (ptr[2] - 0x80); \
+ \
+ if (ptr[0] >= 0xe0 && ptr[0] <= 0xef) \
+ { \
+ c |= (ptr[0] - 0xe0) << 12; \
+ ptr += 3; \
+ \
+ if (c < 0x800 || (c >= 0xd800 && c < 0xe000)) \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else if (ptr + 3 < end && ptr[3] >= 0x80 && ptr[3] < 0xc0) \
+ { \
+ c = c << 6 | (ptr[3] - 0x80); \
+ \
+ if (ptr[0] >= 0xf0 && ptr[0] <= 0xf4) \
+ { \
+ c |= (ptr[0] - 0xf0) << 18; \
+ ptr += 4; \
+ \
+ if (c >= 0x110000 || c < 0x10000) \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ }
+
+#define GETCHARBACK_INVALID(c, ptr, start, invalid_action) \
+ { \
+ c = ptr[-1]; \
+ if (c <= 0x7f) \
+ ptr--; \
+ else if (ptr - 1 > start && ptr[-1] >= 0x80 && ptr[-1] < 0xc0) \
+ { \
+ c -= 0x80; \
+ \
+ if (ptr[-2] >= 0xc2 && ptr[-2] <= 0xdf) \
+ { \
+ c |= (ptr[-2] - 0xc0) << 6; \
+ ptr -= 2; \
+ } \
+ else if (ptr - 2 > start && ptr[-2] >= 0x80 && ptr[-2] < 0xc0) \
+ { \
+ c = c << 6 | (ptr[-2] - 0x80); \
+ \
+ if (ptr[-3] >= 0xe0 && ptr[-3] <= 0xef) \
+ { \
+ c |= (ptr[-3] - 0xe0) << 12; \
+ ptr -= 3; \
+ \
+ if (c < 0x800 || (c >= 0xd800 && c < 0xe000)) \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else if (ptr - 3 > start && ptr[-3] >= 0x80 && ptr[-3] < 0xc0) \
+ { \
+ c = c << 6 | (ptr[-3] - 0x80); \
+ \
+ if (ptr[-4] >= 0xf0 && ptr[-4] <= 0xf4) \
+ { \
+ c |= (ptr[-4] - 0xf0) << 18; \
+ ptr -= 4; \
+ \
+ if (c >= 0x110000 || c < 0x10000) \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ }
+
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+
+#define GETCHARINC_INVALID(c, ptr, end, invalid_action) \
+ { \
+ if (ptr[0] < 0xd800 || ptr[0] >= 0xe000) \
+ c = *ptr++; \
+ else if (ptr[0] < 0xdc00 && ptr + 1 < end && ptr[1] >= 0xdc00 && ptr[1] < 0xe000) \
+ { \
+ c = (((ptr[0] - 0xd800) << 10) | (ptr[1] - 0xdc00)) + 0x10000; \
+ ptr += 2; \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ }
+
+#define GETCHARBACK_INVALID(c, ptr, start, invalid_action) \
+ { \
+ c = ptr[-1]; \
+ if (c < 0xd800 || c >= 0xe000) \
+ ptr--; \
+ else if (c >= 0xdc00 && ptr - 1 > start && ptr[-2] >= 0xd800 && ptr[-2] < 0xdc00) \
+ { \
+ c = (((ptr[-2] - 0xd800) << 10) | (c - 0xdc00)) + 0x10000; \
+ ptr -= 2; \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ }
+
+
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+
+#define GETCHARINC_INVALID(c, ptr, end, invalid_action) \
+ { \
+ if (ptr[0] < 0xd800 || (ptr[0] >= 0xe000 && ptr[0] < 0x110000)) \
+ c = *ptr++; \
+ else \
+ { \
+ invalid_action; \
+ } \
+ }
+
+#define GETCHARBACK_INVALID(c, ptr, start, invalid_action) \
+ { \
+ c = ptr[-1]; \
+ if (ptr[-1] < 0xd800 || (ptr[-1] >= 0xe000 && ptr[-1] < 0x110000)) \
+ ptr--; \
+ else \
+ { \
+ invalid_action; \
+ } \
+ }
+
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */
+#endif /* SUPPORT_UNICODE */
+
+static PCRE2_SPTR bracketend(PCRE2_SPTR cc)
+{
+SLJIT_ASSERT((*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NA) || (*cc >= OP_ONCE && *cc <= OP_SCOND));
+do cc += GET(cc, 1); while (*cc == OP_ALT);
+SLJIT_ASSERT(*cc >= OP_KET && *cc <= OP_KETRPOS);
+cc += 1 + LINK_SIZE;
+return cc;
+}
+
+static int no_alternatives(PCRE2_SPTR cc)
+{
+int count = 0;
+SLJIT_ASSERT((*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NA) || (*cc >= OP_ONCE && *cc <= OP_SCOND));
+do
+ {
+ cc += GET(cc, 1);
+ count++;
+ }
+while (*cc == OP_ALT);
+SLJIT_ASSERT(*cc >= OP_KET && *cc <= OP_KETRPOS);
+return count;
+}
+
+/* Functions whose might need modification for all new supported opcodes:
+ next_opcode
+ check_opcode_types
+ set_private_data_ptrs
+ get_framesize
+ init_frame
+ get_recurse_data_length
+ copy_recurse_data
+ compile_matchingpath
+ compile_backtrackingpath
+*/
+
+static PCRE2_SPTR next_opcode(compiler_common *common, PCRE2_SPTR cc)
+{
+SLJIT_UNUSED_ARG(common);
+switch(*cc)
+ {
+ case OP_SOD:
+ case OP_SOM:
+ case OP_SET_SOM:
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ case OP_NOT_DIGIT:
+ case OP_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ case OP_WORDCHAR:
+ case OP_ANY:
+ case OP_ALLANY:
+ case OP_NOTPROP:
+ case OP_PROP:
+ case OP_ANYNL:
+ case OP_NOT_HSPACE:
+ case OP_HSPACE:
+ case OP_NOT_VSPACE:
+ case OP_VSPACE:
+ case OP_EXTUNI:
+ case OP_EODN:
+ case OP_EOD:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_DOLL:
+ case OP_DOLLM:
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSSTAR:
+ case OP_CRPOSPLUS:
+ case OP_CRPOSQUERY:
+ case OP_CRPOSRANGE:
+ case OP_CLASS:
+ case OP_NCLASS:
+ case OP_REF:
+ case OP_REFI:
+ case OP_DNREF:
+ case OP_DNREFI:
+ case OP_RECURSE:
+ case OP_CALLOUT:
+ case OP_ALT:
+ case OP_KET:
+ case OP_KETRMAX:
+ case OP_KETRMIN:
+ case OP_KETRPOS:
+ case OP_REVERSE:
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_BRA:
+ case OP_BRAPOS:
+ case OP_CBRA:
+ case OP_CBRAPOS:
+ case OP_COND:
+ case OP_SBRA:
+ case OP_SBRAPOS:
+ case OP_SCBRA:
+ case OP_SCBRAPOS:
+ case OP_SCOND:
+ case OP_CREF:
+ case OP_DNCREF:
+ case OP_RREF:
+ case OP_DNRREF:
+ case OP_FALSE:
+ case OP_TRUE:
+ case OP_BRAZERO:
+ case OP_BRAMINZERO:
+ case OP_BRAPOSZERO:
+ case OP_PRUNE:
+ case OP_SKIP:
+ case OP_THEN:
+ case OP_COMMIT:
+ case OP_FAIL:
+ case OP_ACCEPT:
+ case OP_ASSERT_ACCEPT:
+ case OP_CLOSE:
+ case OP_SKIPZERO:
+ return cc + PRIV(OP_lengths)[*cc];
+
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_EXACT:
+ case OP_POSSTAR:
+ case OP_POSPLUS:
+ case OP_POSQUERY:
+ case OP_POSUPTO:
+ case OP_STARI:
+ case OP_MINSTARI:
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_UPTOI:
+ case OP_MINUPTOI:
+ case OP_EXACTI:
+ case OP_POSSTARI:
+ case OP_POSPLUSI:
+ case OP_POSQUERYI:
+ case OP_POSUPTOI:
+ case OP_NOTSTAR:
+ case OP_NOTMINSTAR:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTQUERY:
+ case OP_NOTMINQUERY:
+ case OP_NOTUPTO:
+ case OP_NOTMINUPTO:
+ case OP_NOTEXACT:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSUPTO:
+ case OP_NOTSTARI:
+ case OP_NOTMINSTARI:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUSI:
+ case OP_NOTQUERYI:
+ case OP_NOTMINQUERYI:
+ case OP_NOTUPTOI:
+ case OP_NOTMINUPTOI:
+ case OP_NOTEXACTI:
+ case OP_NOTPOSSTARI:
+ case OP_NOTPOSPLUSI:
+ case OP_NOTPOSQUERYI:
+ case OP_NOTPOSUPTOI:
+ cc += PRIV(OP_lengths)[*cc];
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ return cc;
+
+ /* Special cases. */
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEEXACT:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ case OP_TYPEPOSQUERY:
+ case OP_TYPEPOSUPTO:
+ return cc + PRIV(OP_lengths)[*cc] - 1;
+
+ case OP_ANYBYTE:
+#ifdef SUPPORT_UNICODE
+ if (common->utf) return NULL;
+#endif
+ return cc + 1;
+
+ case OP_CALLOUT_STR:
+ return cc + GET(cc, 1 + 2*LINK_SIZE);
+
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ case OP_XCLASS:
+ return cc + GET(cc, 1);
+#endif
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_SKIP_ARG:
+ case OP_THEN_ARG:
+ return cc + 1 + 2 + cc[1];
+
+ default:
+ SLJIT_UNREACHABLE();
+ return NULL;
+ }
+}
+
+static BOOL check_opcode_types(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend)
+{
+int count;
+PCRE2_SPTR slot;
+PCRE2_SPTR assert_back_end = cc - 1;
+PCRE2_SPTR assert_na_end = cc - 1;
+
+/* Calculate important variables (like stack size) and checks whether all opcodes are supported. */
+while (cc < ccend)
+ {
+ switch(*cc)
+ {
+ case OP_SET_SOM:
+ common->has_set_som = TRUE;
+ common->might_be_empty = TRUE;
+ cc += 1;
+ break;
+
+ case OP_REFI:
+#ifdef SUPPORT_UNICODE
+ if (common->iref_ptr == 0)
+ {
+ common->iref_ptr = common->ovector_start;
+ common->ovector_start += 3 * sizeof(sljit_sw);
+ }
+#endif /* SUPPORT_UNICODE */
+ /* Fall through. */
+ case OP_REF:
+ common->optimized_cbracket[GET2(cc, 1)] = 0;
+ cc += 1 + IMM2_SIZE;
+ break;
+
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ slot = bracketend(cc);
+ if (slot > assert_na_end)
+ assert_na_end = slot;
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] = 0;
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ case OP_COND:
+ case OP_SCOND:
+ /* Only AUTO_CALLOUT can insert this opcode. We do
+ not intend to support this case. */
+ if (cc[1 + LINK_SIZE] == OP_CALLOUT || cc[1 + LINK_SIZE] == OP_CALLOUT_STR)
+ return FALSE;
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_CREF:
+ common->optimized_cbracket[GET2(cc, 1)] = 0;
+ cc += 1 + IMM2_SIZE;
+ break;
+
+ case OP_DNREF:
+ case OP_DNREFI:
+ case OP_DNCREF:
+ count = GET2(cc, 1 + IMM2_SIZE);
+ slot = common->name_table + GET2(cc, 1) * common->name_entry_size;
+ while (count-- > 0)
+ {
+ common->optimized_cbracket[GET2(slot, 0)] = 0;
+ slot += common->name_entry_size;
+ }
+ cc += 1 + 2 * IMM2_SIZE;
+ break;
+
+ case OP_RECURSE:
+ /* Set its value only once. */
+ if (common->recursive_head_ptr == 0)
+ {
+ common->recursive_head_ptr = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ }
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_CALLOUT:
+ case OP_CALLOUT_STR:
+ if (common->capture_last_ptr == 0)
+ {
+ common->capture_last_ptr = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ }
+ cc += (*cc == OP_CALLOUT) ? PRIV(OP_lengths)[OP_CALLOUT] : GET(cc, 1 + 2*LINK_SIZE);
+ break;
+
+ case OP_ASSERTBACK:
+ slot = bracketend(cc);
+ if (slot > assert_back_end)
+ assert_back_end = slot;
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_THEN_ARG:
+ common->has_then = TRUE;
+ common->control_head_ptr = 1;
+ /* Fall through. */
+
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ if (cc < assert_na_end)
+ return FALSE;
+ /* Fall through */
+ case OP_MARK:
+ if (common->mark_ptr == 0)
+ {
+ common->mark_ptr = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ }
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_THEN:
+ common->has_then = TRUE;
+ common->control_head_ptr = 1;
+ cc += 1;
+ break;
+
+ case OP_SKIP:
+ if (cc < assert_back_end)
+ common->has_skip_in_assert_back = TRUE;
+ if (cc < assert_na_end)
+ return FALSE;
+ cc += 1;
+ break;
+
+ case OP_SKIP_ARG:
+ common->control_head_ptr = 1;
+ common->has_skip_arg = TRUE;
+ if (cc < assert_back_end)
+ common->has_skip_in_assert_back = TRUE;
+ if (cc < assert_na_end)
+ return FALSE;
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_PRUNE:
+ case OP_COMMIT:
+ case OP_ASSERT_ACCEPT:
+ if (cc < assert_na_end)
+ return FALSE;
+ cc++;
+ break;
+
+ default:
+ cc = next_opcode(common, cc);
+ if (cc == NULL)
+ return FALSE;
+ break;
+ }
+ }
+return TRUE;
+}
+
+#define EARLY_FAIL_ENHANCE_MAX (1 + 3)
+
+/*
+start:
+ 0 - skip / early fail allowed
+ 1 - only early fail with range allowed
+ >1 - (start - 1) early fail is processed
+
+return: current number of iterators enhanced with fast fail
+*/
+static int detect_early_fail(compiler_common *common, PCRE2_SPTR cc, int *private_data_start,
+ sljit_s32 depth, int start, BOOL fast_forward_allowed)
+{
+PCRE2_SPTR begin = cc;
+PCRE2_SPTR next_alt;
+PCRE2_SPTR end;
+PCRE2_SPTR accelerated_start;
+BOOL prev_fast_forward_allowed;
+int result = 0;
+int count;
+
+SLJIT_ASSERT(*cc == OP_ONCE || *cc == OP_BRA || *cc == OP_CBRA);
+SLJIT_ASSERT(*cc != OP_CBRA || common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] != 0);
+SLJIT_ASSERT(start < EARLY_FAIL_ENHANCE_MAX);
+
+next_alt = cc + GET(cc, 1);
+if (*next_alt == OP_ALT)
+ fast_forward_allowed = FALSE;
+
+do
+ {
+ count = start;
+ cc += 1 + LINK_SIZE + ((*cc == OP_CBRA) ? IMM2_SIZE : 0);
+
+ while (TRUE)
+ {
+ accelerated_start = NULL;
+
+ switch(*cc)
+ {
+ case OP_SOD:
+ case OP_SOM:
+ case OP_SET_SOM:
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ case OP_EODN:
+ case OP_EOD:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_DOLL:
+ case OP_DOLLM:
+ /* Zero width assertions. */
+ cc++;
+ continue;
+
+ case OP_NOT_DIGIT:
+ case OP_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ case OP_WORDCHAR:
+ case OP_ANY:
+ case OP_ALLANY:
+ case OP_ANYBYTE:
+ case OP_NOT_HSPACE:
+ case OP_HSPACE:
+ case OP_NOT_VSPACE:
+ case OP_VSPACE:
+ fast_forward_allowed = FALSE;
+ cc++;
+ continue;
+
+ case OP_ANYNL:
+ case OP_EXTUNI:
+ fast_forward_allowed = FALSE;
+ if (count == 0)
+ count = 1;
+ cc++;
+ continue;
+
+ case OP_NOTPROP:
+ case OP_PROP:
+ fast_forward_allowed = FALSE;
+ cc += 1 + 2;
+ continue;
+
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ fast_forward_allowed = FALSE;
+ cc += 2;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ continue;
+
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ /* The type or prop opcode is skipped in the next iteration. */
+ cc += 1;
+
+ if (cc[0] != OP_ANYNL && cc[0] != OP_EXTUNI)
+ {
+ accelerated_start = cc - 1;
+ break;
+ }
+
+ if (count == 0)
+ count = 1;
+ fast_forward_allowed = FALSE;
+ continue;
+
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEEXACT:
+ case OP_TYPEPOSUPTO:
+ cc += IMM2_SIZE;
+ /* Fall through */
+
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEPOSQUERY:
+ /* The type or prop opcode is skipped in the next iteration. */
+ fast_forward_allowed = FALSE;
+ if (count == 0)
+ count = 1;
+ cc += 1;
+ continue;
+
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_POSSTAR:
+ case OP_POSPLUS:
+
+ case OP_STARI:
+ case OP_MINSTARI:
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_POSSTARI:
+ case OP_POSPLUSI:
+
+ case OP_NOTSTAR:
+ case OP_NOTMINSTAR:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSPLUS:
+
+ case OP_NOTSTARI:
+ case OP_NOTMINSTARI:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUSI:
+ case OP_NOTPOSSTARI:
+ case OP_NOTPOSPLUSI:
+ accelerated_start = cc;
+ cc += 2;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_EXACT:
+ case OP_POSUPTO:
+ case OP_UPTOI:
+ case OP_MINUPTOI:
+ case OP_EXACTI:
+ case OP_POSUPTOI:
+ case OP_NOTUPTO:
+ case OP_NOTMINUPTO:
+ case OP_NOTEXACT:
+ case OP_NOTPOSUPTO:
+ case OP_NOTUPTOI:
+ case OP_NOTMINUPTOI:
+ case OP_NOTEXACTI:
+ case OP_NOTPOSUPTOI:
+ cc += IMM2_SIZE;
+ /* Fall through */
+
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_POSQUERY:
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_POSQUERYI:
+ case OP_NOTQUERY:
+ case OP_NOTMINQUERY:
+ case OP_NOTPOSQUERY:
+ case OP_NOTQUERYI:
+ case OP_NOTMINQUERYI:
+ case OP_NOTPOSQUERYI:
+ fast_forward_allowed = FALSE;
+ if (count == 0)
+ count = 1;
+ cc += 2;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ continue;
+
+ case OP_CLASS:
+ case OP_NCLASS:
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ case OP_XCLASS:
+ accelerated_start = cc;
+ cc += ((*cc == OP_XCLASS) ? GET(cc, 1) : (unsigned int)(1 + (32 / sizeof(PCRE2_UCHAR))));
+#else
+ accelerated_start = cc;
+ cc += (1 + (32 / sizeof(PCRE2_UCHAR)));
+#endif
+
+ switch (*cc)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRPOSSTAR:
+ case OP_CRPOSPLUS:
+ cc++;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ cc += 2 * IMM2_SIZE;
+ /* Fall through */
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSQUERY:
+ cc++;
+ if (count == 0)
+ count = 1;
+ /* Fall through */
+ default:
+ accelerated_start = NULL;
+ fast_forward_allowed = FALSE;
+ continue;
+ }
+ break;
+
+ case OP_ONCE:
+ case OP_BRA:
+ case OP_CBRA:
+ end = cc + GET(cc, 1);
+
+ prev_fast_forward_allowed = fast_forward_allowed;
+ fast_forward_allowed = FALSE;
+ if (depth >= 4)
+ break;
+
+ end = bracketend(cc) - (1 + LINK_SIZE);
+ if (*end != OP_KET || (*cc == OP_CBRA && common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0))
+ break;
+
+ count = detect_early_fail(common, cc, private_data_start, depth + 1, count, prev_fast_forward_allowed);
+
+ if (PRIVATE_DATA(cc) != 0)
+ common->private_data_ptrs[begin - common->start] = 1;
+
+ if (count < EARLY_FAIL_ENHANCE_MAX)
+ {
+ cc = end + (1 + LINK_SIZE);
+ continue;
+ }
+ break;
+
+ case OP_KET:
+ SLJIT_ASSERT(PRIVATE_DATA(cc) == 0);
+ if (cc >= next_alt)
+ break;
+ cc += 1 + LINK_SIZE;
+ continue;
+ }
+
+ if (accelerated_start != NULL)
+ {
+ if (count == 0)
+ {
+ count++;
+
+ if (fast_forward_allowed)
+ {
+ common->fast_forward_bc_ptr = accelerated_start;
+ common->private_data_ptrs[(accelerated_start + 1) - common->start] = ((*private_data_start) << 3) | type_skip;
+ *private_data_start += sizeof(sljit_sw);
+ }
+ else
+ {
+ common->private_data_ptrs[(accelerated_start + 1) - common->start] = ((*private_data_start) << 3) | type_fail;
+
+ if (common->early_fail_start_ptr == 0)
+ common->early_fail_start_ptr = *private_data_start;
+
+ *private_data_start += sizeof(sljit_sw);
+ common->early_fail_end_ptr = *private_data_start;
+
+ if (*private_data_start > SLJIT_MAX_LOCAL_SIZE)
+ return EARLY_FAIL_ENHANCE_MAX;
+ }
+ }
+ else
+ {
+ common->private_data_ptrs[(accelerated_start + 1) - common->start] = ((*private_data_start) << 3) | type_fail_range;
+
+ if (common->early_fail_start_ptr == 0)
+ common->early_fail_start_ptr = *private_data_start;
+
+ *private_data_start += 2 * sizeof(sljit_sw);
+ common->early_fail_end_ptr = *private_data_start;
+
+ if (*private_data_start > SLJIT_MAX_LOCAL_SIZE)
+ return EARLY_FAIL_ENHANCE_MAX;
+ }
+
+ /* Cannot be part of a repeat. */
+ common->private_data_ptrs[begin - common->start] = 1;
+ count++;
+
+ if (count < EARLY_FAIL_ENHANCE_MAX)
+ continue;
+ }
+
+ break;
+ }
+
+ if (*cc != OP_ALT && *cc != OP_KET)
+ result = EARLY_FAIL_ENHANCE_MAX;
+ else if (result < count)
+ result = count;
+
+ cc = next_alt;
+ next_alt = cc + GET(cc, 1);
+ }
+while (*cc == OP_ALT);
+
+return result;
+}
+
+static int get_class_iterator_size(PCRE2_SPTR cc)
+{
+sljit_u32 min;
+sljit_u32 max;
+switch(*cc)
+ {
+ case OP_CRSTAR:
+ case OP_CRPLUS:
+ return 2;
+
+ case OP_CRMINSTAR:
+ case OP_CRMINPLUS:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ return 1;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ min = GET2(cc, 1);
+ max = GET2(cc, 1 + IMM2_SIZE);
+ if (max == 0)
+ return (*cc == OP_CRRANGE) ? 2 : 1;
+ max -= min;
+ if (max > 2)
+ max = 2;
+ return max;
+
+ default:
+ return 0;
+ }
+}
+
+static BOOL detect_repeat(compiler_common *common, PCRE2_SPTR begin)
+{
+PCRE2_SPTR end = bracketend(begin);
+PCRE2_SPTR next;
+PCRE2_SPTR next_end;
+PCRE2_SPTR max_end;
+PCRE2_UCHAR type;
+sljit_sw length = end - begin;
+sljit_s32 min, max, i;
+
+/* Detect fixed iterations first. */
+if (end[-(1 + LINK_SIZE)] != OP_KET || PRIVATE_DATA(begin) != 0)
+ return FALSE;
+
+/* /(?:AB){4,6}/ is currently converted to /(?:AB){3}(?AB){1,3}/
+ * Skip the check of the second part. */
+if (PRIVATE_DATA(end - LINK_SIZE) != 0)
+ return TRUE;
+
+next = end;
+min = 1;
+while (1)
+ {
+ if (*next != *begin)
+ break;
+ next_end = bracketend(next);
+ if (next_end - next != length || memcmp(begin, next, IN_UCHARS(length)) != 0)
+ break;
+ next = next_end;
+ min++;
+ }
+
+if (min == 2)
+ return FALSE;
+
+max = 0;
+max_end = next;
+if (*next == OP_BRAZERO || *next == OP_BRAMINZERO)
+ {
+ type = *next;
+ while (1)
+ {
+ if (next[0] != type || next[1] != OP_BRA || next[2 + LINK_SIZE] != *begin)
+ break;
+ next_end = bracketend(next + 2 + LINK_SIZE);
+ if (next_end - next != (length + 2 + LINK_SIZE) || memcmp(begin, next + 2 + LINK_SIZE, IN_UCHARS(length)) != 0)
+ break;
+ next = next_end;
+ max++;
+ }
+
+ if (next[0] == type && next[1] == *begin && max >= 1)
+ {
+ next_end = bracketend(next + 1);
+ if (next_end - next == (length + 1) && memcmp(begin, next + 1, IN_UCHARS(length)) == 0)
+ {
+ for (i = 0; i < max; i++, next_end += 1 + LINK_SIZE)
+ if (*next_end != OP_KET)
+ break;
+
+ if (i == max)
+ {
+ common->private_data_ptrs[max_end - common->start - LINK_SIZE] = next_end - max_end;
+ common->private_data_ptrs[max_end - common->start - LINK_SIZE + 1] = (type == OP_BRAZERO) ? OP_UPTO : OP_MINUPTO;
+ /* +2 the original and the last. */
+ common->private_data_ptrs[max_end - common->start - LINK_SIZE + 2] = max + 2;
+ if (min == 1)
+ return TRUE;
+ min--;
+ max_end -= (1 + LINK_SIZE) + GET(max_end, -LINK_SIZE);
+ }
+ }
+ }
+ }
+
+if (min >= 3)
+ {
+ common->private_data_ptrs[end - common->start - LINK_SIZE] = max_end - end;
+ common->private_data_ptrs[end - common->start - LINK_SIZE + 1] = OP_EXACT;
+ common->private_data_ptrs[end - common->start - LINK_SIZE + 2] = min;
+ return TRUE;
+ }
+
+return FALSE;
+}
+
+#define CASE_ITERATOR_PRIVATE_DATA_1 \
+ case OP_MINSTAR: \
+ case OP_MINPLUS: \
+ case OP_QUERY: \
+ case OP_MINQUERY: \
+ case OP_MINSTARI: \
+ case OP_MINPLUSI: \
+ case OP_QUERYI: \
+ case OP_MINQUERYI: \
+ case OP_NOTMINSTAR: \
+ case OP_NOTMINPLUS: \
+ case OP_NOTQUERY: \
+ case OP_NOTMINQUERY: \
+ case OP_NOTMINSTARI: \
+ case OP_NOTMINPLUSI: \
+ case OP_NOTQUERYI: \
+ case OP_NOTMINQUERYI:
+
+#define CASE_ITERATOR_PRIVATE_DATA_2A \
+ case OP_STAR: \
+ case OP_PLUS: \
+ case OP_STARI: \
+ case OP_PLUSI: \
+ case OP_NOTSTAR: \
+ case OP_NOTPLUS: \
+ case OP_NOTSTARI: \
+ case OP_NOTPLUSI:
+
+#define CASE_ITERATOR_PRIVATE_DATA_2B \
+ case OP_UPTO: \
+ case OP_MINUPTO: \
+ case OP_UPTOI: \
+ case OP_MINUPTOI: \
+ case OP_NOTUPTO: \
+ case OP_NOTMINUPTO: \
+ case OP_NOTUPTOI: \
+ case OP_NOTMINUPTOI:
+
+#define CASE_ITERATOR_TYPE_PRIVATE_DATA_1 \
+ case OP_TYPEMINSTAR: \
+ case OP_TYPEMINPLUS: \
+ case OP_TYPEQUERY: \
+ case OP_TYPEMINQUERY:
+
+#define CASE_ITERATOR_TYPE_PRIVATE_DATA_2A \
+ case OP_TYPESTAR: \
+ case OP_TYPEPLUS:
+
+#define CASE_ITERATOR_TYPE_PRIVATE_DATA_2B \
+ case OP_TYPEUPTO: \
+ case OP_TYPEMINUPTO:
+
+static void set_private_data_ptrs(compiler_common *common, int *private_data_start, PCRE2_SPTR ccend)
+{
+PCRE2_SPTR cc = common->start;
+PCRE2_SPTR alternative;
+PCRE2_SPTR end = NULL;
+int private_data_ptr = *private_data_start;
+int space, size, bracketlen;
+BOOL repeat_check = TRUE;
+
+while (cc < ccend)
+ {
+ space = 0;
+ size = 0;
+ bracketlen = 0;
+ if (private_data_ptr > SLJIT_MAX_LOCAL_SIZE)
+ break;
+
+ /* When the bracket is prefixed by a zero iteration, skip the repeat check (at this point). */
+ if (repeat_check && (*cc == OP_ONCE || *cc == OP_BRA || *cc == OP_CBRA || *cc == OP_COND))
+ {
+ if (detect_repeat(common, cc))
+ {
+ /* These brackets are converted to repeats, so no global
+ based single character repeat is allowed. */
+ if (cc >= end)
+ end = bracketend(cc);
+ }
+ }
+ repeat_check = TRUE;
+
+ switch(*cc)
+ {
+ case OP_KET:
+ if (common->private_data_ptrs[cc + 1 - common->start] != 0)
+ {
+ common->private_data_ptrs[cc - common->start] = private_data_ptr;
+ private_data_ptr += sizeof(sljit_sw);
+ cc += common->private_data_ptrs[cc + 1 - common->start];
+ }
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_BRAPOS:
+ case OP_SBRA:
+ case OP_SBRAPOS:
+ case OP_SCOND:
+ common->private_data_ptrs[cc - common->start] = private_data_ptr;
+ private_data_ptr += sizeof(sljit_sw);
+ bracketlen = 1 + LINK_SIZE;
+ break;
+
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ common->private_data_ptrs[cc - common->start] = private_data_ptr;
+ private_data_ptr += sizeof(sljit_sw);
+ bracketlen = 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ case OP_COND:
+ /* Might be a hidden SCOND. */
+ common->private_data_ptrs[cc - common->start] = 0;
+ alternative = cc + GET(cc, 1);
+ if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN)
+ {
+ common->private_data_ptrs[cc - common->start] = private_data_ptr;
+ private_data_ptr += sizeof(sljit_sw);
+ }
+ bracketlen = 1 + LINK_SIZE;
+ break;
+
+ case OP_BRA:
+ bracketlen = 1 + LINK_SIZE;
+ break;
+
+ case OP_CBRA:
+ case OP_SCBRA:
+ bracketlen = 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ case OP_BRAZERO:
+ case OP_BRAMINZERO:
+ case OP_BRAPOSZERO:
+ size = 1;
+ repeat_check = FALSE;
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_1
+ size = -2;
+ space = 1;
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_2A
+ size = -2;
+ space = 2;
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_2B
+ size = -(2 + IMM2_SIZE);
+ space = 2;
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_1
+ size = 1;
+ space = 1;
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_2A
+ size = 1;
+ if (cc[1] != OP_ANYNL && cc[1] != OP_EXTUNI)
+ space = 2;
+ break;
+
+ case OP_TYPEUPTO:
+ size = 1 + IMM2_SIZE;
+ if (cc[1 + IMM2_SIZE] != OP_ANYNL && cc[1 + IMM2_SIZE] != OP_EXTUNI)
+ space = 2;
+ break;
+
+ case OP_TYPEMINUPTO:
+ size = 1 + IMM2_SIZE;
+ space = 2;
+ break;
+
+ case OP_CLASS:
+ case OP_NCLASS:
+ size = 1 + 32 / sizeof(PCRE2_UCHAR);
+ space = get_class_iterator_size(cc + size);
+ break;
+
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ case OP_XCLASS:
+ size = GET(cc, 1);
+ space = get_class_iterator_size(cc + size);
+ break;
+#endif
+
+ default:
+ cc = next_opcode(common, cc);
+ SLJIT_ASSERT(cc != NULL);
+ break;
+ }
+
+ /* Character iterators, which are not inside a repeated bracket,
+ gets a private slot instead of allocating it on the stack. */
+ if (space > 0 && cc >= end)
+ {
+ common->private_data_ptrs[cc - common->start] = private_data_ptr;
+ private_data_ptr += sizeof(sljit_sw) * space;
+ }
+
+ if (size != 0)
+ {
+ if (size < 0)
+ {
+ cc += -size;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ }
+ else
+ cc += size;
+ }
+
+ if (bracketlen > 0)
+ {
+ if (cc >= end)
+ {
+ end = bracketend(cc);
+ if (end[-1 - LINK_SIZE] == OP_KET)
+ end = NULL;
+ }
+ cc += bracketlen;
+ }
+ }
+*private_data_start = private_data_ptr;
+}
+
+/* Returns with a frame_types (always < 0) if no need for frame. */
+static int get_framesize(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, BOOL recursive, BOOL *needs_control_head)
+{
+int length = 0;
+int possessive = 0;
+BOOL stack_restore = FALSE;
+BOOL setsom_found = recursive;
+BOOL setmark_found = recursive;
+/* The last capture is a local variable even for recursions. */
+BOOL capture_last_found = FALSE;
+
+#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD
+SLJIT_ASSERT(common->control_head_ptr != 0);
+*needs_control_head = TRUE;
+#else
+*needs_control_head = FALSE;
+#endif
+
+if (ccend == NULL)
+ {
+ ccend = bracketend(cc) - (1 + LINK_SIZE);
+ if (!recursive && (*cc == OP_CBRAPOS || *cc == OP_SCBRAPOS))
+ {
+ possessive = length = (common->capture_last_ptr != 0) ? 5 : 3;
+ /* This is correct regardless of common->capture_last_ptr. */
+ capture_last_found = TRUE;
+ }
+ cc = next_opcode(common, cc);
+ }
+
+SLJIT_ASSERT(cc != NULL);
+while (cc < ccend)
+ switch(*cc)
+ {
+ case OP_SET_SOM:
+ SLJIT_ASSERT(common->has_set_som);
+ stack_restore = TRUE;
+ if (!setsom_found)
+ {
+ length += 2;
+ setsom_found = TRUE;
+ }
+ cc += 1;
+ break;
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_THEN_ARG:
+ SLJIT_ASSERT(common->mark_ptr != 0);
+ stack_restore = TRUE;
+ if (!setmark_found)
+ {
+ length += 2;
+ setmark_found = TRUE;
+ }
+ if (common->control_head_ptr != 0)
+ *needs_control_head = TRUE;
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_RECURSE:
+ stack_restore = TRUE;
+ if (common->has_set_som && !setsom_found)
+ {
+ length += 2;
+ setsom_found = TRUE;
+ }
+ if (common->mark_ptr != 0 && !setmark_found)
+ {
+ length += 2;
+ setmark_found = TRUE;
+ }
+ if (common->capture_last_ptr != 0 && !capture_last_found)
+ {
+ length += 2;
+ capture_last_found = TRUE;
+ }
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_CBRA:
+ case OP_CBRAPOS:
+ case OP_SCBRA:
+ case OP_SCBRAPOS:
+ stack_restore = TRUE;
+ if (common->capture_last_ptr != 0 && !capture_last_found)
+ {
+ length += 2;
+ capture_last_found = TRUE;
+ }
+ length += 3;
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ case OP_THEN:
+ stack_restore = TRUE;
+ if (common->control_head_ptr != 0)
+ *needs_control_head = TRUE;
+ cc ++;
+ break;
+
+ default:
+ stack_restore = TRUE;
+ /* Fall through. */
+
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ case OP_NOT_DIGIT:
+ case OP_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ case OP_WORDCHAR:
+ case OP_ANY:
+ case OP_ALLANY:
+ case OP_ANYBYTE:
+ case OP_NOTPROP:
+ case OP_PROP:
+ case OP_ANYNL:
+ case OP_NOT_HSPACE:
+ case OP_HSPACE:
+ case OP_NOT_VSPACE:
+ case OP_VSPACE:
+ case OP_EXTUNI:
+ case OP_EODN:
+ case OP_EOD:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_DOLL:
+ case OP_DOLLM:
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+
+ case OP_EXACT:
+ case OP_POSSTAR:
+ case OP_POSPLUS:
+ case OP_POSQUERY:
+ case OP_POSUPTO:
+
+ case OP_EXACTI:
+ case OP_POSSTARI:
+ case OP_POSPLUSI:
+ case OP_POSQUERYI:
+ case OP_POSUPTOI:
+
+ case OP_NOTEXACT:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSUPTO:
+
+ case OP_NOTEXACTI:
+ case OP_NOTPOSSTARI:
+ case OP_NOTPOSPLUSI:
+ case OP_NOTPOSQUERYI:
+ case OP_NOTPOSUPTOI:
+
+ case OP_TYPEEXACT:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ case OP_TYPEPOSQUERY:
+ case OP_TYPEPOSUPTO:
+
+ case OP_CLASS:
+ case OP_NCLASS:
+ case OP_XCLASS:
+
+ case OP_CALLOUT:
+ case OP_CALLOUT_STR:
+
+ cc = next_opcode(common, cc);
+ SLJIT_ASSERT(cc != NULL);
+ break;
+ }
+
+/* Possessive quantifiers can use a special case. */
+if (SLJIT_UNLIKELY(possessive == length))
+ return stack_restore ? no_frame : no_stack;
+
+if (length > 0)
+ return length + 1;
+return stack_restore ? no_frame : no_stack;
+}
+
+static void init_frame(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, int stackpos, int stacktop)
+{
+DEFINE_COMPILER;
+BOOL setsom_found = FALSE;
+BOOL setmark_found = FALSE;
+/* The last capture is a local variable even for recursions. */
+BOOL capture_last_found = FALSE;
+int offset;
+
+/* >= 1 + shortest item size (2) */
+SLJIT_UNUSED_ARG(stacktop);
+SLJIT_ASSERT(stackpos >= stacktop + 2);
+
+stackpos = STACK(stackpos);
+if (ccend == NULL)
+ {
+ ccend = bracketend(cc) - (1 + LINK_SIZE);
+ if (*cc != OP_CBRAPOS && *cc != OP_SCBRAPOS)
+ cc = next_opcode(common, cc);
+ }
+
+SLJIT_ASSERT(cc != NULL);
+while (cc < ccend)
+ switch(*cc)
+ {
+ case OP_SET_SOM:
+ SLJIT_ASSERT(common->has_set_som);
+ if (!setsom_found)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0));
+ stackpos -= SSIZE_OF(sw);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
+ stackpos -= SSIZE_OF(sw);
+ setsom_found = TRUE;
+ }
+ cc += 1;
+ break;
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_THEN_ARG:
+ SLJIT_ASSERT(common->mark_ptr != 0);
+ if (!setmark_found)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr);
+ stackpos -= SSIZE_OF(sw);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
+ stackpos -= SSIZE_OF(sw);
+ setmark_found = TRUE;
+ }
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_RECURSE:
+ if (common->has_set_som && !setsom_found)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0));
+ stackpos -= SSIZE_OF(sw);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
+ stackpos -= SSIZE_OF(sw);
+ setsom_found = TRUE;
+ }
+ if (common->mark_ptr != 0 && !setmark_found)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr);
+ stackpos -= SSIZE_OF(sw);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
+ stackpos -= SSIZE_OF(sw);
+ setmark_found = TRUE;
+ }
+ if (common->capture_last_ptr != 0 && !capture_last_found)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr);
+ stackpos -= SSIZE_OF(sw);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
+ stackpos -= SSIZE_OF(sw);
+ capture_last_found = TRUE;
+ }
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_CBRA:
+ case OP_CBRAPOS:
+ case OP_SCBRA:
+ case OP_SCBRAPOS:
+ if (common->capture_last_ptr != 0 && !capture_last_found)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr);
+ stackpos -= SSIZE_OF(sw);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
+ stackpos -= SSIZE_OF(sw);
+ capture_last_found = TRUE;
+ }
+ offset = (GET2(cc, 1 + LINK_SIZE)) << 1;
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, OVECTOR(offset));
+ stackpos -= SSIZE_OF(sw);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
+ stackpos -= SSIZE_OF(sw);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP2, 0);
+ stackpos -= SSIZE_OF(sw);
+
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ default:
+ cc = next_opcode(common, cc);
+ SLJIT_ASSERT(cc != NULL);
+ break;
+ }
+
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, 0);
+SLJIT_ASSERT(stackpos == STACK(stacktop));
+}
+
+#define RECURSE_TMP_REG_COUNT 3
+
+typedef struct delayed_mem_copy_status {
+ struct sljit_compiler *compiler;
+ int store_bases[RECURSE_TMP_REG_COUNT];
+ int store_offsets[RECURSE_TMP_REG_COUNT];
+ int tmp_regs[RECURSE_TMP_REG_COUNT];
+ int saved_tmp_regs[RECURSE_TMP_REG_COUNT];
+ int next_tmp_reg;
+} delayed_mem_copy_status;
+
+static void delayed_mem_copy_init(delayed_mem_copy_status *status, compiler_common *common)
+{
+int i;
+
+for (i = 0; i < RECURSE_TMP_REG_COUNT; i++)
+ {
+ SLJIT_ASSERT(status->tmp_regs[i] >= 0);
+ SLJIT_ASSERT(sljit_get_register_index(status->saved_tmp_regs[i]) < 0 || status->tmp_regs[i] == status->saved_tmp_regs[i]);
+
+ status->store_bases[i] = -1;
+ }
+status->next_tmp_reg = 0;
+status->compiler = common->compiler;
+}
+
+static void delayed_mem_copy_move(delayed_mem_copy_status *status, int load_base, sljit_sw load_offset,
+ int store_base, sljit_sw store_offset)
+{
+struct sljit_compiler *compiler = status->compiler;
+int next_tmp_reg = status->next_tmp_reg;
+int tmp_reg = status->tmp_regs[next_tmp_reg];
+
+SLJIT_ASSERT(load_base > 0 && store_base > 0);
+
+if (status->store_bases[next_tmp_reg] == -1)
+ {
+ /* Preserve virtual registers. */
+ if (sljit_get_register_index(status->saved_tmp_regs[next_tmp_reg]) < 0)
+ OP1(SLJIT_MOV, status->saved_tmp_regs[next_tmp_reg], 0, tmp_reg, 0);
+ }
+else
+ OP1(SLJIT_MOV, SLJIT_MEM1(status->store_bases[next_tmp_reg]), status->store_offsets[next_tmp_reg], tmp_reg, 0);
+
+OP1(SLJIT_MOV, tmp_reg, 0, SLJIT_MEM1(load_base), load_offset);
+status->store_bases[next_tmp_reg] = store_base;
+status->store_offsets[next_tmp_reg] = store_offset;
+
+status->next_tmp_reg = (next_tmp_reg + 1) % RECURSE_TMP_REG_COUNT;
+}
+
+static void delayed_mem_copy_finish(delayed_mem_copy_status *status)
+{
+struct sljit_compiler *compiler = status->compiler;
+int next_tmp_reg = status->next_tmp_reg;
+int tmp_reg, saved_tmp_reg, i;
+
+for (i = 0; i < RECURSE_TMP_REG_COUNT; i++)
+ {
+ if (status->store_bases[next_tmp_reg] != -1)
+ {
+ tmp_reg = status->tmp_regs[next_tmp_reg];
+ saved_tmp_reg = status->saved_tmp_regs[next_tmp_reg];
+
+ OP1(SLJIT_MOV, SLJIT_MEM1(status->store_bases[next_tmp_reg]), status->store_offsets[next_tmp_reg], tmp_reg, 0);
+
+ /* Restore virtual registers. */
+ if (sljit_get_register_index(saved_tmp_reg) < 0)
+ OP1(SLJIT_MOV, tmp_reg, 0, saved_tmp_reg, 0);
+ }
+
+ next_tmp_reg = (next_tmp_reg + 1) % RECURSE_TMP_REG_COUNT;
+ }
+}
+
+#undef RECURSE_TMP_REG_COUNT
+
+static BOOL recurse_check_bit(compiler_common *common, sljit_sw bit_index)
+{
+uint8_t *byte;
+uint8_t mask;
+
+SLJIT_ASSERT((bit_index & (sizeof(sljit_sw) - 1)) == 0);
+
+bit_index >>= SLJIT_WORD_SHIFT;
+
+SLJIT_ASSERT((bit_index >> 3) < common->recurse_bitset_size);
+
+mask = 1 << (bit_index & 0x7);
+byte = common->recurse_bitset + (bit_index >> 3);
+
+if (*byte & mask)
+ return FALSE;
+
+*byte |= mask;
+return TRUE;
+}
+
+enum get_recurse_flags {
+ recurse_flag_quit_found = (1 << 0),
+ recurse_flag_accept_found = (1 << 1),
+ recurse_flag_setsom_found = (1 << 2),
+ recurse_flag_setmark_found = (1 << 3),
+ recurse_flag_control_head_found = (1 << 4),
+};
+
+static int get_recurse_data_length(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, uint32_t *result_flags)
+{
+int length = 1;
+int size, offset;
+PCRE2_SPTR alternative;
+uint32_t recurse_flags = 0;
+
+memset(common->recurse_bitset, 0, common->recurse_bitset_size);
+
+#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD
+SLJIT_ASSERT(common->control_head_ptr != 0);
+recurse_flags |= recurse_flag_control_head_found;
+#endif
+
+/* Calculate the sum of the private machine words. */
+while (cc < ccend)
+ {
+ size = 0;
+ switch(*cc)
+ {
+ case OP_SET_SOM:
+ SLJIT_ASSERT(common->has_set_som);
+ recurse_flags |= recurse_flag_setsom_found;
+ cc += 1;
+ break;
+
+ case OP_RECURSE:
+ if (common->has_set_som)
+ recurse_flags |= recurse_flag_setsom_found;
+ if (common->mark_ptr != 0)
+ recurse_flags |= recurse_flag_setmark_found;
+ if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
+ length++;
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_KET:
+ offset = PRIVATE_DATA(cc);
+ if (offset != 0)
+ {
+ if (recurse_check_bit(common, offset))
+ length++;
+ SLJIT_ASSERT(PRIVATE_DATA(cc + 1) != 0);
+ cc += PRIVATE_DATA(cc + 1);
+ }
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_BRAPOS:
+ case OP_SBRA:
+ case OP_SBRAPOS:
+ case OP_SCOND:
+ SLJIT_ASSERT(PRIVATE_DATA(cc) != 0);
+ if (recurse_check_bit(common, PRIVATE_DATA(cc)))
+ length++;
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_CBRA:
+ case OP_SCBRA:
+ offset = GET2(cc, 1 + LINK_SIZE);
+ if (recurse_check_bit(common, OVECTOR(offset << 1)))
+ {
+ SLJIT_ASSERT(recurse_check_bit(common, OVECTOR((offset << 1) + 1)));
+ length += 2;
+ }
+ if (common->optimized_cbracket[offset] == 0 && recurse_check_bit(common, OVECTOR_PRIV(offset)))
+ length++;
+ if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
+ length++;
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ offset = GET2(cc, 1 + LINK_SIZE);
+ if (recurse_check_bit(common, OVECTOR(offset << 1)))
+ {
+ SLJIT_ASSERT(recurse_check_bit(common, OVECTOR((offset << 1) + 1)));
+ length += 2;
+ }
+ if (recurse_check_bit(common, OVECTOR_PRIV(offset)))
+ length++;
+ if (recurse_check_bit(common, PRIVATE_DATA(cc)))
+ length++;
+ if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
+ length++;
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ case OP_COND:
+ /* Might be a hidden SCOND. */
+ alternative = cc + GET(cc, 1);
+ if ((*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) && recurse_check_bit(common, PRIVATE_DATA(cc)))
+ length++;
+ cc += 1 + LINK_SIZE;
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_1
+ offset = PRIVATE_DATA(cc);
+ if (offset != 0 && recurse_check_bit(common, offset))
+ length++;
+ cc += 2;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_2A
+ offset = PRIVATE_DATA(cc);
+ if (offset != 0 && recurse_check_bit(common, offset))
+ {
+ SLJIT_ASSERT(recurse_check_bit(common, offset + sizeof(sljit_sw)));
+ length += 2;
+ }
+ cc += 2;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_2B
+ offset = PRIVATE_DATA(cc);
+ if (offset != 0 && recurse_check_bit(common, offset))
+ {
+ SLJIT_ASSERT(recurse_check_bit(common, offset + sizeof(sljit_sw)));
+ length += 2;
+ }
+ cc += 2 + IMM2_SIZE;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_1
+ offset = PRIVATE_DATA(cc);
+ if (offset != 0 && recurse_check_bit(common, offset))
+ length++;
+ cc += 1;
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_2A
+ offset = PRIVATE_DATA(cc);
+ if (offset != 0 && recurse_check_bit(common, offset))
+ {
+ SLJIT_ASSERT(recurse_check_bit(common, offset + sizeof(sljit_sw)));
+ length += 2;
+ }
+ cc += 1;
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_2B
+ offset = PRIVATE_DATA(cc);
+ if (offset != 0 && recurse_check_bit(common, offset))
+ {
+ SLJIT_ASSERT(recurse_check_bit(common, offset + sizeof(sljit_sw)));
+ length += 2;
+ }
+ cc += 1 + IMM2_SIZE;
+ break;
+
+ case OP_CLASS:
+ case OP_NCLASS:
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ case OP_XCLASS:
+ size = (*cc == OP_XCLASS) ? GET(cc, 1) : 1 + 32 / (int)sizeof(PCRE2_UCHAR);
+#else
+ size = 1 + 32 / (int)sizeof(PCRE2_UCHAR);
+#endif
+
+ offset = PRIVATE_DATA(cc);
+ if (offset != 0 && recurse_check_bit(common, offset))
+ length += get_class_iterator_size(cc + size);
+ cc += size;
+ break;
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_THEN_ARG:
+ SLJIT_ASSERT(common->mark_ptr != 0);
+ recurse_flags |= recurse_flag_setmark_found;
+ if (common->control_head_ptr != 0)
+ recurse_flags |= recurse_flag_control_head_found;
+ if (*cc != OP_MARK)
+ recurse_flags |= recurse_flag_quit_found;
+
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_PRUNE:
+ case OP_SKIP:
+ case OP_COMMIT:
+ recurse_flags |= recurse_flag_quit_found;
+ cc++;
+ break;
+
+ case OP_SKIP_ARG:
+ recurse_flags |= recurse_flag_quit_found;
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_THEN:
+ SLJIT_ASSERT(common->control_head_ptr != 0);
+ recurse_flags |= recurse_flag_quit_found | recurse_flag_control_head_found;
+ cc++;
+ break;
+
+ case OP_ACCEPT:
+ case OP_ASSERT_ACCEPT:
+ recurse_flags |= recurse_flag_accept_found;
+ cc++;
+ break;
+
+ default:
+ cc = next_opcode(common, cc);
+ SLJIT_ASSERT(cc != NULL);
+ break;
+ }
+ }
+SLJIT_ASSERT(cc == ccend);
+
+if (recurse_flags & recurse_flag_control_head_found)
+ length++;
+if (recurse_flags & recurse_flag_quit_found)
+ {
+ if (recurse_flags & recurse_flag_setsom_found)
+ length++;
+ if (recurse_flags & recurse_flag_setmark_found)
+ length++;
+ }
+
+*result_flags = recurse_flags;
+return length;
+}
+
+enum copy_recurse_data_types {
+ recurse_copy_from_global,
+ recurse_copy_private_to_global,
+ recurse_copy_shared_to_global,
+ recurse_copy_kept_shared_to_global,
+ recurse_swap_global
+};
+
+static void copy_recurse_data(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend,
+ int type, int stackptr, int stacktop, uint32_t recurse_flags)
+{
+delayed_mem_copy_status status;
+PCRE2_SPTR alternative;
+sljit_sw private_srcw[2];
+sljit_sw shared_srcw[3];
+sljit_sw kept_shared_srcw[2];
+int private_count, shared_count, kept_shared_count;
+int from_sp, base_reg, offset, i;
+
+memset(common->recurse_bitset, 0, common->recurse_bitset_size);
+
+#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD
+SLJIT_ASSERT(common->control_head_ptr != 0);
+recurse_check_bit(common, common->control_head_ptr);
+#endif
+
+switch (type)
+ {
+ case recurse_copy_from_global:
+ from_sp = TRUE;
+ base_reg = STACK_TOP;
+ break;
+
+ case recurse_copy_private_to_global:
+ case recurse_copy_shared_to_global:
+ case recurse_copy_kept_shared_to_global:
+ from_sp = FALSE;
+ base_reg = STACK_TOP;
+ break;
+
+ default:
+ SLJIT_ASSERT(type == recurse_swap_global);
+ from_sp = FALSE;
+ base_reg = TMP2;
+ break;
+ }
+
+stackptr = STACK(stackptr);
+stacktop = STACK(stacktop);
+
+status.tmp_regs[0] = TMP1;
+status.saved_tmp_regs[0] = TMP1;
+
+if (base_reg != TMP2)
+ {
+ status.tmp_regs[1] = TMP2;
+ status.saved_tmp_regs[1] = TMP2;
+ }
+else
+ {
+ status.saved_tmp_regs[1] = RETURN_ADDR;
+ if (HAS_VIRTUAL_REGISTERS)
+ status.tmp_regs[1] = STR_PTR;
+ else
+ status.tmp_regs[1] = RETURN_ADDR;
+ }
+
+status.saved_tmp_regs[2] = TMP3;
+if (HAS_VIRTUAL_REGISTERS)
+ status.tmp_regs[2] = STR_END;
+else
+ status.tmp_regs[2] = TMP3;
+
+delayed_mem_copy_init(&status, common);
+
+if (type != recurse_copy_shared_to_global && type != recurse_copy_kept_shared_to_global)
+ {
+ SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_private_to_global || type == recurse_swap_global);
+
+ if (!from_sp)
+ delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, common->recursive_head_ptr);
+
+ if (from_sp || type == recurse_swap_global)
+ delayed_mem_copy_move(&status, SLJIT_SP, common->recursive_head_ptr, base_reg, stackptr);
+ }
+
+stackptr += sizeof(sljit_sw);
+
+#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD
+if (type != recurse_copy_shared_to_global)
+ {
+ if (!from_sp)
+ delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, common->control_head_ptr);
+
+ if (from_sp || type == recurse_swap_global)
+ delayed_mem_copy_move(&status, SLJIT_SP, common->control_head_ptr, base_reg, stackptr);
+ }
+
+stackptr += sizeof(sljit_sw);
+#endif
+
+while (cc < ccend)
+ {
+ private_count = 0;
+ shared_count = 0;
+ kept_shared_count = 0;
+
+ switch(*cc)
+ {
+ case OP_SET_SOM:
+ SLJIT_ASSERT(common->has_set_som);
+ if ((recurse_flags & recurse_flag_quit_found) && recurse_check_bit(common, OVECTOR(0)))
+ {
+ kept_shared_srcw[0] = OVECTOR(0);
+ kept_shared_count = 1;
+ }
+ cc += 1;
+ break;
+
+ case OP_RECURSE:
+ if (recurse_flags & recurse_flag_quit_found)
+ {
+ if (common->has_set_som && recurse_check_bit(common, OVECTOR(0)))
+ {
+ kept_shared_srcw[0] = OVECTOR(0);
+ kept_shared_count = 1;
+ }
+ if (common->mark_ptr != 0 && recurse_check_bit(common, common->mark_ptr))
+ {
+ kept_shared_srcw[kept_shared_count] = common->mark_ptr;
+ kept_shared_count++;
+ }
+ }
+ if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
+ {
+ shared_srcw[0] = common->capture_last_ptr;
+ shared_count = 1;
+ }
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_KET:
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (private_srcw[0] != 0)
+ {
+ if (recurse_check_bit(common, private_srcw[0]))
+ private_count = 1;
+ SLJIT_ASSERT(PRIVATE_DATA(cc + 1) != 0);
+ cc += PRIVATE_DATA(cc + 1);
+ }
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_BRAPOS:
+ case OP_SBRA:
+ case OP_SBRAPOS:
+ case OP_SCOND:
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (recurse_check_bit(common, private_srcw[0]))
+ private_count = 1;
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_CBRA:
+ case OP_SCBRA:
+ offset = GET2(cc, 1 + LINK_SIZE);
+ shared_srcw[0] = OVECTOR(offset << 1);
+ if (recurse_check_bit(common, shared_srcw[0]))
+ {
+ shared_srcw[1] = shared_srcw[0] + sizeof(sljit_sw);
+ SLJIT_ASSERT(recurse_check_bit(common, shared_srcw[1]));
+ shared_count = 2;
+ }
+
+ if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
+ {
+ shared_srcw[shared_count] = common->capture_last_ptr;
+ shared_count++;
+ }
+
+ if (common->optimized_cbracket[offset] == 0)
+ {
+ private_srcw[0] = OVECTOR_PRIV(offset);
+ if (recurse_check_bit(common, private_srcw[0]))
+ private_count = 1;
+ }
+
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ offset = GET2(cc, 1 + LINK_SIZE);
+ shared_srcw[0] = OVECTOR(offset << 1);
+ if (recurse_check_bit(common, shared_srcw[0]))
+ {
+ shared_srcw[1] = shared_srcw[0] + sizeof(sljit_sw);
+ SLJIT_ASSERT(recurse_check_bit(common, shared_srcw[1]));
+ shared_count = 2;
+ }
+
+ if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
+ {
+ shared_srcw[shared_count] = common->capture_last_ptr;
+ shared_count++;
+ }
+
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (recurse_check_bit(common, private_srcw[0]))
+ private_count = 1;
+
+ offset = OVECTOR_PRIV(offset);
+ if (recurse_check_bit(common, offset))
+ {
+ private_srcw[private_count] = offset;
+ private_count++;
+ }
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ case OP_COND:
+ /* Might be a hidden SCOND. */
+ alternative = cc + GET(cc, 1);
+ if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN)
+ {
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (recurse_check_bit(common, private_srcw[0]))
+ private_count = 1;
+ }
+ cc += 1 + LINK_SIZE;
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_1
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
+ private_count = 1;
+ cc += 2;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_2A
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
+ {
+ private_count = 2;
+ private_srcw[1] = private_srcw[0] + sizeof(sljit_sw);
+ SLJIT_ASSERT(recurse_check_bit(common, private_srcw[1]));
+ }
+ cc += 2;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_2B
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
+ {
+ private_count = 2;
+ private_srcw[1] = private_srcw[0] + sizeof(sljit_sw);
+ SLJIT_ASSERT(recurse_check_bit(common, private_srcw[1]));
+ }
+ cc += 2 + IMM2_SIZE;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_1
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
+ private_count = 1;
+ cc += 1;
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_2A
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
+ {
+ private_count = 2;
+ private_srcw[1] = private_srcw[0] + sizeof(sljit_sw);
+ SLJIT_ASSERT(recurse_check_bit(common, private_srcw[1]));
+ }
+ cc += 1;
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_2B
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
+ {
+ private_count = 2;
+ private_srcw[1] = private_srcw[0] + sizeof(sljit_sw);
+ SLJIT_ASSERT(recurse_check_bit(common, private_srcw[1]));
+ }
+ cc += 1 + IMM2_SIZE;
+ break;
+
+ case OP_CLASS:
+ case OP_NCLASS:
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ case OP_XCLASS:
+ i = (*cc == OP_XCLASS) ? GET(cc, 1) : 1 + 32 / (int)sizeof(PCRE2_UCHAR);
+#else
+ i = 1 + 32 / (int)sizeof(PCRE2_UCHAR);
+#endif
+ if (PRIVATE_DATA(cc) != 0)
+ {
+ private_count = 1;
+ private_srcw[0] = PRIVATE_DATA(cc);
+ switch(get_class_iterator_size(cc + i))
+ {
+ case 1:
+ break;
+
+ case 2:
+ if (recurse_check_bit(common, private_srcw[0]))
+ {
+ private_count = 2;
+ private_srcw[1] = private_srcw[0] + sizeof(sljit_sw);
+ SLJIT_ASSERT(recurse_check_bit(common, private_srcw[1]));
+ }
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+ }
+ cc += i;
+ break;
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_THEN_ARG:
+ SLJIT_ASSERT(common->mark_ptr != 0);
+ if ((recurse_flags & recurse_flag_quit_found) && recurse_check_bit(common, common->mark_ptr))
+ {
+ kept_shared_srcw[0] = common->mark_ptr;
+ kept_shared_count = 1;
+ }
+ if (common->control_head_ptr != 0 && recurse_check_bit(common, common->control_head_ptr))
+ {
+ private_srcw[0] = common->control_head_ptr;
+ private_count = 1;
+ }
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_THEN:
+ SLJIT_ASSERT(common->control_head_ptr != 0);
+ if (recurse_check_bit(common, common->control_head_ptr))
+ {
+ private_srcw[0] = common->control_head_ptr;
+ private_count = 1;
+ }
+ cc++;
+ break;
+
+ default:
+ cc = next_opcode(common, cc);
+ SLJIT_ASSERT(cc != NULL);
+ continue;
+ }
+
+ if (type != recurse_copy_shared_to_global && type != recurse_copy_kept_shared_to_global)
+ {
+ SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_private_to_global || type == recurse_swap_global);
+
+ for (i = 0; i < private_count; i++)
+ {
+ SLJIT_ASSERT(private_srcw[i] != 0);
+
+ if (!from_sp)
+ delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, private_srcw[i]);
+
+ if (from_sp || type == recurse_swap_global)
+ delayed_mem_copy_move(&status, SLJIT_SP, private_srcw[i], base_reg, stackptr);
+
+ stackptr += sizeof(sljit_sw);
+ }
+ }
+ else
+ stackptr += sizeof(sljit_sw) * private_count;
+
+ if (type != recurse_copy_private_to_global && type != recurse_copy_kept_shared_to_global)
+ {
+ SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_shared_to_global || type == recurse_swap_global);
+
+ for (i = 0; i < shared_count; i++)
+ {
+ SLJIT_ASSERT(shared_srcw[i] != 0);
+
+ if (!from_sp)
+ delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, shared_srcw[i]);
+
+ if (from_sp || type == recurse_swap_global)
+ delayed_mem_copy_move(&status, SLJIT_SP, shared_srcw[i], base_reg, stackptr);
+
+ stackptr += sizeof(sljit_sw);
+ }
+ }
+ else
+ stackptr += sizeof(sljit_sw) * shared_count;
+
+ if (type != recurse_copy_private_to_global && type != recurse_swap_global)
+ {
+ SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_shared_to_global || type == recurse_copy_kept_shared_to_global);
+
+ for (i = 0; i < kept_shared_count; i++)
+ {
+ SLJIT_ASSERT(kept_shared_srcw[i] != 0);
+
+ if (!from_sp)
+ delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, kept_shared_srcw[i]);
+
+ if (from_sp || type == recurse_swap_global)
+ delayed_mem_copy_move(&status, SLJIT_SP, kept_shared_srcw[i], base_reg, stackptr);
+
+ stackptr += sizeof(sljit_sw);
+ }
+ }
+ else
+ stackptr += sizeof(sljit_sw) * kept_shared_count;
+ }
+
+SLJIT_ASSERT(cc == ccend && stackptr == stacktop);
+
+delayed_mem_copy_finish(&status);
+}
+
+static SLJIT_INLINE PCRE2_SPTR set_then_offsets(compiler_common *common, PCRE2_SPTR cc, sljit_u8 *current_offset)
+{
+PCRE2_SPTR end = bracketend(cc);
+BOOL has_alternatives = cc[GET(cc, 1)] == OP_ALT;
+
+/* Assert captures then. */
+if (*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NA)
+ current_offset = NULL;
+/* Conditional block does not. */
+if (*cc == OP_COND || *cc == OP_SCOND)
+ has_alternatives = FALSE;
+
+cc = next_opcode(common, cc);
+if (has_alternatives)
+ current_offset = common->then_offsets + (cc - common->start);
+
+while (cc < end)
+ {
+ if ((*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NA) || (*cc >= OP_ONCE && *cc <= OP_SCOND))
+ cc = set_then_offsets(common, cc, current_offset);
+ else
+ {
+ if (*cc == OP_ALT && has_alternatives)
+ current_offset = common->then_offsets + (cc + 1 + LINK_SIZE - common->start);
+ if (*cc >= OP_THEN && *cc <= OP_THEN_ARG && current_offset != NULL)
+ *current_offset = 1;
+ cc = next_opcode(common, cc);
+ }
+ }
+
+return end;
+}
+
+#undef CASE_ITERATOR_PRIVATE_DATA_1
+#undef CASE_ITERATOR_PRIVATE_DATA_2A
+#undef CASE_ITERATOR_PRIVATE_DATA_2B
+#undef CASE_ITERATOR_TYPE_PRIVATE_DATA_1
+#undef CASE_ITERATOR_TYPE_PRIVATE_DATA_2A
+#undef CASE_ITERATOR_TYPE_PRIVATE_DATA_2B
+
+static SLJIT_INLINE BOOL is_powerof2(unsigned int value)
+{
+return (value & (value - 1)) == 0;
+}
+
+static SLJIT_INLINE void set_jumps(jump_list *list, struct sljit_label *label)
+{
+while (list)
+ {
+ /* sljit_set_label is clever enough to do nothing
+ if either the jump or the label is NULL. */
+ SET_LABEL(list->jump, label);
+ list = list->next;
+ }
+}
+
+static SLJIT_INLINE void add_jump(struct sljit_compiler *compiler, jump_list **list, struct sljit_jump *jump)
+{
+jump_list *list_item = sljit_alloc_memory(compiler, sizeof(jump_list));
+if (list_item)
+ {
+ list_item->next = *list;
+ list_item->jump = jump;
+ *list = list_item;
+ }
+}
+
+static void add_stub(compiler_common *common, struct sljit_jump *start)
+{
+DEFINE_COMPILER;
+stub_list *list_item = sljit_alloc_memory(compiler, sizeof(stub_list));
+
+if (list_item)
+ {
+ list_item->start = start;
+ list_item->quit = LABEL();
+ list_item->next = common->stubs;
+ common->stubs = list_item;
+ }
+}
+
+static void flush_stubs(compiler_common *common)
+{
+DEFINE_COMPILER;
+stub_list *list_item = common->stubs;
+
+while (list_item)
+ {
+ JUMPHERE(list_item->start);
+ add_jump(compiler, &common->stackalloc, JUMP(SLJIT_FAST_CALL));
+ JUMPTO(SLJIT_JUMP, list_item->quit);
+ list_item = list_item->next;
+ }
+common->stubs = NULL;
+}
+
+static SLJIT_INLINE void count_match(compiler_common *common)
+{
+DEFINE_COMPILER;
+
+OP2(SLJIT_SUB | SLJIT_SET_Z, COUNT_MATCH, 0, COUNT_MATCH, 0, SLJIT_IMM, 1);
+add_jump(compiler, &common->calllimit, JUMP(SLJIT_ZERO));
+}
+
+static SLJIT_INLINE void allocate_stack(compiler_common *common, int size)
+{
+/* May destroy all locals and registers except TMP2. */
+DEFINE_COMPILER;
+
+SLJIT_ASSERT(size > 0);
+OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * SSIZE_OF(sw));
+#ifdef DESTROY_REGISTERS
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 12345);
+OP1(SLJIT_MOV, TMP3, 0, TMP1, 0);
+OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0);
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, TMP1, 0);
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP1, 0);
+#endif
+add_stub(common, CMP(SLJIT_LESS, STACK_TOP, 0, STACK_LIMIT, 0));
+}
+
+static SLJIT_INLINE void free_stack(compiler_common *common, int size)
+{
+DEFINE_COMPILER;
+
+SLJIT_ASSERT(size > 0);
+OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * SSIZE_OF(sw));
+}
+
+static sljit_uw * allocate_read_only_data(compiler_common *common, sljit_uw size)
+{
+DEFINE_COMPILER;
+sljit_uw *result;
+
+if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return NULL;
+
+result = (sljit_uw *)SLJIT_MALLOC(size + sizeof(sljit_uw), compiler->allocator_data);
+if (SLJIT_UNLIKELY(result == NULL))
+ {
+ sljit_set_compiler_memory_error(compiler);
+ return NULL;
+ }
+
+*(void**)result = common->read_only_data_head;
+common->read_only_data_head = (void *)result;
+return result + 1;
+}
+
+static SLJIT_INLINE void reset_ovector(compiler_common *common, int length)
+{
+DEFINE_COMPILER;
+struct sljit_label *loop;
+sljit_s32 i;
+
+/* At this point we can freely use all temporary registers. */
+SLJIT_ASSERT(length > 1);
+/* TMP1 returns with begin - 1. */
+OP2(SLJIT_SUB, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_S0), SLJIT_OFFSETOF(jit_arguments, begin), SLJIT_IMM, IN_UCHARS(1));
+if (length < 8)
+ {
+ for (i = 1; i < length; i++)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(i), SLJIT_R0, 0);
+ }
+else
+ {
+ if (sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw)) == SLJIT_SUCCESS)
+ {
+ GET_LOCAL_BASE(SLJIT_R1, 0, OVECTOR_START);
+ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1);
+ loop = LABEL();
+ sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw));
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, loop);
+ }
+ else
+ {
+ GET_LOCAL_BASE(SLJIT_R1, 0, OVECTOR_START + sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1);
+ loop = LABEL();
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R1), 0, SLJIT_R0, 0);
+ OP2(SLJIT_ADD, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, loop);
+ }
+ }
+}
+
+static SLJIT_INLINE void reset_early_fail(compiler_common *common)
+{
+DEFINE_COMPILER;
+sljit_u32 size = (sljit_u32)(common->early_fail_end_ptr - common->early_fail_start_ptr);
+sljit_u32 uncleared_size;
+sljit_s32 src = SLJIT_IMM;
+sljit_s32 i;
+struct sljit_label *loop;
+
+SLJIT_ASSERT(common->early_fail_start_ptr < common->early_fail_end_ptr);
+
+if (size == sizeof(sljit_sw))
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->early_fail_start_ptr, SLJIT_IMM, 0);
+ return;
+ }
+
+if (sljit_get_register_index(TMP3) >= 0 && !sljit_has_cpu_feature(SLJIT_HAS_ZERO_REGISTER))
+ {
+ OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 0);
+ src = TMP3;
+ }
+
+if (size <= 6 * sizeof(sljit_sw))
+ {
+ for (i = common->early_fail_start_ptr; i < common->early_fail_end_ptr; i += sizeof(sljit_sw))
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), i, src, 0);
+ return;
+ }
+
+GET_LOCAL_BASE(TMP1, 0, common->early_fail_start_ptr);
+
+uncleared_size = ((size / sizeof(sljit_sw)) % 3) * sizeof(sljit_sw);
+
+OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, size - uncleared_size);
+
+loop = LABEL();
+OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), 0, src, 0);
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 3 * sizeof(sljit_sw));
+OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), -2 * SSIZE_OF(sw), src, 0);
+OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), -1 * SSIZE_OF(sw), src, 0);
+CMPTO(SLJIT_LESS, TMP1, 0, TMP2, 0, loop);
+
+if (uncleared_size >= sizeof(sljit_sw))
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), 0, src, 0);
+
+if (uncleared_size >= 2 * sizeof(sljit_sw))
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), sizeof(sljit_sw), src, 0);
+}
+
+static SLJIT_INLINE void do_reset_match(compiler_common *common, int length)
+{
+DEFINE_COMPILER;
+struct sljit_label *loop;
+int i;
+
+SLJIT_ASSERT(length > 1);
+/* OVECTOR(1) contains the "string begin - 1" constant. */
+if (length > 2)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1));
+if (length < 8)
+ {
+ for (i = 2; i < length; i++)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(i), TMP1, 0);
+ }
+else
+ {
+ if (sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw)) == SLJIT_SUCCESS)
+ {
+ GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2);
+ loop = LABEL();
+ sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw));
+ OP2(SLJIT_SUB | SLJIT_SET_Z, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, loop);
+ }
+ else
+ {
+ GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + 2 * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2);
+ loop = LABEL();
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP1, 0);
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_SUB | SLJIT_SET_Z, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, loop);
+ }
+ }
+
+if (!HAS_VIRTUAL_REGISTERS)
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, stack));
+else
+ OP1(SLJIT_MOV, STACK_TOP, 0, ARGUMENTS, 0);
+
+if (common->mark_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, SLJIT_IMM, 0);
+if (common->control_head_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0);
+if (HAS_VIRTUAL_REGISTERS)
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), SLJIT_OFFSETOF(jit_arguments, stack));
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr);
+OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), SLJIT_OFFSETOF(struct sljit_stack, end));
+}
+
+static sljit_sw SLJIT_FUNC do_search_mark(sljit_sw *current, PCRE2_SPTR skip_arg)
+{
+while (current != NULL)
+ {
+ switch (current[1])
+ {
+ case type_then_trap:
+ break;
+
+ case type_mark:
+ if (PRIV(strcmp)(skip_arg, (PCRE2_SPTR)current[2]) == 0)
+ return current[3];
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+ SLJIT_ASSERT(current[0] == 0 || current < (sljit_sw*)current[0]);
+ current = (sljit_sw*)current[0];
+ }
+return 0;
+}
+
+static SLJIT_INLINE void copy_ovector(compiler_common *common, int topbracket)
+{
+DEFINE_COMPILER;
+struct sljit_label *loop;
+BOOL has_pre;
+
+/* At this point we can freely use all registers. */
+OP1(SLJIT_MOV, SLJIT_S2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1));
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(1), STR_PTR, 0);
+
+if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, SLJIT_R0, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, SLJIT_S0, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr);
+ if (common->mark_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr);
+ OP1(SLJIT_MOV_U32, SLJIT_R1, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, oveccount));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, startchar_ptr), SLJIT_S0, 0);
+ if (common->mark_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, mark_ptr), SLJIT_R2, 0);
+ OP2(SLJIT_ADD, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, match_data),
+ SLJIT_IMM, SLJIT_OFFSETOF(pcre2_match_data, ovector) - sizeof(PCRE2_SIZE));
+ }
+else
+ {
+ OP1(SLJIT_MOV, SLJIT_S0, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr);
+ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, match_data));
+ if (common->mark_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr);
+ OP1(SLJIT_MOV_U32, SLJIT_R1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, oveccount));
+ OP1(SLJIT_MOV, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, startchar_ptr), SLJIT_S0, 0);
+ if (common->mark_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, mark_ptr), SLJIT_R0, 0);
+ OP2(SLJIT_ADD, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, SLJIT_OFFSETOF(pcre2_match_data, ovector) - sizeof(PCRE2_SIZE));
+ }
+
+has_pre = sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw)) == SLJIT_SUCCESS;
+
+GET_LOCAL_BASE(SLJIT_S0, 0, OVECTOR_START - (has_pre ? sizeof(sljit_sw) : 0));
+OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(HAS_VIRTUAL_REGISTERS ? SLJIT_R0 : ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin));
+
+loop = LABEL();
+
+if (has_pre)
+ sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw));
+else
+ {
+ OP1(SLJIT_MOV, SLJIT_S1, 0, SLJIT_MEM1(SLJIT_S0), 0);
+ OP2(SLJIT_ADD, SLJIT_S0, 0, SLJIT_S0, 0, SLJIT_IMM, sizeof(sljit_sw));
+ }
+
+OP2(SLJIT_ADD, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, sizeof(PCRE2_SIZE));
+OP2(SLJIT_SUB, SLJIT_S1, 0, SLJIT_S1, 0, SLJIT_R0, 0);
+/* Copy the integer value to the output buffer */
+#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+OP2(SLJIT_ASHR, SLJIT_S1, 0, SLJIT_S1, 0, SLJIT_IMM, UCHAR_SHIFT);
+#endif
+
+SLJIT_ASSERT(sizeof(PCRE2_SIZE) == 4 || sizeof(PCRE2_SIZE) == 8);
+OP1(((sizeof(PCRE2_SIZE) == 4) ? SLJIT_MOV_U32 : SLJIT_MOV), SLJIT_MEM1(SLJIT_R2), 0, SLJIT_S1, 0);
+
+OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1);
+JUMPTO(SLJIT_NOT_ZERO, loop);
+
+/* Calculate the return value, which is the maximum ovector value. */
+if (topbracket > 1)
+ {
+ if (sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * SSIZE_OF(sw))) == SLJIT_SUCCESS)
+ {
+ GET_LOCAL_BASE(SLJIT_R0, 0, OVECTOR_START + topbracket * 2 * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, topbracket + 1);
+
+ /* OVECTOR(0) is never equal to SLJIT_S2. */
+ loop = LABEL();
+ sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * SSIZE_OF(sw)));
+ OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1);
+ CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop);
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_R1, 0);
+ }
+ else
+ {
+ GET_LOCAL_BASE(SLJIT_R0, 0, OVECTOR_START + (topbracket - 1) * 2 * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, topbracket + 1);
+
+ /* OVECTOR(0) is never equal to SLJIT_S2. */
+ loop = LABEL();
+ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), 0);
+ OP2(SLJIT_SUB, SLJIT_R0, 0, SLJIT_R0, 0, SLJIT_IMM, 2 * SSIZE_OF(sw));
+ OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1);
+ CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop);
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_R1, 0);
+ }
+ }
+else
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1);
+}
+
+static SLJIT_INLINE void return_with_partial_match(compiler_common *common, struct sljit_label *quit)
+{
+DEFINE_COMPILER;
+sljit_s32 mov_opcode;
+sljit_s32 arguments_reg = !HAS_VIRTUAL_REGISTERS ? ARGUMENTS : SLJIT_R1;
+
+SLJIT_COMPILE_ASSERT(STR_END == SLJIT_S0, str_end_must_be_saved_reg0);
+SLJIT_ASSERT(common->start_used_ptr != 0 && common->start_ptr != 0
+ && (common->mode == PCRE2_JIT_PARTIAL_SOFT ? common->hit_start != 0 : common->hit_start == 0));
+
+if (arguments_reg != ARGUMENTS)
+ OP1(SLJIT_MOV, arguments_reg, 0, ARGUMENTS, 0);
+OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP),
+ common->mode == PCRE2_JIT_PARTIAL_SOFT ? common->hit_start : common->start_ptr);
+OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_PARTIAL);
+
+/* Store match begin and end. */
+OP1(SLJIT_MOV, SLJIT_S1, 0, SLJIT_MEM1(arguments_reg), SLJIT_OFFSETOF(jit_arguments, begin));
+OP1(SLJIT_MOV, SLJIT_MEM1(arguments_reg), SLJIT_OFFSETOF(jit_arguments, startchar_ptr), SLJIT_R2, 0);
+OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_MEM1(arguments_reg), SLJIT_OFFSETOF(jit_arguments, match_data));
+
+mov_opcode = (sizeof(PCRE2_SIZE) == 4) ? SLJIT_MOV_U32 : SLJIT_MOV;
+
+OP2(SLJIT_SUB, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_S1, 0);
+#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+OP2(SLJIT_ASHR, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, UCHAR_SHIFT);
+#endif
+OP1(mov_opcode, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(pcre2_match_data, ovector), SLJIT_R2, 0);
+
+OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_S1, 0);
+#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+OP2(SLJIT_ASHR, STR_END, 0, STR_END, 0, SLJIT_IMM, UCHAR_SHIFT);
+#endif
+OP1(mov_opcode, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(pcre2_match_data, ovector) + sizeof(PCRE2_SIZE), STR_END, 0);
+
+JUMPTO(SLJIT_JUMP, quit);
+}
+
+static SLJIT_INLINE void check_start_used_ptr(compiler_common *common)
+{
+/* May destroy TMP1. */
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+
+if (common->mode == PCRE2_JIT_PARTIAL_SOFT)
+ {
+ /* The value of -1 must be kept for start_used_ptr! */
+ OP2(SLJIT_ADD, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, SLJIT_IMM, 1);
+ /* Jumps if start_used_ptr < STR_PTR, or start_used_ptr == -1. Although overwriting
+ is not necessary if start_used_ptr == STR_PTR, it does not hurt as well. */
+ jump = CMP(SLJIT_LESS_EQUAL, TMP1, 0, STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0);
+ JUMPHERE(jump);
+ }
+else if (common->mode == PCRE2_JIT_PARTIAL_HARD)
+ {
+ jump = CMP(SLJIT_LESS_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0);
+ JUMPHERE(jump);
+ }
+}
+
+static SLJIT_INLINE BOOL char_has_othercase(compiler_common *common, PCRE2_SPTR cc)
+{
+/* Detects if the character has an othercase. */
+unsigned int c;
+
+#ifdef SUPPORT_UNICODE
+if (common->utf || common->ucp)
+ {
+ if (common->utf)
+ {
+ GETCHAR(c, cc);
+ }
+ else
+ c = *cc;
+
+ if (c > 127)
+ return c != UCD_OTHERCASE(c);
+
+ return common->fcc[c] != c;
+ }
+else
+#endif
+ c = *cc;
+return MAX_255(c) ? common->fcc[c] != c : FALSE;
+}
+
+static SLJIT_INLINE unsigned int char_othercase(compiler_common *common, unsigned int c)
+{
+/* Returns with the othercase. */
+#ifdef SUPPORT_UNICODE
+if ((common->utf || common->ucp) && c > 127)
+ return UCD_OTHERCASE(c);
+#endif
+return TABLE_GET(c, common->fcc, c);
+}
+
+static unsigned int char_get_othercase_bit(compiler_common *common, PCRE2_SPTR cc)
+{
+/* Detects if the character and its othercase has only 1 bit difference. */
+unsigned int c, oc, bit;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+int n;
+#endif
+
+#ifdef SUPPORT_UNICODE
+if (common->utf || common->ucp)
+ {
+ if (common->utf)
+ {
+ GETCHAR(c, cc);
+ }
+ else
+ c = *cc;
+
+ if (c <= 127)
+ oc = common->fcc[c];
+ else
+ oc = UCD_OTHERCASE(c);
+ }
+else
+ {
+ c = *cc;
+ oc = TABLE_GET(c, common->fcc, c);
+ }
+#else
+c = *cc;
+oc = TABLE_GET(c, common->fcc, c);
+#endif
+
+SLJIT_ASSERT(c != oc);
+
+bit = c ^ oc;
+/* Optimized for English alphabet. */
+if (c <= 127 && bit == 0x20)
+ return (0 << 8) | 0x20;
+
+/* Since c != oc, they must have at least 1 bit difference. */
+if (!is_powerof2(bit))
+ return 0;
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+
+#ifdef SUPPORT_UNICODE
+if (common->utf && c > 127)
+ {
+ n = GET_EXTRALEN(*cc);
+ while ((bit & 0x3f) == 0)
+ {
+ n--;
+ bit >>= 6;
+ }
+ return (n << 8) | bit;
+ }
+#endif /* SUPPORT_UNICODE */
+return (0 << 8) | bit;
+
+#elif PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+
+#ifdef SUPPORT_UNICODE
+if (common->utf && c > 65535)
+ {
+ if (bit >= (1u << 10))
+ bit >>= 10;
+ else
+ return (bit < 256) ? ((2 << 8) | bit) : ((3 << 8) | (bit >> 8));
+ }
+#endif /* SUPPORT_UNICODE */
+return (bit < 256) ? ((0u << 8) | bit) : ((1u << 8) | (bit >> 8));
+
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */
+}
+
+static void check_partial(compiler_common *common, BOOL force)
+{
+/* Checks whether a partial matching is occurred. Does not modify registers. */
+DEFINE_COMPILER;
+struct sljit_jump *jump = NULL;
+
+SLJIT_ASSERT(!force || common->mode != PCRE2_JIT_COMPLETE);
+
+if (common->mode == PCRE2_JIT_COMPLETE)
+ return;
+
+if (!force && !common->allow_empty_partial)
+ jump = CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0);
+else if (common->mode == PCRE2_JIT_PARTIAL_SOFT)
+ jump = CMP(SLJIT_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, SLJIT_IMM, -1);
+
+if (common->mode == PCRE2_JIT_PARTIAL_SOFT)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, 0);
+else
+ {
+ if (common->partialmatchlabel != NULL)
+ JUMPTO(SLJIT_JUMP, common->partialmatchlabel);
+ else
+ add_jump(compiler, &common->partialmatch, JUMP(SLJIT_JUMP));
+ }
+
+if (jump != NULL)
+ JUMPHERE(jump);
+}
+
+static void check_str_end(compiler_common *common, jump_list **end_reached)
+{
+/* Does not affect registers. Usually used in a tight spot. */
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+
+if (common->mode == PCRE2_JIT_COMPLETE)
+ {
+ add_jump(compiler, end_reached, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+ return;
+ }
+
+jump = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_PARTIAL_SOFT)
+ {
+ add_jump(compiler, end_reached, CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, 0);
+ add_jump(compiler, end_reached, JUMP(SLJIT_JUMP));
+ }
+else
+ {
+ add_jump(compiler, end_reached, CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0));
+ if (common->partialmatchlabel != NULL)
+ JUMPTO(SLJIT_JUMP, common->partialmatchlabel);
+ else
+ add_jump(compiler, &common->partialmatch, JUMP(SLJIT_JUMP));
+ }
+JUMPHERE(jump);
+}
+
+static void detect_partial_match(compiler_common *common, jump_list **backtracks)
+{
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+
+if (common->mode == PCRE2_JIT_COMPLETE)
+ {
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+ return;
+ }
+
+/* Partial matching mode. */
+jump = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0);
+if (!common->allow_empty_partial)
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0));
+else if (common->mode == PCRE2_JIT_PARTIAL_SOFT)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, SLJIT_IMM, -1));
+
+if (common->mode == PCRE2_JIT_PARTIAL_SOFT)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, 0);
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+ }
+else
+ {
+ if (common->partialmatchlabel != NULL)
+ JUMPTO(SLJIT_JUMP, common->partialmatchlabel);
+ else
+ add_jump(compiler, &common->partialmatch, JUMP(SLJIT_JUMP));
+ }
+JUMPHERE(jump);
+}
+
+static void process_partial_match(compiler_common *common)
+{
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+
+/* Partial matching mode. */
+if (common->mode == PCRE2_JIT_PARTIAL_SOFT)
+ {
+ jump = CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, 0);
+ JUMPHERE(jump);
+ }
+else if (common->mode == PCRE2_JIT_PARTIAL_HARD)
+ {
+ if (common->partialmatchlabel != NULL)
+ CMPTO(SLJIT_LESS, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0, common->partialmatchlabel);
+ else
+ add_jump(compiler, &common->partialmatch, CMP(SLJIT_LESS, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0));
+ }
+}
+
+static void detect_partial_match_to(compiler_common *common, struct sljit_label *label)
+{
+DEFINE_COMPILER;
+
+CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, label);
+process_partial_match(common);
+}
+
+static void peek_char(compiler_common *common, sljit_u32 max, sljit_s32 dst, sljit_sw dstw, jump_list **backtracks)
+{
+/* Reads the character into TMP1, keeps STR_PTR.
+Does not check STR_END. TMP2, dst, RETURN_ADDR Destroyed. */
+DEFINE_COMPILER;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_jump *jump;
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 */
+
+SLJIT_UNUSED_ARG(max);
+SLJIT_UNUSED_ARG(dst);
+SLJIT_UNUSED_ARG(dstw);
+SLJIT_UNUSED_ARG(backtracks);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+if (common->utf)
+ {
+ if (max < 128) return;
+
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x80);
+ OP1(SLJIT_MOV, dst, dstw, STR_PTR, 0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ add_jump(compiler, common->invalid_utf ? &common->utfreadchar_invalid : &common->utfreadchar, JUMP(SLJIT_FAST_CALL));
+ OP1(SLJIT_MOV, STR_PTR, 0, dst, dstw);
+ if (backtracks && common->invalid_utf)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR));
+ JUMPHERE(jump);
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+if (common->utf)
+ {
+ if (max < 0xd800) return;
+
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+
+ if (common->invalid_utf)
+ {
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800);
+ OP1(SLJIT_MOV, dst, dstw, STR_PTR, 0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ add_jump(compiler, &common->utfreadchar_invalid, JUMP(SLJIT_FAST_CALL));
+ OP1(SLJIT_MOV, STR_PTR, 0, dst, dstw);
+ if (backtracks && common->invalid_utf)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR));
+ }
+ else
+ {
+ /* TMP2 contains the high surrogate. */
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xdc00 - 0xd800);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 10);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000 - 0xdc00);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+ }
+
+ JUMPHERE(jump);
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+if (common->invalid_utf)
+ {
+ if (max < 0xd800) return;
+
+ if (backtracks != NULL)
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x110000));
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800));
+ }
+ else
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x110000);
+ CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, INVALID_UTF_CHAR);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800);
+ CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, INVALID_UTF_CHAR);
+ }
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */
+#endif /* SUPPORT_UNICODE */
+}
+
+static void peek_char_back(compiler_common *common, sljit_u32 max, jump_list **backtracks)
+{
+/* Reads one character back without moving STR_PTR. TMP2 must
+contain the start of the subject buffer. Affects TMP1, TMP2, and RETURN_ADDR. */
+DEFINE_COMPILER;
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_jump *jump;
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 */
+
+SLJIT_UNUSED_ARG(max);
+SLJIT_UNUSED_ARG(backtracks);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+if (common->utf)
+ {
+ if (max < 128) return;
+
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x80);
+ if (common->invalid_utf)
+ {
+ add_jump(compiler, &common->utfpeakcharback_invalid, JUMP(SLJIT_FAST_CALL));
+ if (backtracks != NULL)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR));
+ }
+ else
+ add_jump(compiler, &common->utfpeakcharback, JUMP(SLJIT_FAST_CALL));
+ JUMPHERE(jump);
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+if (common->utf)
+ {
+ if (max < 0xd800) return;
+
+ if (common->invalid_utf)
+ {
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800);
+ add_jump(compiler, &common->utfpeakcharback_invalid, JUMP(SLJIT_FAST_CALL));
+ if (backtracks != NULL)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR));
+ }
+ else
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xdc00);
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xe000 - 0xdc00);
+ /* TMP2 contains the low surrogate. */
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x10000);
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 10);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+ }
+ JUMPHERE(jump);
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+if (common->invalid_utf)
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x110000));
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800));
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */
+#endif /* SUPPORT_UNICODE */
+}
+
+#define READ_CHAR_UPDATE_STR_PTR 0x1
+#define READ_CHAR_UTF8_NEWLINE 0x2
+#define READ_CHAR_NEWLINE (READ_CHAR_UPDATE_STR_PTR | READ_CHAR_UTF8_NEWLINE)
+#define READ_CHAR_VALID_UTF 0x4
+
+static void read_char(compiler_common *common, sljit_u32 min, sljit_u32 max,
+ jump_list **backtracks, sljit_u32 options)
+{
+/* Reads the precise value of a character into TMP1, if the character is
+between min and max (c >= min && c <= max). Otherwise it returns with a value
+outside the range. Does not check STR_END. */
+DEFINE_COMPILER;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_jump *jump;
+#endif
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+struct sljit_jump *jump2;
+#endif
+
+SLJIT_UNUSED_ARG(min);
+SLJIT_UNUSED_ARG(max);
+SLJIT_UNUSED_ARG(backtracks);
+SLJIT_UNUSED_ARG(options);
+SLJIT_ASSERT(min <= max);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+if (common->utf)
+ {
+ if (max < 128 && !(options & READ_CHAR_UPDATE_STR_PTR)) return;
+
+ if (common->invalid_utf && !(options & READ_CHAR_VALID_UTF))
+ {
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x80);
+
+ if (options & READ_CHAR_UTF8_NEWLINE)
+ add_jump(compiler, &common->utfreadnewline_invalid, JUMP(SLJIT_FAST_CALL));
+ else
+ add_jump(compiler, &common->utfreadchar_invalid, JUMP(SLJIT_FAST_CALL));
+
+ if (backtracks != NULL)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR));
+ JUMPHERE(jump);
+ return;
+ }
+
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0);
+ if (min >= 0x10000)
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xf0);
+ if (options & READ_CHAR_UPDATE_STR_PTR)
+ OP1(SLJIT_MOV_U8, RETURN_ADDR, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ jump2 = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 0x7);
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3f);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(2));
+ if (!(options & READ_CHAR_UPDATE_STR_PTR))
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(3));
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+ JUMPHERE(jump2);
+ if (options & READ_CHAR_UPDATE_STR_PTR)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, RETURN_ADDR, 0);
+ }
+ else if (min >= 0x800 && max <= 0xffff)
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xe0);
+ if (options & READ_CHAR_UPDATE_STR_PTR)
+ OP1(SLJIT_MOV_U8, RETURN_ADDR, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ jump2 = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 0xf);
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3f);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ if (!(options & READ_CHAR_UPDATE_STR_PTR))
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+ JUMPHERE(jump2);
+ if (options & READ_CHAR_UPDATE_STR_PTR)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, RETURN_ADDR, 0);
+ }
+ else if (max >= 0x800)
+ {
+ add_jump(compiler, &common->utfreadchar, JUMP(SLJIT_FAST_CALL));
+ }
+ else if (max < 128)
+ {
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+ }
+ else
+ {
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ if (!(options & READ_CHAR_UPDATE_STR_PTR))
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ else
+ OP1(SLJIT_MOV_U8, RETURN_ADDR, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3f);
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+ if (options & READ_CHAR_UPDATE_STR_PTR)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, RETURN_ADDR, 0);
+ }
+ JUMPHERE(jump);
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+if (common->utf)
+ {
+ if (max < 0xd800 && !(options & READ_CHAR_UPDATE_STR_PTR)) return;
+
+ if (common->invalid_utf && !(options & READ_CHAR_VALID_UTF))
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800);
+
+ if (options & READ_CHAR_UTF8_NEWLINE)
+ add_jump(compiler, &common->utfreadnewline_invalid, JUMP(SLJIT_FAST_CALL));
+ else
+ add_jump(compiler, &common->utfreadchar_invalid, JUMP(SLJIT_FAST_CALL));
+
+ if (backtracks != NULL)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR));
+ JUMPHERE(jump);
+ return;
+ }
+
+ if (max >= 0x10000)
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xdc00 - 0xd800);
+ /* TMP2 contains the high surrogate. */
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 10);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000 - 0xdc00);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+ JUMPHERE(jump);
+ return;
+ }
+
+ /* Skip low surrogate if necessary. */
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+
+ if (sljit_has_cpu_feature(SLJIT_HAS_CMOV) && !HAS_VIRTUAL_REGISTERS)
+ {
+ if (options & READ_CHAR_UPDATE_STR_PTR)
+ OP2(SLJIT_ADD, RETURN_ADDR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP2, 0, SLJIT_IMM, 0x400);
+ if (options & READ_CHAR_UPDATE_STR_PTR)
+ CMOV(SLJIT_LESS, STR_PTR, RETURN_ADDR, 0);
+ if (max >= 0xd800)
+ CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, 0x10000);
+ }
+ else
+ {
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x400);
+ if (options & READ_CHAR_UPDATE_STR_PTR)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ if (max >= 0xd800)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0x10000);
+ JUMPHERE(jump);
+ }
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+if (common->invalid_utf)
+ {
+ if (backtracks != NULL)
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x110000));
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800));
+ }
+ else
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x110000);
+ CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, INVALID_UTF_CHAR);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800);
+ CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, INVALID_UTF_CHAR);
+ }
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */
+#endif /* SUPPORT_UNICODE */
+}
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+
+static BOOL is_char7_bitset(const sljit_u8 *bitset, BOOL nclass)
+{
+/* Tells whether the character codes below 128 are enough
+to determine a match. */
+const sljit_u8 value = nclass ? 0xff : 0;
+const sljit_u8 *end = bitset + 32;
+
+bitset += 16;
+do
+ {
+ if (*bitset++ != value)
+ return FALSE;
+ }
+while (bitset < end);
+return TRUE;
+}
+
+static void read_char7_type(compiler_common *common, jump_list **backtracks, BOOL negated)
+{
+/* Reads the precise character type of a character into TMP1, if the character
+is less than 128. Otherwise it returns with zero. Does not check STR_END. The
+full_read argument tells whether characters above max are accepted or not. */
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+
+SLJIT_ASSERT(common->utf);
+
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), 0);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+/* All values > 127 are zero in ctypes. */
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
+
+if (negated)
+ {
+ jump = CMP(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0x80);
+
+ if (common->invalid_utf)
+ {
+ add_jump(compiler, &common->utfreadchar_invalid, JUMP(SLJIT_FAST_CALL));
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+ }
+ else
+ {
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+ }
+ JUMPHERE(jump);
+ }
+}
+
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 */
+
+static void read_char8_type(compiler_common *common, jump_list **backtracks, BOOL negated)
+{
+/* Reads the character type into TMP1, updates STR_PTR. Does not check STR_END. */
+DEFINE_COMPILER;
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+struct sljit_jump *jump;
+#endif
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+struct sljit_jump *jump2;
+#endif
+
+SLJIT_UNUSED_ARG(backtracks);
+SLJIT_UNUSED_ARG(negated);
+
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), 0);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+if (common->utf)
+ {
+ /* The result of this read may be unused, but saves an "else" part. */
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
+ jump = CMP(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0x80);
+
+ if (!negated)
+ {
+ if (common->invalid_utf)
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2);
+ if (common->invalid_utf)
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xe0 - 0xc2));
+
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0);
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x80);
+ if (common->invalid_utf)
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40));
+
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+ jump2 = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 255);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
+ JUMPHERE(jump2);
+ }
+ else if (common->invalid_utf)
+ {
+ add_jump(compiler, &common->utfreadchar_invalid, JUMP(SLJIT_FAST_CALL));
+ OP1(SLJIT_MOV, TMP2, 0, TMP1, 0);
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR));
+
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+ jump2 = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 255);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
+ JUMPHERE(jump2);
+ }
+ else
+ add_jump(compiler, &common->utfreadtype8, JUMP(SLJIT_FAST_CALL));
+
+ JUMPHERE(jump);
+ return;
+ }
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 */
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 32
+if (common->invalid_utf && negated)
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x110000));
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 32 */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+/* The ctypes array contains only 256 values. */
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+jump = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 255);
+#endif /* PCRE2_CODE_UNIT_WIDTH != 8 */
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
+#if PCRE2_CODE_UNIT_WIDTH != 8
+JUMPHERE(jump);
+#endif /* PCRE2_CODE_UNIT_WIDTH != 8 */
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 16
+if (common->utf && negated)
+ {
+ /* Skip low surrogate if necessary. */
+ if (!common->invalid_utf)
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xd800);
+
+ if (sljit_has_cpu_feature(SLJIT_HAS_CMOV) && !HAS_VIRTUAL_REGISTERS)
+ {
+ OP2(SLJIT_ADD, RETURN_ADDR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP2, 0, SLJIT_IMM, 0x400);
+ CMOV(SLJIT_LESS, STR_PTR, RETURN_ADDR, 0);
+ }
+ else
+ {
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x400);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPHERE(jump);
+ }
+ return;
+ }
+
+ OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xd800);
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800);
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x400));
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xdc00);
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x400));
+
+ JUMPHERE(jump);
+ return;
+ }
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 16 */
+}
+
+static void move_back(compiler_common *common, jump_list **backtracks, BOOL must_be_valid)
+{
+/* Goes one character back. Affects STR_PTR and TMP1. If must_be_valid is TRUE,
+TMP2 is not used. Otherwise TMP2 must contain the start of the subject buffer,
+and it is destroyed. Does not modify STR_PTR for invalid character sequences. */
+DEFINE_COMPILER;
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_jump *jump;
+#endif
+
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+struct sljit_label *label;
+
+if (common->utf)
+ {
+ if (!must_be_valid && common->invalid_utf)
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -IN_UCHARS(1));
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x80);
+ add_jump(compiler, &common->utfmoveback_invalid, JUMP(SLJIT_FAST_CALL));
+ if (backtracks != NULL)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0));
+ JUMPHERE(jump);
+ return;
+ }
+
+ label = LABEL();
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -IN_UCHARS(1));
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0);
+ CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0x80, label);
+ return;
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+if (common->utf)
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -IN_UCHARS(1));
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ if (!must_be_valid && common->invalid_utf)
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xe000 - 0xd800);
+ add_jump(compiler, &common->utfmoveback_invalid, JUMP(SLJIT_FAST_CALL));
+ if (backtracks != NULL)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0));
+ JUMPHERE(jump);
+ return;
+ }
+
+ /* Skip low surrogate if necessary. */
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0xdc00);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+ return;
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+if (common->invalid_utf && !must_be_valid)
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -IN_UCHARS(1));
+ if (backtracks != NULL)
+ {
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x110000));
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ return;
+ }
+
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, SLJIT_IMM, 0x110000);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_LESS);
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+ return;
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */
+#endif /* SUPPORT_UNICODE */
+
+SLJIT_UNUSED_ARG(backtracks);
+SLJIT_UNUSED_ARG(must_be_valid);
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+}
+
+static void check_newlinechar(compiler_common *common, int nltype, jump_list **backtracks, BOOL jumpifmatch)
+{
+/* Character comes in TMP1. Checks if it is a newline. TMP2 may be destroyed. */
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+
+if (nltype == NLTYPE_ANY)
+ {
+ add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
+ add_jump(compiler, backtracks, JUMP(jumpifmatch ? SLJIT_NOT_ZERO : SLJIT_ZERO));
+ }
+else if (nltype == NLTYPE_ANYCRLF)
+ {
+ if (jumpifmatch)
+ {
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR));
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL));
+ }
+ else
+ {
+ jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR);
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL));
+ JUMPHERE(jump);
+ }
+ }
+else
+ {
+ SLJIT_ASSERT(nltype == NLTYPE_FIXED && common->newline < 256);
+ add_jump(compiler, backtracks, CMP(jumpifmatch ? SLJIT_EQUAL : SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, common->newline));
+ }
+}
+
+#ifdef SUPPORT_UNICODE
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+static void do_utfreadchar(compiler_common *common)
+{
+/* Fast decoding a UTF-8 character. TMP1 contains the first byte
+of the character (>= 0xc0). Return char value in TMP1. */
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+/* Searching for the first zero. */
+OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x800);
+jump = JUMP(SLJIT_NOT_ZERO);
+/* Two byte sequence. */
+OP2(SLJIT_XOR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3000);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(jump);
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x10000);
+jump = JUMP(SLJIT_NOT_ZERO);
+/* Three byte sequence. */
+OP2(SLJIT_XOR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0000);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+/* Four byte sequence. */
+JUMPHERE(jump);
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(2));
+OP2(SLJIT_XOR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xf0000);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(3));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfreadtype8(compiler_common *common)
+{
+/* Fast decoding a UTF-8 character type. TMP2 contains the first byte
+of the character (>= 0xc0). Return value in TMP1. */
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+struct sljit_jump *compare;
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+OP2U(SLJIT_AND | SLJIT_SET_Z, TMP2, 0, SLJIT_IMM, 0x20);
+jump = JUMP(SLJIT_NOT_ZERO);
+/* Two byte sequence. */
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x1f);
+/* The upper 5 bits are known at this point. */
+compare = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 0x3);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
+OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3f);
+OP2(SLJIT_OR, TMP2, 0, TMP2, 0, TMP1, 0);
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(compare);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+/* We only have types for characters less than 256. */
+JUMPHERE(jump);
+OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfreadchar_invalid(compiler_common *common)
+{
+/* Slow decoding a UTF-8 character. TMP1 contains the first byte
+of the character (>= 0xc0). Return char value in TMP1. STR_PTR is
+undefined for invalid characters. */
+DEFINE_COMPILER;
+sljit_s32 i;
+sljit_s32 has_cmov = sljit_has_cpu_feature(SLJIT_HAS_CMOV);
+struct sljit_jump *jump;
+struct sljit_jump *buffer_end_close;
+struct sljit_label *three_byte_entry;
+struct sljit_label *exit_invalid_label;
+struct sljit_jump *exit_invalid[11];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc2);
+
+/* Usually more than 3 characters remained in the subject buffer. */
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(3));
+
+/* Not a valid start of a multi-byte sequence, no more bytes read. */
+exit_invalid[0] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xf5 - 0xc2);
+
+buffer_end_close = CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0);
+
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-3));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+/* If TMP2 is in 0x80-0xbf range, TMP1 is also increased by (0x2 << 6). */
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80);
+exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+
+OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x800);
+jump = JUMP(SLJIT_NOT_ZERO);
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(jump);
+
+/* Three-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+ CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, 0x20000);
+ exit_invalid[2] = NULL;
+ }
+else
+ exit_invalid[2] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+
+OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x10000);
+jump = JUMP(SLJIT_NOT_ZERO);
+
+three_byte_entry = LABEL();
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x2d800);
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, SLJIT_IMM, 0x800);
+ CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, INVALID_UTF_CHAR - 0xd800);
+ exit_invalid[3] = NULL;
+ }
+else
+ exit_invalid[3] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x800);
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, SLJIT_IMM, 0x800);
+ CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, INVALID_UTF_CHAR);
+ exit_invalid[4] = NULL;
+ }
+else
+ exit_invalid[4] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x800);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(jump);
+
+/* Four-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+ CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, 0);
+ exit_invalid[5] = NULL;
+ }
+else
+ exit_invalid[5] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc10000);
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x100000);
+ CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, INVALID_UTF_CHAR - 0x10000);
+ exit_invalid[6] = NULL;
+ }
+else
+ exit_invalid[6] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x100000);
+
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(buffer_end_close);
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+exit_invalid[7] = CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0);
+
+/* Two-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+/* If TMP2 is in 0x80-0xbf range, TMP1 is also increased by (0x2 << 6). */
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80);
+exit_invalid[8] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+
+OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x800);
+jump = JUMP(SLJIT_NOT_ZERO);
+
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+/* Three-byte sequence. */
+JUMPHERE(jump);
+exit_invalid[9] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+ CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, INVALID_UTF_CHAR);
+ exit_invalid[10] = NULL;
+ }
+else
+ exit_invalid[10] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+
+/* One will be substracted from STR_PTR later. */
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+
+/* Four byte sequences are not possible. */
+CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x30000, three_byte_entry);
+
+exit_invalid_label = LABEL();
+for (i = 0; i < 11; i++)
+ sljit_set_label(exit_invalid[i], exit_invalid_label);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfreadnewline_invalid(compiler_common *common)
+{
+/* Slow decoding a UTF-8 character, specialized for newlines.
+TMP1 contains the first byte of the character (>= 0xc0). Return
+char value in TMP1. */
+DEFINE_COMPILER;
+struct sljit_label *loop;
+struct sljit_label *skip_start;
+struct sljit_label *three_byte_exit;
+struct sljit_jump *jump[5];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+if (common->nltype != NLTYPE_ANY)
+ {
+ SLJIT_ASSERT(common->nltype != NLTYPE_FIXED || common->newline < 128);
+
+ /* All newlines are ascii, just skip intermediate octets. */
+ jump[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ loop = LABEL();
+ if (sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, TMP2, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)) == SLJIT_SUCCESS)
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_POST, TMP2, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ else
+ {
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ }
+
+ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc0);
+ CMPTO(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, 0x80, loop);
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ JUMPHERE(jump[0]);
+
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+ OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+ return;
+ }
+
+jump[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+jump[1] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xc2);
+jump[2] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xe2);
+
+skip_start = LABEL();
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc0);
+jump[3] = CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0x80);
+
+/* Skip intermediate octets. */
+loop = LABEL();
+jump[4] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc0);
+CMPTO(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, 0x80, loop);
+
+JUMPHERE(jump[3]);
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+three_byte_exit = LABEL();
+JUMPHERE(jump[0]);
+JUMPHERE(jump[4]);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+/* Two byte long newline: 0x85. */
+JUMPHERE(jump[1]);
+CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0x85, skip_start);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0x85);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+/* Three byte long newlines: 0x2028 and 0x2029. */
+JUMPHERE(jump[2]);
+CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0x80, skip_start);
+CMPTO(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0, three_byte_exit);
+
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+OP2(SLJIT_SUB, TMP1, 0, TMP2, 0, SLJIT_IMM, 0x80);
+CMPTO(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x40, skip_start);
+
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0x2000);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfmoveback_invalid(compiler_common *common)
+{
+/* Goes one character back. */
+DEFINE_COMPILER;
+sljit_s32 i;
+struct sljit_jump *jump;
+struct sljit_jump *buffer_start_close;
+struct sljit_label *exit_ok_label;
+struct sljit_label *exit_invalid_label;
+struct sljit_jump *exit_invalid[7];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(3));
+exit_invalid[0] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xc0);
+
+/* Two-byte sequence. */
+buffer_start_close = CMP(SLJIT_LESS, STR_PTR, 0, TMP2, 0);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(2));
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0);
+jump = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x20);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+/* Three-byte sequence. */
+JUMPHERE(jump);
+exit_invalid[1] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, -0x40);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0);
+jump = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x10);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+/* Four-byte sequence. */
+JUMPHERE(jump);
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0 - 0x80);
+exit_invalid[2] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x40);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xf0);
+exit_invalid[3] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x05);
+
+exit_ok_label = LABEL();
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+/* Two-byte sequence. */
+JUMPHERE(buffer_start_close);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+
+exit_invalid[4] = CMP(SLJIT_LESS, STR_PTR, 0, TMP2, 0);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0);
+CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x20, exit_ok_label);
+
+/* Three-byte sequence. */
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+exit_invalid[5] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, -0x40);
+exit_invalid[6] = CMP(SLJIT_LESS, STR_PTR, 0, TMP2, 0);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0);
+CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x10, exit_ok_label);
+
+/* Four-byte sequences are not possible. */
+
+exit_invalid_label = LABEL();
+sljit_set_label(exit_invalid[5], exit_invalid_label);
+sljit_set_label(exit_invalid[6], exit_invalid_label);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(3));
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(exit_invalid[4]);
+/* -2 + 4 = 2 */
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+
+exit_invalid_label = LABEL();
+for (i = 0; i < 4; i++)
+ sljit_set_label(exit_invalid[i], exit_invalid_label);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(4));
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfpeakcharback(compiler_common *common)
+{
+/* Peak a character back. Does not modify STR_PTR. */
+DEFINE_COMPILER;
+struct sljit_jump *jump[2];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0);
+jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x20);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-3));
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0);
+jump[1] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x10);
+
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-4));
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0 - 0x80);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf0);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+JUMPHERE(jump[1]);
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+JUMPHERE(jump[0]);
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfpeakcharback_invalid(compiler_common *common)
+{
+/* Peak a character back. Does not modify STR_PTR. */
+DEFINE_COMPILER;
+sljit_s32 i;
+sljit_s32 has_cmov = sljit_has_cpu_feature(SLJIT_HAS_CMOV);
+struct sljit_jump *jump[2];
+struct sljit_label *two_byte_entry;
+struct sljit_label *three_byte_entry;
+struct sljit_label *exit_invalid_label;
+struct sljit_jump *exit_invalid[8];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(3));
+exit_invalid[0] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xc0);
+jump[0] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, STR_PTR, 0);
+
+/* Two-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2);
+jump[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x1e);
+
+two_byte_entry = LABEL();
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
+/* If TMP1 is in 0x80-0xbf range, TMP1 is also increased by (0x2 << 6). */
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(jump[1]);
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2 - 0x80);
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x80);
+exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+/* Three-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-3));
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xe0);
+jump[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x10);
+
+three_byte_entry = LABEL();
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 12);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, SLJIT_IMM, 0x800);
+ CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, -0xd800);
+ exit_invalid[2] = NULL;
+ }
+else
+ exit_invalid[2] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x800);
+
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, SLJIT_IMM, 0x800);
+ CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, INVALID_UTF_CHAR);
+ exit_invalid[3] = NULL;
+ }
+else
+ exit_invalid[3] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x800);
+
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(jump[1]);
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xe0 - 0x80);
+exit_invalid[4] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 12);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+/* Four-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-4));
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf0);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 18);
+/* ADD is used instead of OR because of the SUB 0x10000 above. */
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x100000);
+ CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, INVALID_UTF_CHAR - 0x10000);
+ exit_invalid[5] = NULL;
+ }
+else
+ exit_invalid[5] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x100000);
+
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(jump[0]);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+jump[0] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, STR_PTR, 0);
+
+/* Two-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2);
+CMPTO(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0x1e, two_byte_entry);
+
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2 - 0x80);
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x80);
+exit_invalid[6] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+/* Three-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-3));
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xe0);
+CMPTO(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0x10, three_byte_entry);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(jump[0]);
+exit_invalid[7] = CMP(SLJIT_GREATER, TMP2, 0, STR_PTR, 0);
+
+/* Two-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2);
+CMPTO(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0x1e, two_byte_entry);
+
+exit_invalid_label = LABEL();
+for (i = 0; i < 8; i++)
+ sljit_set_label(exit_invalid[i], exit_invalid_label);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+
+#if PCRE2_CODE_UNIT_WIDTH == 16
+
+static void do_utfreadchar_invalid(compiler_common *common)
+{
+/* Slow decoding a UTF-16 character. TMP1 contains the first half
+of the character (>= 0xd800). Return char value in TMP1. STR_PTR is
+undefined for invalid characters. */
+DEFINE_COMPILER;
+struct sljit_jump *exit_invalid[3];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+/* TMP2 contains the high surrogate. */
+exit_invalid[0] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00);
+exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 10);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xdc00);
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x10000);
+exit_invalid[2] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x400);
+
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(exit_invalid[0]);
+JUMPHERE(exit_invalid[1]);
+JUMPHERE(exit_invalid[2]);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfreadnewline_invalid(compiler_common *common)
+{
+/* Slow decoding a UTF-16 character, specialized for newlines.
+TMP1 contains the first half of the character (>= 0xd800). Return
+char value in TMP1. */
+
+DEFINE_COMPILER;
+struct sljit_jump *exit_invalid[2];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+/* TMP2 contains the high surrogate. */
+exit_invalid[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00);
+
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xdc00);
+OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP2, 0, SLJIT_IMM, 0x400);
+OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0x10000);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCHAR_SHIFT);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(exit_invalid[0]);
+JUMPHERE(exit_invalid[1]);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfmoveback_invalid(compiler_common *common)
+{
+/* Goes one character back. */
+DEFINE_COMPILER;
+struct sljit_jump *exit_invalid[3];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+exit_invalid[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x400);
+exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, STR_PTR, 0);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+exit_invalid[2] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x400);
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(exit_invalid[0]);
+JUMPHERE(exit_invalid[1]);
+JUMPHERE(exit_invalid[2]);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfpeakcharback_invalid(compiler_common *common)
+{
+/* Peak a character back. Does not modify STR_PTR. */
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+struct sljit_jump *exit_invalid[3];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+jump = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xe000);
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+exit_invalid[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xdc00);
+exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, STR_PTR, 0);
+
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000 - 0xdc00);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xd800);
+exit_invalid[2] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x400);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 10);
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+
+JUMPHERE(jump);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(exit_invalid[0]);
+JUMPHERE(exit_invalid[1]);
+JUMPHERE(exit_invalid[2]);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+#endif /* PCRE2_CODE_UNIT_WIDTH == 16 */
+
+/* UCD_BLOCK_SIZE must be 128 (see the assert below). */
+#define UCD_BLOCK_MASK 127
+#define UCD_BLOCK_SHIFT 7
+
+static void do_getucd(compiler_common *common)
+{
+/* Search the UCD record for the character comes in TMP1.
+Returns chartype in TMP1 and UCD offset in TMP2. */
+DEFINE_COMPILER;
+#if PCRE2_CODE_UNIT_WIDTH == 32
+struct sljit_jump *jump;
+#endif
+
+#if defined SLJIT_DEBUG && SLJIT_DEBUG
+/* dummy_ucd_record */
+const ucd_record *record = GET_UCD(UNASSIGNED_UTF_CHAR);
+SLJIT_ASSERT(record->script == ucp_Unknown && record->chartype == ucp_Cn && record->gbprop == ucp_gbOther);
+SLJIT_ASSERT(record->caseset == 0 && record->other_case == 0);
+#endif
+
+SLJIT_ASSERT(UCD_BLOCK_SIZE == 128 && sizeof(ucd_record) == 12);
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+if (!common->utf)
+ {
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, MAX_UTF_CODE_POINT + 1);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, UNASSIGNED_UTF_CHAR);
+ JUMPHERE(jump);
+ }
+#endif
+
+OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 1);
+OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1));
+OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_stage2));
+OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM2(TMP2, TMP1), 1);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_getucdtype(compiler_common *common)
+{
+/* Search the UCD record for the character comes in TMP1.
+Returns chartype in TMP1 and UCD offset in TMP2. */
+DEFINE_COMPILER;
+#if PCRE2_CODE_UNIT_WIDTH == 32
+struct sljit_jump *jump;
+#endif
+
+#if defined SLJIT_DEBUG && SLJIT_DEBUG
+/* dummy_ucd_record */
+const ucd_record *record = GET_UCD(UNASSIGNED_UTF_CHAR);
+SLJIT_ASSERT(record->script == ucp_Unknown && record->chartype == ucp_Cn && record->gbprop == ucp_gbOther);
+SLJIT_ASSERT(record->caseset == 0 && record->other_case == 0);
+#endif
+
+SLJIT_ASSERT(UCD_BLOCK_SIZE == 128 && sizeof(ucd_record) == 12);
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+if (!common->utf)
+ {
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, MAX_UTF_CODE_POINT + 1);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, UNASSIGNED_UTF_CHAR);
+ JUMPHERE(jump);
+ }
+#endif
+
+OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 1);
+OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1));
+OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_stage2));
+OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM2(TMP2, TMP1), 1);
+
+/* TMP2 is multiplied by 12. Same as (TMP2 << 2) + ((TMP2 << 2) << 1). */
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, chartype));
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 2);
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM2(TMP1, TMP2), 1);
+
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+#endif /* SUPPORT_UNICODE */
+
+static SLJIT_INLINE struct sljit_label *mainloop_entry(compiler_common *common)
+{
+DEFINE_COMPILER;
+struct sljit_label *mainloop;
+struct sljit_label *newlinelabel = NULL;
+struct sljit_jump *start;
+struct sljit_jump *end = NULL;
+struct sljit_jump *end2 = NULL;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_label *loop;
+struct sljit_jump *jump;
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 */
+jump_list *newline = NULL;
+sljit_u32 overall_options = common->re->overall_options;
+BOOL hascrorlf = (common->re->flags & PCRE2_HASCRORLF) != 0;
+BOOL newlinecheck = FALSE;
+BOOL readuchar = FALSE;
+
+if (!(hascrorlf || (overall_options & PCRE2_FIRSTLINE) != 0)
+ && (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF || common->newline > 255))
+ newlinecheck = TRUE;
+
+SLJIT_ASSERT(common->abort_label == NULL);
+
+if ((overall_options & PCRE2_FIRSTLINE) != 0)
+ {
+ /* Search for the end of the first line. */
+ SLJIT_ASSERT(common->match_end_ptr != 0);
+ OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0);
+
+ if (common->nltype == NLTYPE_FIXED && common->newline > 255)
+ {
+ mainloop = LABEL();
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ end = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff, mainloop);
+ CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff, mainloop);
+ JUMPHERE(end);
+ OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ }
+ else
+ {
+ end = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ mainloop = LABEL();
+ /* Continual stores does not cause data dependency. */
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, STR_PTR, 0);
+ read_char(common, common->nlmin, common->nlmax, NULL, READ_CHAR_NEWLINE);
+ check_newlinechar(common, common->nltype, &newline, TRUE);
+ CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, mainloop);
+ JUMPHERE(end);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, STR_PTR, 0);
+ set_jumps(newline, LABEL());
+ }
+
+ OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0);
+ }
+else if ((overall_options & PCRE2_USE_OFFSET_LIMIT) != 0)
+ {
+ /* Check whether offset limit is set and valid. */
+ SLJIT_ASSERT(common->match_end_ptr != 0);
+
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, offset_limit));
+ }
+ else
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, offset_limit));
+
+ OP1(SLJIT_MOV, TMP2, 0, STR_END, 0);
+ end = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, (sljit_sw) PCRE2_UNSET);
+ if (HAS_VIRTUAL_REGISTERS)
+ OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
+ else
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin));
+
+#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
+#endif /* PCRE2_CODE_UNIT_WIDTH == [16|32] */
+ if (HAS_VIRTUAL_REGISTERS)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin));
+
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0);
+ end2 = CMP(SLJIT_LESS_EQUAL, TMP2, 0, STR_END, 0);
+ OP1(SLJIT_MOV, TMP2, 0, STR_END, 0);
+ JUMPHERE(end2);
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH);
+ add_jump(compiler, &common->abort, CMP(SLJIT_LESS, TMP2, 0, STR_PTR, 0));
+ JUMPHERE(end);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, TMP2, 0);
+ }
+
+start = JUMP(SLJIT_JUMP);
+
+if (newlinecheck)
+ {
+ newlinelabel = LABEL();
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ end = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, common->newline & 0xff);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
+#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
+#endif /* PCRE2_CODE_UNIT_WIDTH == [16|32] */
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+ end2 = JUMP(SLJIT_JUMP);
+ }
+
+mainloop = LABEL();
+
+/* Increasing the STR_PTR here requires one less jump in the most common case. */
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+if (common->utf && !common->invalid_utf) readuchar = TRUE;
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 */
+if (newlinecheck) readuchar = TRUE;
+
+if (readuchar)
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+
+if (newlinecheck)
+ CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff, newlinelabel);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+#if PCRE2_CODE_UNIT_WIDTH == 8
+if (common->invalid_utf)
+ {
+ /* Skip continuation code units. */
+ loop = LABEL();
+ jump = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x80);
+ CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x40, loop);
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPHERE(jump);
+ }
+else if (common->utf)
+ {
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+ JUMPHERE(jump);
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+if (common->invalid_utf)
+ {
+ /* Skip continuation code units. */
+ loop = LABEL();
+ jump = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xdc00);
+ CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x400, loop);
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPHERE(jump);
+ }
+else if (common->utf)
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+
+ if (sljit_has_cpu_feature(SLJIT_HAS_CMOV))
+ {
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, SLJIT_IMM, 0x400);
+ CMOV(SLJIT_LESS, STR_PTR, TMP2, 0);
+ }
+ else
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, SLJIT_IMM, 0x400);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_LESS);
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+ }
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16] */
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 */
+JUMPHERE(start);
+
+if (newlinecheck)
+ {
+ JUMPHERE(end);
+ JUMPHERE(end2);
+ }
+
+return mainloop;
+}
+
+
+static SLJIT_INLINE void add_prefix_char(PCRE2_UCHAR chr, fast_forward_char_data *chars, BOOL last)
+{
+sljit_u32 i, count = chars->count;
+
+if (count == 255)
+ return;
+
+if (count == 0)
+ {
+ chars->count = 1;
+ chars->chars[0] = chr;
+
+ if (last)
+ chars->last_count = 1;
+ return;
+ }
+
+for (i = 0; i < count; i++)
+ if (chars->chars[i] == chr)
+ return;
+
+if (count >= MAX_DIFF_CHARS)
+ {
+ chars->count = 255;
+ return;
+ }
+
+chars->chars[count] = chr;
+chars->count = count + 1;
+
+if (last)
+ chars->last_count++;
+}
+
+static int scan_prefix(compiler_common *common, PCRE2_SPTR cc, fast_forward_char_data *chars, int max_chars, sljit_u32 *rec_count)
+{
+/* Recursive function, which scans prefix literals. */
+BOOL last, any, class, caseless;
+int len, repeat, len_save, consumed = 0;
+sljit_u32 chr; /* Any unicode character. */
+sljit_u8 *bytes, *bytes_end, byte;
+PCRE2_SPTR alternative, cc_save, oc;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+PCRE2_UCHAR othercase[4];
+#elif defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 16
+PCRE2_UCHAR othercase[2];
+#else
+PCRE2_UCHAR othercase[1];
+#endif
+
+repeat = 1;
+while (TRUE)
+ {
+ if (*rec_count == 0)
+ return 0;
+ (*rec_count)--;
+
+ last = TRUE;
+ any = FALSE;
+ class = FALSE;
+ caseless = FALSE;
+
+ switch (*cc)
+ {
+ case OP_CHARI:
+ caseless = TRUE;
+ /* Fall through */
+ case OP_CHAR:
+ last = FALSE;
+ cc++;
+ break;
+
+ case OP_SOD:
+ case OP_SOM:
+ case OP_SET_SOM:
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ case OP_EODN:
+ case OP_EOD:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_DOLL:
+ case OP_DOLLM:
+ /* Zero width assertions. */
+ cc++;
+ continue;
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ cc = bracketend(cc);
+ continue;
+
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_POSPLUSI:
+ caseless = TRUE;
+ /* Fall through */
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_POSPLUS:
+ cc++;
+ break;
+
+ case OP_EXACTI:
+ caseless = TRUE;
+ /* Fall through */
+ case OP_EXACT:
+ repeat = GET2(cc, 1);
+ last = FALSE;
+ cc += 1 + IMM2_SIZE;
+ break;
+
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_POSQUERYI:
+ caseless = TRUE;
+ /* Fall through */
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_POSQUERY:
+ len = 1;
+ cc++;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(*cc)) len += GET_EXTRALEN(*cc);
+#endif
+ max_chars = scan_prefix(common, cc + len, chars, max_chars, rec_count);
+ if (max_chars == 0)
+ return consumed;
+ last = FALSE;
+ break;
+
+ case OP_KET:
+ cc += 1 + LINK_SIZE;
+ continue;
+
+ case OP_ALT:
+ cc += GET(cc, 1);
+ continue;
+
+ case OP_ONCE:
+ case OP_BRA:
+ case OP_BRAPOS:
+ case OP_CBRA:
+ case OP_CBRAPOS:
+ alternative = cc + GET(cc, 1);
+ while (*alternative == OP_ALT)
+ {
+ max_chars = scan_prefix(common, alternative + 1 + LINK_SIZE, chars, max_chars, rec_count);
+ if (max_chars == 0)
+ return consumed;
+ alternative += GET(alternative, 1);
+ }
+
+ if (*cc == OP_CBRA || *cc == OP_CBRAPOS)
+ cc += IMM2_SIZE;
+ cc += 1 + LINK_SIZE;
+ continue;
+
+ case OP_CLASS:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (common->utf && !is_char7_bitset((const sljit_u8 *)(cc + 1), FALSE))
+ return consumed;
+#endif
+ class = TRUE;
+ break;
+
+ case OP_NCLASS:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf) return consumed;
+#endif
+ class = TRUE;
+ break;
+
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ case OP_XCLASS:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf) return consumed;
+#endif
+ any = TRUE;
+ cc += GET(cc, 1);
+ break;
+#endif
+
+ case OP_DIGIT:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (common->utf && !is_char7_bitset((const sljit_u8 *)common->ctypes - cbit_length + cbit_digit, FALSE))
+ return consumed;
+#endif
+ any = TRUE;
+ cc++;
+ break;
+
+ case OP_WHITESPACE:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (common->utf && !is_char7_bitset((const sljit_u8 *)common->ctypes - cbit_length + cbit_space, FALSE))
+ return consumed;
+#endif
+ any = TRUE;
+ cc++;
+ break;
+
+ case OP_WORDCHAR:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (common->utf && !is_char7_bitset((const sljit_u8 *)common->ctypes - cbit_length + cbit_word, FALSE))
+ return consumed;
+#endif
+ any = TRUE;
+ cc++;
+ break;
+
+ case OP_NOT:
+ case OP_NOTI:
+ cc++;
+ /* Fall through. */
+ case OP_NOT_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ case OP_ANY:
+ case OP_ALLANY:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf) return consumed;
+#endif
+ any = TRUE;
+ cc++;
+ break;
+
+#ifdef SUPPORT_UNICODE
+ case OP_NOTPROP:
+ case OP_PROP:
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf) return consumed;
+#endif
+ any = TRUE;
+ cc += 1 + 2;
+ break;
+#endif
+
+ case OP_TYPEEXACT:
+ repeat = GET2(cc, 1);
+ cc += 1 + IMM2_SIZE;
+ continue;
+
+ case OP_NOTEXACT:
+ case OP_NOTEXACTI:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf) return consumed;
+#endif
+ any = TRUE;
+ repeat = GET2(cc, 1);
+ cc += 1 + IMM2_SIZE + 1;
+ break;
+
+ default:
+ return consumed;
+ }
+
+ if (any)
+ {
+ do
+ {
+ chars->count = 255;
+
+ consumed++;
+ if (--max_chars == 0)
+ return consumed;
+ chars++;
+ }
+ while (--repeat > 0);
+
+ repeat = 1;
+ continue;
+ }
+
+ if (class)
+ {
+ bytes = (sljit_u8*) (cc + 1);
+ cc += 1 + 32 / sizeof(PCRE2_UCHAR);
+
+ switch (*cc)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPOSSTAR:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSQUERY:
+ max_chars = scan_prefix(common, cc + 1, chars, max_chars, rec_count);
+ if (max_chars == 0)
+ return consumed;
+ break;
+
+ default:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRPOSPLUS:
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ repeat = GET2(cc, 1);
+ if (repeat <= 0)
+ return consumed;
+ break;
+ }
+
+ do
+ {
+ if (bytes[31] & 0x80)
+ chars->count = 255;
+ else if (chars->count != 255)
+ {
+ bytes_end = bytes + 32;
+ chr = 0;
+ do
+ {
+ byte = *bytes++;
+ SLJIT_ASSERT((chr & 0x7) == 0);
+ if (byte == 0)
+ chr += 8;
+ else
+ {
+ do
+ {
+ if ((byte & 0x1) != 0)
+ add_prefix_char(chr, chars, TRUE);
+ byte >>= 1;
+ chr++;
+ }
+ while (byte != 0);
+ chr = (chr + 7) & ~7;
+ }
+ }
+ while (chars->count != 255 && bytes < bytes_end);
+ bytes = bytes_end - 32;
+ }
+
+ consumed++;
+ if (--max_chars == 0)
+ return consumed;
+ chars++;
+ }
+ while (--repeat > 0);
+
+ switch (*cc)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPOSSTAR:
+ return consumed;
+
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSQUERY:
+ cc++;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ if (GET2(cc, 1) != GET2(cc, 1 + IMM2_SIZE))
+ return consumed;
+ cc += 1 + 2 * IMM2_SIZE;
+ break;
+ }
+
+ repeat = 1;
+ continue;
+ }
+
+ len = 1;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(*cc)) len += GET_EXTRALEN(*cc);
+#endif
+
+ if (caseless && char_has_othercase(common, cc))
+ {
+#ifdef SUPPORT_UNICODE
+ if (common->utf)
+ {
+ GETCHAR(chr, cc);
+ if ((int)PRIV(ord2utf)(char_othercase(common, chr), othercase) != len)
+ return consumed;
+ }
+ else
+#endif
+ {
+ chr = *cc;
+#ifdef SUPPORT_UNICODE
+ if (common->ucp && chr > 127)
+ othercase[0] = UCD_OTHERCASE(chr);
+ else
+#endif
+ othercase[0] = TABLE_GET(chr, common->fcc, chr);
+ }
+ }
+ else
+ {
+ caseless = FALSE;
+ othercase[0] = 0; /* Stops compiler warning - PH */
+ }
+
+ len_save = len;
+ cc_save = cc;
+ while (TRUE)
+ {
+ oc = othercase;
+ do
+ {
+ len--;
+ consumed++;
+
+ chr = *cc;
+ add_prefix_char(*cc, chars, len == 0);
+
+ if (caseless)
+ add_prefix_char(*oc, chars, len == 0);
+
+ if (--max_chars == 0)
+ return consumed;
+ chars++;
+ cc++;
+ oc++;
+ }
+ while (len > 0);
+
+ if (--repeat == 0)
+ break;
+
+ len = len_save;
+ cc = cc_save;
+ }
+
+ repeat = 1;
+ if (last)
+ return consumed;
+ }
+}
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+static void jumpto_if_not_utf_char_start(struct sljit_compiler *compiler, sljit_s32 reg, struct sljit_label *label)
+{
+#if PCRE2_CODE_UNIT_WIDTH == 8
+OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xc0);
+CMPTO(SLJIT_EQUAL, reg, 0, SLJIT_IMM, 0x80, label);
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xfc00);
+CMPTO(SLJIT_EQUAL, reg, 0, SLJIT_IMM, 0xdc00, label);
+#else
+#error "Unknown code width"
+#endif
+}
+#endif
+
+#include "pcre2_jit_simd_inc.h"
+
+#ifdef JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD
+
+static BOOL check_fast_forward_char_pair_simd(compiler_common *common, fast_forward_char_data *chars, int max)
+{
+ sljit_s32 i, j, max_i = 0, max_j = 0;
+ sljit_u32 max_pri = 0;
+ PCRE2_UCHAR a1, a2, a_pri, b1, b2, b_pri;
+
+ for (i = max - 1; i >= 1; i--)
+ {
+ if (chars[i].last_count > 2)
+ {
+ a1 = chars[i].chars[0];
+ a2 = chars[i].chars[1];
+ a_pri = chars[i].last_count;
+
+ j = i - max_fast_forward_char_pair_offset();
+ if (j < 0)
+ j = 0;
+
+ while (j < i)
+ {
+ b_pri = chars[j].last_count;
+ if (b_pri > 2 && (sljit_u32)a_pri + (sljit_u32)b_pri >= max_pri)
+ {
+ b1 = chars[j].chars[0];
+ b2 = chars[j].chars[1];
+
+ if (a1 != b1 && a1 != b2 && a2 != b1 && a2 != b2)
+ {
+ max_pri = a_pri + b_pri;
+ max_i = i;
+ max_j = j;
+ }
+ }
+ j++;
+ }
+ }
+ }
+
+if (max_pri == 0)
+ return FALSE;
+
+fast_forward_char_pair_simd(common, max_i, chars[max_i].chars[0], chars[max_i].chars[1], max_j, chars[max_j].chars[0], chars[max_j].chars[1]);
+return TRUE;
+}
+
+#endif /* JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD */
+
+static void fast_forward_first_char2(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2, sljit_s32 offset)
+{
+DEFINE_COMPILER;
+struct sljit_label *start;
+struct sljit_jump *match;
+struct sljit_jump *partial_quit;
+PCRE2_UCHAR mask;
+BOOL has_match_end = (common->match_end_ptr != 0);
+
+SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE || offset == 0);
+
+if (has_match_end)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+
+if (offset > 0)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset));
+
+if (has_match_end)
+ {
+ OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
+
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(offset + 1));
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, STR_END, 0, TMP1, 0);
+ CMOV(SLJIT_GREATER, STR_END, TMP1, 0);
+ }
+
+#ifdef JIT_HAS_FAST_FORWARD_CHAR_SIMD
+
+if (JIT_HAS_FAST_FORWARD_CHAR_SIMD)
+ {
+ fast_forward_char_simd(common, char1, char2, offset);
+
+ if (offset > 0)
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset));
+
+ if (has_match_end)
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+ return;
+ }
+
+#endif
+
+start = LABEL();
+
+partial_quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+if (char1 == char2)
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char1, start);
+else
+ {
+ mask = char1 ^ char2;
+ if (is_powerof2(mask))
+ {
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, mask);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char1 | mask, start);
+ }
+ else
+ {
+ match = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, char1);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char2, start);
+ JUMPHERE(match);
+ }
+ }
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+if (common->utf && offset > 0)
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-(offset + 1)));
+ jumpto_if_not_utf_char_start(compiler, TMP1, start);
+ }
+#endif
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset + 1));
+
+if (common->mode != PCRE2_JIT_COMPLETE)
+ JUMPHERE(partial_quit);
+
+if (has_match_end)
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+}
+
+static SLJIT_INLINE BOOL fast_forward_first_n_chars(compiler_common *common)
+{
+DEFINE_COMPILER;
+struct sljit_label *start;
+struct sljit_jump *match;
+fast_forward_char_data chars[MAX_N_CHARS];
+sljit_s32 offset;
+PCRE2_UCHAR mask;
+PCRE2_UCHAR *char_set, *char_set_end;
+int i, max, from;
+int range_right = -1, range_len;
+sljit_u8 *update_table = NULL;
+BOOL in_range;
+sljit_u32 rec_count;
+
+for (i = 0; i < MAX_N_CHARS; i++)
+ {
+ chars[i].count = 0;
+ chars[i].last_count = 0;
+ }
+
+rec_count = 10000;
+max = scan_prefix(common, common->start, chars, MAX_N_CHARS, &rec_count);
+
+if (max < 1)
+ return FALSE;
+
+/* Convert last_count to priority. */
+for (i = 0; i < max; i++)
+ {
+ SLJIT_ASSERT(chars[i].count > 0 && chars[i].last_count <= chars[i].count);
+
+ if (chars[i].count == 1)
+ {
+ chars[i].last_count = (chars[i].last_count == 1) ? 7 : 5;
+ /* Simplifies algorithms later. */
+ chars[i].chars[1] = chars[i].chars[0];
+ }
+ else if (chars[i].count == 2)
+ {
+ SLJIT_ASSERT(chars[i].chars[0] != chars[i].chars[1]);
+
+ if (is_powerof2(chars[i].chars[0] ^ chars[i].chars[1]))
+ chars[i].last_count = (chars[i].last_count == 2) ? 6 : 4;
+ else
+ chars[i].last_count = (chars[i].last_count == 2) ? 3 : 2;
+ }
+ else
+ chars[i].last_count = (chars[i].count == 255) ? 0 : 1;
+ }
+
+#ifdef JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD
+if (JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD && check_fast_forward_char_pair_simd(common, chars, max))
+ return TRUE;
+#endif
+
+in_range = FALSE;
+/* Prevent compiler "uninitialized" warning */
+from = 0;
+range_len = 4 /* minimum length */ - 1;
+for (i = 0; i <= max; i++)
+ {
+ if (in_range && (i - from) > range_len && (chars[i - 1].count < 255))
+ {
+ range_len = i - from;
+ range_right = i - 1;
+ }
+
+ if (i < max && chars[i].count < 255)
+ {
+ SLJIT_ASSERT(chars[i].count > 0);
+ if (!in_range)
+ {
+ in_range = TRUE;
+ from = i;
+ }
+ }
+ else
+ in_range = FALSE;
+ }
+
+if (range_right >= 0)
+ {
+ update_table = (sljit_u8 *)allocate_read_only_data(common, 256);
+ if (update_table == NULL)
+ return TRUE;
+ memset(update_table, IN_UCHARS(range_len), 256);
+
+ for (i = 0; i < range_len; i++)
+ {
+ SLJIT_ASSERT(chars[range_right - i].count > 0 && chars[range_right - i].count < 255);
+
+ char_set = chars[range_right - i].chars;
+ char_set_end = char_set + chars[range_right - i].count;
+ do
+ {
+ if (update_table[(*char_set) & 0xff] > IN_UCHARS(i))
+ update_table[(*char_set) & 0xff] = IN_UCHARS(i);
+ char_set++;
+ }
+ while (char_set < char_set_end);
+ }
+ }
+
+offset = -1;
+/* Scan forward. */
+for (i = 0; i < max; i++)
+ {
+ if (range_right == i)
+ continue;
+
+ if (offset == -1)
+ {
+ if (chars[i].last_count >= 2)
+ offset = i;
+ }
+ else if (chars[offset].last_count < chars[i].last_count)
+ offset = i;
+ }
+
+SLJIT_ASSERT(offset == -1 || (chars[offset].count >= 1 && chars[offset].count <= 2));
+
+if (range_right < 0)
+ {
+ if (offset < 0)
+ return FALSE;
+ /* Works regardless the value is 1 or 2. */
+ fast_forward_first_char2(common, chars[offset].chars[0], chars[offset].chars[1], offset);
+ return TRUE;
+ }
+
+SLJIT_ASSERT(range_right != offset);
+
+if (common->match_end_ptr != 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max));
+ add_jump(compiler, &common->failed_match, JUMP(SLJIT_LESS));
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, STR_END, 0, TMP1, 0);
+ CMOV(SLJIT_GREATER, STR_END, TMP1, 0);
+ }
+else
+ {
+ OP2(SLJIT_SUB | SLJIT_SET_LESS, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max));
+ add_jump(compiler, &common->failed_match, JUMP(SLJIT_LESS));
+ }
+
+SLJIT_ASSERT(range_right >= 0);
+
+if (!HAS_VIRTUAL_REGISTERS)
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, SLJIT_IMM, (sljit_sw)update_table);
+
+start = LABEL();
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0));
+
+#if PCRE2_CODE_UNIT_WIDTH == 8 || (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(range_right));
+#else
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(range_right + 1) - 1);
+#endif
+
+if (!HAS_VIRTUAL_REGISTERS)
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM2(RETURN_ADDR, TMP1), 0);
+else
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)update_table);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, start);
+
+if (offset >= 0)
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(offset));
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ if (chars[offset].count == 1)
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0], start);
+ else
+ {
+ mask = chars[offset].chars[0] ^ chars[offset].chars[1];
+ if (is_powerof2(mask))
+ {
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, mask);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0] | mask, start);
+ }
+ else
+ {
+ match = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0]);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[1], start);
+ JUMPHERE(match);
+ }
+ }
+ }
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+if (common->utf && offset != 0)
+ {
+ if (offset < 0)
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ }
+ else
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+
+ jumpto_if_not_utf_char_start(compiler, TMP1, start);
+
+ if (offset < 0)
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ }
+#endif
+
+if (offset >= 0)
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+if (common->match_end_ptr != 0)
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+else
+ OP2(SLJIT_ADD, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max));
+return TRUE;
+}
+
+static SLJIT_INLINE void fast_forward_first_char(compiler_common *common)
+{
+PCRE2_UCHAR first_char = (PCRE2_UCHAR)(common->re->first_codeunit);
+PCRE2_UCHAR oc;
+
+oc = first_char;
+if ((common->re->flags & PCRE2_FIRSTCASELESS) != 0)
+ {
+ oc = TABLE_GET(first_char, common->fcc, first_char);
+#if defined SUPPORT_UNICODE
+ if (first_char > 127 && (common->utf || common->ucp))
+ oc = UCD_OTHERCASE(first_char);
+#endif
+ }
+
+fast_forward_first_char2(common, first_char, oc, 0);
+}
+
+static SLJIT_INLINE void fast_forward_newline(compiler_common *common)
+{
+DEFINE_COMPILER;
+struct sljit_label *loop;
+struct sljit_jump *lastchar = NULL;
+struct sljit_jump *firstchar;
+struct sljit_jump *quit = NULL;
+struct sljit_jump *foundcr = NULL;
+struct sljit_jump *notfoundnl;
+jump_list *newline = NULL;
+
+if (common->match_end_ptr != 0)
+ {
+ OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
+ OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ }
+
+if (common->nltype == NLTYPE_FIXED && common->newline > 255)
+ {
+#ifdef JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD
+ if (JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD && common->mode == PCRE2_JIT_COMPLETE)
+ {
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin));
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, str));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin));
+ }
+ firstchar = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0);
+
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, STR_PTR, 0, TMP1, 0);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_NOT_EQUAL);
+#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
+#endif
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+
+ fast_forward_char_pair_simd(common, 1, common->newline & 0xff, common->newline & 0xff, 0, (common->newline >> 8) & 0xff, (common->newline >> 8) & 0xff);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+ }
+ else
+#endif /* JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD */
+ {
+ lastchar = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin));
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, str));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin));
+ }
+ firstchar = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0);
+
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(2));
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, STR_PTR, 0, TMP1, 0);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER_EQUAL);
+#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCHAR_SHIFT);
+#endif
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+
+ loop = LABEL();
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff, loop);
+ CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff, loop);
+
+ JUMPHERE(quit);
+ JUMPHERE(lastchar);
+ }
+
+ JUMPHERE(firstchar);
+
+ if (common->match_end_ptr != 0)
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+ return;
+ }
+
+if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str));
+ }
+else
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, str));
+
+/* Example: match /^/ to \r\n from offset 1. */
+firstchar = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0);
+
+if (common->nltype == NLTYPE_ANY)
+ move_back(common, NULL, FALSE);
+else
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+loop = LABEL();
+common->ff_newline_shortcut = loop;
+
+#ifdef JIT_HAS_FAST_FORWARD_CHAR_SIMD
+if (JIT_HAS_FAST_FORWARD_CHAR_SIMD && (common->nltype == NLTYPE_FIXED || common->nltype == NLTYPE_ANYCRLF))
+ {
+ if (common->nltype == NLTYPE_ANYCRLF)
+ {
+ fast_forward_char_simd(common, CHAR_CR, CHAR_LF, 0);
+ if (common->mode != PCRE2_JIT_COMPLETE)
+ lastchar = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ quit = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR);
+ }
+ else
+ {
+ fast_forward_char_simd(common, common->newline, common->newline, 0);
+
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ if (common->mode != PCRE2_JIT_COMPLETE)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, STR_PTR, 0, STR_END, 0);
+ CMOV(SLJIT_GREATER, STR_PTR, STR_END, 0);
+ }
+ }
+ }
+else
+#endif /* JIT_HAS_FAST_FORWARD_CHAR_SIMD */
+ {
+ read_char(common, common->nlmin, common->nlmax, NULL, READ_CHAR_NEWLINE);
+ lastchar = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ if (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF)
+ foundcr = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR);
+ check_newlinechar(common, common->nltype, &newline, FALSE);
+ set_jumps(newline, loop);
+ }
+
+if (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF)
+ {
+ if (quit == NULL)
+ {
+ quit = JUMP(SLJIT_JUMP);
+ JUMPHERE(foundcr);
+ }
+
+ notfoundnl = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, CHAR_NL);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
+#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
+#endif
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+ JUMPHERE(notfoundnl);
+ JUMPHERE(quit);
+ }
+
+if (lastchar)
+ JUMPHERE(lastchar);
+JUMPHERE(firstchar);
+
+if (common->match_end_ptr != 0)
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+}
+
+static BOOL optimize_class(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks);
+
+static SLJIT_INLINE void fast_forward_start_bits(compiler_common *common)
+{
+DEFINE_COMPILER;
+const sljit_u8 *start_bits = common->re->start_bitmap;
+struct sljit_label *start;
+struct sljit_jump *partial_quit;
+#if PCRE2_CODE_UNIT_WIDTH != 8
+struct sljit_jump *found = NULL;
+#endif
+jump_list *matches = NULL;
+
+if (common->match_end_ptr != 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, STR_END, 0);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, STR_END, 0, TMP1, 0);
+ CMOV(SLJIT_GREATER, STR_END, TMP1, 0);
+ }
+
+start = LABEL();
+
+partial_quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+if (!optimize_class(common, start_bits, (start_bits[31] & 0x80) != 0, FALSE, &matches))
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if ((start_bits[31] & 0x80) != 0)
+ found = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 255);
+ else
+ CMPTO(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 255, start);
+#elif defined SUPPORT_UNICODE
+ if (common->utf && is_char7_bitset(start_bits, FALSE))
+ CMPTO(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 127, start);
+#endif
+ OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7);
+ OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)start_bits);
+ if (!HAS_VIRTUAL_REGISTERS)
+ {
+ OP2(SLJIT_SHL, TMP3, 0, SLJIT_IMM, 1, TMP2, 0);
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, TMP3, 0);
+ }
+ else
+ {
+ OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, TMP2, 0);
+ }
+ JUMPTO(SLJIT_ZERO, start);
+ }
+else
+ set_jumps(matches, start);
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+if (found != NULL)
+ JUMPHERE(found);
+#endif
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+if (common->mode != PCRE2_JIT_COMPLETE)
+ JUMPHERE(partial_quit);
+
+if (common->match_end_ptr != 0)
+ OP1(SLJIT_MOV, STR_END, 0, RETURN_ADDR, 0);
+}
+
+static SLJIT_INLINE jump_list *search_requested_char(compiler_common *common, PCRE2_UCHAR req_char, BOOL caseless, BOOL has_firstchar)
+{
+DEFINE_COMPILER;
+struct sljit_label *loop;
+struct sljit_jump *toolong;
+struct sljit_jump *already_found;
+struct sljit_jump *found;
+struct sljit_jump *found_oc = NULL;
+jump_list *not_found = NULL;
+sljit_u32 oc, bit;
+
+SLJIT_ASSERT(common->req_char_ptr != 0);
+OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(REQ_CU_MAX) * 100);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->req_char_ptr);
+toolong = CMP(SLJIT_LESS, TMP2, 0, STR_END, 0);
+already_found = CMP(SLJIT_LESS, STR_PTR, 0, TMP1, 0);
+
+if (has_firstchar)
+ OP2(SLJIT_ADD, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+else
+ OP1(SLJIT_MOV, TMP1, 0, STR_PTR, 0);
+
+oc = req_char;
+if (caseless)
+ {
+ oc = TABLE_GET(req_char, common->fcc, req_char);
+#if defined SUPPORT_UNICODE
+ if (req_char > 127 && (common->utf || common->ucp))
+ oc = UCD_OTHERCASE(req_char);
+#endif
+ }
+
+#ifdef JIT_HAS_FAST_REQUESTED_CHAR_SIMD
+if (JIT_HAS_FAST_REQUESTED_CHAR_SIMD)
+ {
+ not_found = fast_requested_char_simd(common, req_char, oc);
+ }
+else
+#endif
+ {
+ loop = LABEL();
+ add_jump(compiler, &not_found, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, STR_END, 0));
+
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(TMP1), 0);
+
+ if (req_char == oc)
+ found = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, req_char);
+ else
+ {
+ bit = req_char ^ oc;
+ if (is_powerof2(bit))
+ {
+ OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, bit);
+ found = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, req_char | bit);
+ }
+ else
+ {
+ found = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, req_char);
+ found_oc = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, oc);
+ }
+ }
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPTO(SLJIT_JUMP, loop);
+
+ JUMPHERE(found);
+ if (found_oc)
+ JUMPHERE(found_oc);
+ }
+
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->req_char_ptr, TMP1, 0);
+
+JUMPHERE(already_found);
+JUMPHERE(toolong);
+return not_found;
+}
+
+static void do_revertframes(compiler_common *common)
+{
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+struct sljit_label *mainloop;
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+GET_LOCAL_BASE(TMP1, 0, 0);
+
+/* Drop frames until we reach STACK_TOP. */
+mainloop = LABEL();
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), -SSIZE_OF(sw));
+jump = CMP(SLJIT_SIG_LESS_EQUAL, TMP2, 0, SLJIT_IMM, 0);
+
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0);
+if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -(2 * SSIZE_OF(sw)));
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), SLJIT_MEM1(STACK_TOP), -(3 * SSIZE_OF(sw)));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 3 * SSIZE_OF(sw));
+ }
+else
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), -(2 * SSIZE_OF(sw)));
+ OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(STACK_TOP), -(3 * SSIZE_OF(sw)));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 3 * SSIZE_OF(sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP1, 0);
+ GET_LOCAL_BASE(TMP1, 0, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP3, 0);
+ }
+JUMPTO(SLJIT_JUMP, mainloop);
+
+JUMPHERE(jump);
+jump = CMP(SLJIT_NOT_ZERO /* SIG_LESS */, TMP2, 0, SLJIT_IMM, 0);
+/* End of reverting values. */
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(jump);
+OP2(SLJIT_SUB, TMP2, 0, SLJIT_IMM, 0, TMP2, 0);
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0);
+if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -(2 * SSIZE_OF(sw)));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2 * SSIZE_OF(sw));
+ }
+else
+ {
+ OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(STACK_TOP), -(2 * SSIZE_OF(sw)));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2 * SSIZE_OF(sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP3, 0);
+ }
+JUMPTO(SLJIT_JUMP, mainloop);
+}
+
+static void check_wordboundary(compiler_common *common)
+{
+DEFINE_COMPILER;
+struct sljit_jump *skipread;
+jump_list *skipread_list = NULL;
+#ifdef SUPPORT_UNICODE
+struct sljit_label *valid_utf;
+jump_list *invalid_utf1 = NULL;
+#endif /* SUPPORT_UNICODE */
+jump_list *invalid_utf2 = NULL;
+#if PCRE2_CODE_UNIT_WIDTH != 8 || defined SUPPORT_UNICODE
+struct sljit_jump *jump;
+#endif /* PCRE2_CODE_UNIT_WIDTH != 8 || SUPPORT_UNICODE */
+
+SLJIT_COMPILE_ASSERT(ctype_word == 0x10, ctype_word_must_be_16);
+
+sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+/* Get type of the previous char, and put it to TMP3. */
+OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin));
+OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 0);
+skipread = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0);
+
+#ifdef SUPPORT_UNICODE
+if (common->invalid_utf)
+ {
+ peek_char_back(common, READ_CHAR_MAX, &invalid_utf1);
+
+ if (common->mode != PCRE2_JIT_COMPLETE)
+ {
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0);
+ OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0);
+ move_back(common, NULL, TRUE);
+ check_start_used_ptr(common);
+ OP1(SLJIT_MOV, TMP1, 0, RETURN_ADDR, 0);
+ OP1(SLJIT_MOV, STR_PTR, 0, TMP2, 0);
+ }
+ }
+else
+#endif /* SUPPORT_UNICODE */
+ {
+ if (common->mode == PCRE2_JIT_COMPLETE)
+ peek_char_back(common, READ_CHAR_MAX, NULL);
+ else
+ {
+ move_back(common, NULL, TRUE);
+ check_start_used_ptr(common);
+ read_char(common, 0, READ_CHAR_MAX, NULL, READ_CHAR_UPDATE_STR_PTR);
+ }
+ }
+
+/* Testing char type. */
+#ifdef SUPPORT_UNICODE
+if (common->ucp)
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 1);
+ jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE);
+ add_jump(compiler, &common->getucdtype, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
+ JUMPHERE(jump);
+ OP1(SLJIT_MOV, TMP3, 0, TMP2, 0);
+ }
+else
+#endif /* SUPPORT_UNICODE */
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
+#elif defined SUPPORT_UNICODE
+ /* Here TMP3 has already been zeroed. */
+ jump = NULL;
+ if (common->utf)
+ jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), common->ctypes);
+ OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 4 /* ctype_word */);
+ OP2(SLJIT_AND, TMP3, 0, TMP1, 0, SLJIT_IMM, 1);
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ JUMPHERE(jump);
+#elif defined SUPPORT_UNICODE
+ if (jump != NULL)
+ JUMPHERE(jump);
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ }
+JUMPHERE(skipread);
+
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0);
+check_str_end(common, &skipread_list);
+peek_char(common, READ_CHAR_MAX, SLJIT_MEM1(SLJIT_SP), LOCALS1, &invalid_utf2);
+
+/* Testing char type. This is a code duplication. */
+#ifdef SUPPORT_UNICODE
+
+valid_utf = LABEL();
+
+if (common->ucp)
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 1);
+ jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE);
+ add_jump(compiler, &common->getucdtype, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
+ JUMPHERE(jump);
+ }
+else
+#endif /* SUPPORT_UNICODE */
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ /* TMP2 may be destroyed by peek_char. */
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0);
+ jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
+#elif defined SUPPORT_UNICODE
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0);
+ jump = NULL;
+ if (common->utf)
+ jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
+#endif
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP1), common->ctypes);
+ OP2(SLJIT_LSHR, TMP2, 0, TMP2, 0, SLJIT_IMM, 4 /* ctype_word */);
+ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 1);
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ JUMPHERE(jump);
+#elif defined SUPPORT_UNICODE
+ if (jump != NULL)
+ JUMPHERE(jump);
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ }
+set_jumps(skipread_list, LABEL());
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+OP2(SLJIT_XOR | SLJIT_SET_Z, TMP2, 0, TMP2, 0, TMP3, 0);
+OP_SRC(SLJIT_FAST_RETURN, TMP1, 0);
+
+#ifdef SUPPORT_UNICODE
+if (common->invalid_utf)
+ {
+ set_jumps(invalid_utf1, LABEL());
+
+ peek_char(common, READ_CHAR_MAX, SLJIT_MEM1(SLJIT_SP), LOCALS1, NULL);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR, valid_utf);
+
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, -1);
+ OP_SRC(SLJIT_FAST_RETURN, TMP1, 0);
+
+ set_jumps(invalid_utf2, LABEL());
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ OP1(SLJIT_MOV, TMP2, 0, TMP3, 0);
+ OP_SRC(SLJIT_FAST_RETURN, TMP1, 0);
+ }
+#endif /* SUPPORT_UNICODE */
+}
+
+static BOOL optimize_class_ranges(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks)
+{
+/* May destroy TMP1. */
+DEFINE_COMPILER;
+int ranges[MAX_CLASS_RANGE_SIZE];
+sljit_u8 bit, cbit, all;
+int i, byte, length = 0;
+
+bit = bits[0] & 0x1;
+/* All bits will be zero or one (since bit is zero or one). */
+all = -bit;
+
+for (i = 0; i < 256; )
+ {
+ byte = i >> 3;
+ if ((i & 0x7) == 0 && bits[byte] == all)
+ i += 8;
+ else
+ {
+ cbit = (bits[byte] >> (i & 0x7)) & 0x1;
+ if (cbit != bit)
+ {
+ if (length >= MAX_CLASS_RANGE_SIZE)
+ return FALSE;
+ ranges[length] = i;
+ length++;
+ bit = cbit;
+ all = -cbit;
+ }
+ i++;
+ }
+ }
+
+if (((bit == 0) && nclass) || ((bit == 1) && !nclass))
+ {
+ if (length >= MAX_CLASS_RANGE_SIZE)
+ return FALSE;
+ ranges[length] = 256;
+ length++;
+ }
+
+if (length < 0 || length > 4)
+ return FALSE;
+
+bit = bits[0] & 0x1;
+if (invert) bit ^= 0x1;
+
+/* No character is accepted. */
+if (length == 0 && bit == 0)
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+
+switch(length)
+ {
+ case 0:
+ /* When bit != 0, all characters are accepted. */
+ return TRUE;
+
+ case 1:
+ add_jump(compiler, backtracks, CMP(bit == 0 ? SLJIT_LESS : SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[0]));
+ return TRUE;
+
+ case 2:
+ if (ranges[0] + 1 != ranges[1])
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[0]);
+ add_jump(compiler, backtracks, CMP(bit != 0 ? SLJIT_LESS : SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[1] - ranges[0]));
+ }
+ else
+ add_jump(compiler, backtracks, CMP(bit != 0 ? SLJIT_EQUAL : SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[0]));
+ return TRUE;
+
+ case 3:
+ if (bit != 0)
+ {
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[2]));
+ if (ranges[0] + 1 != ranges[1])
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[0]);
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[1] - ranges[0]));
+ }
+ else
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[0]));
+ return TRUE;
+ }
+
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[0]));
+ if (ranges[1] + 1 != ranges[2])
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[1]);
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[2] - ranges[1]));
+ }
+ else
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[1]));
+ return TRUE;
+
+ case 4:
+ if ((ranges[1] - ranges[0]) == (ranges[3] - ranges[2])
+ && (ranges[0] | (ranges[2] - ranges[0])) == ranges[2]
+ && (ranges[1] & (ranges[2] - ranges[0])) == 0
+ && is_powerof2(ranges[2] - ranges[0]))
+ {
+ SLJIT_ASSERT((ranges[0] & (ranges[2] - ranges[0])) == 0 && (ranges[2] & ranges[3] & (ranges[2] - ranges[0])) != 0);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[2] - ranges[0]);
+ if (ranges[2] + 1 != ranges[3])
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[2]);
+ add_jump(compiler, backtracks, CMP(bit != 0 ? SLJIT_LESS : SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[3] - ranges[2]));
+ }
+ else
+ add_jump(compiler, backtracks, CMP(bit != 0 ? SLJIT_EQUAL : SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[2]));
+ return TRUE;
+ }
+
+ if (bit != 0)
+ {
+ i = 0;
+ if (ranges[0] + 1 != ranges[1])
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[0]);
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[1] - ranges[0]));
+ i = ranges[0];
+ }
+ else
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[0]));
+
+ if (ranges[2] + 1 != ranges[3])
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[2] - i);
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[3] - ranges[2]));
+ }
+ else
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[2] - i));
+ return TRUE;
+ }
+
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[0]);
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[3] - ranges[0]));
+ if (ranges[1] + 1 != ranges[2])
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[1] - ranges[0]);
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[2] - ranges[1]));
+ }
+ else
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[1] - ranges[0]));
+ return TRUE;
+
+ default:
+ SLJIT_UNREACHABLE();
+ return FALSE;
+ }
+}
+
+static BOOL optimize_class_chars(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks)
+{
+/* May destroy TMP1. */
+DEFINE_COMPILER;
+uint16_t char_list[MAX_CLASS_CHARS_SIZE];
+uint8_t byte;
+sljit_s32 type;
+int i, j, k, len, c;
+
+if (!sljit_has_cpu_feature(SLJIT_HAS_CMOV))
+ return FALSE;
+
+len = 0;
+
+for (i = 0; i < 32; i++)
+ {
+ byte = bits[i];
+
+ if (nclass)
+ byte = ~byte;
+
+ j = 0;
+ while (byte != 0)
+ {
+ if (byte & 0x1)
+ {
+ c = i * 8 + j;
+
+ k = len;
+
+ if ((c & 0x20) != 0)
+ {
+ for (k = 0; k < len; k++)
+ if (char_list[k] == c - 0x20)
+ {
+ char_list[k] |= 0x120;
+ break;
+ }
+ }
+
+ if (k == len)
+ {
+ if (len >= MAX_CLASS_CHARS_SIZE)
+ return FALSE;
+
+ char_list[len++] = (uint16_t) c;
+ }
+ }
+
+ byte >>= 1;
+ j++;
+ }
+ }
+
+if (len == 0) return FALSE; /* Should never occur, but stops analyzers complaining. */
+
+i = 0;
+j = 0;
+
+if (char_list[0] == 0)
+ {
+ i++;
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_ZERO);
+ }
+else
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0);
+
+while (i < len)
+ {
+ if ((char_list[i] & 0x100) != 0)
+ j++;
+ else
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, char_list[i]);
+ CMOV(SLJIT_ZERO, TMP2, TMP1, 0);
+ }
+ i++;
+ }
+
+if (j != 0)
+ {
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x20);
+
+ for (i = 0; i < len; i++)
+ if ((char_list[i] & 0x100) != 0)
+ {
+ j--;
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, char_list[i] & 0xff);
+ CMOV(SLJIT_ZERO, TMP2, TMP1, 0);
+ }
+ }
+
+if (invert)
+ nclass = !nclass;
+
+type = nclass ? SLJIT_NOT_EQUAL : SLJIT_EQUAL;
+add_jump(compiler, backtracks, CMP(type, TMP2, 0, SLJIT_IMM, 0));
+return TRUE;
+}
+
+static BOOL optimize_class(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks)
+{
+/* May destroy TMP1. */
+if (optimize_class_ranges(common, bits, nclass, invert, backtracks))
+ return TRUE;
+return optimize_class_chars(common, bits, nclass, invert, backtracks);
+}
+
+static void check_anynewline(compiler_common *common)
+{
+/* Check whether TMP1 contains a newline character. TMP2 destroyed. */
+DEFINE_COMPILER;
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a);
+OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a);
+OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a);
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+#if PCRE2_CODE_UNIT_WIDTH == 8
+if (common->utf)
+ {
+#endif
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a);
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ }
+#endif
+#endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */
+OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void check_hspace(compiler_common *common)
+{
+/* Check whether TMP1 contains a newline character. TMP2 destroyed. */
+DEFINE_COMPILER;
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x09);
+OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x20);
+OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0xa0);
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+#if PCRE2_CODE_UNIT_WIDTH == 8
+if (common->utf)
+ {
+#endif
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x1680);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x180e);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x2000);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, 0x200A - 0x2000);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x202f - 0x2000);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x205f - 0x2000);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x3000 - 0x2000);
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ }
+#endif
+#endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */
+OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
+
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void check_vspace(compiler_common *common)
+{
+/* Check whether TMP1 contains a newline character. TMP2 destroyed. */
+DEFINE_COMPILER;
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a);
+OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a);
+OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a);
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+#if PCRE2_CODE_UNIT_WIDTH == 8
+if (common->utf)
+ {
+#endif
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a);
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ }
+#endif
+#endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */
+OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
+
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_casefulcmp(compiler_common *common)
+{
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+struct sljit_label *label;
+int char1_reg;
+int char2_reg;
+
+if (HAS_VIRTUAL_REGISTERS)
+ {
+ char1_reg = STR_END;
+ char2_reg = STACK_TOP;
+ }
+else
+ {
+ char1_reg = TMP3;
+ char2_reg = RETURN_ADDR;
+ }
+
+sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+
+if (char1_reg == STR_END)
+ {
+ OP1(SLJIT_MOV, TMP3, 0, char1_reg, 0);
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, char2_reg, 0);
+ }
+
+if (sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS)
+ {
+ label = LABEL();
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1));
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPTO(SLJIT_NOT_ZERO, label);
+
+ JUMPHERE(jump);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ }
+else if (sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS)
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ label = LABEL();
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1));
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPTO(SLJIT_NOT_ZERO, label);
+
+ JUMPHERE(jump);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ }
+else
+ {
+ label = LABEL();
+ OP1(MOV_UCHAR, char1_reg, 0, SLJIT_MEM1(TMP1), 0);
+ OP1(MOV_UCHAR, char2_reg, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPTO(SLJIT_NOT_ZERO, label);
+
+ JUMPHERE(jump);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ }
+
+if (char1_reg == STR_END)
+ {
+ OP1(SLJIT_MOV, char1_reg, 0, TMP3, 0);
+ OP1(SLJIT_MOV, char2_reg, 0, RETURN_ADDR, 0);
+ }
+
+OP_SRC(SLJIT_FAST_RETURN, TMP1, 0);
+}
+
+static void do_caselesscmp(compiler_common *common)
+{
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+struct sljit_label *label;
+int char1_reg = STR_END;
+int char2_reg;
+int lcc_table;
+int opt_type = 0;
+
+if (HAS_VIRTUAL_REGISTERS)
+ {
+ char2_reg = STACK_TOP;
+ lcc_table = STACK_LIMIT;
+ }
+else
+ {
+ char2_reg = RETURN_ADDR;
+ lcc_table = TMP3;
+ }
+
+if (sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS)
+ opt_type = 1;
+else if (sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS)
+ opt_type = 2;
+
+sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, char1_reg, 0);
+
+if (char2_reg == STACK_TOP)
+ {
+ OP1(SLJIT_MOV, TMP3, 0, char2_reg, 0);
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, lcc_table, 0);
+ }
+
+OP1(SLJIT_MOV, lcc_table, 0, SLJIT_IMM, common->lcc);
+
+if (opt_type == 1)
+ {
+ label = LABEL();
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1));
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ }
+else if (opt_type == 2)
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ label = LABEL();
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1));
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ }
+else
+ {
+ label = LABEL();
+ OP1(MOV_UCHAR, char1_reg, 0, SLJIT_MEM1(TMP1), 0);
+ OP1(MOV_UCHAR, char2_reg, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
+ }
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+jump = CMP(SLJIT_GREATER, char1_reg, 0, SLJIT_IMM, 255);
+#endif
+OP1(SLJIT_MOV_U8, char1_reg, 0, SLJIT_MEM2(lcc_table, char1_reg), 0);
+#if PCRE2_CODE_UNIT_WIDTH != 8
+JUMPHERE(jump);
+jump = CMP(SLJIT_GREATER, char2_reg, 0, SLJIT_IMM, 255);
+#endif
+OP1(SLJIT_MOV_U8, char2_reg, 0, SLJIT_MEM2(lcc_table, char2_reg), 0);
+#if PCRE2_CODE_UNIT_WIDTH != 8
+JUMPHERE(jump);
+#endif
+
+if (opt_type == 0)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0);
+OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+JUMPTO(SLJIT_NOT_ZERO, label);
+
+JUMPHERE(jump);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+
+if (opt_type == 2)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+if (char2_reg == STACK_TOP)
+ {
+ OP1(SLJIT_MOV, char2_reg, 0, TMP3, 0);
+ OP1(SLJIT_MOV, lcc_table, 0, RETURN_ADDR, 0);
+ }
+
+OP1(SLJIT_MOV, char1_reg, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
+OP_SRC(SLJIT_FAST_RETURN, TMP1, 0);
+}
+
+static PCRE2_SPTR byte_sequence_compare(compiler_common *common, BOOL caseless, PCRE2_SPTR cc,
+ compare_context *context, jump_list **backtracks)
+{
+DEFINE_COMPILER;
+unsigned int othercasebit = 0;
+PCRE2_SPTR othercasechar = NULL;
+#ifdef SUPPORT_UNICODE
+int utflength;
+#endif
+
+if (caseless && char_has_othercase(common, cc))
+ {
+ othercasebit = char_get_othercase_bit(common, cc);
+ SLJIT_ASSERT(othercasebit);
+ /* Extracting bit difference info. */
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ othercasechar = cc + (othercasebit >> 8);
+ othercasebit &= 0xff;
+#elif PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ /* Note that this code only handles characters in the BMP. If there
+ ever are characters outside the BMP whose othercase differs in only one
+ bit from itself (there currently are none), this code will need to be
+ revised for PCRE2_CODE_UNIT_WIDTH == 32. */
+ othercasechar = cc + (othercasebit >> 9);
+ if ((othercasebit & 0x100) != 0)
+ othercasebit = (othercasebit & 0xff) << 8;
+ else
+ othercasebit &= 0xff;
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */
+ }
+
+if (context->sourcereg == -1)
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED
+ if (context->length >= 4)
+ OP1(SLJIT_MOV_S32, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
+ else if (context->length >= 2)
+ OP1(SLJIT_MOV_U16, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
+ else
+#endif
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED
+ if (context->length >= 4)
+ OP1(SLJIT_MOV_S32, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
+ else
+#endif
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */
+ context->sourcereg = TMP2;
+ }
+
+#ifdef SUPPORT_UNICODE
+utflength = 1;
+if (common->utf && HAS_EXTRALEN(*cc))
+ utflength += GET_EXTRALEN(*cc);
+
+do
+ {
+#endif
+
+ context->length -= IN_UCHARS(1);
+#if (defined SLJIT_UNALIGNED && SLJIT_UNALIGNED) && (PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16)
+
+ /* Unaligned read is supported. */
+ if (othercasebit != 0 && othercasechar == cc)
+ {
+ context->c.asuchars[context->ucharptr] = *cc | othercasebit;
+ context->oc.asuchars[context->ucharptr] = othercasebit;
+ }
+ else
+ {
+ context->c.asuchars[context->ucharptr] = *cc;
+ context->oc.asuchars[context->ucharptr] = 0;
+ }
+ context->ucharptr++;
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (context->ucharptr >= 4 || context->length == 0 || (context->ucharptr == 2 && context->length == 1))
+#else
+ if (context->ucharptr >= 2 || context->length == 0)
+#endif
+ {
+ if (context->length >= 4)
+ OP1(SLJIT_MOV_S32, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length);
+ else if (context->length >= 2)
+ OP1(SLJIT_MOV_U16, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length);
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ else if (context->length >= 1)
+ OP1(SLJIT_MOV_U8, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length);
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ context->sourcereg = context->sourcereg == TMP1 ? TMP2 : TMP1;
+
+ switch(context->ucharptr)
+ {
+ case 4 / sizeof(PCRE2_UCHAR):
+ if (context->oc.asint != 0)
+ OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, context->oc.asint);
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, context->c.asint | context->oc.asint));
+ break;
+
+ case 2 / sizeof(PCRE2_UCHAR):
+ if (context->oc.asushort != 0)
+ OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, context->oc.asushort);
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, context->c.asushort | context->oc.asushort));
+ break;
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ case 1:
+ if (context->oc.asbyte != 0)
+ OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, context->oc.asbyte);
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, context->c.asbyte | context->oc.asbyte));
+ break;
+#endif
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+ context->ucharptr = 0;
+ }
+
+#else
+
+ /* Unaligned read is unsupported or in 32 bit mode. */
+ if (context->length >= 1)
+ OP1(MOV_UCHAR, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length);
+
+ context->sourcereg = context->sourcereg == TMP1 ? TMP2 : TMP1;
+
+ if (othercasebit != 0 && othercasechar == cc)
+ {
+ OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, othercasebit);
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, *cc | othercasebit));
+ }
+ else
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, *cc));
+
+#endif
+
+ cc++;
+#ifdef SUPPORT_UNICODE
+ utflength--;
+ }
+while (utflength > 0);
+#endif
+
+return cc;
+}
+
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+
+#define SET_TYPE_OFFSET(value) \
+ if ((value) != typeoffset) \
+ { \
+ if ((value) < typeoffset) \
+ OP2(SLJIT_ADD, typereg, 0, typereg, 0, SLJIT_IMM, typeoffset - (value)); \
+ else \
+ OP2(SLJIT_SUB, typereg, 0, typereg, 0, SLJIT_IMM, (value) - typeoffset); \
+ } \
+ typeoffset = (value);
+
+#define SET_CHAR_OFFSET(value) \
+ if ((value) != charoffset) \
+ { \
+ if ((value) < charoffset) \
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(charoffset - (value))); \
+ else \
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)((value) - charoffset)); \
+ } \
+ charoffset = (value);
+
+static PCRE2_SPTR compile_char1_matchingpath(compiler_common *common, PCRE2_UCHAR type, PCRE2_SPTR cc, jump_list **backtracks, BOOL check_str_ptr);
+
+#ifdef SUPPORT_UNICODE
+#define XCLASS_SAVE_CHAR 0x001
+#define XCLASS_CHAR_SAVED 0x002
+#define XCLASS_HAS_TYPE 0x004
+#define XCLASS_HAS_SCRIPT 0x008
+#define XCLASS_HAS_SCRIPT_EXTENSION 0x010
+#define XCLASS_HAS_BOOL 0x020
+#define XCLASS_HAS_BIDICL 0x040
+#define XCLASS_NEEDS_UCD (XCLASS_HAS_TYPE | XCLASS_HAS_SCRIPT | XCLASS_HAS_SCRIPT_EXTENSION | XCLASS_HAS_BOOL | XCLASS_HAS_BIDICL)
+#define XCLASS_SCRIPT_EXTENSION_NOTPROP 0x080
+#define XCLASS_SCRIPT_EXTENSION_RESTORE_RETURN_ADDR 0x100
+#define XCLASS_SCRIPT_EXTENSION_RESTORE_LOCALS0 0x200
+
+#endif /* SUPPORT_UNICODE */
+
+static void compile_xclass_matchingpath(compiler_common *common, PCRE2_SPTR cc, jump_list **backtracks)
+{
+DEFINE_COMPILER;
+jump_list *found = NULL;
+jump_list **list = (cc[0] & XCL_NOT) == 0 ? &found : backtracks;
+sljit_uw c, charoffset, max = 256, min = READ_CHAR_MAX;
+struct sljit_jump *jump = NULL;
+PCRE2_SPTR ccbegin;
+int compares, invertcmp, numberofcmps;
+#if defined SUPPORT_UNICODE && (PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16)
+BOOL utf = common->utf;
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == [8|16] */
+
+#ifdef SUPPORT_UNICODE
+sljit_u32 unicode_status = 0;
+int typereg = TMP1;
+const sljit_u32 *other_cases;
+sljit_uw typeoffset;
+#endif /* SUPPORT_UNICODE */
+
+/* Scanning the necessary info. */
+cc++;
+ccbegin = cc;
+compares = 0;
+
+if (cc[-1] & XCL_MAP)
+ {
+ min = 0;
+ cc += 32 / sizeof(PCRE2_UCHAR);
+ }
+
+while (*cc != XCL_END)
+ {
+ compares++;
+ if (*cc == XCL_SINGLE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ if (c > max) max = c;
+ if (c < min) min = c;
+#ifdef SUPPORT_UNICODE
+ unicode_status |= XCLASS_SAVE_CHAR;
+#endif /* SUPPORT_UNICODE */
+ }
+ else if (*cc == XCL_RANGE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ if (c < min) min = c;
+ GETCHARINCTEST(c, cc);
+ if (c > max) max = c;
+#ifdef SUPPORT_UNICODE
+ unicode_status |= XCLASS_SAVE_CHAR;
+#endif /* SUPPORT_UNICODE */
+ }
+#ifdef SUPPORT_UNICODE
+ else
+ {
+ SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP);
+ cc++;
+ if (*cc == PT_CLIST && cc[-1] == XCL_PROP)
+ {
+ other_cases = PRIV(ucd_caseless_sets) + cc[1];
+ while (*other_cases != NOTACHAR)
+ {
+ if (*other_cases > max) max = *other_cases;
+ if (*other_cases < min) min = *other_cases;
+ other_cases++;
+ }
+ }
+ else
+ {
+ max = READ_CHAR_MAX;
+ min = 0;
+ }
+
+ switch(*cc)
+ {
+ case PT_ANY:
+ /* Any either accepts everything or ignored. */
+ if (cc[-1] == XCL_PROP)
+ {
+ compile_char1_matchingpath(common, OP_ALLANY, cc, backtracks, FALSE);
+ if (list == backtracks)
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+ return;
+ }
+ break;
+
+ case PT_LAMP:
+ case PT_GC:
+ case PT_PC:
+ case PT_ALNUM:
+ unicode_status |= XCLASS_HAS_TYPE;
+ break;
+
+ case PT_SCX:
+ unicode_status |= XCLASS_HAS_SCRIPT_EXTENSION;
+ if (cc[-1] == XCL_NOTPROP)
+ {
+ unicode_status |= XCLASS_SCRIPT_EXTENSION_NOTPROP;
+ break;
+ }
+ compares++;
+ /* Fall through */
+
+ case PT_SC:
+ unicode_status |= XCLASS_HAS_SCRIPT;
+ break;
+
+ case PT_SPACE:
+ case PT_PXSPACE:
+ case PT_WORD:
+ case PT_PXGRAPH:
+ case PT_PXPRINT:
+ case PT_PXPUNCT:
+ unicode_status |= XCLASS_SAVE_CHAR | XCLASS_HAS_TYPE;
+ break;
+
+ case PT_CLIST:
+ case PT_UCNC:
+ unicode_status |= XCLASS_SAVE_CHAR;
+ break;
+
+ case PT_BOOL:
+ unicode_status |= XCLASS_HAS_BOOL;
+ break;
+
+ case PT_BIDICL:
+ unicode_status |= XCLASS_HAS_BIDICL;
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+ cc += 2;
+ }
+#endif /* SUPPORT_UNICODE */
+ }
+SLJIT_ASSERT(compares > 0);
+
+/* We are not necessary in utf mode even in 8 bit mode. */
+cc = ccbegin;
+if ((cc[-1] & XCL_NOT) != 0)
+ read_char(common, min, max, backtracks, READ_CHAR_UPDATE_STR_PTR);
+else
+ {
+#ifdef SUPPORT_UNICODE
+ read_char(common, min, max, (unicode_status & XCLASS_NEEDS_UCD) ? backtracks : NULL, 0);
+#else /* !SUPPORT_UNICODE */
+ read_char(common, min, max, NULL, 0);
+#endif /* SUPPORT_UNICODE */
+ }
+
+if ((cc[-1] & XCL_HASPROP) == 0)
+ {
+ if ((cc[-1] & XCL_MAP) != 0)
+ {
+ jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
+ if (!optimize_class(common, (const sljit_u8 *)cc, (((const sljit_u8 *)cc)[31] & 0x80) != 0, TRUE, &found))
+ {
+ OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7);
+ OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
+ OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, TMP2, 0);
+ add_jump(compiler, &found, JUMP(SLJIT_NOT_ZERO));
+ }
+
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+ JUMPHERE(jump);
+
+ cc += 32 / sizeof(PCRE2_UCHAR);
+ }
+ else
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, min);
+ add_jump(compiler, (cc[-1] & XCL_NOT) == 0 ? backtracks : &found, CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, max - min));
+ }
+ }
+else if ((cc[-1] & XCL_MAP) != 0)
+ {
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0);
+#ifdef SUPPORT_UNICODE
+ unicode_status |= XCLASS_CHAR_SAVED;
+#endif /* SUPPORT_UNICODE */
+ if (!optimize_class(common, (const sljit_u8 *)cc, FALSE, TRUE, list))
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ jump = NULL;
+ if (common->utf)
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
+
+ OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7);
+ OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
+ OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, TMP2, 0);
+ add_jump(compiler, list, JUMP(SLJIT_NOT_ZERO));
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (common->utf)
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ JUMPHERE(jump);
+ }
+
+ OP1(SLJIT_MOV, TMP1, 0, RETURN_ADDR, 0);
+ cc += 32 / sizeof(PCRE2_UCHAR);
+ }
+
+#ifdef SUPPORT_UNICODE
+if (unicode_status & XCLASS_NEEDS_UCD)
+ {
+ if ((unicode_status & (XCLASS_SAVE_CHAR | XCLASS_CHAR_SAVED)) == XCLASS_SAVE_CHAR)
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0);
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+ if (!common->utf)
+ {
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, MAX_UTF_CODE_POINT + 1);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, UNASSIGNED_UTF_CHAR);
+ JUMPHERE(jump);
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == 32 */
+
+ OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 1);
+ OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1));
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK);
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_stage2));
+ OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM2(TMP2, TMP1), 1);
+ OP2(SLJIT_SHL, TMP1, 0, TMP2, 0, SLJIT_IMM, 3);
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 2);
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0);
+
+ ccbegin = cc;
+
+ if (unicode_status & XCLASS_HAS_BIDICL)
+ {
+ OP1(SLJIT_MOV_U16, TMP1, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, scriptx_bidiclass));
+ OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BIDICLASS_SHIFT);
+
+ while (*cc != XCL_END)
+ {
+ if (*cc == XCL_SINGLE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ }
+ else if (*cc == XCL_RANGE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ GETCHARINCTEST(c, cc);
+ }
+ else
+ {
+ SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP);
+ cc++;
+ if (*cc == PT_BIDICL)
+ {
+ compares--;
+ invertcmp = (compares == 0 && list != backtracks);
+ if (cc[-1] == XCL_NOTPROP)
+ invertcmp ^= 0x1;
+ jump = CMP(SLJIT_EQUAL ^ invertcmp, TMP1, 0, SLJIT_IMM, (int)cc[1]);
+ add_jump(compiler, compares > 0 ? list : backtracks, jump);
+ }
+ cc += 2;
+ }
+ }
+
+ cc = ccbegin;
+ }
+
+ if (unicode_status & XCLASS_HAS_BOOL)
+ {
+ OP1(SLJIT_MOV_U16, TMP1, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, bprops));
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BPROPS_MASK);
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 2);
+
+ while (*cc != XCL_END)
+ {
+ if (*cc == XCL_SINGLE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ }
+ else if (*cc == XCL_RANGE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ GETCHARINCTEST(c, cc);
+ }
+ else
+ {
+ SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP);
+ cc++;
+ if (*cc == PT_BOOL)
+ {
+ compares--;
+ invertcmp = (compares == 0 && list != backtracks);
+ if (cc[-1] == XCL_NOTPROP)
+ invertcmp ^= 0x1;
+
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(TMP1), (sljit_sw)(PRIV(ucd_boolprop_sets) + (cc[1] >> 5)), SLJIT_IMM, (sljit_sw)1 << (cc[1] & 0x1f));
+ add_jump(compiler, compares > 0 ? list : backtracks, JUMP(SLJIT_NOT_ZERO ^ invertcmp));
+ }
+ cc += 2;
+ }
+ }
+
+ cc = ccbegin;
+ }
+
+ if (unicode_status & XCLASS_HAS_SCRIPT)
+ {
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, script));
+
+ while (*cc != XCL_END)
+ {
+ if (*cc == XCL_SINGLE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ }
+ else if (*cc == XCL_RANGE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ GETCHARINCTEST(c, cc);
+ }
+ else
+ {
+ SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP);
+ cc++;
+ switch (*cc)
+ {
+ case PT_SCX:
+ if (cc[-1] == XCL_NOTPROP)
+ break;
+ /* Fall through */
+
+ case PT_SC:
+ compares--;
+ invertcmp = (compares == 0 && list != backtracks);
+ if (cc[-1] == XCL_NOTPROP)
+ invertcmp ^= 0x1;
+
+ add_jump(compiler, compares > 0 ? list : backtracks, CMP(SLJIT_EQUAL ^ invertcmp, TMP1, 0, SLJIT_IMM, (int)cc[1]));
+ }
+ cc += 2;
+ }
+ }
+
+ cc = ccbegin;
+ }
+
+ if (unicode_status & XCLASS_HAS_SCRIPT_EXTENSION)
+ {
+ OP1(SLJIT_MOV_U16, TMP1, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, scriptx_bidiclass));
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_SCRIPTX_MASK);
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 2);
+
+ if (unicode_status & XCLASS_SCRIPT_EXTENSION_NOTPROP)
+ {
+ if (unicode_status & XCLASS_HAS_TYPE)
+ {
+ if (unicode_status & XCLASS_SAVE_CHAR)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, TMP2, 0);
+ unicode_status |= XCLASS_SCRIPT_EXTENSION_RESTORE_LOCALS0;
+ }
+ else
+ {
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP2, 0);
+ unicode_status |= XCLASS_SCRIPT_EXTENSION_RESTORE_RETURN_ADDR;
+ }
+ }
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, script));
+ }
+
+ while (*cc != XCL_END)
+ {
+ if (*cc == XCL_SINGLE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ }
+ else if (*cc == XCL_RANGE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ GETCHARINCTEST(c, cc);
+ }
+ else
+ {
+ SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP);
+ cc++;
+ if (*cc == PT_SCX)
+ {
+ compares--;
+ invertcmp = (compares == 0 && list != backtracks);
+
+ jump = NULL;
+ if (cc[-1] == XCL_NOTPROP)
+ {
+ jump = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, (int)cc[1]);
+ if (invertcmp)
+ {
+ add_jump(compiler, backtracks, jump);
+ jump = NULL;
+ }
+ invertcmp ^= 0x1;
+ }
+
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(TMP1), (sljit_sw)(PRIV(ucd_script_sets) + (cc[1] >> 5)), SLJIT_IMM, (sljit_sw)1 << (cc[1] & 0x1f));
+ add_jump(compiler, compares > 0 ? list : backtracks, JUMP(SLJIT_NOT_ZERO ^ invertcmp));
+
+ if (jump != NULL)
+ JUMPHERE(jump);
+ }
+ cc += 2;
+ }
+ }
+
+ if (unicode_status & XCLASS_SCRIPT_EXTENSION_RESTORE_LOCALS0)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ else if (unicode_status & XCLASS_SCRIPT_EXTENSION_RESTORE_RETURN_ADDR)
+ OP1(SLJIT_MOV, TMP2, 0, RETURN_ADDR, 0);
+ cc = ccbegin;
+ }
+
+ if (unicode_status & XCLASS_SAVE_CHAR)
+ OP1(SLJIT_MOV, TMP1, 0, RETURN_ADDR, 0);
+
+ if (unicode_status & XCLASS_HAS_TYPE)
+ {
+ if (unicode_status & XCLASS_SAVE_CHAR)
+ typereg = RETURN_ADDR;
+
+ OP1(SLJIT_MOV_U8, typereg, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, chartype));
+ }
+ }
+#endif /* SUPPORT_UNICODE */
+
+/* Generating code. */
+charoffset = 0;
+numberofcmps = 0;
+#ifdef SUPPORT_UNICODE
+typeoffset = 0;
+#endif /* SUPPORT_UNICODE */
+
+while (*cc != XCL_END)
+ {
+ compares--;
+ invertcmp = (compares == 0 && list != backtracks);
+ jump = NULL;
+
+ if (*cc == XCL_SINGLE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+
+ if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE))
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ numberofcmps++;
+ }
+ else if (numberofcmps > 0)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
+ jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
+ numberofcmps = 0;
+ }
+ else
+ {
+ jump = CMP(SLJIT_EQUAL ^ invertcmp, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ numberofcmps = 0;
+ }
+ }
+ else if (*cc == XCL_RANGE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ SET_CHAR_OFFSET(c);
+ GETCHARINCTEST(c, cc);
+
+ if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE))
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
+ numberofcmps++;
+ }
+ else if (numberofcmps > 0)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL);
+ jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
+ numberofcmps = 0;
+ }
+ else
+ {
+ jump = CMP(SLJIT_LESS_EQUAL ^ invertcmp, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ numberofcmps = 0;
+ }
+ }
+#ifdef SUPPORT_UNICODE
+ else
+ {
+ SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP);
+ if (*cc == XCL_NOTPROP)
+ invertcmp ^= 0x1;
+ cc++;
+ switch(*cc)
+ {
+ case PT_ANY:
+ if (!invertcmp)
+ jump = JUMP(SLJIT_JUMP);
+ break;
+
+ case PT_LAMP:
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, typereg, 0, SLJIT_IMM, ucp_Lu - typeoffset);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, typereg, 0, SLJIT_IMM, ucp_Ll - typeoffset);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, typereg, 0, SLJIT_IMM, ucp_Lt - typeoffset);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
+ jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
+ break;
+
+ case PT_GC:
+ c = PRIV(ucp_typerange)[(int)cc[1] * 2];
+ SET_TYPE_OFFSET(c);
+ jump = CMP(SLJIT_LESS_EQUAL ^ invertcmp, typereg, 0, SLJIT_IMM, PRIV(ucp_typerange)[(int)cc[1] * 2 + 1] - c);
+ break;
+
+ case PT_PC:
+ jump = CMP(SLJIT_EQUAL ^ invertcmp, typereg, 0, SLJIT_IMM, (int)cc[1] - typeoffset);
+ break;
+
+ case PT_SC:
+ case PT_SCX:
+ case PT_BOOL:
+ case PT_BIDICL:
+ compares++;
+ /* Do nothing. */
+ break;
+
+ case PT_SPACE:
+ case PT_PXSPACE:
+ SET_CHAR_OFFSET(9);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, 0xd - 0x9);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x85 - 0x9);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x180e - 0x9);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+
+ SET_TYPE_OFFSET(ucp_Zl);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Zl);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL);
+ jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
+ break;
+
+ case PT_WORD:
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_UNDERSCORE - charoffset));
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+ /* Fall through. */
+
+ case PT_ALNUM:
+ SET_TYPE_OFFSET(ucp_Ll);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, typereg, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
+ OP_FLAGS((*cc == PT_ALNUM) ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
+ SET_TYPE_OFFSET(ucp_Nd);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, typereg, 0, SLJIT_IMM, ucp_No - ucp_Nd);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL);
+ jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
+ break;
+
+ case PT_CLIST:
+ other_cases = PRIV(ucd_caseless_sets) + cc[1];
+
+ /* At least three characters are required.
+ Otherwise this case would be handled by the normal code path. */
+ SLJIT_ASSERT(other_cases[0] != NOTACHAR && other_cases[1] != NOTACHAR && other_cases[2] != NOTACHAR);
+ SLJIT_ASSERT(other_cases[0] < other_cases[1] && other_cases[1] < other_cases[2]);
+
+ /* Optimizing character pairs, if their difference is power of 2. */
+ if (is_powerof2(other_cases[1] ^ other_cases[0]))
+ {
+ if (charoffset == 0)
+ OP2(SLJIT_OR, TMP2, 0, TMP1, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]);
+ else
+ {
+ OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)charoffset);
+ OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]);
+ }
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_IMM, other_cases[1]);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+ other_cases += 2;
+ }
+ else if (is_powerof2(other_cases[2] ^ other_cases[1]))
+ {
+ if (charoffset == 0)
+ OP2(SLJIT_OR, TMP2, 0, TMP1, 0, SLJIT_IMM, other_cases[2] ^ other_cases[1]);
+ else
+ {
+ OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)charoffset);
+ OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]);
+ }
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_IMM, other_cases[2]);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(other_cases[0] - charoffset));
+ OP_FLAGS(SLJIT_OR | ((other_cases[3] == NOTACHAR) ? SLJIT_SET_Z : 0), TMP2, 0, SLJIT_EQUAL);
+
+ other_cases += 3;
+ }
+ else
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset));
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+ }
+
+ while (*other_cases != NOTACHAR)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset));
+ OP_FLAGS(SLJIT_OR | ((*other_cases == NOTACHAR) ? SLJIT_SET_Z : 0), TMP2, 0, SLJIT_EQUAL);
+ }
+ jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
+ break;
+
+ case PT_UCNC:
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_DOLLAR_SIGN - charoffset));
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_COMMERCIAL_AT - charoffset));
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_GRAVE_ACCENT - charoffset));
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+
+ SET_CHAR_OFFSET(0xa0);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, (sljit_sw)(0xd7ff - charoffset));
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
+ SET_CHAR_OFFSET(0);
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xe000 - 0);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_GREATER_EQUAL);
+ jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
+ break;
+
+ case PT_PXGRAPH:
+ /* C and Z groups are the farthest two groups. */
+ SET_TYPE_OFFSET(ucp_Ll);
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER);
+
+ jump = CMP(SLJIT_NOT_EQUAL, typereg, 0, SLJIT_IMM, ucp_Cf - ucp_Ll);
+
+ /* In case of ucp_Cf, we overwrite the result. */
+ SET_CHAR_OFFSET(0x2066);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x180e - 0x2066);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+
+ JUMPHERE(jump);
+ jump = CMP(SLJIT_ZERO ^ invertcmp, TMP2, 0, SLJIT_IMM, 0);
+ break;
+
+ case PT_PXPRINT:
+ /* C and Z groups are the farthest two groups. */
+ SET_TYPE_OFFSET(ucp_Ll);
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER);
+
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Ll);
+ OP_FLAGS(SLJIT_AND, TMP2, 0, SLJIT_NOT_EQUAL);
+
+ jump = CMP(SLJIT_NOT_EQUAL, typereg, 0, SLJIT_IMM, ucp_Cf - ucp_Ll);
+
+ /* In case of ucp_Cf, we overwrite the result. */
+ SET_CHAR_OFFSET(0x2066);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+
+ JUMPHERE(jump);
+ jump = CMP(SLJIT_ZERO ^ invertcmp, TMP2, 0, SLJIT_IMM, 0);
+ break;
+
+ case PT_PXPUNCT:
+ SET_TYPE_OFFSET(ucp_Sc);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, typereg, 0, SLJIT_IMM, ucp_So - ucp_Sc);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+
+ SET_CHAR_OFFSET(0);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, 0x7f);
+ OP_FLAGS(SLJIT_AND, TMP2, 0, SLJIT_LESS_EQUAL);
+
+ SET_TYPE_OFFSET(ucp_Pc);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, typereg, 0, SLJIT_IMM, ucp_Ps - ucp_Pc);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL);
+ jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+ cc += 2;
+ }
+#endif /* SUPPORT_UNICODE */
+
+ if (jump != NULL)
+ add_jump(compiler, compares > 0 ? list : backtracks, jump);
+ }
+
+if (found != NULL)
+ set_jumps(found, LABEL());
+}
+
+#undef SET_TYPE_OFFSET
+#undef SET_CHAR_OFFSET
+
+#endif
+
+static PCRE2_SPTR compile_simple_assertion_matchingpath(compiler_common *common, PCRE2_UCHAR type, PCRE2_SPTR cc, jump_list **backtracks)
+{
+DEFINE_COMPILER;
+int length;
+struct sljit_jump *jump[4];
+#ifdef SUPPORT_UNICODE
+struct sljit_label *label;
+#endif /* SUPPORT_UNICODE */
+
+switch(type)
+ {
+ case OP_SOD:
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin));
+ }
+ else
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, TMP1, 0));
+ return cc;
+
+ case OP_SOM:
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str));
+ }
+ else
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, str));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, TMP1, 0));
+ return cc;
+
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ add_jump(compiler, &common->wordboundary, JUMP(SLJIT_FAST_CALL));
+#ifdef SUPPORT_UNICODE
+ if (common->invalid_utf)
+ {
+ add_jump(compiler, backtracks, CMP((type == OP_NOT_WORD_BOUNDARY) ? SLJIT_NOT_EQUAL : SLJIT_SIG_LESS_EQUAL, TMP2, 0, SLJIT_IMM, 0));
+ return cc;
+ }
+#endif /* SUPPORT_UNICODE */
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
+ add_jump(compiler, backtracks, JUMP(type == OP_NOT_WORD_BOUNDARY ? SLJIT_NOT_ZERO : SLJIT_ZERO));
+ return cc;
+
+ case OP_EODN:
+ /* Requires rather complex checks. */
+ jump[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ if (common->nltype == NLTYPE_FIXED && common->newline > 255)
+ {
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, STR_END, 0));
+ else
+ {
+ jump[1] = CMP(SLJIT_EQUAL, TMP2, 0, STR_END, 0);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP2, 0, STR_END, 0);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_NOT_EQUAL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_EQUAL));
+ check_partial(common, TRUE);
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+ JUMPHERE(jump[1]);
+ }
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff));
+ }
+ else if (common->nltype == NLTYPE_FIXED)
+ {
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, STR_END, 0));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, common->newline));
+ }
+ else
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ jump[1] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR);
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+ OP2U(SLJIT_SUB | SLJIT_SET_Z | SLJIT_SET_GREATER, TMP2, 0, STR_END, 0);
+ jump[2] = JUMP(SLJIT_GREATER);
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_EQUAL) /* LESS */);
+ /* Equal. */
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ jump[3] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+
+ JUMPHERE(jump[1]);
+ if (common->nltype == NLTYPE_ANYCRLF)
+ {
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP2, 0, STR_END, 0));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL));
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0);
+ read_char(common, common->nlmin, common->nlmax, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0));
+ add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
+ add_jump(compiler, backtracks, JUMP(SLJIT_ZERO));
+ OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0);
+ }
+ JUMPHERE(jump[2]);
+ JUMPHERE(jump[3]);
+ }
+ JUMPHERE(jump[0]);
+ if (common->mode != PCRE2_JIT_COMPLETE)
+ check_partial(common, TRUE);
+ return cc;
+
+ case OP_EOD:
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0));
+ if (common->mode != PCRE2_JIT_COMPLETE)
+ check_partial(common, TRUE);
+ return cc;
+
+ case OP_DOLL:
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL);
+ }
+ else
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO));
+
+ if (!common->endonly)
+ compile_simple_assertion_matchingpath(common, OP_EODN, cc, backtracks);
+ else
+ {
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0));
+ check_partial(common, FALSE);
+ }
+ return cc;
+
+ case OP_DOLLM:
+ jump[1] = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0);
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL);
+ }
+ else
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO));
+ check_partial(common, FALSE);
+ jump[0] = JUMP(SLJIT_JUMP);
+ JUMPHERE(jump[1]);
+
+ if (common->nltype == NLTYPE_FIXED && common->newline > 255)
+ {
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER, TMP2, 0, STR_END, 0));
+ else
+ {
+ jump[1] = CMP(SLJIT_LESS_EQUAL, TMP2, 0, STR_END, 0);
+ /* STR_PTR = STR_END - IN_UCHARS(1) */
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff));
+ check_partial(common, TRUE);
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+ JUMPHERE(jump[1]);
+ }
+
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff));
+ }
+ else
+ {
+ peek_char(common, common->nlmax, TMP3, 0, NULL);
+ check_newlinechar(common, common->nltype, backtracks, FALSE);
+ }
+ JUMPHERE(jump[0]);
+ return cc;
+
+ case OP_CIRC:
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin));
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, TMP1, 0));
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO));
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin));
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, TMP1, 0));
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO));
+ }
+ return cc;
+
+ case OP_CIRCM:
+ /* TMP2 might be used by peek_char_back. */
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin));
+ jump[1] = CMP(SLJIT_GREATER, STR_PTR, 0, TMP2, 0);
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin));
+ jump[1] = CMP(SLJIT_GREATER, STR_PTR, 0, TMP2, 0);
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL);
+ }
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO));
+ jump[0] = JUMP(SLJIT_JUMP);
+ JUMPHERE(jump[1]);
+
+ if (!common->alt_circumflex)
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+ if (common->nltype == NLTYPE_FIXED && common->newline > 255)
+ {
+ OP2(SLJIT_SUB, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, TMP2, 0));
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff));
+ }
+ else
+ {
+ peek_char_back(common, common->nlmax, backtracks);
+ check_newlinechar(common, common->nltype, backtracks, FALSE);
+ }
+ JUMPHERE(jump[0]);
+ return cc;
+
+ case OP_REVERSE:
+ length = GET(cc, 0);
+ if (length == 0)
+ return cc + LINK_SIZE;
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin));
+ }
+ else
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin));
+#ifdef SUPPORT_UNICODE
+ if (common->utf)
+ {
+ OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, length);
+ label = LABEL();
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0));
+ move_back(common, backtracks, FALSE);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP3, 0, TMP3, 0, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, label);
+ }
+ else
+#endif
+ {
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(length));
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, STR_PTR, 0, TMP2, 0));
+ }
+ check_start_used_ptr(common);
+ return cc + LINK_SIZE;
+ }
+SLJIT_UNREACHABLE();
+return cc;
+}
+
+#ifdef SUPPORT_UNICODE
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+
+static PCRE2_SPTR SLJIT_FUNC do_extuni_utf(jit_arguments *args, PCRE2_SPTR cc)
+{
+PCRE2_SPTR start_subject = args->begin;
+PCRE2_SPTR end_subject = args->end;
+int lgb, rgb, ricount;
+PCRE2_SPTR prevcc, endcc, bptr;
+BOOL first = TRUE;
+uint32_t c;
+
+prevcc = cc;
+endcc = NULL;
+do
+ {
+ GETCHARINC(c, cc);
+ rgb = UCD_GRAPHBREAK(c);
+
+ if (first)
+ {
+ lgb = rgb;
+ endcc = cc;
+ first = FALSE;
+ continue;
+ }
+
+ if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0)
+ break;
+
+ /* Not breaking between Regional Indicators is allowed only if there
+ are an even number of preceding RIs. */
+
+ if (lgb == ucp_gbRegional_Indicator && rgb == ucp_gbRegional_Indicator)
+ {
+ ricount = 0;
+ bptr = prevcc;
+
+ /* bptr is pointing to the left-hand character */
+ while (bptr > start_subject)
+ {
+ bptr--;
+ BACKCHAR(bptr);
+ GETCHAR(c, bptr);
+
+ if (UCD_GRAPHBREAK(c) != ucp_gbRegional_Indicator)
+ break;
+
+ ricount++;
+ }
+
+ if ((ricount & 1) != 0) break; /* Grapheme break required */
+ }
+
+ /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this
+ allows any number of them before a following Extended_Pictographic. */
+
+ if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) ||
+ lgb != ucp_gbExtended_Pictographic)
+ lgb = rgb;
+
+ prevcc = endcc;
+ endcc = cc;
+ }
+while (cc < end_subject);
+
+return endcc;
+}
+
+#endif /* PCRE2_CODE_UNIT_WIDTH != 32 */
+
+static PCRE2_SPTR SLJIT_FUNC do_extuni_utf_invalid(jit_arguments *args, PCRE2_SPTR cc)
+{
+PCRE2_SPTR start_subject = args->begin;
+PCRE2_SPTR end_subject = args->end;
+int lgb, rgb, ricount;
+PCRE2_SPTR prevcc, endcc, bptr;
+BOOL first = TRUE;
+uint32_t c;
+
+prevcc = cc;
+endcc = NULL;
+do
+ {
+ GETCHARINC_INVALID(c, cc, end_subject, break);
+ rgb = UCD_GRAPHBREAK(c);
+
+ if (first)
+ {
+ lgb = rgb;
+ endcc = cc;
+ first = FALSE;
+ continue;
+ }
+
+ if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0)
+ break;
+
+ /* Not breaking between Regional Indicators is allowed only if there
+ are an even number of preceding RIs. */
+
+ if (lgb == ucp_gbRegional_Indicator && rgb == ucp_gbRegional_Indicator)
+ {
+ ricount = 0;
+ bptr = prevcc;
+
+ /* bptr is pointing to the left-hand character */
+ while (bptr > start_subject)
+ {
+ GETCHARBACK_INVALID(c, bptr, start_subject, break);
+
+ if (UCD_GRAPHBREAK(c) != ucp_gbRegional_Indicator)
+ break;
+
+ ricount++;
+ }
+
+ if ((ricount & 1) != 0)
+ break; /* Grapheme break required */
+ }
+
+ /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this
+ allows any number of them before a following Extended_Pictographic. */
+
+ if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) ||
+ lgb != ucp_gbExtended_Pictographic)
+ lgb = rgb;
+
+ prevcc = endcc;
+ endcc = cc;
+ }
+while (cc < end_subject);
+
+return endcc;
+}
+
+static PCRE2_SPTR SLJIT_FUNC do_extuni_no_utf(jit_arguments *args, PCRE2_SPTR cc)
+{
+PCRE2_SPTR start_subject = args->begin;
+PCRE2_SPTR end_subject = args->end;
+int lgb, rgb, ricount;
+PCRE2_SPTR bptr;
+uint32_t c;
+
+/* Patch by PH */
+/* GETCHARINC(c, cc); */
+c = *cc++;
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+if (c >= 0x110000)
+ return NULL;
+#endif /* PCRE2_CODE_UNIT_WIDTH == 32 */
+lgb = UCD_GRAPHBREAK(c);
+
+while (cc < end_subject)
+ {
+ c = *cc;
+#if PCRE2_CODE_UNIT_WIDTH == 32
+ if (c >= 0x110000)
+ break;
+#endif /* PCRE2_CODE_UNIT_WIDTH == 32 */
+ rgb = UCD_GRAPHBREAK(c);
+
+ if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0)
+ break;
+
+ /* Not breaking between Regional Indicators is allowed only if there
+ are an even number of preceding RIs. */
+
+ if (lgb == ucp_gbRegional_Indicator && rgb == ucp_gbRegional_Indicator)
+ {
+ ricount = 0;
+ bptr = cc - 1;
+
+ /* bptr is pointing to the left-hand character */
+ while (bptr > start_subject)
+ {
+ bptr--;
+ c = *bptr;
+#if PCRE2_CODE_UNIT_WIDTH == 32
+ if (c >= 0x110000)
+ break;
+#endif /* PCRE2_CODE_UNIT_WIDTH == 32 */
+
+ if (UCD_GRAPHBREAK(c) != ucp_gbRegional_Indicator) break;
+
+ ricount++;
+ }
+
+ if ((ricount & 1) != 0)
+ break; /* Grapheme break required */
+ }
+
+ /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this
+ allows any number of them before a following Extended_Pictographic. */
+
+ if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) ||
+ lgb != ucp_gbExtended_Pictographic)
+ lgb = rgb;
+
+ cc++;
+ }
+
+return cc;
+}
+
+#endif /* SUPPORT_UNICODE */
+
+static PCRE2_SPTR compile_char1_matchingpath(compiler_common *common, PCRE2_UCHAR type, PCRE2_SPTR cc, jump_list **backtracks, BOOL check_str_ptr)
+{
+DEFINE_COMPILER;
+int length;
+unsigned int c, oc, bit;
+compare_context context;
+struct sljit_jump *jump[3];
+jump_list *end_list;
+#ifdef SUPPORT_UNICODE
+PCRE2_UCHAR propdata[5];
+#endif /* SUPPORT_UNICODE */
+
+switch(type)
+ {
+ case OP_NOT_DIGIT:
+ case OP_DIGIT:
+ /* Digits are usually 0-9, so it is worth to optimize them. */
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (common->utf && is_char7_bitset((const sljit_u8*)common->ctypes - cbit_length + cbit_digit, FALSE))
+ read_char7_type(common, backtracks, type == OP_NOT_DIGIT);
+ else
+#endif
+ read_char8_type(common, backtracks, type == OP_NOT_DIGIT);
+ /* Flip the starting bit in the negative case. */
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, ctype_digit);
+ add_jump(compiler, backtracks, JUMP(type == OP_DIGIT ? SLJIT_ZERO : SLJIT_NOT_ZERO));
+ return cc;
+
+ case OP_NOT_WHITESPACE:
+ case OP_WHITESPACE:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (common->utf && is_char7_bitset((const sljit_u8*)common->ctypes - cbit_length + cbit_space, FALSE))
+ read_char7_type(common, backtracks, type == OP_NOT_WHITESPACE);
+ else
+#endif
+ read_char8_type(common, backtracks, type == OP_NOT_WHITESPACE);
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, ctype_space);
+ add_jump(compiler, backtracks, JUMP(type == OP_WHITESPACE ? SLJIT_ZERO : SLJIT_NOT_ZERO));
+ return cc;
+
+ case OP_NOT_WORDCHAR:
+ case OP_WORDCHAR:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (common->utf && is_char7_bitset((const sljit_u8*)common->ctypes - cbit_length + cbit_word, FALSE))
+ read_char7_type(common, backtracks, type == OP_NOT_WORDCHAR);
+ else
+#endif
+ read_char8_type(common, backtracks, type == OP_NOT_WORDCHAR);
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, ctype_word);
+ add_jump(compiler, backtracks, JUMP(type == OP_WORDCHAR ? SLJIT_ZERO : SLJIT_NOT_ZERO));
+ return cc;
+
+ case OP_ANY:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+ read_char(common, common->nlmin, common->nlmax, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ if (common->nltype == NLTYPE_FIXED && common->newline > 255)
+ {
+ jump[0] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff);
+ end_list = NULL;
+ if (common->mode != PCRE2_JIT_PARTIAL_HARD)
+ add_jump(compiler, &end_list, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+ else
+ check_str_end(common, &end_list);
+
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, common->newline & 0xff));
+ set_jumps(end_list, LABEL());
+ JUMPHERE(jump[0]);
+ }
+ else
+ check_newlinechar(common, common->nltype, backtracks, TRUE);
+ return cc;
+
+ case OP_ALLANY:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+#ifdef SUPPORT_UNICODE
+ if (common->utf)
+ {
+ if (common->invalid_utf)
+ {
+ read_char(common, 0, READ_CHAR_MAX, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ return cc;
+ }
+
+#if PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+ jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800);
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0xd800);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ JUMPHERE(jump[0]);
+ return cc;
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16] */
+ }
+#endif /* SUPPORT_UNICODE */
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ return cc;
+
+ case OP_ANYBYTE:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ return cc;
+
+#ifdef SUPPORT_UNICODE
+ case OP_NOTPROP:
+ case OP_PROP:
+ propdata[0] = XCL_HASPROP;
+ propdata[1] = type == OP_NOTPROP ? XCL_NOTPROP : XCL_PROP;
+ propdata[2] = cc[0];
+ propdata[3] = cc[1];
+ propdata[4] = XCL_END;
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+ compile_xclass_matchingpath(common, propdata, backtracks);
+ return cc + 2;
+#endif
+
+ case OP_ANYNL:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+ read_char(common, common->bsr_nlmin, common->bsr_nlmax, NULL, 0);
+ jump[0] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR);
+ /* We don't need to handle soft partial matching case. */
+ end_list = NULL;
+ if (common->mode != PCRE2_JIT_PARTIAL_HARD)
+ add_jump(compiler, &end_list, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+ else
+ check_str_end(common, &end_list);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ jump[1] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ jump[2] = JUMP(SLJIT_JUMP);
+ JUMPHERE(jump[0]);
+ check_newlinechar(common, common->bsr_nltype, backtracks, FALSE);
+ set_jumps(end_list, LABEL());
+ JUMPHERE(jump[1]);
+ JUMPHERE(jump[2]);
+ return cc;
+
+ case OP_NOT_HSPACE:
+ case OP_HSPACE:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+
+ if (type == OP_NOT_HSPACE)
+ read_char(common, 0x9, 0x3000, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ else
+ read_char(common, 0x9, 0x3000, NULL, 0);
+
+ add_jump(compiler, &common->hspace, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
+ add_jump(compiler, backtracks, JUMP(type == OP_NOT_HSPACE ? SLJIT_NOT_ZERO : SLJIT_ZERO));
+ return cc;
+
+ case OP_NOT_VSPACE:
+ case OP_VSPACE:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+
+ if (type == OP_NOT_VSPACE)
+ read_char(common, 0xa, 0x2029, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ else
+ read_char(common, 0xa, 0x2029, NULL, 0);
+
+ add_jump(compiler, &common->vspace, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
+ add_jump(compiler, backtracks, JUMP(type == OP_NOT_VSPACE ? SLJIT_NOT_ZERO : SLJIT_ZERO));
+ return cc;
+
+#ifdef SUPPORT_UNICODE
+ case OP_EXTUNI:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+
+ SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1);
+ OP1(SLJIT_MOV, SLJIT_R0, 0, ARGUMENTS, 0);
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2(W, W, W), SLJIT_IMM,
+ common->utf ? (common->invalid_utf ? SLJIT_FUNC_ADDR(do_extuni_utf_invalid) : SLJIT_FUNC_ADDR(do_extuni_utf)) : SLJIT_FUNC_ADDR(do_extuni_no_utf));
+ if (common->invalid_utf)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0));
+#else
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2(W, W, W), SLJIT_IMM,
+ common->invalid_utf ? SLJIT_FUNC_ADDR(do_extuni_utf_invalid) : SLJIT_FUNC_ADDR(do_extuni_no_utf));
+ if (!common->utf || common->invalid_utf)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0));
+#endif
+
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0);
+
+ if (common->mode == PCRE2_JIT_PARTIAL_HARD)
+ {
+ jump[0] = CMP(SLJIT_LESS, SLJIT_RETURN_REG, 0, STR_END, 0);
+ /* Since we successfully read a char above, partial matching must occure. */
+ check_partial(common, TRUE);
+ JUMPHERE(jump[0]);
+ }
+ return cc;
+#endif
+
+ case OP_CHAR:
+ case OP_CHARI:
+ length = 1;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(*cc)) length += GET_EXTRALEN(*cc);
+#endif
+
+ if (check_str_ptr && common->mode != PCRE2_JIT_COMPLETE)
+ detect_partial_match(common, backtracks);
+
+ if (type == OP_CHAR || !char_has_othercase(common, cc) || char_get_othercase_bit(common, cc) != 0)
+ {
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(length));
+ if (length > 1 || (check_str_ptr && common->mode == PCRE2_JIT_COMPLETE))
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0));
+
+ context.length = IN_UCHARS(length);
+ context.sourcereg = -1;
+#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED
+ context.ucharptr = 0;
+#endif
+ return byte_sequence_compare(common, type == OP_CHARI, cc, &context, backtracks);
+ }
+
+#ifdef SUPPORT_UNICODE
+ if (common->utf)
+ {
+ GETCHAR(c, cc);
+ }
+ else
+#endif
+ c = *cc;
+
+ SLJIT_ASSERT(type == OP_CHARI && char_has_othercase(common, cc));
+
+ if (check_str_ptr && common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+ oc = char_othercase(common, c);
+ read_char(common, c < oc ? c : oc, c > oc ? c : oc, NULL, 0);
+
+ SLJIT_ASSERT(!is_powerof2(c ^ oc));
+
+ if (sljit_has_cpu_feature(SLJIT_HAS_CMOV))
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, oc);
+ CMOV(SLJIT_EQUAL, TMP1, SLJIT_IMM, c);
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, c));
+ }
+ else
+ {
+ jump[0] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, c);
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, oc));
+ JUMPHERE(jump[0]);
+ }
+ return cc + length;
+
+ case OP_NOT:
+ case OP_NOTI:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+
+ length = 1;
+#ifdef SUPPORT_UNICODE
+ if (common->utf)
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ c = *cc;
+ if (c < 128 && !common->invalid_utf)
+ {
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ if (type == OP_NOT || !char_has_othercase(common, cc))
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, c));
+ else
+ {
+ /* Since UTF8 code page is fixed, we know that c is in [a-z] or [A-Z] range. */
+ OP2(SLJIT_OR, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x20);
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, c | 0x20));
+ }
+ /* Skip the variable-length character. */
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+ JUMPHERE(jump[0]);
+ return cc + 1;
+ }
+ else
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ {
+ GETCHARLEN(c, cc, length);
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+ c = *cc;
+
+ if (type == OP_NOT || !char_has_othercase(common, cc))
+ {
+ read_char(common, c, c, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, c));
+ }
+ else
+ {
+ oc = char_othercase(common, c);
+ read_char(common, c < oc ? c : oc, c > oc ? c : oc, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ bit = c ^ oc;
+ if (is_powerof2(bit))
+ {
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, bit);
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, c | bit));
+ }
+ else
+ {
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, c));
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, oc));
+ }
+ }
+ return cc + length;
+
+ case OP_CLASS:
+ case OP_NCLASS:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ bit = (common->utf && is_char7_bitset((const sljit_u8 *)cc, type == OP_NCLASS)) ? 127 : 255;
+ if (type == OP_NCLASS)
+ read_char(common, 0, bit, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ else
+ read_char(common, 0, bit, NULL, 0);
+#else
+ if (type == OP_NCLASS)
+ read_char(common, 0, 255, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ else
+ read_char(common, 0, 255, NULL, 0);
+#endif
+
+ if (optimize_class(common, (const sljit_u8 *)cc, type == OP_NCLASS, FALSE, backtracks))
+ return cc + 32 / sizeof(PCRE2_UCHAR);
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ jump[0] = NULL;
+ if (common->utf)
+ {
+ jump[0] = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, bit);
+ if (type == OP_CLASS)
+ {
+ add_jump(compiler, backtracks, jump[0]);
+ jump[0] = NULL;
+ }
+ }
+#elif PCRE2_CODE_UNIT_WIDTH != 8
+ jump[0] = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
+ if (type == OP_CLASS)
+ {
+ add_jump(compiler, backtracks, jump[0]);
+ jump[0] = NULL;
+ }
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 */
+
+ OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7);
+ OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
+ OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, TMP2, 0);
+ add_jump(compiler, backtracks, JUMP(SLJIT_ZERO));
+
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ if (jump[0] != NULL)
+ JUMPHERE(jump[0]);
+#endif
+ return cc + 32 / sizeof(PCRE2_UCHAR);
+
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ case OP_XCLASS:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+ compile_xclass_matchingpath(common, cc + LINK_SIZE, backtracks);
+ return cc + GET(cc, 0) - 1;
+#endif
+ }
+SLJIT_UNREACHABLE();
+return cc;
+}
+
+static SLJIT_INLINE PCRE2_SPTR compile_charn_matchingpath(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, jump_list **backtracks)
+{
+/* This function consumes at least one input character. */
+/* To decrease the number of length checks, we try to concatenate the fixed length character sequences. */
+DEFINE_COMPILER;
+PCRE2_SPTR ccbegin = cc;
+compare_context context;
+int size;
+
+context.length = 0;
+do
+ {
+ if (cc >= ccend)
+ break;
+
+ if (*cc == OP_CHAR)
+ {
+ size = 1;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[1]))
+ size += GET_EXTRALEN(cc[1]);
+#endif
+ }
+ else if (*cc == OP_CHARI)
+ {
+ size = 1;
+#ifdef SUPPORT_UNICODE
+ if (common->utf)
+ {
+ if (char_has_othercase(common, cc + 1) && char_get_othercase_bit(common, cc + 1) == 0)
+ size = 0;
+ else if (HAS_EXTRALEN(cc[1]))
+ size += GET_EXTRALEN(cc[1]);
+ }
+ else
+#endif
+ if (char_has_othercase(common, cc + 1) && char_get_othercase_bit(common, cc + 1) == 0)
+ size = 0;
+ }
+ else
+ size = 0;
+
+ cc += 1 + size;
+ context.length += IN_UCHARS(size);
+ }
+while (size > 0 && context.length <= 128);
+
+cc = ccbegin;
+if (context.length > 0)
+ {
+ /* We have a fixed-length byte sequence. */
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, context.length);
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0));
+
+ context.sourcereg = -1;
+#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED
+ context.ucharptr = 0;
+#endif
+ do cc = byte_sequence_compare(common, *cc == OP_CHARI, cc + 1, &context, backtracks); while (context.length > 0);
+ return cc;
+ }
+
+/* A non-fixed length character will be checked if length == 0. */
+return compile_char1_matchingpath(common, *cc, cc + 1, backtracks, TRUE);
+}
+
+/* Forward definitions. */
+static void compile_matchingpath(compiler_common *, PCRE2_SPTR, PCRE2_SPTR, backtrack_common *);
+static void compile_backtrackingpath(compiler_common *, struct backtrack_common *);
+
+#define PUSH_BACKTRACK(size, ccstart, error) \
+ do \
+ { \
+ backtrack = sljit_alloc_memory(compiler, (size)); \
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) \
+ return error; \
+ memset(backtrack, 0, size); \
+ backtrack->prev = parent->top; \
+ backtrack->cc = (ccstart); \
+ parent->top = backtrack; \
+ } \
+ while (0)
+
+#define PUSH_BACKTRACK_NOVALUE(size, ccstart) \
+ do \
+ { \
+ backtrack = sljit_alloc_memory(compiler, (size)); \
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) \
+ return; \
+ memset(backtrack, 0, size); \
+ backtrack->prev = parent->top; \
+ backtrack->cc = (ccstart); \
+ parent->top = backtrack; \
+ } \
+ while (0)
+
+#define BACKTRACK_AS(type) ((type *)backtrack)
+
+static void compile_dnref_search(compiler_common *common, PCRE2_SPTR cc, jump_list **backtracks)
+{
+/* The OVECTOR offset goes to TMP2. */
+DEFINE_COMPILER;
+int count = GET2(cc, 1 + IMM2_SIZE);
+PCRE2_SPTR slot = common->name_table + GET2(cc, 1) * common->name_entry_size;
+unsigned int offset;
+jump_list *found = NULL;
+
+SLJIT_ASSERT(*cc == OP_DNREF || *cc == OP_DNREFI);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1));
+
+count--;
+while (count-- > 0)
+ {
+ offset = GET2(slot, 0) << 1;
+ GET_LOCAL_BASE(TMP2, 0, OVECTOR(offset));
+ add_jump(compiler, &found, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0));
+ slot += common->name_entry_size;
+ }
+
+offset = GET2(slot, 0) << 1;
+GET_LOCAL_BASE(TMP2, 0, OVECTOR(offset));
+if (backtracks != NULL && !common->unset_backref)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0));
+
+set_jumps(found, LABEL());
+}
+
+static void compile_ref_matchingpath(compiler_common *common, PCRE2_SPTR cc, jump_list **backtracks, BOOL withchecks, BOOL emptyfail)
+{
+DEFINE_COMPILER;
+BOOL ref = (*cc == OP_REF || *cc == OP_REFI);
+int offset = 0;
+struct sljit_jump *jump = NULL;
+struct sljit_jump *partial;
+struct sljit_jump *nopartial;
+#if defined SUPPORT_UNICODE
+struct sljit_label *loop;
+struct sljit_label *caseless_loop;
+jump_list *no_match = NULL;
+int source_reg = COUNT_MATCH;
+int source_end_reg = ARGUMENTS;
+int char1_reg = STACK_LIMIT;
+#endif /* SUPPORT_UNICODE */
+
+if (ref)
+ {
+ offset = GET2(cc, 1) << 1;
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset));
+ /* OVECTOR(1) contains the "string begin - 1" constant. */
+ if (withchecks && !common->unset_backref)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1)));
+ }
+else
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), 0);
+
+#if defined SUPPORT_UNICODE
+if (common->utf && *cc == OP_REFI)
+ {
+ SLJIT_ASSERT(common->iref_ptr != 0);
+
+ if (ref)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
+ else
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw));
+
+ if (withchecks && emptyfail)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, TMP2, 0));
+
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->iref_ptr, source_reg, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw), source_end_reg, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw) * 2, char1_reg, 0);
+
+ OP1(SLJIT_MOV, source_reg, 0, TMP1, 0);
+ OP1(SLJIT_MOV, source_end_reg, 0, TMP2, 0);
+
+ loop = LABEL();
+ jump = CMP(SLJIT_GREATER_EQUAL, source_reg, 0, source_end_reg, 0);
+ partial = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+
+ /* Read original character. It must be a valid UTF character. */
+ OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0);
+ OP1(SLJIT_MOV, STR_PTR, 0, source_reg, 0);
+
+ read_char(common, 0, READ_CHAR_MAX, NULL, READ_CHAR_UPDATE_STR_PTR | READ_CHAR_VALID_UTF);
+
+ OP1(SLJIT_MOV, source_reg, 0, STR_PTR, 0);
+ OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0);
+ OP1(SLJIT_MOV, char1_reg, 0, TMP1, 0);
+
+ /* Read second character. */
+ read_char(common, 0, READ_CHAR_MAX, &no_match, READ_CHAR_UPDATE_STR_PTR);
+
+ CMPTO(SLJIT_EQUAL, TMP1, 0, char1_reg, 0, loop);
+
+ OP1(SLJIT_MOV, TMP3, 0, TMP1, 0);
+
+ add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL));
+
+ OP2(SLJIT_SHL, TMP1, 0, TMP2, 0, SLJIT_IMM, 2);
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 3);
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0);
+
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records));
+
+ OP1(SLJIT_MOV_S32, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(ucd_record, other_case));
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(ucd_record, caseset));
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP3, 0);
+ CMPTO(SLJIT_EQUAL, TMP1, 0, char1_reg, 0, loop);
+
+ add_jump(compiler, &no_match, CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, 0));
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 2);
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_caseless_sets));
+
+ caseless_loop = LABEL();
+ OP1(SLJIT_MOV_U32, TMP1, 0, SLJIT_MEM1(TMP2), 0);
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, sizeof(uint32_t));
+ OP2U(SLJIT_SUB | SLJIT_SET_Z | SLJIT_SET_LESS, TMP1, 0, char1_reg, 0);
+ JUMPTO(SLJIT_EQUAL, loop);
+ JUMPTO(SLJIT_LESS, caseless_loop);
+
+ set_jumps(no_match, LABEL());
+ if (common->mode == PCRE2_JIT_COMPLETE)
+ JUMPHERE(partial);
+
+ OP1(SLJIT_MOV, source_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr);
+ OP1(SLJIT_MOV, source_end_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw));
+ OP1(SLJIT_MOV, char1_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw) * 2);
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+
+ if (common->mode != PCRE2_JIT_COMPLETE)
+ {
+ JUMPHERE(partial);
+ OP1(SLJIT_MOV, source_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr);
+ OP1(SLJIT_MOV, source_end_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw));
+ OP1(SLJIT_MOV, char1_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw) * 2);
+
+ check_partial(common, FALSE);
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+ }
+
+ JUMPHERE(jump);
+ OP1(SLJIT_MOV, source_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr);
+ OP1(SLJIT_MOV, source_end_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw));
+ OP1(SLJIT_MOV, char1_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw) * 2);
+ return;
+ }
+else
+#endif /* SUPPORT_UNICODE */
+ {
+ if (ref)
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP1, 0);
+ else
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP1, 0);
+
+ if (withchecks)
+ jump = JUMP(SLJIT_ZERO);
+
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+ partial = CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0);
+ if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, backtracks, partial);
+
+ add_jump(compiler, *cc == OP_REF ? &common->casefulcmp : &common->caselesscmp, JUMP(SLJIT_FAST_CALL));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0));
+
+ if (common->mode != PCRE2_JIT_COMPLETE)
+ {
+ nopartial = JUMP(SLJIT_JUMP);
+ JUMPHERE(partial);
+ /* TMP2 -= STR_END - STR_PTR */
+ OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, STR_PTR, 0);
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, STR_END, 0);
+ partial = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, 0);
+ OP1(SLJIT_MOV, STR_PTR, 0, STR_END, 0);
+ add_jump(compiler, *cc == OP_REF ? &common->casefulcmp : &common->caselesscmp, JUMP(SLJIT_FAST_CALL));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0));
+ JUMPHERE(partial);
+ check_partial(common, FALSE);
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+ JUMPHERE(nopartial);
+ }
+ }
+
+if (jump != NULL)
+ {
+ if (emptyfail)
+ add_jump(compiler, backtracks, jump);
+ else
+ JUMPHERE(jump);
+ }
+}
+
+static SLJIT_INLINE PCRE2_SPTR compile_ref_iterator_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+BOOL ref = (*cc == OP_REF || *cc == OP_REFI);
+backtrack_common *backtrack;
+PCRE2_UCHAR type;
+int offset = 0;
+struct sljit_label *label;
+struct sljit_jump *zerolength;
+struct sljit_jump *jump = NULL;
+PCRE2_SPTR ccbegin = cc;
+int min = 0, max = 0;
+BOOL minimize;
+
+PUSH_BACKTRACK(sizeof(ref_iterator_backtrack), cc, NULL);
+
+if (ref)
+ offset = GET2(cc, 1) << 1;
+else
+ cc += IMM2_SIZE;
+type = cc[1 + IMM2_SIZE];
+
+SLJIT_COMPILE_ASSERT((OP_CRSTAR & 0x1) == 0, crstar_opcode_must_be_even);
+minimize = (type & 0x1) != 0;
+switch(type)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ min = 0;
+ max = 0;
+ cc += 1 + IMM2_SIZE + 1;
+ break;
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ min = 1;
+ max = 0;
+ cc += 1 + IMM2_SIZE + 1;
+ break;
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ min = 0;
+ max = 1;
+ cc += 1 + IMM2_SIZE + 1;
+ break;
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ min = GET2(cc, 1 + IMM2_SIZE + 1);
+ max = GET2(cc, 1 + IMM2_SIZE + 1 + IMM2_SIZE);
+ cc += 1 + IMM2_SIZE + 1 + 2 * IMM2_SIZE;
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+if (!minimize)
+ {
+ if (min == 0)
+ {
+ allocate_stack(common, 2);
+ if (ref)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0);
+ /* Temporary release of STR_PTR. */
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ /* Handles both invalid and empty cases. Since the minimum repeat,
+ is zero the invalid case is basically the same as an empty case. */
+ if (ref)
+ zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
+ else
+ {
+ compile_dnref_search(common, ccbegin, NULL);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, TMP2, 0);
+ zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw));
+ }
+ /* Restore if not zero length. */
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ }
+ else
+ {
+ allocate_stack(common, 1);
+ if (ref)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ if (ref)
+ {
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1)));
+ zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
+ }
+ else
+ {
+ compile_dnref_search(common, ccbegin, &backtrack->topbacktracks);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, TMP2, 0);
+ zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw));
+ }
+ }
+
+ if (min > 1 || max > 1)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, SLJIT_IMM, 0);
+
+ label = LABEL();
+ if (!ref)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1);
+ compile_ref_matchingpath(common, ccbegin, &backtrack->topbacktracks, FALSE, FALSE);
+
+ if (min > 1 || max > 1)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, TMP1, 0);
+ if (min > 1)
+ CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, min, label);
+ if (max > 1)
+ {
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, max);
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, label);
+ JUMPHERE(jump);
+ }
+ }
+
+ if (max == 0)
+ {
+ /* Includes min > 1 case as well. */
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, label);
+ }
+
+ JUMPHERE(zerolength);
+ BACKTRACK_AS(ref_iterator_backtrack)->matchingpath = LABEL();
+
+ count_match(common);
+ return cc;
+ }
+
+allocate_stack(common, ref ? 2 : 3);
+if (ref)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset));
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+if (type != OP_CRMINSTAR)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0);
+
+if (min == 0)
+ {
+ /* Handles both invalid and empty cases. Since the minimum repeat,
+ is zero the invalid case is basically the same as an empty case. */
+ if (ref)
+ zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
+ else
+ {
+ compile_dnref_search(common, ccbegin, NULL);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), TMP2, 0);
+ zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw));
+ }
+ /* Length is non-zero, we can match real repeats. */
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ jump = JUMP(SLJIT_JUMP);
+ }
+else
+ {
+ if (ref)
+ {
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1)));
+ zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
+ }
+ else
+ {
+ compile_dnref_search(common, ccbegin, &backtrack->topbacktracks);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), TMP2, 0);
+ zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw));
+ }
+ }
+
+BACKTRACK_AS(ref_iterator_backtrack)->matchingpath = LABEL();
+if (max > 0)
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, max));
+
+if (!ref)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(2));
+compile_ref_matchingpath(common, ccbegin, &backtrack->topbacktracks, TRUE, TRUE);
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+
+if (min > 1)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0);
+ CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, min, BACKTRACK_AS(ref_iterator_backtrack)->matchingpath);
+ }
+else if (max > 0)
+ OP2(SLJIT_ADD, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 1);
+
+if (jump != NULL)
+ JUMPHERE(jump);
+JUMPHERE(zerolength);
+
+count_match(common);
+return cc;
+}
+
+static SLJIT_INLINE PCRE2_SPTR compile_recurse_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+recurse_entry *entry = common->entries;
+recurse_entry *prev = NULL;
+sljit_sw start = GET(cc, 1);
+PCRE2_SPTR start_cc;
+BOOL needs_control_head;
+
+PUSH_BACKTRACK(sizeof(recurse_backtrack), cc, NULL);
+
+/* Inlining simple patterns. */
+if (get_framesize(common, common->start + start, NULL, TRUE, &needs_control_head) == no_stack)
+ {
+ start_cc = common->start + start;
+ compile_matchingpath(common, next_opcode(common, start_cc), bracketend(start_cc) - (1 + LINK_SIZE), backtrack);
+ BACKTRACK_AS(recurse_backtrack)->inlined_pattern = TRUE;
+ return cc + 1 + LINK_SIZE;
+ }
+
+while (entry != NULL)
+ {
+ if (entry->start == start)
+ break;
+ prev = entry;
+ entry = entry->next;
+ }
+
+if (entry == NULL)
+ {
+ entry = sljit_alloc_memory(compiler, sizeof(recurse_entry));
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return NULL;
+ entry->next = NULL;
+ entry->entry_label = NULL;
+ entry->backtrack_label = NULL;
+ entry->entry_calls = NULL;
+ entry->backtrack_calls = NULL;
+ entry->start = start;
+
+ if (prev != NULL)
+ prev->next = entry;
+ else
+ common->entries = entry;
+ }
+
+BACKTRACK_AS(recurse_backtrack)->entry = entry;
+
+if (entry->entry_label == NULL)
+ add_jump(compiler, &entry->entry_calls, JUMP(SLJIT_FAST_CALL));
+else
+ JUMPTO(SLJIT_FAST_CALL, entry->entry_label);
+/* Leave if the match is failed. */
+add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0));
+BACKTRACK_AS(recurse_backtrack)->matchingpath = LABEL();
+return cc + 1 + LINK_SIZE;
+}
+
+static sljit_s32 SLJIT_FUNC do_callout_jit(struct jit_arguments *arguments, pcre2_callout_block *callout_block, PCRE2_SPTR *jit_ovector)
+{
+PCRE2_SPTR begin;
+PCRE2_SIZE *ovector;
+sljit_u32 oveccount, capture_top;
+
+if (arguments->callout == NULL)
+ return 0;
+
+SLJIT_COMPILE_ASSERT(sizeof (PCRE2_SIZE) <= sizeof (sljit_sw), pcre2_size_must_be_lower_than_sljit_sw_size);
+
+begin = arguments->begin;
+ovector = (PCRE2_SIZE*)(callout_block + 1);
+oveccount = callout_block->capture_top;
+
+SLJIT_ASSERT(oveccount >= 1);
+
+callout_block->version = 2;
+callout_block->callout_flags = 0;
+
+/* Offsets in subject. */
+callout_block->subject_length = arguments->end - arguments->begin;
+callout_block->start_match = jit_ovector[0] - begin;
+callout_block->current_position = (PCRE2_SPTR)callout_block->offset_vector - begin;
+callout_block->subject = begin;
+
+/* Convert and copy the JIT offset vector to the ovector array. */
+callout_block->capture_top = 1;
+callout_block->offset_vector = ovector;
+
+ovector[0] = PCRE2_UNSET;
+ovector[1] = PCRE2_UNSET;
+ovector += 2;
+jit_ovector += 2;
+capture_top = 1;
+
+/* Convert pointers to sizes. */
+while (--oveccount != 0)
+ {
+ capture_top++;
+
+ ovector[0] = (PCRE2_SIZE)(jit_ovector[0] - begin);
+ ovector[1] = (PCRE2_SIZE)(jit_ovector[1] - begin);
+
+ if (ovector[0] != PCRE2_UNSET)
+ callout_block->capture_top = capture_top;
+
+ ovector += 2;
+ jit_ovector += 2;
+ }
+
+return (arguments->callout)(callout_block, arguments->callout_data);
+}
+
+#define CALLOUT_ARG_OFFSET(arg) \
+ SLJIT_OFFSETOF(pcre2_callout_block, arg)
+
+static SLJIT_INLINE PCRE2_SPTR compile_callout_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+sljit_s32 mov_opcode;
+unsigned int callout_length = (*cc == OP_CALLOUT)
+ ? PRIV(OP_lengths)[OP_CALLOUT] : GET(cc, 1 + 2 * LINK_SIZE);
+sljit_sw value1;
+sljit_sw value2;
+sljit_sw value3;
+sljit_uw callout_arg_size = (common->re->top_bracket + 1) * 2 * SSIZE_OF(sw);
+
+PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL);
+
+callout_arg_size = (sizeof(pcre2_callout_block) + callout_arg_size + sizeof(sljit_sw) - 1) / sizeof(sljit_sw);
+
+allocate_stack(common, callout_arg_size);
+
+SLJIT_ASSERT(common->capture_last_ptr != 0);
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr);
+OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+value1 = (*cc == OP_CALLOUT) ? cc[1 + 2 * LINK_SIZE] : 0;
+OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_number), SLJIT_IMM, value1);
+OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(capture_last), TMP2, 0);
+OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(capture_top), SLJIT_IMM, common->re->top_bracket + 1);
+
+/* These pointer sized fields temporarly stores internal variables. */
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(offset_vector), STR_PTR, 0);
+
+if (common->mark_ptr != 0)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, mark_ptr));
+mov_opcode = (sizeof(PCRE2_SIZE) == 4) ? SLJIT_MOV_U32 : SLJIT_MOV;
+OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(pattern_position), SLJIT_IMM, GET(cc, 1));
+OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(next_item_length), SLJIT_IMM, GET(cc, 1 + LINK_SIZE));
+
+if (*cc == OP_CALLOUT)
+ {
+ value1 = 0;
+ value2 = 0;
+ value3 = 0;
+ }
+else
+ {
+ value1 = (sljit_sw) (cc + (1 + 4*LINK_SIZE) + 1);
+ value2 = (callout_length - (1 + 4*LINK_SIZE + 2));
+ value3 = (sljit_sw) (GET(cc, 1 + 3*LINK_SIZE));
+ }
+
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_string), SLJIT_IMM, value1);
+OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_string_length), SLJIT_IMM, value2);
+OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_string_offset), SLJIT_IMM, value3);
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(mark), (common->mark_ptr != 0) ? TMP2 : SLJIT_IMM, 0);
+
+SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1);
+
+/* Needed to save important temporary registers. */
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STR_PTR, 0);
+/* SLJIT_R0 = arguments */
+OP1(SLJIT_MOV, SLJIT_R1, 0, STACK_TOP, 0);
+GET_LOCAL_BASE(SLJIT_R2, 0, OVECTOR_START);
+sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS3(32, W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(do_callout_jit));
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+free_stack(common, callout_arg_size);
+
+/* Check return value. */
+OP2U(SLJIT_SUB32 | SLJIT_SET_Z | SLJIT_SET_SIG_GREATER, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0);
+add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_SIG_GREATER));
+if (common->abort_label == NULL)
+ add_jump(compiler, &common->abort, JUMP(SLJIT_NOT_EQUAL) /* SIG_LESS */);
+else
+ JUMPTO(SLJIT_NOT_EQUAL /* SIG_LESS */, common->abort_label);
+return cc + callout_length;
+}
+
+#undef CALLOUT_ARG_SIZE
+#undef CALLOUT_ARG_OFFSET
+
+static SLJIT_INLINE BOOL assert_needs_str_ptr_saving(PCRE2_SPTR cc)
+{
+while (TRUE)
+ {
+ switch (*cc)
+ {
+ case OP_CALLOUT_STR:
+ cc += GET(cc, 1 + 2*LINK_SIZE);
+ break;
+
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_DOLL:
+ case OP_DOLLM:
+ case OP_CALLOUT:
+ case OP_ALT:
+ cc += PRIV(OP_lengths)[*cc];
+ break;
+
+ case OP_KET:
+ return FALSE;
+
+ default:
+ return TRUE;
+ }
+ }
+}
+
+static PCRE2_SPTR compile_assert_matchingpath(compiler_common *common, PCRE2_SPTR cc, assert_backtrack *backtrack, BOOL conditional)
+{
+DEFINE_COMPILER;
+int framesize;
+int extrasize;
+BOOL local_quit_available = FALSE;
+BOOL needs_control_head;
+int private_data_ptr;
+backtrack_common altbacktrack;
+PCRE2_SPTR ccbegin;
+PCRE2_UCHAR opcode;
+PCRE2_UCHAR bra = OP_BRA;
+jump_list *tmp = NULL;
+jump_list **target = (conditional) ? &backtrack->condfailed : &backtrack->common.topbacktracks;
+jump_list **found;
+/* Saving previous accept variables. */
+BOOL save_local_quit_available = common->local_quit_available;
+BOOL save_in_positive_assertion = common->in_positive_assertion;
+then_trap_backtrack *save_then_trap = common->then_trap;
+struct sljit_label *save_quit_label = common->quit_label;
+struct sljit_label *save_accept_label = common->accept_label;
+jump_list *save_quit = common->quit;
+jump_list *save_positive_assertion_quit = common->positive_assertion_quit;
+jump_list *save_accept = common->accept;
+struct sljit_jump *jump;
+struct sljit_jump *brajump = NULL;
+
+/* Assert captures then. */
+common->then_trap = NULL;
+
+if (*cc == OP_BRAZERO || *cc == OP_BRAMINZERO)
+ {
+ SLJIT_ASSERT(!conditional);
+ bra = *cc;
+ cc++;
+ }
+private_data_ptr = PRIVATE_DATA(cc);
+SLJIT_ASSERT(private_data_ptr != 0);
+framesize = get_framesize(common, cc, NULL, FALSE, &needs_control_head);
+backtrack->framesize = framesize;
+backtrack->private_data_ptr = private_data_ptr;
+opcode = *cc;
+SLJIT_ASSERT(opcode >= OP_ASSERT && opcode <= OP_ASSERTBACK_NOT);
+found = (opcode == OP_ASSERT || opcode == OP_ASSERTBACK) ? &tmp : target;
+ccbegin = cc;
+cc += GET(cc, 1);
+
+if (bra == OP_BRAMINZERO)
+ {
+ /* This is a braminzero backtrack path. */
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ brajump = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0);
+ }
+
+if (framesize < 0)
+ {
+ extrasize = 1;
+ if (bra == OP_BRA && !assert_needs_str_ptr_saving(ccbegin + 1 + LINK_SIZE))
+ extrasize = 0;
+
+ if (needs_control_head)
+ extrasize++;
+
+ if (framesize == no_frame)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0);
+
+ if (extrasize > 0)
+ allocate_stack(common, extrasize);
+
+ if (needs_control_head)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+
+ if (extrasize > 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+
+ if (needs_control_head)
+ {
+ SLJIT_ASSERT(extrasize == 2);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0);
+ }
+ }
+else
+ {
+ extrasize = needs_control_head ? 3 : 2;
+ allocate_stack(common, framesize + extrasize);
+
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ OP2(SLJIT_ADD, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP2, 0);
+ if (needs_control_head)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+
+ if (needs_control_head)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), TMP1, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP2, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0);
+ }
+ else
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0);
+
+ init_frame(common, ccbegin, NULL, framesize + extrasize - 1, extrasize);
+ }
+
+memset(&altbacktrack, 0, sizeof(backtrack_common));
+if (conditional || (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT))
+ {
+ /* Control verbs cannot escape from these asserts. */
+ local_quit_available = TRUE;
+ common->local_quit_available = TRUE;
+ common->quit_label = NULL;
+ common->quit = NULL;
+ }
+
+common->in_positive_assertion = (opcode == OP_ASSERT || opcode == OP_ASSERTBACK);
+common->positive_assertion_quit = NULL;
+
+while (1)
+ {
+ common->accept_label = NULL;
+ common->accept = NULL;
+ altbacktrack.top = NULL;
+ altbacktrack.topbacktracks = NULL;
+
+ if (*ccbegin == OP_ALT && extrasize > 0)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+
+ altbacktrack.cc = ccbegin;
+ compile_matchingpath(common, ccbegin + 1 + LINK_SIZE, cc, &altbacktrack);
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ {
+ if (local_quit_available)
+ {
+ common->local_quit_available = save_local_quit_available;
+ common->quit_label = save_quit_label;
+ common->quit = save_quit;
+ }
+ common->in_positive_assertion = save_in_positive_assertion;
+ common->then_trap = save_then_trap;
+ common->accept_label = save_accept_label;
+ common->positive_assertion_quit = save_positive_assertion_quit;
+ common->accept = save_accept;
+ return NULL;
+ }
+ common->accept_label = LABEL();
+ if (common->accept != NULL)
+ set_jumps(common->accept, common->accept_label);
+
+ /* Reset stack. */
+ if (framesize < 0)
+ {
+ if (framesize == no_frame)
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ else if (extrasize > 0)
+ free_stack(common, extrasize);
+
+ if (needs_control_head)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1));
+ }
+ else
+ {
+ if ((opcode != OP_ASSERT_NOT && opcode != OP_ASSERTBACK_NOT) || conditional)
+ {
+ /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw));
+ if (needs_control_head)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1));
+ }
+ else
+ {
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ if (needs_control_head)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 2));
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize - 1) * sizeof(sljit_sw));
+ }
+ }
+
+ if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT)
+ {
+ /* We know that STR_PTR was stored on the top of the stack. */
+ if (conditional)
+ {
+ if (extrasize > 0)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), needs_control_head ? STACK(-2) : STACK(-1));
+ }
+ else if (bra == OP_BRAZERO)
+ {
+ if (framesize < 0)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize));
+ else
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 1));
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-framesize - extrasize));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0);
+ }
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ }
+ else if (framesize >= 0)
+ {
+ /* For OP_BRA and OP_BRAMINZERO. */
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 1));
+ }
+ }
+ add_jump(compiler, found, JUMP(SLJIT_JUMP));
+
+ compile_backtrackingpath(common, altbacktrack.top);
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ {
+ if (local_quit_available)
+ {
+ common->local_quit_available = save_local_quit_available;
+ common->quit_label = save_quit_label;
+ common->quit = save_quit;
+ }
+ common->in_positive_assertion = save_in_positive_assertion;
+ common->then_trap = save_then_trap;
+ common->accept_label = save_accept_label;
+ common->positive_assertion_quit = save_positive_assertion_quit;
+ common->accept = save_accept;
+ return NULL;
+ }
+ set_jumps(altbacktrack.topbacktracks, LABEL());
+
+ if (*cc != OP_ALT)
+ break;
+
+ ccbegin = cc;
+ cc += GET(cc, 1);
+ }
+
+if (local_quit_available)
+ {
+ SLJIT_ASSERT(common->positive_assertion_quit == NULL);
+ /* Makes the check less complicated below. */
+ common->positive_assertion_quit = common->quit;
+ }
+
+/* None of them matched. */
+if (common->positive_assertion_quit != NULL)
+ {
+ jump = JUMP(SLJIT_JUMP);
+ set_jumps(common->positive_assertion_quit, LABEL());
+ SLJIT_ASSERT(framesize != no_stack);
+ if (framesize < 0)
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, extrasize * sizeof(sljit_sw));
+ else
+ {
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (extrasize + 1) * sizeof(sljit_sw));
+ }
+ JUMPHERE(jump);
+ }
+
+if (needs_control_head)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(1));
+
+if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK)
+ {
+ /* Assert is failed. */
+ if ((conditional && extrasize > 0) || bra == OP_BRAZERO)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+
+ if (framesize < 0)
+ {
+ /* The topmost item should be 0. */
+ if (bra == OP_BRAZERO)
+ {
+ if (extrasize == 2)
+ free_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ }
+ else if (extrasize > 0)
+ free_stack(common, extrasize);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(extrasize - 1));
+ /* The topmost item should be 0. */
+ if (bra == OP_BRAZERO)
+ {
+ free_stack(common, framesize + extrasize - 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ }
+ else
+ free_stack(common, framesize + extrasize);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0);
+ }
+ jump = JUMP(SLJIT_JUMP);
+ if (bra != OP_BRAZERO)
+ add_jump(compiler, target, jump);
+
+ /* Assert is successful. */
+ set_jumps(tmp, LABEL());
+ if (framesize < 0)
+ {
+ /* We know that STR_PTR was stored on the top of the stack. */
+ if (extrasize > 0)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize));
+
+ /* Keep the STR_PTR on the top of the stack. */
+ if (bra == OP_BRAZERO)
+ {
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ if (extrasize == 2)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ }
+ else if (bra == OP_BRAMINZERO)
+ {
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ }
+ }
+ else
+ {
+ if (bra == OP_BRA)
+ {
+ /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize + 1));
+ }
+ else
+ {
+ /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 2) * sizeof(sljit_sw));
+ if (extrasize == 2)
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ if (bra == OP_BRAMINZERO)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ }
+ else
+ {
+ SLJIT_ASSERT(extrasize == 3);
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-1));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), bra == OP_BRAZERO ? STR_PTR : SLJIT_IMM, 0);
+ }
+ }
+ }
+
+ if (bra == OP_BRAZERO)
+ {
+ backtrack->matchingpath = LABEL();
+ SET_LABEL(jump, backtrack->matchingpath);
+ }
+ else if (bra == OP_BRAMINZERO)
+ {
+ JUMPTO(SLJIT_JUMP, backtrack->matchingpath);
+ JUMPHERE(brajump);
+ if (framesize >= 0)
+ {
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0);
+ }
+ set_jumps(backtrack->common.topbacktracks, LABEL());
+ }
+ }
+else
+ {
+ /* AssertNot is successful. */
+ if (framesize < 0)
+ {
+ if (extrasize > 0)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+
+ if (bra != OP_BRA)
+ {
+ if (extrasize == 2)
+ free_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ }
+ else if (extrasize > 0)
+ free_stack(common, extrasize);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(extrasize - 1));
+ /* The topmost item should be 0. */
+ if (bra != OP_BRA)
+ {
+ free_stack(common, framesize + extrasize - 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ }
+ else
+ free_stack(common, framesize + extrasize);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0);
+ }
+
+ if (bra == OP_BRAZERO)
+ backtrack->matchingpath = LABEL();
+ else if (bra == OP_BRAMINZERO)
+ {
+ JUMPTO(SLJIT_JUMP, backtrack->matchingpath);
+ JUMPHERE(brajump);
+ }
+
+ if (bra != OP_BRA)
+ {
+ SLJIT_ASSERT(found == &backtrack->common.topbacktracks);
+ set_jumps(backtrack->common.topbacktracks, LABEL());
+ backtrack->common.topbacktracks = NULL;
+ }
+ }
+
+if (local_quit_available)
+ {
+ common->local_quit_available = save_local_quit_available;
+ common->quit_label = save_quit_label;
+ common->quit = save_quit;
+ }
+common->in_positive_assertion = save_in_positive_assertion;
+common->then_trap = save_then_trap;
+common->accept_label = save_accept_label;
+common->positive_assertion_quit = save_positive_assertion_quit;
+common->accept = save_accept;
+return cc + 1 + LINK_SIZE;
+}
+
+static SLJIT_INLINE void match_once_common(compiler_common *common, PCRE2_UCHAR ket, int framesize, int private_data_ptr, BOOL has_alternatives, BOOL needs_control_head)
+{
+DEFINE_COMPILER;
+int stacksize;
+
+if (framesize < 0)
+ {
+ if (framesize == no_frame)
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ else
+ {
+ stacksize = needs_control_head ? 1 : 0;
+ if (ket != OP_KET || has_alternatives)
+ stacksize++;
+
+ if (stacksize > 0)
+ free_stack(common, stacksize);
+ }
+
+ if (needs_control_head)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), (ket != OP_KET || has_alternatives) ? STACK(-2) : STACK(-1));
+
+ /* TMP2 which is set here used by OP_KETRMAX below. */
+ if (ket == OP_KETRMAX)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(-1));
+ else if (ket == OP_KETRMIN)
+ {
+ /* Move the STR_PTR to the private_data_ptr. */
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1));
+ }
+ }
+else
+ {
+ stacksize = (ket != OP_KET || has_alternatives) ? 2 : 1;
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + stacksize) * sizeof(sljit_sw));
+ if (needs_control_head)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-1));
+
+ if (ket == OP_KETRMAX)
+ {
+ /* TMP2 which is set here used by OP_KETRMAX below. */
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ }
+ }
+if (needs_control_head)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, TMP1, 0);
+}
+
+static SLJIT_INLINE int match_capture_common(compiler_common *common, int stacksize, int offset, int private_data_ptr)
+{
+DEFINE_COMPILER;
+
+if (common->capture_last_ptr != 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, SLJIT_IMM, offset >> 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0);
+ stacksize++;
+ }
+if (common->optimized_cbracket[offset >> 1] == 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize + 1), TMP2, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0);
+ stacksize += 2;
+ }
+return stacksize;
+}
+
+static PCRE2_SPTR SLJIT_FUNC do_script_run(PCRE2_SPTR ptr, PCRE2_SPTR endptr)
+{
+ if (PRIV(script_run)(ptr, endptr, FALSE))
+ return endptr;
+ return NULL;
+}
+
+#ifdef SUPPORT_UNICODE
+
+static PCRE2_SPTR SLJIT_FUNC do_script_run_utf(PCRE2_SPTR ptr, PCRE2_SPTR endptr)
+{
+ if (PRIV(script_run)(ptr, endptr, TRUE))
+ return endptr;
+ return NULL;
+}
+
+#endif /* SUPPORT_UNICODE */
+
+static SLJIT_INLINE void match_script_run_common(compiler_common *common, int private_data_ptr, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+
+SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+#ifdef SUPPORT_UNICODE
+sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2(W, W, W), SLJIT_IMM,
+ common->utf ? SLJIT_FUNC_ADDR(do_script_run_utf) : SLJIT_FUNC_ADDR(do_script_run));
+#else
+sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(do_script_run));
+#endif
+
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0);
+add_jump(compiler, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0));
+}
+
+/*
+ Handling bracketed expressions is probably the most complex part.
+
+ Stack layout naming characters:
+ S - Push the current STR_PTR
+ 0 - Push a 0 (NULL)
+ A - Push the current STR_PTR. Needed for restoring the STR_PTR
+ before the next alternative. Not pushed if there are no alternatives.
+ M - Any values pushed by the current alternative. Can be empty, or anything.
+ C - Push the previous OVECTOR(i), OVECTOR(i+1) and OVECTOR_PRIV(i) to the stack.
+ L - Push the previous local (pointed by localptr) to the stack
+ () - opional values stored on the stack
+ ()* - optonal, can be stored multiple times
+
+ The following list shows the regular expression templates, their PCRE byte codes
+ and stack layout supported by pcre-sljit.
+
+ (?:) OP_BRA | OP_KET A M
+ () OP_CBRA | OP_KET C M
+ (?:)+ OP_BRA | OP_KETRMAX 0 A M S ( A M S )*
+ OP_SBRA | OP_KETRMAX 0 L M S ( L M S )*
+ (?:)+? OP_BRA | OP_KETRMIN 0 A M S ( A M S )*
+ OP_SBRA | OP_KETRMIN 0 L M S ( L M S )*
+ ()+ OP_CBRA | OP_KETRMAX 0 C M S ( C M S )*
+ OP_SCBRA | OP_KETRMAX 0 C M S ( C M S )*
+ ()+? OP_CBRA | OP_KETRMIN 0 C M S ( C M S )*
+ OP_SCBRA | OP_KETRMIN 0 C M S ( C M S )*
+ (?:)? OP_BRAZERO | OP_BRA | OP_KET S ( A M 0 )
+ (?:)?? OP_BRAMINZERO | OP_BRA | OP_KET S ( A M 0 )
+ ()? OP_BRAZERO | OP_CBRA | OP_KET S ( C M 0 )
+ ()?? OP_BRAMINZERO | OP_CBRA | OP_KET S ( C M 0 )
+ (?:)* OP_BRAZERO | OP_BRA | OP_KETRMAX S 0 ( A M S )*
+ OP_BRAZERO | OP_SBRA | OP_KETRMAX S 0 ( L M S )*
+ (?:)*? OP_BRAMINZERO | OP_BRA | OP_KETRMIN S 0 ( A M S )*
+ OP_BRAMINZERO | OP_SBRA | OP_KETRMIN S 0 ( L M S )*
+ ()* OP_BRAZERO | OP_CBRA | OP_KETRMAX S 0 ( C M S )*
+ OP_BRAZERO | OP_SCBRA | OP_KETRMAX S 0 ( C M S )*
+ ()*? OP_BRAMINZERO | OP_CBRA | OP_KETRMIN S 0 ( C M S )*
+ OP_BRAMINZERO | OP_SCBRA | OP_KETRMIN S 0 ( C M S )*
+
+
+ Stack layout naming characters:
+ A - Push the alternative index (starting from 0) on the stack.
+ Not pushed if there is no alternatives.
+ M - Any values pushed by the current alternative. Can be empty, or anything.
+
+ The next list shows the possible content of a bracket:
+ (|) OP_*BRA | OP_ALT ... M A
+ (?()|) OP_*COND | OP_ALT M A
+ (?>|) OP_ONCE | OP_ALT ... [stack trace] M A
+ Or nothing, if trace is unnecessary
+*/
+
+static PCRE2_SPTR compile_bracket_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+PCRE2_UCHAR opcode;
+int private_data_ptr = 0;
+int offset = 0;
+int i, stacksize;
+int repeat_ptr = 0, repeat_length = 0;
+int repeat_type = 0, repeat_count = 0;
+PCRE2_SPTR ccbegin;
+PCRE2_SPTR matchingpath;
+PCRE2_SPTR slot;
+PCRE2_UCHAR bra = OP_BRA;
+PCRE2_UCHAR ket;
+assert_backtrack *assert;
+BOOL has_alternatives;
+BOOL needs_control_head = FALSE;
+struct sljit_jump *jump;
+struct sljit_jump *skip;
+struct sljit_label *rmax_label = NULL;
+struct sljit_jump *braminzero = NULL;
+
+PUSH_BACKTRACK(sizeof(bracket_backtrack), cc, NULL);
+
+if (*cc == OP_BRAZERO || *cc == OP_BRAMINZERO)
+ {
+ bra = *cc;
+ cc++;
+ opcode = *cc;
+ }
+
+opcode = *cc;
+ccbegin = cc;
+matchingpath = bracketend(cc) - 1 - LINK_SIZE;
+ket = *matchingpath;
+if (ket == OP_KET && PRIVATE_DATA(matchingpath) != 0)
+ {
+ repeat_ptr = PRIVATE_DATA(matchingpath);
+ repeat_length = PRIVATE_DATA(matchingpath + 1);
+ repeat_type = PRIVATE_DATA(matchingpath + 2);
+ repeat_count = PRIVATE_DATA(matchingpath + 3);
+ SLJIT_ASSERT(repeat_length != 0 && repeat_type != 0 && repeat_count != 0);
+ if (repeat_type == OP_UPTO)
+ ket = OP_KETRMAX;
+ if (repeat_type == OP_MINUPTO)
+ ket = OP_KETRMIN;
+ }
+
+matchingpath = ccbegin + 1 + LINK_SIZE;
+SLJIT_ASSERT(ket == OP_KET || ket == OP_KETRMAX || ket == OP_KETRMIN);
+SLJIT_ASSERT(!((bra == OP_BRAZERO && ket == OP_KETRMIN) || (bra == OP_BRAMINZERO && ket == OP_KETRMAX)));
+cc += GET(cc, 1);
+
+has_alternatives = *cc == OP_ALT;
+if (SLJIT_UNLIKELY(opcode == OP_COND || opcode == OP_SCOND))
+ {
+ SLJIT_COMPILE_ASSERT(OP_DNRREF == OP_RREF + 1 && OP_FALSE == OP_RREF + 2 && OP_TRUE == OP_RREF + 3,
+ compile_time_checks_must_be_grouped_together);
+ has_alternatives = ((*matchingpath >= OP_RREF && *matchingpath <= OP_TRUE) || *matchingpath == OP_FAIL) ? FALSE : TRUE;
+ }
+
+if (SLJIT_UNLIKELY(opcode == OP_COND) && (*cc == OP_KETRMAX || *cc == OP_KETRMIN))
+ opcode = OP_SCOND;
+
+if (opcode == OP_CBRA || opcode == OP_SCBRA)
+ {
+ /* Capturing brackets has a pre-allocated space. */
+ offset = GET2(ccbegin, 1 + LINK_SIZE);
+ if (common->optimized_cbracket[offset] == 0)
+ {
+ private_data_ptr = OVECTOR_PRIV(offset);
+ offset <<= 1;
+ }
+ else
+ {
+ offset <<= 1;
+ private_data_ptr = OVECTOR(offset);
+ }
+ BACKTRACK_AS(bracket_backtrack)->private_data_ptr = private_data_ptr;
+ matchingpath += IMM2_SIZE;
+ }
+else if (opcode == OP_ASSERT_NA || opcode == OP_ASSERTBACK_NA || opcode == OP_ONCE || opcode == OP_SCRIPT_RUN || opcode == OP_SBRA || opcode == OP_SCOND)
+ {
+ /* Other brackets simply allocate the next entry. */
+ private_data_ptr = PRIVATE_DATA(ccbegin);
+ SLJIT_ASSERT(private_data_ptr != 0);
+ BACKTRACK_AS(bracket_backtrack)->private_data_ptr = private_data_ptr;
+ if (opcode == OP_ONCE)
+ BACKTRACK_AS(bracket_backtrack)->u.framesize = get_framesize(common, ccbegin, NULL, FALSE, &needs_control_head);
+ }
+
+/* Instructions before the first alternative. */
+stacksize = 0;
+if (ket == OP_KETRMAX || (ket == OP_KETRMIN && bra != OP_BRAMINZERO))
+ stacksize++;
+if (bra == OP_BRAZERO)
+ stacksize++;
+
+if (stacksize > 0)
+ allocate_stack(common, stacksize);
+
+stacksize = 0;
+if (ket == OP_KETRMAX || (ket == OP_KETRMIN && bra != OP_BRAMINZERO))
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0);
+ stacksize++;
+ }
+
+if (bra == OP_BRAZERO)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0);
+
+if (bra == OP_BRAMINZERO)
+ {
+ /* This is a backtrack path! (Since the try-path of OP_BRAMINZERO matches to the empty string) */
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ if (ket != OP_KETRMIN)
+ {
+ free_stack(common, 1);
+ braminzero = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0);
+ }
+ else if (opcode == OP_ONCE || opcode >= OP_SBRA)
+ {
+ jump = CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0);
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ /* Nothing stored during the first run. */
+ skip = JUMP(SLJIT_JUMP);
+ JUMPHERE(jump);
+ /* Checking zero-length iteration. */
+ if (opcode != OP_ONCE || BACKTRACK_AS(bracket_backtrack)->u.framesize < 0)
+ {
+ /* When we come from outside, private_data_ptr contains the previous STR_PTR. */
+ braminzero = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ }
+ else
+ {
+ /* Except when the whole stack frame must be saved. */
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ braminzero = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), STACK(-BACKTRACK_AS(bracket_backtrack)->u.framesize - 2));
+ }
+ JUMPHERE(skip);
+ }
+ else
+ {
+ jump = CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0);
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ JUMPHERE(jump);
+ }
+ }
+
+if (repeat_type != 0)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, repeat_count);
+ if (repeat_type == OP_EXACT)
+ rmax_label = LABEL();
+ }
+
+if (ket == OP_KETRMIN)
+ BACKTRACK_AS(bracket_backtrack)->recursive_matchingpath = LABEL();
+
+if (ket == OP_KETRMAX)
+ {
+ rmax_label = LABEL();
+ if (has_alternatives && opcode >= OP_BRA && opcode < OP_SBRA && repeat_type == 0)
+ BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = rmax_label;
+ }
+
+/* Handling capturing brackets and alternatives. */
+if (opcode == OP_ONCE)
+ {
+ stacksize = 0;
+ if (needs_control_head)
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+ stacksize++;
+ }
+
+ if (BACKTRACK_AS(bracket_backtrack)->u.framesize < 0)
+ {
+ /* Neither capturing brackets nor recursions are found in the block. */
+ if (ket == OP_KETRMIN)
+ {
+ stacksize += 2;
+ if (!needs_control_head)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ }
+ else
+ {
+ if (BACKTRACK_AS(bracket_backtrack)->u.framesize == no_frame)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0);
+ if (ket == OP_KETRMAX || has_alternatives)
+ stacksize++;
+ }
+
+ if (stacksize > 0)
+ allocate_stack(common, stacksize);
+
+ stacksize = 0;
+ if (needs_control_head)
+ {
+ stacksize++;
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
+ }
+
+ if (ket == OP_KETRMIN)
+ {
+ if (needs_control_head)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0);
+ if (BACKTRACK_AS(bracket_backtrack)->u.framesize == no_frame)
+ OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, needs_control_head ? (2 * sizeof(sljit_sw)) : sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize + 1), TMP2, 0);
+ }
+ else if (ket == OP_KETRMAX || has_alternatives)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0);
+ }
+ else
+ {
+ if (ket != OP_KET || has_alternatives)
+ stacksize++;
+
+ stacksize += BACKTRACK_AS(bracket_backtrack)->u.framesize + 1;
+ allocate_stack(common, stacksize);
+
+ if (needs_control_head)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
+
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ OP2(SLJIT_ADD, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw));
+
+ stacksize = needs_control_head ? 1 : 0;
+ if (ket != OP_KET || has_alternatives)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP2, 0);
+ stacksize++;
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP2, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0);
+ }
+ init_frame(common, ccbegin, NULL, BACKTRACK_AS(bracket_backtrack)->u.framesize + stacksize, stacksize + 1);
+ }
+ }
+else if (opcode == OP_CBRA || opcode == OP_SCBRA)
+ {
+ /* Saving the previous values. */
+ if (common->optimized_cbracket[offset >> 1] != 0)
+ {
+ SLJIT_ASSERT(private_data_ptr == OVECTOR(offset));
+ allocate_stack(common, 2);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr + sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP1, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP2, 0);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
+ }
+ }
+else if (opcode == OP_ASSERT_NA || opcode == OP_ASSERTBACK_NA || opcode == OP_SCRIPT_RUN || opcode == OP_SBRA || opcode == OP_SCOND)
+ {
+ /* Saving the previous value. */
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
+ }
+else if (has_alternatives)
+ {
+ /* Pushing the starting string pointer. */
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ }
+
+/* Generating code for the first alternative. */
+if (opcode == OP_COND || opcode == OP_SCOND)
+ {
+ if (*matchingpath == OP_CREF)
+ {
+ SLJIT_ASSERT(has_alternatives);
+ add_jump(compiler, &(BACKTRACK_AS(bracket_backtrack)->u.condfailed),
+ CMP(SLJIT_EQUAL, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(matchingpath, 1) << 1), SLJIT_MEM1(SLJIT_SP), OVECTOR(1)));
+ matchingpath += 1 + IMM2_SIZE;
+ }
+ else if (*matchingpath == OP_DNCREF)
+ {
+ SLJIT_ASSERT(has_alternatives);
+
+ i = GET2(matchingpath, 1 + IMM2_SIZE);
+ slot = common->name_table + GET2(matchingpath, 1) * common->name_entry_size;
+ OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1));
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0);
+ slot += common->name_entry_size;
+ i--;
+ while (i-- > 0)
+ {
+ OP2(SLJIT_SUB, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0);
+ OP2(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, TMP2, 0, STR_PTR, 0);
+ slot += common->name_entry_size;
+ }
+ OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0);
+ add_jump(compiler, &(BACKTRACK_AS(bracket_backtrack)->u.condfailed), JUMP(SLJIT_ZERO));
+ matchingpath += 1 + 2 * IMM2_SIZE;
+ }
+ else if ((*matchingpath >= OP_RREF && *matchingpath <= OP_TRUE) || *matchingpath == OP_FAIL)
+ {
+ /* Never has other case. */
+ BACKTRACK_AS(bracket_backtrack)->u.condfailed = NULL;
+ SLJIT_ASSERT(!has_alternatives);
+
+ if (*matchingpath == OP_TRUE)
+ {
+ stacksize = 1;
+ matchingpath++;
+ }
+ else if (*matchingpath == OP_FALSE || *matchingpath == OP_FAIL)
+ stacksize = 0;
+ else if (*matchingpath == OP_RREF)
+ {
+ stacksize = GET2(matchingpath, 1);
+ if (common->currententry == NULL)
+ stacksize = 0;
+ else if (stacksize == RREF_ANY)
+ stacksize = 1;
+ else if (common->currententry->start == 0)
+ stacksize = stacksize == 0;
+ else
+ stacksize = stacksize == (int)GET2(common->start, common->currententry->start + 1 + LINK_SIZE);
+
+ if (stacksize != 0)
+ matchingpath += 1 + IMM2_SIZE;
+ }
+ else
+ {
+ if (common->currententry == NULL || common->currententry->start == 0)
+ stacksize = 0;
+ else
+ {
+ stacksize = GET2(matchingpath, 1 + IMM2_SIZE);
+ slot = common->name_table + GET2(matchingpath, 1) * common->name_entry_size;
+ i = (int)GET2(common->start, common->currententry->start + 1 + LINK_SIZE);
+ while (stacksize > 0)
+ {
+ if ((int)GET2(slot, 0) == i)
+ break;
+ slot += common->name_entry_size;
+ stacksize--;
+ }
+ }
+
+ if (stacksize != 0)
+ matchingpath += 1 + 2 * IMM2_SIZE;
+ }
+
+ /* The stacksize == 0 is a common "else" case. */
+ if (stacksize == 0)
+ {
+ if (*cc == OP_ALT)
+ {
+ matchingpath = cc + 1 + LINK_SIZE;
+ cc += GET(cc, 1);
+ }
+ else
+ matchingpath = cc;
+ }
+ }
+ else
+ {
+ SLJIT_ASSERT(has_alternatives && *matchingpath >= OP_ASSERT && *matchingpath <= OP_ASSERTBACK_NOT);
+ /* Similar code as PUSH_BACKTRACK macro. */
+ assert = sljit_alloc_memory(compiler, sizeof(assert_backtrack));
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return NULL;
+ memset(assert, 0, sizeof(assert_backtrack));
+ assert->common.cc = matchingpath;
+ BACKTRACK_AS(bracket_backtrack)->u.assert = assert;
+ matchingpath = compile_assert_matchingpath(common, matchingpath, assert, TRUE);
+ }
+ }
+
+compile_matchingpath(common, matchingpath, cc, backtrack);
+if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return NULL;
+
+if (opcode == OP_ASSERT_NA || opcode == OP_ASSERTBACK_NA)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+
+if (opcode == OP_ONCE)
+ match_once_common(common, ket, BACKTRACK_AS(bracket_backtrack)->u.framesize, private_data_ptr, has_alternatives, needs_control_head);
+
+if (opcode == OP_SCRIPT_RUN)
+ match_script_run_common(common, private_data_ptr, backtrack);
+
+stacksize = 0;
+if (repeat_type == OP_MINUPTO)
+ {
+ /* We need to preserve the counter. TMP2 will be used below. */
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), repeat_ptr);
+ stacksize++;
+ }
+if (ket != OP_KET || bra != OP_BRA)
+ stacksize++;
+if (offset != 0)
+ {
+ if (common->capture_last_ptr != 0)
+ stacksize++;
+ if (common->optimized_cbracket[offset >> 1] == 0)
+ stacksize += 2;
+ }
+if (has_alternatives && opcode != OP_ONCE)
+ stacksize++;
+
+if (stacksize > 0)
+ allocate_stack(common, stacksize);
+
+stacksize = 0;
+if (repeat_type == OP_MINUPTO)
+ {
+ /* TMP2 was set above. */
+ OP2(SLJIT_SUB, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP2, 0, SLJIT_IMM, 1);
+ stacksize++;
+ }
+
+if (ket != OP_KET || bra != OP_BRA)
+ {
+ if (ket != OP_KET)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0);
+ else
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0);
+ stacksize++;
+ }
+
+if (offset != 0)
+ stacksize = match_capture_common(common, stacksize, offset, private_data_ptr);
+
+/* Skip and count the other alternatives. */
+i = 1;
+while (*cc == OP_ALT)
+ {
+ cc += GET(cc, 1);
+ i++;
+ }
+
+if (has_alternatives)
+ {
+ if (opcode != OP_ONCE)
+ {
+ if (i <= 3)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0);
+ else
+ BACKTRACK_AS(bracket_backtrack)->u.matching_put_label = sljit_emit_put_label(compiler, SLJIT_MEM1(STACK_TOP), STACK(stacksize));
+ }
+ if (ket != OP_KETRMAX)
+ BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = LABEL();
+ }
+
+/* Must be after the matchingpath label. */
+if (offset != 0 && common->optimized_cbracket[offset >> 1] != 0)
+ {
+ SLJIT_ASSERT(private_data_ptr == OVECTOR(offset + 0));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0);
+ }
+
+if (ket == OP_KETRMAX)
+ {
+ if (repeat_type != 0)
+ {
+ if (has_alternatives)
+ BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = LABEL();
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, rmax_label);
+ /* Drop STR_PTR for greedy plus quantifier. */
+ if (opcode != OP_ONCE)
+ free_stack(common, 1);
+ }
+ else if (opcode < OP_BRA || opcode >= OP_SBRA)
+ {
+ if (has_alternatives)
+ BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = LABEL();
+
+ /* Checking zero-length iteration. */
+ if (opcode != OP_ONCE)
+ {
+ /* This case includes opcodes such as OP_SCRIPT_RUN. */
+ CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STR_PTR, 0, rmax_label);
+ /* Drop STR_PTR for greedy plus quantifier. */
+ if (bra != OP_BRAZERO)
+ free_stack(common, 1);
+ }
+ else
+ /* TMP2 must contain the starting STR_PTR. */
+ CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, STR_PTR, 0, rmax_label);
+ }
+ else
+ JUMPTO(SLJIT_JUMP, rmax_label);
+ BACKTRACK_AS(bracket_backtrack)->recursive_matchingpath = LABEL();
+ }
+
+if (repeat_type == OP_EXACT)
+ {
+ count_match(common);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, rmax_label);
+ }
+else if (repeat_type == OP_UPTO)
+ {
+ /* We need to preserve the counter. */
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), repeat_ptr);
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
+ }
+
+if (bra == OP_BRAZERO)
+ BACKTRACK_AS(bracket_backtrack)->zero_matchingpath = LABEL();
+
+if (bra == OP_BRAMINZERO)
+ {
+ /* This is a backtrack path! (From the viewpoint of OP_BRAMINZERO) */
+ JUMPTO(SLJIT_JUMP, ((braminzero_backtrack *)parent)->matchingpath);
+ if (braminzero != NULL)
+ {
+ JUMPHERE(braminzero);
+ /* We need to release the end pointer to perform the
+ backtrack for the zero-length iteration. When
+ framesize is < 0, OP_ONCE will do the release itself. */
+ if (opcode == OP_ONCE && BACKTRACK_AS(bracket_backtrack)->u.framesize >= 0)
+ {
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (BACKTRACK_AS(bracket_backtrack)->u.framesize - 1) * sizeof(sljit_sw));
+ }
+ else if (ket == OP_KETRMIN && opcode != OP_ONCE)
+ free_stack(common, 1);
+ }
+ /* Continue to the normal backtrack. */
+ }
+
+if ((ket != OP_KET && bra != OP_BRAMINZERO) || bra == OP_BRAZERO)
+ count_match(common);
+
+cc += 1 + LINK_SIZE;
+
+if (opcode == OP_ONCE)
+ {
+ /* We temporarily encode the needs_control_head in the lowest bit.
+ Note: on the target architectures of SLJIT the ((x << 1) >> 1) returns
+ the same value for small signed numbers (including negative numbers). */
+ BACKTRACK_AS(bracket_backtrack)->u.framesize = (int)((unsigned)BACKTRACK_AS(bracket_backtrack)->u.framesize << 1) | (needs_control_head ? 1 : 0);
+ }
+return cc + repeat_length;
+}
+
+static PCRE2_SPTR compile_bracketpos_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+PCRE2_UCHAR opcode;
+int private_data_ptr;
+int cbraprivptr = 0;
+BOOL needs_control_head;
+int framesize;
+int stacksize;
+int offset = 0;
+BOOL zero = FALSE;
+PCRE2_SPTR ccbegin = NULL;
+int stack; /* Also contains the offset of control head. */
+struct sljit_label *loop = NULL;
+struct jump_list *emptymatch = NULL;
+
+PUSH_BACKTRACK(sizeof(bracketpos_backtrack), cc, NULL);
+if (*cc == OP_BRAPOSZERO)
+ {
+ zero = TRUE;
+ cc++;
+ }
+
+opcode = *cc;
+private_data_ptr = PRIVATE_DATA(cc);
+SLJIT_ASSERT(private_data_ptr != 0);
+BACKTRACK_AS(bracketpos_backtrack)->private_data_ptr = private_data_ptr;
+switch(opcode)
+ {
+ case OP_BRAPOS:
+ case OP_SBRAPOS:
+ ccbegin = cc + 1 + LINK_SIZE;
+ break;
+
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ offset = GET2(cc, 1 + LINK_SIZE);
+ /* This case cannot be optimized in the same was as
+ normal capturing brackets. */
+ SLJIT_ASSERT(common->optimized_cbracket[offset] == 0);
+ cbraprivptr = OVECTOR_PRIV(offset);
+ offset <<= 1;
+ ccbegin = cc + 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+framesize = get_framesize(common, cc, NULL, FALSE, &needs_control_head);
+BACKTRACK_AS(bracketpos_backtrack)->framesize = framesize;
+if (framesize < 0)
+ {
+ if (offset != 0)
+ {
+ stacksize = 2;
+ if (common->capture_last_ptr != 0)
+ stacksize++;
+ }
+ else
+ stacksize = 1;
+
+ if (needs_control_head)
+ stacksize++;
+ if (!zero)
+ stacksize++;
+
+ BACKTRACK_AS(bracketpos_backtrack)->stacksize = stacksize;
+ allocate_stack(common, stacksize);
+ if (framesize == no_frame)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0);
+
+ stack = 0;
+ if (offset != 0)
+ {
+ stack = 2;
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP1, 0);
+ if (common->capture_last_ptr != 0)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP2, 0);
+ if (needs_control_head)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+ if (common->capture_last_ptr != 0)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), TMP1, 0);
+ stack = 3;
+ }
+ }
+ else
+ {
+ if (needs_control_head)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ stack = 1;
+ }
+
+ if (needs_control_head)
+ stack++;
+ if (!zero)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), SLJIT_IMM, 1);
+ if (needs_control_head)
+ {
+ stack--;
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), TMP2, 0);
+ }
+ }
+else
+ {
+ stacksize = framesize + 1;
+ if (!zero)
+ stacksize++;
+ if (needs_control_head)
+ stacksize++;
+ if (offset == 0)
+ stacksize++;
+ BACKTRACK_AS(bracketpos_backtrack)->stacksize = stacksize;
+
+ allocate_stack(common, stacksize);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ if (needs_control_head)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+ OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw));
+
+ stack = 0;
+ if (!zero)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 1);
+ stack = 1;
+ }
+ if (needs_control_head)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), TMP2, 0);
+ stack++;
+ }
+ if (offset == 0)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), STR_PTR, 0);
+ stack++;
+ }
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), TMP1, 0);
+ init_frame(common, cc, NULL, stacksize - 1, stacksize - framesize);
+ stack -= 1 + (offset == 0);
+ }
+
+if (offset != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), cbraprivptr, STR_PTR, 0);
+
+loop = LABEL();
+while (*cc != OP_KETRPOS)
+ {
+ backtrack->top = NULL;
+ backtrack->topbacktracks = NULL;
+ cc += GET(cc, 1);
+
+ compile_matchingpath(common, ccbegin, cc, backtrack);
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return NULL;
+
+ if (framesize < 0)
+ {
+ if (framesize == no_frame)
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+
+ if (offset != 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), cbraprivptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), cbraprivptr, STR_PTR, 0);
+ if (common->capture_last_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, SLJIT_IMM, offset >> 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0);
+ }
+ else
+ {
+ if (opcode == OP_SBRAPOS)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ }
+
+ /* Even if the match is empty, we need to reset the control head. */
+ if (needs_control_head)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(stack));
+
+ if (opcode == OP_SBRAPOS || opcode == OP_SCBRAPOS)
+ add_jump(compiler, &emptymatch, CMP(SLJIT_EQUAL, TMP1, 0, STR_PTR, 0));
+
+ if (!zero)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0);
+ }
+ else
+ {
+ if (offset != 0)
+ {
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, stacksize * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), cbraprivptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), cbraprivptr, STR_PTR, 0);
+ if (common->capture_last_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, SLJIT_IMM, offset >> 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ OP2(SLJIT_SUB, STACK_TOP, 0, TMP2, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw));
+ if (opcode == OP_SBRAPOS)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), STACK(-framesize - 2));
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), STACK(-framesize - 2), STR_PTR, 0);
+ }
+
+ /* Even if the match is empty, we need to reset the control head. */
+ if (needs_control_head)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(stack));
+
+ if (opcode == OP_SBRAPOS || opcode == OP_SCBRAPOS)
+ add_jump(compiler, &emptymatch, CMP(SLJIT_EQUAL, TMP1, 0, STR_PTR, 0));
+
+ if (!zero)
+ {
+ if (framesize < 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0);
+ else
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ }
+ }
+
+ JUMPTO(SLJIT_JUMP, loop);
+ flush_stubs(common);
+
+ compile_backtrackingpath(common, backtrack->top);
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return NULL;
+ set_jumps(backtrack->topbacktracks, LABEL());
+
+ if (framesize < 0)
+ {
+ if (offset != 0)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), cbraprivptr);
+ else
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ }
+ else
+ {
+ if (offset != 0)
+ {
+ /* Last alternative. */
+ if (*cc == OP_KETRPOS)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), cbraprivptr);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP2), STACK(-framesize - 2));
+ }
+ }
+
+ if (*cc == OP_KETRPOS)
+ break;
+ ccbegin = cc + 1 + LINK_SIZE;
+ }
+
+/* We don't have to restore the control head in case of a failed match. */
+
+backtrack->topbacktracks = NULL;
+if (!zero)
+ {
+ if (framesize < 0)
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0));
+ else /* TMP2 is set to [private_data_ptr] above. */
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(TMP2), STACK(-stacksize), SLJIT_IMM, 0));
+ }
+
+/* None of them matched. */
+set_jumps(emptymatch, LABEL());
+count_match(common);
+return cc + 1 + LINK_SIZE;
+}
+
+static SLJIT_INLINE PCRE2_SPTR get_iterator_parameters(compiler_common *common, PCRE2_SPTR cc, PCRE2_UCHAR *opcode, PCRE2_UCHAR *type, sljit_u32 *max, sljit_u32 *exact, PCRE2_SPTR *end)
+{
+int class_len;
+
+*opcode = *cc;
+*exact = 0;
+
+if (*opcode >= OP_STAR && *opcode <= OP_POSUPTO)
+ {
+ cc++;
+ *type = OP_CHAR;
+ }
+else if (*opcode >= OP_STARI && *opcode <= OP_POSUPTOI)
+ {
+ cc++;
+ *type = OP_CHARI;
+ *opcode -= OP_STARI - OP_STAR;
+ }
+else if (*opcode >= OP_NOTSTAR && *opcode <= OP_NOTPOSUPTO)
+ {
+ cc++;
+ *type = OP_NOT;
+ *opcode -= OP_NOTSTAR - OP_STAR;
+ }
+else if (*opcode >= OP_NOTSTARI && *opcode <= OP_NOTPOSUPTOI)
+ {
+ cc++;
+ *type = OP_NOTI;
+ *opcode -= OP_NOTSTARI - OP_STAR;
+ }
+else if (*opcode >= OP_TYPESTAR && *opcode <= OP_TYPEPOSUPTO)
+ {
+ cc++;
+ *opcode -= OP_TYPESTAR - OP_STAR;
+ *type = OP_END;
+ }
+else
+ {
+ SLJIT_ASSERT(*opcode == OP_CLASS || *opcode == OP_NCLASS || *opcode == OP_XCLASS);
+ *type = *opcode;
+ cc++;
+ class_len = (*type < OP_XCLASS) ? (int)(1 + (32 / sizeof(PCRE2_UCHAR))) : GET(cc, 0);
+ *opcode = cc[class_len - 1];
+
+ if (*opcode >= OP_CRSTAR && *opcode <= OP_CRMINQUERY)
+ {
+ *opcode -= OP_CRSTAR - OP_STAR;
+ *end = cc + class_len;
+
+ if (*opcode == OP_PLUS || *opcode == OP_MINPLUS)
+ {
+ *exact = 1;
+ *opcode -= OP_PLUS - OP_STAR;
+ }
+ }
+ else if (*opcode >= OP_CRPOSSTAR && *opcode <= OP_CRPOSQUERY)
+ {
+ *opcode -= OP_CRPOSSTAR - OP_POSSTAR;
+ *end = cc + class_len;
+
+ if (*opcode == OP_POSPLUS)
+ {
+ *exact = 1;
+ *opcode = OP_POSSTAR;
+ }
+ }
+ else
+ {
+ SLJIT_ASSERT(*opcode == OP_CRRANGE || *opcode == OP_CRMINRANGE || *opcode == OP_CRPOSRANGE);
+ *max = GET2(cc, (class_len + IMM2_SIZE));
+ *exact = GET2(cc, class_len);
+
+ if (*max == 0)
+ {
+ if (*opcode == OP_CRPOSRANGE)
+ *opcode = OP_POSSTAR;
+ else
+ *opcode -= OP_CRRANGE - OP_STAR;
+ }
+ else
+ {
+ *max -= *exact;
+ if (*max == 0)
+ *opcode = OP_EXACT;
+ else if (*max == 1)
+ {
+ if (*opcode == OP_CRPOSRANGE)
+ *opcode = OP_POSQUERY;
+ else
+ *opcode -= OP_CRRANGE - OP_QUERY;
+ }
+ else
+ {
+ if (*opcode == OP_CRPOSRANGE)
+ *opcode = OP_POSUPTO;
+ else
+ *opcode -= OP_CRRANGE - OP_UPTO;
+ }
+ }
+ *end = cc + class_len + 2 * IMM2_SIZE;
+ }
+ return cc;
+ }
+
+switch(*opcode)
+ {
+ case OP_EXACT:
+ *exact = GET2(cc, 0);
+ cc += IMM2_SIZE;
+ break;
+
+ case OP_PLUS:
+ case OP_MINPLUS:
+ *exact = 1;
+ *opcode -= OP_PLUS - OP_STAR;
+ break;
+
+ case OP_POSPLUS:
+ *exact = 1;
+ *opcode = OP_POSSTAR;
+ break;
+
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_POSUPTO:
+ *max = GET2(cc, 0);
+ cc += IMM2_SIZE;
+ break;
+ }
+
+if (*type == OP_END)
+ {
+ *type = *cc;
+ *end = next_opcode(common, cc);
+ cc++;
+ return cc;
+ }
+
+*end = cc + 1;
+#ifdef SUPPORT_UNICODE
+if (common->utf && HAS_EXTRALEN(*cc)) *end += GET_EXTRALEN(*cc);
+#endif
+return cc;
+}
+
+static PCRE2_SPTR compile_iterator_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+PCRE2_UCHAR opcode;
+PCRE2_UCHAR type;
+sljit_u32 max = 0, exact;
+sljit_s32 early_fail_ptr = PRIVATE_DATA(cc + 1);
+sljit_s32 early_fail_type;
+BOOL charpos_enabled;
+PCRE2_UCHAR charpos_char;
+unsigned int charpos_othercasebit;
+PCRE2_SPTR end;
+jump_list *no_match = NULL;
+jump_list *no_char1_match = NULL;
+struct sljit_jump *jump = NULL;
+struct sljit_label *label;
+int private_data_ptr = PRIVATE_DATA(cc);
+int base = (private_data_ptr == 0) ? SLJIT_MEM1(STACK_TOP) : SLJIT_MEM1(SLJIT_SP);
+int offset0 = (private_data_ptr == 0) ? STACK(0) : private_data_ptr;
+int offset1 = (private_data_ptr == 0) ? STACK(1) : private_data_ptr + SSIZE_OF(sw);
+int tmp_base, tmp_offset;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+BOOL use_tmp;
+#endif
+
+PUSH_BACKTRACK(sizeof(char_iterator_backtrack), cc, NULL);
+
+early_fail_type = (early_fail_ptr & 0x7);
+early_fail_ptr >>= 3;
+
+/* During recursion, these optimizations are disabled. */
+if (common->early_fail_start_ptr == 0 && common->fast_forward_bc_ptr == NULL)
+ {
+ early_fail_ptr = 0;
+ early_fail_type = type_skip;
+ }
+
+SLJIT_ASSERT(common->fast_forward_bc_ptr != NULL || early_fail_ptr == 0
+ || (early_fail_ptr >= common->early_fail_start_ptr && early_fail_ptr <= common->early_fail_end_ptr));
+
+if (early_fail_type == type_fail)
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), early_fail_ptr));
+
+cc = get_iterator_parameters(common, cc, &opcode, &type, &max, &exact, &end);
+
+if (type != OP_EXTUNI)
+ {
+ tmp_base = TMP3;
+ tmp_offset = 0;
+ }
+else
+ {
+ tmp_base = SLJIT_MEM1(SLJIT_SP);
+ tmp_offset = POSSESSIVE0;
+ }
+
+/* Handle fixed part first. */
+if (exact > 1)
+ {
+ SLJIT_ASSERT(early_fail_ptr == 0);
+
+ if (common->mode == PCRE2_JIT_COMPLETE
+#ifdef SUPPORT_UNICODE
+ && !common->utf
+#endif
+ && type != OP_ANYNL && type != OP_EXTUNI)
+ {
+ OP2(SLJIT_ADD, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(exact));
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_GREATER, TMP1, 0, STR_END, 0));
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, exact);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, FALSE);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, label);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, exact);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, TRUE);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, label);
+ }
+ }
+else if (exact == 1)
+ compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, TRUE);
+
+if (early_fail_type == type_fail_range)
+ {
+ /* Range end first, followed by range start. */
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), early_fail_ptr);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), early_fail_ptr + SSIZE_OF(sw));
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, TMP2, 0);
+ OP2(SLJIT_SUB, TMP2, 0, STR_PTR, 0, TMP2, 0);
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_LESS_EQUAL, TMP2, 0, TMP1, 0));
+
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr + SSIZE_OF(sw), STR_PTR, 0);
+ }
+
+switch(opcode)
+ {
+ case OP_STAR:
+ case OP_UPTO:
+ SLJIT_ASSERT(early_fail_ptr == 0 || opcode == OP_STAR);
+
+ if (type == OP_ANYNL || type == OP_EXTUNI)
+ {
+ SLJIT_ASSERT(private_data_ptr == 0);
+ SLJIT_ASSERT(early_fail_ptr == 0);
+
+ allocate_stack(common, 2);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0);
+
+ if (opcode == OP_UPTO)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, SLJIT_IMM, max);
+
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &BACKTRACK_AS(char_iterator_backtrack)->u.backtracks, TRUE);
+ if (opcode == OP_UPTO)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+ jump = JUMP(SLJIT_ZERO);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, TMP1, 0);
+ }
+
+ /* We cannot use TMP3 because of allocate_stack. */
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, label);
+ if (jump != NULL)
+ JUMPHERE(jump);
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
+ break;
+ }
+#ifdef SUPPORT_UNICODE
+ else if (type == OP_ALLANY && !common->invalid_utf)
+#else
+ else if (type == OP_ALLANY)
+#endif
+ {
+ if (opcode == OP_STAR)
+ {
+ if (private_data_ptr == 0)
+ allocate_stack(common, 2);
+
+ OP1(SLJIT_MOV, base, offset0, STR_END, 0);
+ OP1(SLJIT_MOV, base, offset1, STR_PTR, 0);
+
+ OP1(SLJIT_MOV, STR_PTR, 0, STR_END, 0);
+ process_partial_match(common);
+
+ if (early_fail_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_END, 0);
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
+ break;
+ }
+#ifdef SUPPORT_UNICODE
+ else if (!common->utf)
+#else
+ else
+#endif
+ {
+ if (private_data_ptr == 0)
+ allocate_stack(common, 2);
+
+ OP1(SLJIT_MOV, base, offset1, STR_PTR, 0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(max));
+
+ if (common->mode == PCRE2_JIT_COMPLETE)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, STR_PTR, 0, STR_END, 0);
+ CMOV(SLJIT_GREATER, STR_PTR, STR_END, 0);
+ }
+ else
+ {
+ jump = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, STR_END, 0);
+ process_partial_match(common);
+ JUMPHERE(jump);
+ }
+
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+
+ if (early_fail_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0);
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
+ break;
+ }
+ }
+
+ charpos_enabled = FALSE;
+ charpos_char = 0;
+ charpos_othercasebit = 0;
+
+ if ((type != OP_CHAR && type != OP_CHARI) && (*end == OP_CHAR || *end == OP_CHARI))
+ {
+#ifdef SUPPORT_UNICODE
+ charpos_enabled = !common->utf || !HAS_EXTRALEN(end[1]);
+#else
+ charpos_enabled = TRUE;
+#endif
+ if (charpos_enabled && *end == OP_CHARI && char_has_othercase(common, end + 1))
+ {
+ charpos_othercasebit = char_get_othercase_bit(common, end + 1);
+ if (charpos_othercasebit == 0)
+ charpos_enabled = FALSE;
+ }
+
+ if (charpos_enabled)
+ {
+ charpos_char = end[1];
+ /* Consume the OP_CHAR opcode. */
+ end += 2;
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ SLJIT_ASSERT((charpos_othercasebit >> 8) == 0);
+#elif PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ SLJIT_ASSERT((charpos_othercasebit >> 9) == 0);
+ if ((charpos_othercasebit & 0x100) != 0)
+ charpos_othercasebit = (charpos_othercasebit & 0xff) << 8;
+#endif
+ if (charpos_othercasebit != 0)
+ charpos_char |= charpos_othercasebit;
+
+ BACKTRACK_AS(char_iterator_backtrack)->u.charpos.enabled = TRUE;
+ BACKTRACK_AS(char_iterator_backtrack)->u.charpos.chr = charpos_char;
+ BACKTRACK_AS(char_iterator_backtrack)->u.charpos.othercasebit = charpos_othercasebit;
+ }
+ }
+
+ if (charpos_enabled)
+ {
+ if (opcode == OP_UPTO)
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max + 1);
+
+ /* Search the first instance of charpos_char. */
+ jump = JUMP(SLJIT_JUMP);
+ label = LABEL();
+ if (opcode == OP_UPTO)
+ {
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_ZERO));
+ }
+ compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, FALSE);
+ if (early_fail_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0);
+ JUMPHERE(jump);
+
+ detect_partial_match(common, &backtrack->topbacktracks);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ if (charpos_othercasebit != 0)
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, charpos_othercasebit);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, charpos_char, label);
+
+ if (private_data_ptr == 0)
+ allocate_stack(common, 2);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ OP1(SLJIT_MOV, base, offset1, STR_PTR, 0);
+
+ if (opcode == OP_UPTO)
+ {
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ add_jump(compiler, &no_match, JUMP(SLJIT_ZERO));
+ }
+
+ /* Search the last instance of charpos_char. */
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &no_match, FALSE);
+ if (early_fail_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0);
+ detect_partial_match(common, &no_match);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ if (charpos_othercasebit != 0)
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, charpos_othercasebit);
+
+ if (opcode == OP_STAR)
+ {
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, charpos_char, label);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, label);
+ }
+ else
+ {
+ jump = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, charpos_char);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ JUMPHERE(jump);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, label);
+ }
+
+ set_jumps(no_match, LABEL());
+ OP2(SLJIT_ADD, STR_PTR, 0, base, offset0, SLJIT_IMM, IN_UCHARS(1));
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ }
+ else
+ {
+ if (private_data_ptr == 0)
+ allocate_stack(common, 2);
+
+ OP1(SLJIT_MOV, base, offset1, STR_PTR, 0);
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ use_tmp = (!HAS_VIRTUAL_REGISTERS && opcode == OP_STAR);
+ SLJIT_ASSERT(!use_tmp || tmp_base == TMP3);
+
+ if (common->utf)
+ OP1(SLJIT_MOV, use_tmp ? TMP3 : base, use_tmp ? 0 : offset0, STR_PTR, 0);
+#endif
+ if (opcode == OP_UPTO)
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max);
+
+ detect_partial_match(common, &no_match);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE);
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf)
+ OP1(SLJIT_MOV, use_tmp ? TMP3 : base, use_tmp ? 0 : offset0, STR_PTR, 0);
+#endif
+
+ if (opcode == OP_UPTO)
+ {
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ add_jump(compiler, &no_match, JUMP(SLJIT_ZERO));
+ }
+
+ detect_partial_match_to(common, label);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ set_jumps(no_char1_match, LABEL());
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf)
+ {
+ set_jumps(no_match, LABEL());
+ if (use_tmp)
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0);
+ OP1(SLJIT_MOV, base, offset0, TMP3, 0);
+ }
+ else
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ }
+ else
+#endif
+ {
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ set_jumps(no_match, LABEL());
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ }
+
+ if (early_fail_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0);
+ }
+
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
+ break;
+
+ case OP_MINSTAR:
+ if (private_data_ptr == 0)
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
+ if (early_fail_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0);
+ break;
+
+ case OP_MINUPTO:
+ SLJIT_ASSERT(early_fail_ptr == 0);
+ if (private_data_ptr == 0)
+ allocate_stack(common, 2);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ OP1(SLJIT_MOV, base, offset1, SLJIT_IMM, max + 1);
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
+ break;
+
+ case OP_QUERY:
+ case OP_MINQUERY:
+ SLJIT_ASSERT(early_fail_ptr == 0);
+ if (private_data_ptr == 0)
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ if (opcode == OP_QUERY)
+ compile_char1_matchingpath(common, type, cc, &BACKTRACK_AS(char_iterator_backtrack)->u.backtracks, TRUE);
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
+ break;
+
+ case OP_EXACT:
+ break;
+
+ case OP_POSSTAR:
+#if defined SUPPORT_UNICODE
+ if (type == OP_ALLANY && !common->invalid_utf)
+#else
+ if (type == OP_ALLANY)
+#endif
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, STR_END, 0);
+ process_partial_match(common);
+ if (early_fail_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_END, 0);
+ break;
+ }
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf)
+ {
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0);
+ detect_partial_match(common, &no_match);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &no_match, FALSE);
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0);
+ detect_partial_match_to(common, label);
+
+ set_jumps(no_match, LABEL());
+ OP1(SLJIT_MOV, STR_PTR, 0, tmp_base, tmp_offset);
+ if (early_fail_ptr != 0)
+ {
+ if (!HAS_VIRTUAL_REGISTERS && tmp_base == TMP3)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, TMP3, 0);
+ else
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0);
+ }
+ break;
+ }
+#endif
+
+ detect_partial_match(common, &no_match);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE);
+ detect_partial_match_to(common, label);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ set_jumps(no_char1_match, LABEL());
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ set_jumps(no_match, LABEL());
+ if (early_fail_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0);
+ break;
+
+ case OP_POSUPTO:
+ SLJIT_ASSERT(early_fail_ptr == 0);
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, STR_PTR, 0);
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max);
+
+ detect_partial_match(common, &no_match);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &no_match, FALSE);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, STR_PTR, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ add_jump(compiler, &no_match, JUMP(SLJIT_ZERO));
+ detect_partial_match_to(common, label);
+
+ set_jumps(no_match, LABEL());
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1);
+ break;
+ }
+#endif
+
+ if (type == OP_ALLANY)
+ {
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(max));
+
+ if (common->mode == PCRE2_JIT_COMPLETE)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, STR_PTR, 0, STR_END, 0);
+ CMOV(SLJIT_GREATER, STR_PTR, STR_END, 0);
+ }
+ else
+ {
+ jump = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, STR_END, 0);
+ process_partial_match(common);
+ JUMPHERE(jump);
+ }
+ break;
+ }
+
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max);
+
+ detect_partial_match(common, &no_match);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ add_jump(compiler, &no_match, JUMP(SLJIT_ZERO));
+ detect_partial_match_to(common, label);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ set_jumps(no_char1_match, LABEL());
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ set_jumps(no_match, LABEL());
+ break;
+
+ case OP_POSQUERY:
+ SLJIT_ASSERT(early_fail_ptr == 0);
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0);
+ compile_char1_matchingpath(common, type, cc, &no_match, TRUE);
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0);
+ set_jumps(no_match, LABEL());
+ OP1(SLJIT_MOV, STR_PTR, 0, tmp_base, tmp_offset);
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+count_match(common);
+return end;
+}
+
+static SLJIT_INLINE PCRE2_SPTR compile_fail_accept_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+
+PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL);
+
+if (*cc == OP_FAIL)
+ {
+ add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_JUMP));
+ return cc + 1;
+ }
+
+if (*cc == OP_ACCEPT && common->currententry == NULL && (common->re->overall_options & PCRE2_ENDANCHORED) != 0)
+ add_jump(compiler, &common->reset_match, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0));
+
+if (*cc == OP_ASSERT_ACCEPT || common->currententry != NULL || !common->might_be_empty)
+ {
+ /* No need to check notempty conditions. */
+ if (common->accept_label == NULL)
+ add_jump(compiler, &common->accept, JUMP(SLJIT_JUMP));
+ else
+ JUMPTO(SLJIT_JUMP, common->accept_label);
+ return cc + 1;
+ }
+
+if (common->accept_label == NULL)
+ add_jump(compiler, &common->accept, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)));
+else
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), common->accept_label);
+
+if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV_U32, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, options));
+ }
+else
+ OP1(SLJIT_MOV_U32, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, options));
+
+OP2U(SLJIT_AND | SLJIT_SET_Z, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY);
+add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_NOT_ZERO));
+OP2U(SLJIT_AND | SLJIT_SET_Z, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART);
+if (common->accept_label == NULL)
+ add_jump(compiler, &common->accept, JUMP(SLJIT_ZERO));
+else
+ JUMPTO(SLJIT_ZERO, common->accept_label);
+
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(HAS_VIRTUAL_REGISTERS ? TMP1 : ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, str));
+if (common->accept_label == NULL)
+ add_jump(compiler, &common->accept, CMP(SLJIT_NOT_EQUAL, TMP2, 0, STR_PTR, 0));
+else
+ CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, STR_PTR, 0, common->accept_label);
+add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_JUMP));
+return cc + 1;
+}
+
+static SLJIT_INLINE PCRE2_SPTR compile_close_matchingpath(compiler_common *common, PCRE2_SPTR cc)
+{
+DEFINE_COMPILER;
+int offset = GET2(cc, 1);
+BOOL optimized_cbracket = common->optimized_cbracket[offset] != 0;
+
+/* Data will be discarded anyway... */
+if (common->currententry != NULL)
+ return cc + 1 + IMM2_SIZE;
+
+if (!optimized_cbracket)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR_PRIV(offset));
+offset <<= 1;
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0);
+if (!optimized_cbracket)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0);
+return cc + 1 + IMM2_SIZE;
+}
+
+static SLJIT_INLINE PCRE2_SPTR compile_control_verb_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+PCRE2_UCHAR opcode = *cc;
+PCRE2_SPTR ccend = cc + 1;
+
+if (opcode == OP_COMMIT_ARG || opcode == OP_PRUNE_ARG ||
+ opcode == OP_SKIP_ARG || opcode == OP_THEN_ARG)
+ ccend += 2 + cc[1];
+
+PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL);
+
+if (opcode == OP_SKIP)
+ {
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ return ccend;
+ }
+
+if (opcode == OP_COMMIT_ARG || opcode == OP_PRUNE_ARG || opcode == OP_THEN_ARG)
+ {
+ if (HAS_VIRTUAL_REGISTERS)
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)(cc + 2));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, TMP2, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(HAS_VIRTUAL_REGISTERS ? TMP1 : ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, mark_ptr), TMP2, 0);
+ }
+
+return ccend;
+}
+
+static PCRE2_UCHAR then_trap_opcode[1] = { OP_THEN_TRAP };
+
+static SLJIT_INLINE void compile_then_trap_matchingpath(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+BOOL needs_control_head;
+int size;
+
+PUSH_BACKTRACK_NOVALUE(sizeof(then_trap_backtrack), cc);
+common->then_trap = BACKTRACK_AS(then_trap_backtrack);
+BACKTRACK_AS(then_trap_backtrack)->common.cc = then_trap_opcode;
+BACKTRACK_AS(then_trap_backtrack)->start = (sljit_sw)(cc - common->start);
+BACKTRACK_AS(then_trap_backtrack)->framesize = get_framesize(common, cc, ccend, FALSE, &needs_control_head);
+
+size = BACKTRACK_AS(then_trap_backtrack)->framesize;
+size = 3 + (size < 0 ? 0 : size);
+
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+allocate_stack(common, size);
+if (size > 3)
+ OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0, SLJIT_IMM, (size - 3) * sizeof(sljit_sw));
+else
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0);
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 1), SLJIT_IMM, BACKTRACK_AS(then_trap_backtrack)->start);
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 2), SLJIT_IMM, type_then_trap);
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 3), TMP2, 0);
+
+size = BACKTRACK_AS(then_trap_backtrack)->framesize;
+if (size >= 0)
+ init_frame(common, cc, ccend, size - 1, 0);
+}
+
+static void compile_matchingpath(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+BOOL has_then_trap = FALSE;
+then_trap_backtrack *save_then_trap = NULL;
+
+SLJIT_ASSERT(*ccend == OP_END || (*ccend >= OP_ALT && *ccend <= OP_KETRPOS));
+
+if (common->has_then && common->then_offsets[cc - common->start] != 0)
+ {
+ SLJIT_ASSERT(*ccend != OP_END && common->control_head_ptr != 0);
+ has_then_trap = TRUE;
+ save_then_trap = common->then_trap;
+ /* Tail item on backtrack. */
+ compile_then_trap_matchingpath(common, cc, ccend, parent);
+ }
+
+while (cc < ccend)
+ {
+ switch(*cc)
+ {
+ case OP_SOD:
+ case OP_SOM:
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ case OP_EODN:
+ case OP_EOD:
+ case OP_DOLL:
+ case OP_DOLLM:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_REVERSE:
+ cc = compile_simple_assertion_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks);
+ break;
+
+ case OP_NOT_DIGIT:
+ case OP_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ case OP_WORDCHAR:
+ case OP_ANY:
+ case OP_ALLANY:
+ case OP_ANYBYTE:
+ case OP_NOTPROP:
+ case OP_PROP:
+ case OP_ANYNL:
+ case OP_NOT_HSPACE:
+ case OP_HSPACE:
+ case OP_NOT_VSPACE:
+ case OP_VSPACE:
+ case OP_EXTUNI:
+ case OP_NOT:
+ case OP_NOTI:
+ cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE);
+ break;
+
+ case OP_SET_SOM:
+ PUSH_BACKTRACK_NOVALUE(sizeof(backtrack_common), cc);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0));
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
+ cc++;
+ break;
+
+ case OP_CHAR:
+ case OP_CHARI:
+ if (common->mode == PCRE2_JIT_COMPLETE)
+ cc = compile_charn_matchingpath(common, cc, ccend, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks);
+ else
+ cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE);
+ break;
+
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_EXACT:
+ case OP_POSSTAR:
+ case OP_POSPLUS:
+ case OP_POSQUERY:
+ case OP_POSUPTO:
+ case OP_STARI:
+ case OP_MINSTARI:
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_UPTOI:
+ case OP_MINUPTOI:
+ case OP_EXACTI:
+ case OP_POSSTARI:
+ case OP_POSPLUSI:
+ case OP_POSQUERYI:
+ case OP_POSUPTOI:
+ case OP_NOTSTAR:
+ case OP_NOTMINSTAR:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTQUERY:
+ case OP_NOTMINQUERY:
+ case OP_NOTUPTO:
+ case OP_NOTMINUPTO:
+ case OP_NOTEXACT:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSUPTO:
+ case OP_NOTSTARI:
+ case OP_NOTMINSTARI:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUSI:
+ case OP_NOTQUERYI:
+ case OP_NOTMINQUERYI:
+ case OP_NOTUPTOI:
+ case OP_NOTMINUPTOI:
+ case OP_NOTEXACTI:
+ case OP_NOTPOSSTARI:
+ case OP_NOTPOSPLUSI:
+ case OP_NOTPOSQUERYI:
+ case OP_NOTPOSUPTOI:
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEEXACT:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ case OP_TYPEPOSQUERY:
+ case OP_TYPEPOSUPTO:
+ cc = compile_iterator_matchingpath(common, cc, parent);
+ break;
+
+ case OP_CLASS:
+ case OP_NCLASS:
+ if (cc[1 + (32 / sizeof(PCRE2_UCHAR))] >= OP_CRSTAR && cc[1 + (32 / sizeof(PCRE2_UCHAR))] <= OP_CRPOSRANGE)
+ cc = compile_iterator_matchingpath(common, cc, parent);
+ else
+ cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE);
+ break;
+
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ case OP_XCLASS:
+ if (*(cc + GET(cc, 1)) >= OP_CRSTAR && *(cc + GET(cc, 1)) <= OP_CRPOSRANGE)
+ cc = compile_iterator_matchingpath(common, cc, parent);
+ else
+ cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE);
+ break;
+#endif
+
+ case OP_REF:
+ case OP_REFI:
+ if (cc[1 + IMM2_SIZE] >= OP_CRSTAR && cc[1 + IMM2_SIZE] <= OP_CRPOSRANGE)
+ cc = compile_ref_iterator_matchingpath(common, cc, parent);
+ else
+ {
+ compile_ref_matchingpath(common, cc, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE, FALSE);
+ cc += 1 + IMM2_SIZE;
+ }
+ break;
+
+ case OP_DNREF:
+ case OP_DNREFI:
+ if (cc[1 + 2 * IMM2_SIZE] >= OP_CRSTAR && cc[1 + 2 * IMM2_SIZE] <= OP_CRPOSRANGE)
+ cc = compile_ref_iterator_matchingpath(common, cc, parent);
+ else
+ {
+ compile_dnref_search(common, cc, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks);
+ compile_ref_matchingpath(common, cc, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE, FALSE);
+ cc += 1 + 2 * IMM2_SIZE;
+ }
+ break;
+
+ case OP_RECURSE:
+ cc = compile_recurse_matchingpath(common, cc, parent);
+ break;
+
+ case OP_CALLOUT:
+ case OP_CALLOUT_STR:
+ cc = compile_callout_matchingpath(common, cc, parent);
+ break;
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ PUSH_BACKTRACK_NOVALUE(sizeof(assert_backtrack), cc);
+ cc = compile_assert_matchingpath(common, cc, BACKTRACK_AS(assert_backtrack), FALSE);
+ break;
+
+ case OP_BRAMINZERO:
+ PUSH_BACKTRACK_NOVALUE(sizeof(braminzero_backtrack), cc);
+ cc = bracketend(cc + 1);
+ if (*(cc - 1 - LINK_SIZE) != OP_KETRMIN)
+ {
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ }
+ else
+ {
+ allocate_stack(common, 2);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), STR_PTR, 0);
+ }
+ BACKTRACK_AS(braminzero_backtrack)->matchingpath = LABEL();
+ count_match(common);
+ break;
+
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_BRA:
+ case OP_CBRA:
+ case OP_COND:
+ case OP_SBRA:
+ case OP_SCBRA:
+ case OP_SCOND:
+ cc = compile_bracket_matchingpath(common, cc, parent);
+ break;
+
+ case OP_BRAZERO:
+ if (cc[1] > OP_ASSERTBACK_NOT)
+ cc = compile_bracket_matchingpath(common, cc, parent);
+ else
+ {
+ PUSH_BACKTRACK_NOVALUE(sizeof(assert_backtrack), cc);
+ cc = compile_assert_matchingpath(common, cc, BACKTRACK_AS(assert_backtrack), FALSE);
+ }
+ break;
+
+ case OP_BRAPOS:
+ case OP_CBRAPOS:
+ case OP_SBRAPOS:
+ case OP_SCBRAPOS:
+ case OP_BRAPOSZERO:
+ cc = compile_bracketpos_matchingpath(common, cc, parent);
+ break;
+
+ case OP_MARK:
+ PUSH_BACKTRACK_NOVALUE(sizeof(backtrack_common), cc);
+ SLJIT_ASSERT(common->mark_ptr != 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr);
+ allocate_stack(common, common->has_skip_arg ? 5 : 1);
+ if (HAS_VIRTUAL_REGISTERS)
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(common->has_skip_arg ? 4 : 0), TMP2, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)(cc + 2));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, TMP2, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(HAS_VIRTUAL_REGISTERS ? TMP1 : ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, mark_ptr), TMP2, 0);
+ if (common->has_skip_arg)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, type_mark);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), SLJIT_IMM, (sljit_sw)(cc + 2));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(3), STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP1, 0);
+ }
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_PRUNE:
+ case OP_PRUNE_ARG:
+ case OP_SKIP:
+ case OP_SKIP_ARG:
+ case OP_THEN:
+ case OP_THEN_ARG:
+ case OP_COMMIT:
+ case OP_COMMIT_ARG:
+ cc = compile_control_verb_matchingpath(common, cc, parent);
+ break;
+
+ case OP_FAIL:
+ case OP_ACCEPT:
+ case OP_ASSERT_ACCEPT:
+ cc = compile_fail_accept_matchingpath(common, cc, parent);
+ break;
+
+ case OP_CLOSE:
+ cc = compile_close_matchingpath(common, cc);
+ break;
+
+ case OP_SKIPZERO:
+ cc = bracketend(cc + 1);
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ return;
+ }
+ if (cc == NULL)
+ return;
+ }
+
+if (has_then_trap)
+ {
+ /* Head item on backtrack. */
+ PUSH_BACKTRACK_NOVALUE(sizeof(then_trap_backtrack), cc);
+ BACKTRACK_AS(then_trap_backtrack)->common.cc = then_trap_opcode;
+ BACKTRACK_AS(then_trap_backtrack)->then_trap = common->then_trap;
+ common->then_trap = save_then_trap;
+ }
+SLJIT_ASSERT(cc == ccend);
+}
+
+#undef PUSH_BACKTRACK
+#undef PUSH_BACKTRACK_NOVALUE
+#undef BACKTRACK_AS
+
+#define COMPILE_BACKTRACKINGPATH(current) \
+ do \
+ { \
+ compile_backtrackingpath(common, (current)); \
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) \
+ return; \
+ } \
+ while (0)
+
+#define CURRENT_AS(type) ((type *)current)
+
+static void compile_iterator_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+PCRE2_SPTR cc = current->cc;
+PCRE2_UCHAR opcode;
+PCRE2_UCHAR type;
+sljit_u32 max = 0, exact;
+struct sljit_label *label = NULL;
+struct sljit_jump *jump = NULL;
+jump_list *jumplist = NULL;
+PCRE2_SPTR end;
+int private_data_ptr = PRIVATE_DATA(cc);
+int base = (private_data_ptr == 0) ? SLJIT_MEM1(STACK_TOP) : SLJIT_MEM1(SLJIT_SP);
+int offset0 = (private_data_ptr == 0) ? STACK(0) : private_data_ptr;
+int offset1 = (private_data_ptr == 0) ? STACK(1) : private_data_ptr + SSIZE_OF(sw);
+
+cc = get_iterator_parameters(common, cc, &opcode, &type, &max, &exact, &end);
+
+switch(opcode)
+ {
+ case OP_STAR:
+ case OP_UPTO:
+ if (type == OP_ANYNL || type == OP_EXTUNI)
+ {
+ SLJIT_ASSERT(private_data_ptr == 0);
+ set_jumps(CURRENT_AS(char_iterator_backtrack)->u.backtracks, LABEL());
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(char_iterator_backtrack)->matchingpath);
+ }
+ else
+ {
+ if (CURRENT_AS(char_iterator_backtrack)->u.charpos.enabled)
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ OP1(SLJIT_MOV, TMP2, 0, base, offset1);
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ jump = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0);
+ label = LABEL();
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ if (CURRENT_AS(char_iterator_backtrack)->u.charpos.othercasebit != 0)
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, CURRENT_AS(char_iterator_backtrack)->u.charpos.othercasebit);
+ CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CURRENT_AS(char_iterator_backtrack)->u.charpos.chr, CURRENT_AS(char_iterator_backtrack)->matchingpath);
+ move_back(common, NULL, TRUE);
+ CMPTO(SLJIT_GREATER, STR_PTR, 0, TMP2, 0, label);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ jump = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, base, offset1);
+ move_back(common, NULL, TRUE);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath);
+ }
+ JUMPHERE(jump);
+ if (private_data_ptr == 0)
+ free_stack(common, 2);
+ }
+ break;
+
+ case OP_MINSTAR:
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ compile_char1_matchingpath(common, type, cc, &jumplist, TRUE);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath);
+ set_jumps(jumplist, LABEL());
+ if (private_data_ptr == 0)
+ free_stack(common, 1);
+ break;
+
+ case OP_MINUPTO:
+ OP1(SLJIT_MOV, TMP1, 0, base, offset1);
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+ add_jump(compiler, &jumplist, JUMP(SLJIT_ZERO));
+
+ OP1(SLJIT_MOV, base, offset1, TMP1, 0);
+ compile_char1_matchingpath(common, type, cc, &jumplist, TRUE);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath);
+
+ set_jumps(jumplist, LABEL());
+ if (private_data_ptr == 0)
+ free_stack(common, 2);
+ break;
+
+ case OP_QUERY:
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ OP1(SLJIT_MOV, base, offset0, SLJIT_IMM, 0);
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(char_iterator_backtrack)->matchingpath);
+ jump = JUMP(SLJIT_JUMP);
+ set_jumps(CURRENT_AS(char_iterator_backtrack)->u.backtracks, LABEL());
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ OP1(SLJIT_MOV, base, offset0, SLJIT_IMM, 0);
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath);
+ JUMPHERE(jump);
+ if (private_data_ptr == 0)
+ free_stack(common, 1);
+ break;
+
+ case OP_MINQUERY:
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ OP1(SLJIT_MOV, base, offset0, SLJIT_IMM, 0);
+ jump = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0);
+ compile_char1_matchingpath(common, type, cc, &jumplist, TRUE);
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath);
+ set_jumps(jumplist, LABEL());
+ JUMPHERE(jump);
+ if (private_data_ptr == 0)
+ free_stack(common, 1);
+ break;
+
+ case OP_EXACT:
+ case OP_POSSTAR:
+ case OP_POSQUERY:
+ case OP_POSUPTO:
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+set_jumps(current->topbacktracks, LABEL());
+}
+
+static SLJIT_INLINE void compile_ref_iterator_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+PCRE2_SPTR cc = current->cc;
+BOOL ref = (*cc == OP_REF || *cc == OP_REFI);
+PCRE2_UCHAR type;
+
+type = cc[ref ? 1 + IMM2_SIZE : 1 + 2 * IMM2_SIZE];
+
+if ((type & 0x1) == 0)
+ {
+ /* Maximize case. */
+ set_jumps(current->topbacktracks, LABEL());
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(ref_iterator_backtrack)->matchingpath);
+ return;
+ }
+
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(ref_iterator_backtrack)->matchingpath);
+set_jumps(current->topbacktracks, LABEL());
+free_stack(common, ref ? 2 : 3);
+}
+
+static SLJIT_INLINE void compile_recurse_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+recurse_entry *entry;
+
+if (!CURRENT_AS(recurse_backtrack)->inlined_pattern)
+ {
+ entry = CURRENT_AS(recurse_backtrack)->entry;
+ if (entry->backtrack_label == NULL)
+ add_jump(compiler, &entry->backtrack_calls, JUMP(SLJIT_FAST_CALL));
+ else
+ JUMPTO(SLJIT_FAST_CALL, entry->backtrack_label);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, CURRENT_AS(recurse_backtrack)->matchingpath);
+ }
+else
+ compile_backtrackingpath(common, current->top);
+
+set_jumps(current->topbacktracks, LABEL());
+}
+
+static void compile_assert_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+PCRE2_SPTR cc = current->cc;
+PCRE2_UCHAR bra = OP_BRA;
+struct sljit_jump *brajump = NULL;
+
+SLJIT_ASSERT(*cc != OP_BRAMINZERO);
+if (*cc == OP_BRAZERO)
+ {
+ bra = *cc;
+ cc++;
+ }
+
+if (bra == OP_BRAZERO)
+ {
+ SLJIT_ASSERT(current->topbacktracks == NULL);
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ }
+
+if (CURRENT_AS(assert_backtrack)->framesize < 0)
+ {
+ set_jumps(current->topbacktracks, LABEL());
+
+ if (bra == OP_BRAZERO)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(assert_backtrack)->matchingpath);
+ free_stack(common, 1);
+ }
+ return;
+ }
+
+if (bra == OP_BRAZERO)
+ {
+ if (*cc == OP_ASSERT_NOT || *cc == OP_ASSERTBACK_NOT)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(assert_backtrack)->matchingpath);
+ free_stack(common, 1);
+ return;
+ }
+ free_stack(common, 1);
+ brajump = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0);
+ }
+
+if (*cc == OP_ASSERT || *cc == OP_ASSERTBACK)
+ {
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr);
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(assert_backtrack)->framesize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr, TMP1, 0);
+
+ set_jumps(current->topbacktracks, LABEL());
+ }
+else
+ set_jumps(current->topbacktracks, LABEL());
+
+if (bra == OP_BRAZERO)
+ {
+ /* We know there is enough place on the stack. */
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(assert_backtrack)->matchingpath);
+ JUMPHERE(brajump);
+ }
+}
+
+static void compile_bracket_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+int opcode, stacksize, alt_count, alt_max;
+int offset = 0;
+int private_data_ptr = CURRENT_AS(bracket_backtrack)->private_data_ptr;
+int repeat_ptr = 0, repeat_type = 0, repeat_count = 0;
+PCRE2_SPTR cc = current->cc;
+PCRE2_SPTR ccbegin;
+PCRE2_SPTR ccprev;
+PCRE2_UCHAR bra = OP_BRA;
+PCRE2_UCHAR ket;
+assert_backtrack *assert;
+BOOL has_alternatives;
+BOOL needs_control_head = FALSE;
+struct sljit_jump *brazero = NULL;
+struct sljit_jump *next_alt = NULL;
+struct sljit_jump *once = NULL;
+struct sljit_jump *cond = NULL;
+struct sljit_label *rmin_label = NULL;
+struct sljit_label *exact_label = NULL;
+struct sljit_put_label *put_label = NULL;
+
+if (*cc == OP_BRAZERO || *cc == OP_BRAMINZERO)
+ {
+ bra = *cc;
+ cc++;
+ }
+
+opcode = *cc;
+ccbegin = bracketend(cc) - 1 - LINK_SIZE;
+ket = *ccbegin;
+if (ket == OP_KET && PRIVATE_DATA(ccbegin) != 0)
+ {
+ repeat_ptr = PRIVATE_DATA(ccbegin);
+ repeat_type = PRIVATE_DATA(ccbegin + 2);
+ repeat_count = PRIVATE_DATA(ccbegin + 3);
+ SLJIT_ASSERT(repeat_type != 0 && repeat_count != 0);
+ if (repeat_type == OP_UPTO)
+ ket = OP_KETRMAX;
+ if (repeat_type == OP_MINUPTO)
+ ket = OP_KETRMIN;
+ }
+ccbegin = cc;
+cc += GET(cc, 1);
+has_alternatives = *cc == OP_ALT;
+if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND))
+ has_alternatives = (ccbegin[1 + LINK_SIZE] >= OP_ASSERT && ccbegin[1 + LINK_SIZE] <= OP_ASSERTBACK_NOT) || CURRENT_AS(bracket_backtrack)->u.condfailed != NULL;
+if (opcode == OP_CBRA || opcode == OP_SCBRA)
+ offset = (GET2(ccbegin, 1 + LINK_SIZE)) << 1;
+if (SLJIT_UNLIKELY(opcode == OP_COND) && (*cc == OP_KETRMAX || *cc == OP_KETRMIN))
+ opcode = OP_SCOND;
+
+alt_max = has_alternatives ? no_alternatives(ccbegin) : 0;
+
+/* Decoding the needs_control_head in framesize. */
+if (opcode == OP_ONCE)
+ {
+ needs_control_head = (CURRENT_AS(bracket_backtrack)->u.framesize & 0x1) != 0;
+ CURRENT_AS(bracket_backtrack)->u.framesize >>= 1;
+ }
+
+if (ket != OP_KET && repeat_type != 0)
+ {
+ /* TMP1 is used in OP_KETRMIN below. */
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ if (repeat_type == OP_UPTO)
+ OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), repeat_ptr, TMP1, 0, SLJIT_IMM, 1);
+ else
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), repeat_ptr, TMP1, 0);
+ }
+
+if (ket == OP_KETRMAX)
+ {
+ if (bra == OP_BRAZERO)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ brazero = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0);
+ }
+ }
+else if (ket == OP_KETRMIN)
+ {
+ if (bra != OP_BRAMINZERO)
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ if (repeat_type != 0)
+ {
+ /* TMP1 was set a few lines above. */
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, CURRENT_AS(bracket_backtrack)->recursive_matchingpath);
+ /* Drop STR_PTR for non-greedy plus quantifier. */
+ if (opcode != OP_ONCE)
+ free_stack(common, 1);
+ }
+ else if (opcode >= OP_SBRA || opcode == OP_ONCE)
+ {
+ /* Checking zero-length iteration. */
+ if (opcode != OP_ONCE || CURRENT_AS(bracket_backtrack)->u.framesize < 0)
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, CURRENT_AS(bracket_backtrack)->recursive_matchingpath);
+ else
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), STACK(-CURRENT_AS(bracket_backtrack)->u.framesize - 2), CURRENT_AS(bracket_backtrack)->recursive_matchingpath);
+ }
+ /* Drop STR_PTR for non-greedy plus quantifier. */
+ if (opcode != OP_ONCE)
+ free_stack(common, 1);
+ }
+ else
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_backtrack)->recursive_matchingpath);
+ }
+ rmin_label = LABEL();
+ if (repeat_type != 0)
+ OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
+ }
+else if (bra == OP_BRAZERO)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ brazero = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0);
+ }
+else if (repeat_type == OP_EXACT)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
+ exact_label = LABEL();
+ }
+
+if (offset != 0)
+ {
+ if (common->capture_last_ptr != 0)
+ {
+ SLJIT_ASSERT(common->optimized_cbracket[offset >> 1] == 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, TMP1, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(2));
+ free_stack(common, 3);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP2, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP1, 0);
+ }
+ else if (common->optimized_cbracket[offset >> 1] == 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ free_stack(common, 2);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP2, 0);
+ }
+ }
+
+if (SLJIT_UNLIKELY(opcode == OP_ONCE))
+ {
+ if (CURRENT_AS(bracket_backtrack)->u.framesize >= 0)
+ {
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(bracket_backtrack)->u.framesize - 1) * sizeof(sljit_sw));
+ }
+ once = JUMP(SLJIT_JUMP);
+ }
+else if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND))
+ {
+ if (has_alternatives)
+ {
+ /* Always exactly one alternative. */
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+
+ alt_max = 2;
+ next_alt = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0);
+ }
+ }
+else if (has_alternatives)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+
+ if (alt_max > 3)
+ {
+ sljit_emit_ijump(compiler, SLJIT_JUMP, TMP1, 0);
+
+ SLJIT_ASSERT(CURRENT_AS(bracket_backtrack)->u.matching_put_label);
+ sljit_set_put_label(CURRENT_AS(bracket_backtrack)->u.matching_put_label, LABEL());
+ sljit_emit_op0(compiler, SLJIT_ENDBR);
+ }
+ else
+ next_alt = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0);
+ }
+
+COMPILE_BACKTRACKINGPATH(current->top);
+if (current->topbacktracks)
+ set_jumps(current->topbacktracks, LABEL());
+
+if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND))
+ {
+ /* Conditional block always has at most one alternative. */
+ if (ccbegin[1 + LINK_SIZE] >= OP_ASSERT && ccbegin[1 + LINK_SIZE] <= OP_ASSERTBACK_NOT)
+ {
+ SLJIT_ASSERT(has_alternatives);
+ assert = CURRENT_AS(bracket_backtrack)->u.assert;
+ if (assert->framesize >= 0 && (ccbegin[1 + LINK_SIZE] == OP_ASSERT || ccbegin[1 + LINK_SIZE] == OP_ASSERTBACK))
+ {
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr);
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (assert->framesize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, TMP1, 0);
+ }
+ cond = JUMP(SLJIT_JUMP);
+ set_jumps(CURRENT_AS(bracket_backtrack)->u.assert->condfailed, LABEL());
+ }
+ else if (CURRENT_AS(bracket_backtrack)->u.condfailed != NULL)
+ {
+ SLJIT_ASSERT(has_alternatives);
+ cond = JUMP(SLJIT_JUMP);
+ set_jumps(CURRENT_AS(bracket_backtrack)->u.condfailed, LABEL());
+ }
+ else
+ SLJIT_ASSERT(!has_alternatives);
+ }
+
+if (has_alternatives)
+ {
+ alt_count = 1;
+ do
+ {
+ current->top = NULL;
+ current->topbacktracks = NULL;
+ current->nextbacktracks = NULL;
+ /* Conditional blocks always have an additional alternative, even if it is empty. */
+ if (*cc == OP_ALT)
+ {
+ ccprev = cc + 1 + LINK_SIZE;
+ cc += GET(cc, 1);
+ if (opcode != OP_COND && opcode != OP_SCOND)
+ {
+ if (opcode != OP_ONCE)
+ {
+ if (private_data_ptr != 0)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ else
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ }
+ else
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(needs_control_head ? 1 : 0));
+ }
+ compile_matchingpath(common, ccprev, cc, current);
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return;
+
+ if (opcode == OP_ASSERT_NA || opcode == OP_ASSERTBACK_NA)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+
+ if (opcode == OP_SCRIPT_RUN)
+ match_script_run_common(common, private_data_ptr, current);
+ }
+
+ /* Instructions after the current alternative is successfully matched. */
+ /* There is a similar code in compile_bracket_matchingpath. */
+ if (opcode == OP_ONCE)
+ match_once_common(common, ket, CURRENT_AS(bracket_backtrack)->u.framesize, private_data_ptr, has_alternatives, needs_control_head);
+
+ stacksize = 0;
+ if (repeat_type == OP_MINUPTO)
+ {
+ /* We need to preserve the counter. TMP2 will be used below. */
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), repeat_ptr);
+ stacksize++;
+ }
+ if (ket != OP_KET || bra != OP_BRA)
+ stacksize++;
+ if (offset != 0)
+ {
+ if (common->capture_last_ptr != 0)
+ stacksize++;
+ if (common->optimized_cbracket[offset >> 1] == 0)
+ stacksize += 2;
+ }
+ if (opcode != OP_ONCE)
+ stacksize++;
+
+ if (stacksize > 0)
+ allocate_stack(common, stacksize);
+
+ stacksize = 0;
+ if (repeat_type == OP_MINUPTO)
+ {
+ /* TMP2 was set above. */
+ OP2(SLJIT_SUB, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP2, 0, SLJIT_IMM, 1);
+ stacksize++;
+ }
+
+ if (ket != OP_KET || bra != OP_BRA)
+ {
+ if (ket != OP_KET)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0);
+ else
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0);
+ stacksize++;
+ }
+
+ if (offset != 0)
+ stacksize = match_capture_common(common, stacksize, offset, private_data_ptr);
+
+ if (opcode != OP_ONCE)
+ {
+ if (alt_max <= 3)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, alt_count);
+ else
+ put_label = sljit_emit_put_label(compiler, SLJIT_MEM1(STACK_TOP), STACK(stacksize));
+ }
+
+ if (offset != 0 && ket == OP_KETRMAX && common->optimized_cbracket[offset >> 1] != 0)
+ {
+ /* If ket is not OP_KETRMAX, this code path is executed after the jump to alternative_matchingpath. */
+ SLJIT_ASSERT(private_data_ptr == OVECTOR(offset + 0));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0);
+ }
+
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_backtrack)->alternative_matchingpath);
+
+ if (opcode != OP_ONCE)
+ {
+ if (alt_max <= 3)
+ {
+ JUMPHERE(next_alt);
+ alt_count++;
+ if (alt_count < alt_max)
+ {
+ SLJIT_ASSERT(alt_count == 2 && alt_max == 3);
+ next_alt = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 1);
+ }
+ }
+ else
+ {
+ sljit_set_put_label(put_label, LABEL());
+ sljit_emit_op0(compiler, SLJIT_ENDBR);
+ }
+ }
+
+ COMPILE_BACKTRACKINGPATH(current->top);
+ if (current->topbacktracks)
+ set_jumps(current->topbacktracks, LABEL());
+ SLJIT_ASSERT(!current->nextbacktracks);
+ }
+ while (*cc == OP_ALT);
+
+ if (cond != NULL)
+ {
+ SLJIT_ASSERT(opcode == OP_COND || opcode == OP_SCOND);
+ assert = CURRENT_AS(bracket_backtrack)->u.assert;
+ if ((ccbegin[1 + LINK_SIZE] == OP_ASSERT_NOT || ccbegin[1 + LINK_SIZE] == OP_ASSERTBACK_NOT) && assert->framesize >= 0)
+ {
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr);
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (assert->framesize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, TMP1, 0);
+ }
+ JUMPHERE(cond);
+ }
+
+ /* Free the STR_PTR. */
+ if (private_data_ptr == 0)
+ free_stack(common, 1);
+ }
+
+if (offset != 0)
+ {
+ /* Using both tmp register is better for instruction scheduling. */
+ if (common->optimized_cbracket[offset >> 1] != 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ free_stack(common, 2);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP2, 0);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0);
+ }
+ }
+else if (opcode == OP_ASSERT_NA || opcode == OP_ASSERTBACK_NA || opcode == OP_SCRIPT_RUN || opcode == OP_SBRA || opcode == OP_SCOND)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ }
+else if (opcode == OP_ONCE)
+ {
+ cc = ccbegin + GET(ccbegin, 1);
+ stacksize = needs_control_head ? 1 : 0;
+
+ if (CURRENT_AS(bracket_backtrack)->u.framesize >= 0)
+ {
+ /* Reset head and drop saved frame. */
+ stacksize += CURRENT_AS(bracket_backtrack)->u.framesize + ((ket != OP_KET || *cc == OP_ALT) ? 2 : 1);
+ }
+ else if (ket == OP_KETRMAX || (*cc == OP_ALT && ket != OP_KETRMIN))
+ {
+ /* The STR_PTR must be released. */
+ stacksize++;
+ }
+
+ if (stacksize > 0)
+ free_stack(common, stacksize);
+
+ JUMPHERE(once);
+ /* Restore previous private_data_ptr */
+ if (CURRENT_AS(bracket_backtrack)->u.framesize >= 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-CURRENT_AS(bracket_backtrack)->u.framesize - 1));
+ else if (ket == OP_KETRMIN)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ /* See the comment below. */
+ free_stack(common, 2);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0);
+ }
+ }
+
+if (repeat_type == OP_EXACT)
+ {
+ OP2(SLJIT_ADD, TMP1, 0, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), repeat_ptr, TMP1, 0);
+ CMPTO(SLJIT_LESS_EQUAL, TMP1, 0, SLJIT_IMM, repeat_count, exact_label);
+ }
+else if (ket == OP_KETRMAX)
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ if (bra != OP_BRAZERO)
+ free_stack(common, 1);
+
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(bracket_backtrack)->recursive_matchingpath);
+ if (bra == OP_BRAZERO)
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_backtrack)->zero_matchingpath);
+ JUMPHERE(brazero);
+ free_stack(common, 1);
+ }
+ }
+else if (ket == OP_KETRMIN)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+
+ /* OP_ONCE removes everything in case of a backtrack, so we don't
+ need to explicitly release the STR_PTR. The extra release would
+ affect badly the free_stack(2) above. */
+ if (opcode != OP_ONCE)
+ free_stack(common, 1);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, rmin_label);
+ if (opcode == OP_ONCE)
+ free_stack(common, bra == OP_BRAMINZERO ? 2 : 1);
+ else if (bra == OP_BRAMINZERO)
+ free_stack(common, 1);
+ }
+else if (bra == OP_BRAZERO)
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_backtrack)->zero_matchingpath);
+ JUMPHERE(brazero);
+ }
+}
+
+static SLJIT_INLINE void compile_bracketpos_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+int offset;
+struct sljit_jump *jump;
+
+if (CURRENT_AS(bracketpos_backtrack)->framesize < 0)
+ {
+ if (*current->cc == OP_CBRAPOS || *current->cc == OP_SCBRAPOS)
+ {
+ offset = (GET2(current->cc, 1 + LINK_SIZE)) << 1;
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0);
+ if (common->capture_last_ptr != 0)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(2));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP2, 0);
+ if (common->capture_last_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, TMP1, 0);
+ }
+ set_jumps(current->topbacktracks, LABEL());
+ free_stack(common, CURRENT_AS(bracketpos_backtrack)->stacksize);
+ return;
+ }
+
+OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(bracketpos_backtrack)->private_data_ptr);
+add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(bracketpos_backtrack)->framesize - 1) * sizeof(sljit_sw));
+
+if (current->topbacktracks)
+ {
+ jump = JUMP(SLJIT_JUMP);
+ set_jumps(current->topbacktracks, LABEL());
+ /* Drop the stack frame. */
+ free_stack(common, CURRENT_AS(bracketpos_backtrack)->stacksize);
+ JUMPHERE(jump);
+ }
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(bracketpos_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-CURRENT_AS(bracketpos_backtrack)->framesize - 1));
+}
+
+static SLJIT_INLINE void compile_braminzero_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+assert_backtrack backtrack;
+
+current->top = NULL;
+current->topbacktracks = NULL;
+current->nextbacktracks = NULL;
+if (current->cc[1] > OP_ASSERTBACK_NOT)
+ {
+ /* Manual call of compile_bracket_matchingpath and compile_bracket_backtrackingpath. */
+ compile_bracket_matchingpath(common, current->cc, current);
+ compile_bracket_backtrackingpath(common, current->top);
+ }
+else
+ {
+ memset(&backtrack, 0, sizeof(backtrack));
+ backtrack.common.cc = current->cc;
+ backtrack.matchingpath = CURRENT_AS(braminzero_backtrack)->matchingpath;
+ /* Manual call of compile_assert_matchingpath. */
+ compile_assert_matchingpath(common, current->cc, &backtrack, FALSE);
+ }
+SLJIT_ASSERT(!current->nextbacktracks && !current->topbacktracks);
+}
+
+static SLJIT_INLINE void compile_control_verb_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+PCRE2_UCHAR opcode = *current->cc;
+struct sljit_label *loop;
+struct sljit_jump *jump;
+
+if (opcode == OP_THEN || opcode == OP_THEN_ARG)
+ {
+ if (common->then_trap != NULL)
+ {
+ SLJIT_ASSERT(common->control_head_ptr != 0);
+
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, type_then_trap);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, common->then_trap->start);
+ jump = JUMP(SLJIT_JUMP);
+
+ loop = LABEL();
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ JUMPHERE(jump);
+ CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0, loop);
+ CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(2), TMP2, 0, loop);
+ add_jump(compiler, &common->then_trap->quit, JUMP(SLJIT_JUMP));
+ return;
+ }
+ else if (!common->local_quit_available && common->in_positive_assertion)
+ {
+ add_jump(compiler, &common->positive_assertion_quit, JUMP(SLJIT_JUMP));
+ return;
+ }
+ }
+
+if (common->local_quit_available)
+ {
+ /* Abort match with a fail. */
+ if (common->quit_label == NULL)
+ add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP));
+ else
+ JUMPTO(SLJIT_JUMP, common->quit_label);
+ return;
+ }
+
+if (opcode == OP_SKIP_ARG)
+ {
+ SLJIT_ASSERT(common->control_head_ptr != 0 && TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+ OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)(current->cc + 2));
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(do_search_mark));
+
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_R0, 0);
+ add_jump(compiler, &common->reset_match, CMP(SLJIT_NOT_EQUAL, SLJIT_R0, 0, SLJIT_IMM, 0));
+ return;
+ }
+
+if (opcode == OP_SKIP)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+else
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_IMM, 0);
+add_jump(compiler, &common->reset_match, JUMP(SLJIT_JUMP));
+}
+
+static SLJIT_INLINE void compile_then_trap_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+int size;
+
+if (CURRENT_AS(then_trap_backtrack)->then_trap)
+ {
+ common->then_trap = CURRENT_AS(then_trap_backtrack)->then_trap;
+ return;
+ }
+
+size = CURRENT_AS(then_trap_backtrack)->framesize;
+size = 3 + (size < 0 ? 0 : size);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(size - 3));
+free_stack(common, size);
+jump = JUMP(SLJIT_JUMP);
+
+set_jumps(CURRENT_AS(then_trap_backtrack)->quit, LABEL());
+/* STACK_TOP is set by THEN. */
+if (CURRENT_AS(then_trap_backtrack)->framesize >= 0)
+ {
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(then_trap_backtrack)->framesize - 1) * sizeof(sljit_sw));
+ }
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+free_stack(common, 3);
+
+JUMPHERE(jump);
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, TMP1, 0);
+}
+
+static void compile_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+then_trap_backtrack *save_then_trap = common->then_trap;
+
+while (current)
+ {
+ if (current->nextbacktracks != NULL)
+ set_jumps(current->nextbacktracks, LABEL());
+ switch(*current->cc)
+ {
+ case OP_SET_SOM:
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), TMP1, 0);
+ break;
+
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_EXACT:
+ case OP_POSSTAR:
+ case OP_POSPLUS:
+ case OP_POSQUERY:
+ case OP_POSUPTO:
+ case OP_STARI:
+ case OP_MINSTARI:
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_UPTOI:
+ case OP_MINUPTOI:
+ case OP_EXACTI:
+ case OP_POSSTARI:
+ case OP_POSPLUSI:
+ case OP_POSQUERYI:
+ case OP_POSUPTOI:
+ case OP_NOTSTAR:
+ case OP_NOTMINSTAR:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTQUERY:
+ case OP_NOTMINQUERY:
+ case OP_NOTUPTO:
+ case OP_NOTMINUPTO:
+ case OP_NOTEXACT:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSUPTO:
+ case OP_NOTSTARI:
+ case OP_NOTMINSTARI:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUSI:
+ case OP_NOTQUERYI:
+ case OP_NOTMINQUERYI:
+ case OP_NOTUPTOI:
+ case OP_NOTMINUPTOI:
+ case OP_NOTEXACTI:
+ case OP_NOTPOSSTARI:
+ case OP_NOTPOSPLUSI:
+ case OP_NOTPOSQUERYI:
+ case OP_NOTPOSUPTOI:
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEEXACT:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ case OP_TYPEPOSQUERY:
+ case OP_TYPEPOSUPTO:
+ case OP_CLASS:
+ case OP_NCLASS:
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ case OP_XCLASS:
+#endif
+ compile_iterator_backtrackingpath(common, current);
+ break;
+
+ case OP_REF:
+ case OP_REFI:
+ case OP_DNREF:
+ case OP_DNREFI:
+ compile_ref_iterator_backtrackingpath(common, current);
+ break;
+
+ case OP_RECURSE:
+ compile_recurse_backtrackingpath(common, current);
+ break;
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ compile_assert_backtrackingpath(common, current);
+ break;
+
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_BRA:
+ case OP_CBRA:
+ case OP_COND:
+ case OP_SBRA:
+ case OP_SCBRA:
+ case OP_SCOND:
+ compile_bracket_backtrackingpath(common, current);
+ break;
+
+ case OP_BRAZERO:
+ if (current->cc[1] > OP_ASSERTBACK_NOT)
+ compile_bracket_backtrackingpath(common, current);
+ else
+ compile_assert_backtrackingpath(common, current);
+ break;
+
+ case OP_BRAPOS:
+ case OP_CBRAPOS:
+ case OP_SBRAPOS:
+ case OP_SCBRAPOS:
+ case OP_BRAPOSZERO:
+ compile_bracketpos_backtrackingpath(common, current);
+ break;
+
+ case OP_BRAMINZERO:
+ compile_braminzero_backtrackingpath(common, current);
+ break;
+
+ case OP_MARK:
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(common->has_skip_arg ? 4 : 0));
+ if (common->has_skip_arg)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, common->has_skip_arg ? 5 : 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, TMP1, 0);
+ if (common->has_skip_arg)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, TMP2, 0);
+ break;
+
+ case OP_THEN:
+ case OP_THEN_ARG:
+ case OP_PRUNE:
+ case OP_PRUNE_ARG:
+ case OP_SKIP:
+ case OP_SKIP_ARG:
+ compile_control_verb_backtrackingpath(common, current);
+ break;
+
+ case OP_COMMIT:
+ case OP_COMMIT_ARG:
+ if (!common->local_quit_available)
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH);
+ if (common->quit_label == NULL)
+ add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP));
+ else
+ JUMPTO(SLJIT_JUMP, common->quit_label);
+ break;
+
+ case OP_CALLOUT:
+ case OP_CALLOUT_STR:
+ case OP_FAIL:
+ case OP_ACCEPT:
+ case OP_ASSERT_ACCEPT:
+ set_jumps(current->topbacktracks, LABEL());
+ break;
+
+ case OP_THEN_TRAP:
+ /* A virtual opcode for then traps. */
+ compile_then_trap_backtrackingpath(common, current);
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+ current = current->prev;
+ }
+common->then_trap = save_then_trap;
+}
+
+static SLJIT_INLINE void compile_recurse(compiler_common *common)
+{
+DEFINE_COMPILER;
+PCRE2_SPTR cc = common->start + common->currententry->start;
+PCRE2_SPTR ccbegin = cc + 1 + LINK_SIZE + (*cc == OP_BRA ? 0 : IMM2_SIZE);
+PCRE2_SPTR ccend = bracketend(cc) - (1 + LINK_SIZE);
+uint32_t recurse_flags = 0;
+int private_data_size = get_recurse_data_length(common, ccbegin, ccend, &recurse_flags);
+int alt_count, alt_max, local_size;
+backtrack_common altbacktrack;
+jump_list *match = NULL;
+struct sljit_jump *next_alt = NULL;
+struct sljit_jump *accept_exit = NULL;
+struct sljit_label *quit;
+struct sljit_put_label *put_label = NULL;
+
+/* Recurse captures then. */
+common->then_trap = NULL;
+
+SLJIT_ASSERT(*cc == OP_BRA || *cc == OP_CBRA || *cc == OP_CBRAPOS || *cc == OP_SCBRA || *cc == OP_SCBRAPOS);
+
+alt_max = no_alternatives(cc);
+alt_count = 0;
+
+/* Matching path. */
+SLJIT_ASSERT(common->currententry->entry_label == NULL && common->recursive_head_ptr != 0);
+common->currententry->entry_label = LABEL();
+set_jumps(common->currententry->entry_calls, common->currententry->entry_label);
+
+sljit_emit_fast_enter(compiler, TMP2, 0);
+count_match(common);
+
+local_size = (alt_max > 1) ? 2 : 1;
+
+/* (Reversed) stack layout:
+ [private data][return address][optional: str ptr] ... [optional: alternative index][recursive_head_ptr] */
+
+allocate_stack(common, private_data_size + local_size);
+/* Save return address. */
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1), TMP2, 0);
+
+copy_recurse_data(common, ccbegin, ccend, recurse_copy_from_global, local_size, private_data_size + local_size, recurse_flags);
+
+/* This variable is saved and restored all time when we enter or exit from a recursive context. */
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, STACK_TOP, 0);
+
+if (recurse_flags & recurse_flag_control_head_found)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0);
+
+if (alt_max > 1)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+
+memset(&altbacktrack, 0, sizeof(backtrack_common));
+common->quit_label = NULL;
+common->accept_label = NULL;
+common->quit = NULL;
+common->accept = NULL;
+altbacktrack.cc = ccbegin;
+cc += GET(cc, 1);
+while (1)
+ {
+ altbacktrack.top = NULL;
+ altbacktrack.topbacktracks = NULL;
+
+ if (altbacktrack.cc != ccbegin)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+
+ compile_matchingpath(common, altbacktrack.cc, cc, &altbacktrack);
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return;
+
+ allocate_stack(common, (alt_max > 1 || (recurse_flags & recurse_flag_accept_found)) ? 2 : 1);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr);
+
+ if (alt_max > 1 || (recurse_flags & recurse_flag_accept_found))
+ {
+ if (alt_max > 3)
+ put_label = sljit_emit_put_label(compiler, SLJIT_MEM1(STACK_TOP), STACK(1));
+ else
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, alt_count);
+ }
+
+ add_jump(compiler, &match, JUMP(SLJIT_JUMP));
+
+ if (alt_count == 0)
+ {
+ /* Backtracking path entry. */
+ SLJIT_ASSERT(common->currententry->backtrack_label == NULL);
+ common->currententry->backtrack_label = LABEL();
+ set_jumps(common->currententry->backtrack_calls, common->currententry->backtrack_label);
+
+ sljit_emit_fast_enter(compiler, TMP1, 0);
+
+ if (recurse_flags & recurse_flag_accept_found)
+ accept_exit = CMP(SLJIT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, -1);
+
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ /* Save return address. */
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), STACK(local_size - 1), TMP1, 0);
+
+ copy_recurse_data(common, ccbegin, ccend, recurse_swap_global, local_size, private_data_size + local_size, recurse_flags);
+
+ if (alt_max > 1)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ free_stack(common, 2);
+
+ if (alt_max > 3)
+ {
+ sljit_emit_ijump(compiler, SLJIT_JUMP, TMP1, 0);
+ sljit_set_put_label(put_label, LABEL());
+ sljit_emit_op0(compiler, SLJIT_ENDBR);
+ }
+ else
+ next_alt = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0);
+ }
+ else
+ free_stack(common, (recurse_flags & recurse_flag_accept_found) ? 2 : 1);
+ }
+ else if (alt_max > 3)
+ {
+ sljit_set_put_label(put_label, LABEL());
+ sljit_emit_op0(compiler, SLJIT_ENDBR);
+ }
+ else
+ {
+ JUMPHERE(next_alt);
+ if (alt_count + 1 < alt_max)
+ {
+ SLJIT_ASSERT(alt_count == 1 && alt_max == 3);
+ next_alt = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 1);
+ }
+ }
+
+ alt_count++;
+
+ compile_backtrackingpath(common, altbacktrack.top);
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return;
+ set_jumps(altbacktrack.topbacktracks, LABEL());
+
+ if (*cc != OP_ALT)
+ break;
+
+ altbacktrack.cc = cc + 1 + LINK_SIZE;
+ cc += GET(cc, 1);
+ }
+
+/* No alternative is matched. */
+
+quit = LABEL();
+
+copy_recurse_data(common, ccbegin, ccend, recurse_copy_private_to_global, local_size, private_data_size + local_size, recurse_flags);
+
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1));
+free_stack(common, private_data_size + local_size);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+OP_SRC(SLJIT_FAST_RETURN, TMP2, 0);
+
+if (common->quit != NULL)
+ {
+ SLJIT_ASSERT(recurse_flags & recurse_flag_quit_found);
+
+ set_jumps(common->quit, LABEL());
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr);
+ copy_recurse_data(common, ccbegin, ccend, recurse_copy_shared_to_global, local_size, private_data_size + local_size, recurse_flags);
+ JUMPTO(SLJIT_JUMP, quit);
+ }
+
+if (recurse_flags & recurse_flag_accept_found)
+ {
+ JUMPHERE(accept_exit);
+ free_stack(common, 2);
+
+ /* Save return address. */
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1), TMP1, 0);
+
+ copy_recurse_data(common, ccbegin, ccend, recurse_copy_kept_shared_to_global, local_size, private_data_size + local_size, recurse_flags);
+
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1));
+ free_stack(common, private_data_size + local_size);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+ OP_SRC(SLJIT_FAST_RETURN, TMP2, 0);
+ }
+
+if (common->accept != NULL)
+ {
+ SLJIT_ASSERT(recurse_flags & recurse_flag_accept_found);
+
+ set_jumps(common->accept, LABEL());
+
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr);
+ OP1(SLJIT_MOV, TMP2, 0, STACK_TOP, 0);
+
+ allocate_stack(common, 2);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, -1);
+ }
+
+set_jumps(match, LABEL());
+
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
+
+copy_recurse_data(common, ccbegin, ccend, recurse_swap_global, local_size, private_data_size + local_size, recurse_flags);
+
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP2), STACK(local_size - 1));
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1);
+OP_SRC(SLJIT_FAST_RETURN, TMP2, 0);
+}
+
+#undef COMPILE_BACKTRACKINGPATH
+#undef CURRENT_AS
+
+#define PUBLIC_JIT_COMPILE_CONFIGURATION_OPTIONS \
+ (PCRE2_JIT_INVALID_UTF)
+
+static int jit_compile(pcre2_code *code, sljit_u32 mode)
+{
+pcre2_real_code *re = (pcre2_real_code *)code;
+struct sljit_compiler *compiler;
+backtrack_common rootbacktrack;
+compiler_common common_data;
+compiler_common *common = &common_data;
+const sljit_u8 *tables = re->tables;
+void *allocator_data = &re->memctl;
+int private_data_size;
+PCRE2_SPTR ccend;
+executable_functions *functions;
+void *executable_func;
+sljit_uw executable_size;
+sljit_uw total_length;
+struct sljit_label *mainloop_label = NULL;
+struct sljit_label *continue_match_label;
+struct sljit_label *empty_match_found_label = NULL;
+struct sljit_label *empty_match_backtrack_label = NULL;
+struct sljit_label *reset_match_label;
+struct sljit_label *quit_label;
+struct sljit_jump *jump;
+struct sljit_jump *minlength_check_failed = NULL;
+struct sljit_jump *empty_match = NULL;
+struct sljit_jump *end_anchor_failed = NULL;
+jump_list *reqcu_not_found = NULL;
+
+SLJIT_ASSERT(tables);
+
+#if HAS_VIRTUAL_REGISTERS == 1
+SLJIT_ASSERT(sljit_get_register_index(TMP3) < 0 && sljit_get_register_index(ARGUMENTS) < 0 && sljit_get_register_index(RETURN_ADDR) < 0);
+#elif HAS_VIRTUAL_REGISTERS == 0
+SLJIT_ASSERT(sljit_get_register_index(TMP3) >= 0 && sljit_get_register_index(ARGUMENTS) >= 0 && sljit_get_register_index(RETURN_ADDR) >= 0);
+#else
+#error "Invalid value for HAS_VIRTUAL_REGISTERS"
+#endif
+
+memset(&rootbacktrack, 0, sizeof(backtrack_common));
+memset(common, 0, sizeof(compiler_common));
+common->re = re;
+common->name_table = (PCRE2_SPTR)((uint8_t *)re + sizeof(pcre2_real_code));
+rootbacktrack.cc = common->name_table + re->name_count * re->name_entry_size;
+
+#ifdef SUPPORT_UNICODE
+common->invalid_utf = (mode & PCRE2_JIT_INVALID_UTF) != 0;
+#endif /* SUPPORT_UNICODE */
+mode &= ~PUBLIC_JIT_COMPILE_CONFIGURATION_OPTIONS;
+
+common->start = rootbacktrack.cc;
+common->read_only_data_head = NULL;
+common->fcc = tables + fcc_offset;
+common->lcc = (sljit_sw)(tables + lcc_offset);
+common->mode = mode;
+common->might_be_empty = (re->minlength == 0) || (re->flags & PCRE2_MATCH_EMPTY);
+common->allow_empty_partial = (re->max_lookbehind > 0) || (re->flags & PCRE2_MATCH_EMPTY);
+common->nltype = NLTYPE_FIXED;
+switch(re->newline_convention)
+ {
+ case PCRE2_NEWLINE_CR: common->newline = CHAR_CR; break;
+ case PCRE2_NEWLINE_LF: common->newline = CHAR_NL; break;
+ case PCRE2_NEWLINE_CRLF: common->newline = (CHAR_CR << 8) | CHAR_NL; break;
+ case PCRE2_NEWLINE_ANY: common->newline = (CHAR_CR << 8) | CHAR_NL; common->nltype = NLTYPE_ANY; break;
+ case PCRE2_NEWLINE_ANYCRLF: common->newline = (CHAR_CR << 8) | CHAR_NL; common->nltype = NLTYPE_ANYCRLF; break;
+ case PCRE2_NEWLINE_NUL: common->newline = CHAR_NUL; break;
+ default: return PCRE2_ERROR_INTERNAL;
+ }
+common->nlmax = READ_CHAR_MAX;
+common->nlmin = 0;
+if (re->bsr_convention == PCRE2_BSR_UNICODE)
+ common->bsr_nltype = NLTYPE_ANY;
+else if (re->bsr_convention == PCRE2_BSR_ANYCRLF)
+ common->bsr_nltype = NLTYPE_ANYCRLF;
+else
+ {
+#ifdef BSR_ANYCRLF
+ common->bsr_nltype = NLTYPE_ANYCRLF;
+#else
+ common->bsr_nltype = NLTYPE_ANY;
+#endif
+ }
+common->bsr_nlmax = READ_CHAR_MAX;
+common->bsr_nlmin = 0;
+common->endonly = (re->overall_options & PCRE2_DOLLAR_ENDONLY) != 0;
+common->ctypes = (sljit_sw)(tables + ctypes_offset);
+common->name_count = re->name_count;
+common->name_entry_size = re->name_entry_size;
+common->unset_backref = (re->overall_options & PCRE2_MATCH_UNSET_BACKREF) != 0;
+common->alt_circumflex = (re->overall_options & PCRE2_ALT_CIRCUMFLEX) != 0;
+#ifdef SUPPORT_UNICODE
+/* PCRE_UTF[16|32] have the same value as PCRE_UTF8. */
+common->utf = (re->overall_options & PCRE2_UTF) != 0;
+common->ucp = (re->overall_options & PCRE2_UCP) != 0;
+if (common->utf)
+ {
+ if (common->nltype == NLTYPE_ANY)
+ common->nlmax = 0x2029;
+ else if (common->nltype == NLTYPE_ANYCRLF)
+ common->nlmax = (CHAR_CR > CHAR_NL) ? CHAR_CR : CHAR_NL;
+ else
+ {
+ /* We only care about the first newline character. */
+ common->nlmax = common->newline & 0xff;
+ }
+
+ if (common->nltype == NLTYPE_FIXED)
+ common->nlmin = common->newline & 0xff;
+ else
+ common->nlmin = (CHAR_CR < CHAR_NL) ? CHAR_CR : CHAR_NL;
+
+ if (common->bsr_nltype == NLTYPE_ANY)
+ common->bsr_nlmax = 0x2029;
+ else
+ common->bsr_nlmax = (CHAR_CR > CHAR_NL) ? CHAR_CR : CHAR_NL;
+ common->bsr_nlmin = (CHAR_CR < CHAR_NL) ? CHAR_CR : CHAR_NL;
+ }
+else
+ common->invalid_utf = FALSE;
+#endif /* SUPPORT_UNICODE */
+ccend = bracketend(common->start);
+
+/* Calculate the local space size on the stack. */
+common->ovector_start = LIMIT_MATCH + sizeof(sljit_sw);
+common->optimized_cbracket = (sljit_u8 *)SLJIT_MALLOC(re->top_bracket + 1, allocator_data);
+if (!common->optimized_cbracket)
+ return PCRE2_ERROR_NOMEMORY;
+#if defined DEBUG_FORCE_UNOPTIMIZED_CBRAS && DEBUG_FORCE_UNOPTIMIZED_CBRAS == 1
+memset(common->optimized_cbracket, 0, re->top_bracket + 1);
+#else
+memset(common->optimized_cbracket, 1, re->top_bracket + 1);
+#endif
+
+SLJIT_ASSERT(*common->start == OP_BRA && ccend[-(1 + LINK_SIZE)] == OP_KET);
+#if defined DEBUG_FORCE_UNOPTIMIZED_CBRAS && DEBUG_FORCE_UNOPTIMIZED_CBRAS == 2
+common->capture_last_ptr = common->ovector_start;
+common->ovector_start += sizeof(sljit_sw);
+#endif
+if (!check_opcode_types(common, common->start, ccend))
+ {
+ SLJIT_FREE(common->optimized_cbracket, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+
+/* Checking flags and updating ovector_start. */
+if (mode == PCRE2_JIT_COMPLETE && (re->flags & PCRE2_LASTSET) != 0 && (re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0)
+ {
+ common->req_char_ptr = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ }
+if (mode != PCRE2_JIT_COMPLETE)
+ {
+ common->start_used_ptr = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ if (mode == PCRE2_JIT_PARTIAL_SOFT)
+ {
+ common->hit_start = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ }
+ }
+if ((re->overall_options & (PCRE2_FIRSTLINE | PCRE2_USE_OFFSET_LIMIT)) != 0)
+ {
+ common->match_end_ptr = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ }
+#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD
+common->control_head_ptr = 1;
+#endif
+if (common->control_head_ptr != 0)
+ {
+ common->control_head_ptr = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ }
+if (common->has_set_som)
+ {
+ /* Saving the real start pointer is necessary. */
+ common->start_ptr = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ }
+
+/* Aligning ovector to even number of sljit words. */
+if ((common->ovector_start & sizeof(sljit_sw)) != 0)
+ common->ovector_start += sizeof(sljit_sw);
+
+if (common->start_ptr == 0)
+ common->start_ptr = OVECTOR(0);
+
+/* Capturing brackets cannot be optimized if callouts are allowed. */
+if (common->capture_last_ptr != 0)
+ memset(common->optimized_cbracket, 0, re->top_bracket + 1);
+
+SLJIT_ASSERT(!(common->req_char_ptr != 0 && common->start_used_ptr != 0));
+common->cbra_ptr = OVECTOR_START + (re->top_bracket + 1) * 2 * sizeof(sljit_sw);
+
+total_length = ccend - common->start;
+common->private_data_ptrs = (sljit_s32*)SLJIT_MALLOC(total_length * (sizeof(sljit_s32) + (common->has_then ? 1 : 0)), allocator_data);
+if (!common->private_data_ptrs)
+ {
+ SLJIT_FREE(common->optimized_cbracket, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+memset(common->private_data_ptrs, 0, total_length * sizeof(sljit_s32));
+
+private_data_size = common->cbra_ptr + (re->top_bracket + 1) * sizeof(sljit_sw);
+
+if ((re->overall_options & PCRE2_ANCHORED) == 0 && (re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0 && !common->has_skip_in_assert_back)
+ detect_early_fail(common, common->start, &private_data_size, 0, 0, TRUE);
+
+set_private_data_ptrs(common, &private_data_size, ccend);
+
+SLJIT_ASSERT(common->early_fail_start_ptr <= common->early_fail_end_ptr);
+
+if (private_data_size > SLJIT_MAX_LOCAL_SIZE)
+ {
+ SLJIT_FREE(common->private_data_ptrs, allocator_data);
+ SLJIT_FREE(common->optimized_cbracket, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+
+if (common->has_then)
+ {
+ common->then_offsets = (sljit_u8 *)(common->private_data_ptrs + total_length);
+ memset(common->then_offsets, 0, total_length);
+ set_then_offsets(common, common->start, NULL);
+ }
+
+compiler = sljit_create_compiler(allocator_data, NULL);
+if (!compiler)
+ {
+ SLJIT_FREE(common->optimized_cbracket, allocator_data);
+ SLJIT_FREE(common->private_data_ptrs, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+common->compiler = compiler;
+
+/* Main pcre2_jit_exec entry. */
+SLJIT_ASSERT((private_data_size & (sizeof(sljit_sw) - 1)) == 0);
+sljit_emit_enter(compiler, 0, SLJIT_ARGS1(W, W), 5, 5, 0, 0, private_data_size);
+
+/* Register init. */
+reset_ovector(common, (re->top_bracket + 1) * 2);
+if (common->req_char_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->req_char_ptr, SLJIT_R0, 0);
+
+OP1(SLJIT_MOV, ARGUMENTS, 0, SLJIT_S0, 0);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_S0, 0);
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str));
+OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, end));
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack));
+OP1(SLJIT_MOV_U32, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, limit_match));
+OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, end));
+OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, start));
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LIMIT_MATCH, TMP1, 0);
+
+if (common->early_fail_start_ptr < common->early_fail_end_ptr)
+ reset_early_fail(common);
+
+if (mode == PCRE2_JIT_PARTIAL_SOFT)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, -1);
+if (common->mark_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, SLJIT_IMM, 0);
+if (common->control_head_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0);
+
+/* Main part of the matching */
+if ((re->overall_options & PCRE2_ANCHORED) == 0)
+ {
+ mainloop_label = mainloop_entry(common);
+ continue_match_label = LABEL();
+ /* Forward search if possible. */
+ if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0)
+ {
+ if (mode == PCRE2_JIT_COMPLETE && fast_forward_first_n_chars(common))
+ ;
+ else if ((re->flags & PCRE2_FIRSTSET) != 0)
+ fast_forward_first_char(common);
+ else if ((re->flags & PCRE2_STARTLINE) != 0)
+ fast_forward_newline(common);
+ else if ((re->flags & PCRE2_FIRSTMAPSET) != 0)
+ fast_forward_start_bits(common);
+ }
+ }
+else
+ continue_match_label = LABEL();
+
+if (mode == PCRE2_JIT_COMPLETE && re->minlength > 0 && (re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0)
+ {
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH);
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(re->minlength));
+ minlength_check_failed = CMP(SLJIT_GREATER, TMP2, 0, STR_END, 0);
+ }
+if (common->req_char_ptr != 0)
+ reqcu_not_found = search_requested_char(common, (PCRE2_UCHAR)(re->last_codeunit), (re->flags & PCRE2_LASTCASELESS) != 0, (re->flags & PCRE2_FIRSTSET) != 0);
+
+/* Store the current STR_PTR in OVECTOR(0). */
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), STR_PTR, 0);
+/* Copy the limit of allowed recursions. */
+OP1(SLJIT_MOV, COUNT_MATCH, 0, SLJIT_MEM1(SLJIT_SP), LIMIT_MATCH);
+if (common->capture_last_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, SLJIT_IMM, 0);
+if (common->fast_forward_bc_ptr != NULL)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), PRIVATE_DATA(common->fast_forward_bc_ptr + 1) >> 3, STR_PTR, 0);
+
+if (common->start_ptr != OVECTOR(0))
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_ptr, STR_PTR, 0);
+
+/* Copy the beginning of the string. */
+if (mode == PCRE2_JIT_PARTIAL_SOFT)
+ {
+ jump = CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, -1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0);
+ JUMPHERE(jump);
+ }
+else if (mode == PCRE2_JIT_PARTIAL_HARD)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0);
+
+compile_matchingpath(common, common->start, ccend, &rootbacktrack);
+if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ {
+ sljit_free_compiler(compiler);
+ SLJIT_FREE(common->optimized_cbracket, allocator_data);
+ SLJIT_FREE(common->private_data_ptrs, allocator_data);
+ PRIV(jit_free_rodata)(common->read_only_data_head, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+
+if ((re->overall_options & PCRE2_ENDANCHORED) != 0)
+ end_anchor_failed = CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0);
+
+if (common->might_be_empty)
+ {
+ empty_match = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0));
+ empty_match_found_label = LABEL();
+ }
+
+common->accept_label = LABEL();
+if (common->accept != NULL)
+ set_jumps(common->accept, common->accept_label);
+
+/* This means we have a match. Update the ovector. */
+copy_ovector(common, re->top_bracket + 1);
+common->quit_label = common->abort_label = LABEL();
+if (common->quit != NULL)
+ set_jumps(common->quit, common->quit_label);
+if (common->abort != NULL)
+ set_jumps(common->abort, common->abort_label);
+if (minlength_check_failed != NULL)
+ SET_LABEL(minlength_check_failed, common->abort_label);
+
+sljit_emit_op0(compiler, SLJIT_SKIP_FRAMES_BEFORE_RETURN);
+sljit_emit_return(compiler, SLJIT_MOV, SLJIT_RETURN_REG, 0);
+
+if (common->failed_match != NULL)
+ {
+ SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE);
+ set_jumps(common->failed_match, LABEL());
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH);
+ JUMPTO(SLJIT_JUMP, common->abort_label);
+ }
+
+if ((re->overall_options & PCRE2_ENDANCHORED) != 0)
+ JUMPHERE(end_anchor_failed);
+
+if (mode != PCRE2_JIT_COMPLETE)
+ {
+ common->partialmatchlabel = LABEL();
+ set_jumps(common->partialmatch, common->partialmatchlabel);
+ return_with_partial_match(common, common->quit_label);
+ }
+
+if (common->might_be_empty)
+ empty_match_backtrack_label = LABEL();
+compile_backtrackingpath(common, rootbacktrack.top);
+if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ {
+ sljit_free_compiler(compiler);
+ SLJIT_FREE(common->optimized_cbracket, allocator_data);
+ SLJIT_FREE(common->private_data_ptrs, allocator_data);
+ PRIV(jit_free_rodata)(common->read_only_data_head, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+
+SLJIT_ASSERT(rootbacktrack.prev == NULL);
+reset_match_label = LABEL();
+
+if (mode == PCRE2_JIT_PARTIAL_SOFT)
+ {
+ /* Update hit_start only in the first time. */
+ jump = CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, SLJIT_IMM, -1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, TMP1, 0);
+ JUMPHERE(jump);
+ }
+
+/* Check we have remaining characters. */
+if ((re->overall_options & PCRE2_ANCHORED) == 0 && common->match_end_ptr != 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ }
+
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP),
+ (common->fast_forward_bc_ptr != NULL) ? (PRIVATE_DATA(common->fast_forward_bc_ptr + 1) >> 3) : common->start_ptr);
+
+if ((re->overall_options & PCRE2_ANCHORED) == 0)
+ {
+ if (common->ff_newline_shortcut != NULL)
+ {
+ /* There cannot be more newlines if PCRE2_FIRSTLINE is set. */
+ if ((re->overall_options & PCRE2_FIRSTLINE) == 0)
+ {
+ if (common->match_end_ptr != 0)
+ {
+ OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
+ OP1(SLJIT_MOV, STR_END, 0, TMP1, 0);
+ CMPTO(SLJIT_LESS, STR_PTR, 0, TMP1, 0, common->ff_newline_shortcut);
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+ }
+ else
+ CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, common->ff_newline_shortcut);
+ }
+ }
+ else
+ CMPTO(SLJIT_LESS, STR_PTR, 0, (common->match_end_ptr == 0) ? STR_END : TMP1, 0, mainloop_label);
+ }
+
+/* No more remaining characters. */
+if (reqcu_not_found != NULL)
+ set_jumps(reqcu_not_found, LABEL());
+
+if (mode == PCRE2_JIT_PARTIAL_SOFT)
+ CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, -1, common->partialmatchlabel);
+
+OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH);
+JUMPTO(SLJIT_JUMP, common->quit_label);
+
+flush_stubs(common);
+
+if (common->might_be_empty)
+ {
+ JUMPHERE(empty_match);
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV_U32, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, options));
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY);
+ JUMPTO(SLJIT_NOT_ZERO, empty_match_backtrack_label);
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART);
+ JUMPTO(SLJIT_ZERO, empty_match_found_label);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str));
+ CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, STR_PTR, 0, empty_match_found_label);
+ JUMPTO(SLJIT_JUMP, empty_match_backtrack_label);
+ }
+
+common->fast_forward_bc_ptr = NULL;
+common->early_fail_start_ptr = 0;
+common->early_fail_end_ptr = 0;
+common->currententry = common->entries;
+common->local_quit_available = TRUE;
+quit_label = common->quit_label;
+if (common->currententry != NULL)
+ {
+ /* A free bit for each private data. */
+ common->recurse_bitset_size = ((private_data_size / SSIZE_OF(sw)) + 7) >> 3;
+ SLJIT_ASSERT(common->recurse_bitset_size > 0);
+ common->recurse_bitset = (sljit_u8*)SLJIT_MALLOC(common->recurse_bitset_size, allocator_data);;
+
+ if (common->recurse_bitset != NULL)
+ {
+ do
+ {
+ /* Might add new entries. */
+ compile_recurse(common);
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ break;
+ flush_stubs(common);
+ common->currententry = common->currententry->next;
+ }
+ while (common->currententry != NULL);
+
+ SLJIT_FREE(common->recurse_bitset, allocator_data);
+ }
+
+ if (common->currententry != NULL)
+ {
+ /* The common->recurse_bitset has been freed. */
+ SLJIT_ASSERT(sljit_get_compiler_error(compiler) || common->recurse_bitset == NULL);
+
+ sljit_free_compiler(compiler);
+ SLJIT_FREE(common->optimized_cbracket, allocator_data);
+ SLJIT_FREE(common->private_data_ptrs, allocator_data);
+ PRIV(jit_free_rodata)(common->read_only_data_head, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+ }
+common->local_quit_available = FALSE;
+common->quit_label = quit_label;
+
+/* Allocating stack, returns with PCRE_ERROR_JIT_STACKLIMIT if fails. */
+/* This is a (really) rare case. */
+set_jumps(common->stackalloc, LABEL());
+/* RETURN_ADDR is not a saved register. */
+sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+
+SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1);
+
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, STR_PTR, 0);
+OP1(SLJIT_MOV, SLJIT_R0, 0, ARGUMENTS, 0);
+OP2(SLJIT_SUB, SLJIT_R1, 0, STACK_LIMIT, 0, SLJIT_IMM, STACK_GROWTH_RATE);
+OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, stack));
+OP1(SLJIT_MOV, STACK_LIMIT, 0, TMP2, 0);
+
+sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(sljit_stack_resize));
+
+jump = CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0);
+OP1(SLJIT_MOV, TMP2, 0, STACK_LIMIT, 0);
+OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_RETURN_REG, 0);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
+OP_SRC(SLJIT_FAST_RETURN, TMP1, 0);
+
+/* Allocation failed. */
+JUMPHERE(jump);
+/* We break the return address cache here, but this is a really rare case. */
+OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_JIT_STACKLIMIT);
+JUMPTO(SLJIT_JUMP, common->quit_label);
+
+/* Call limit reached. */
+set_jumps(common->calllimit, LABEL());
+OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_MATCHLIMIT);
+JUMPTO(SLJIT_JUMP, common->quit_label);
+
+if (common->revertframes != NULL)
+ {
+ set_jumps(common->revertframes, LABEL());
+ do_revertframes(common);
+ }
+if (common->wordboundary != NULL)
+ {
+ set_jumps(common->wordboundary, LABEL());
+ check_wordboundary(common);
+ }
+if (common->anynewline != NULL)
+ {
+ set_jumps(common->anynewline, LABEL());
+ check_anynewline(common);
+ }
+if (common->hspace != NULL)
+ {
+ set_jumps(common->hspace, LABEL());
+ check_hspace(common);
+ }
+if (common->vspace != NULL)
+ {
+ set_jumps(common->vspace, LABEL());
+ check_vspace(common);
+ }
+if (common->casefulcmp != NULL)
+ {
+ set_jumps(common->casefulcmp, LABEL());
+ do_casefulcmp(common);
+ }
+if (common->caselesscmp != NULL)
+ {
+ set_jumps(common->caselesscmp, LABEL());
+ do_caselesscmp(common);
+ }
+if (common->reset_match != NULL)
+ {
+ set_jumps(common->reset_match, LABEL());
+ do_reset_match(common, (re->top_bracket + 1) * 2);
+ CMPTO(SLJIT_GREATER, STR_PTR, 0, TMP1, 0, continue_match_label);
+ OP1(SLJIT_MOV, STR_PTR, 0, TMP1, 0);
+ JUMPTO(SLJIT_JUMP, reset_match_label);
+ }
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+if (common->utfreadchar != NULL)
+ {
+ set_jumps(common->utfreadchar, LABEL());
+ do_utfreadchar(common);
+ }
+if (common->utfreadtype8 != NULL)
+ {
+ set_jumps(common->utfreadtype8, LABEL());
+ do_utfreadtype8(common);
+ }
+if (common->utfpeakcharback != NULL)
+ {
+ set_jumps(common->utfpeakcharback, LABEL());
+ do_utfpeakcharback(common);
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+#if PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16
+if (common->utfreadchar_invalid != NULL)
+ {
+ set_jumps(common->utfreadchar_invalid, LABEL());
+ do_utfreadchar_invalid(common);
+ }
+if (common->utfreadnewline_invalid != NULL)
+ {
+ set_jumps(common->utfreadnewline_invalid, LABEL());
+ do_utfreadnewline_invalid(common);
+ }
+if (common->utfmoveback_invalid)
+ {
+ set_jumps(common->utfmoveback_invalid, LABEL());
+ do_utfmoveback_invalid(common);
+ }
+if (common->utfpeakcharback_invalid)
+ {
+ set_jumps(common->utfpeakcharback_invalid, LABEL());
+ do_utfpeakcharback_invalid(common);
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16 */
+if (common->getucd != NULL)
+ {
+ set_jumps(common->getucd, LABEL());
+ do_getucd(common);
+ }
+if (common->getucdtype != NULL)
+ {
+ set_jumps(common->getucdtype, LABEL());
+ do_getucdtype(common);
+ }
+#endif /* SUPPORT_UNICODE */
+
+SLJIT_FREE(common->optimized_cbracket, allocator_data);
+SLJIT_FREE(common->private_data_ptrs, allocator_data);
+
+executable_func = sljit_generate_code(compiler);
+executable_size = sljit_get_generated_code_size(compiler);
+sljit_free_compiler(compiler);
+
+if (executable_func == NULL)
+ {
+ PRIV(jit_free_rodata)(common->read_only_data_head, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+
+/* Reuse the function descriptor if possible. */
+if (re->executable_jit != NULL)
+ functions = (executable_functions *)re->executable_jit;
+else
+ {
+ functions = SLJIT_MALLOC(sizeof(executable_functions), allocator_data);
+ if (functions == NULL)
+ {
+ /* This case is highly unlikely since we just recently
+ freed a lot of memory. Not impossible though. */
+ sljit_free_code(executable_func, NULL);
+ PRIV(jit_free_rodata)(common->read_only_data_head, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+ memset(functions, 0, sizeof(executable_functions));
+ functions->top_bracket = re->top_bracket + 1;
+ functions->limit_match = re->limit_match;
+ re->executable_jit = functions;
+ }
+
+/* Turn mode into an index. */
+if (mode == PCRE2_JIT_COMPLETE)
+ mode = 0;
+else
+ mode = (mode == PCRE2_JIT_PARTIAL_SOFT) ? 1 : 2;
+
+SLJIT_ASSERT(mode < JIT_NUMBER_OF_COMPILE_MODES);
+functions->executable_funcs[mode] = executable_func;
+functions->read_only_data_heads[mode] = common->read_only_data_head;
+functions->executable_sizes[mode] = executable_size;
+return 0;
+}
+
+#endif
+
+/*************************************************
+* JIT compile a Regular Expression *
+*************************************************/
+
+/* This function used JIT to convert a previously-compiled pattern into machine
+code.
+
+Arguments:
+ code a compiled pattern
+ options JIT option bits
+
+Returns: 0: success or (*NOJIT) was used
+ <0: an error code
+*/
+
+#define PUBLIC_JIT_COMPILE_OPTIONS \
+ (PCRE2_JIT_COMPLETE|PCRE2_JIT_PARTIAL_SOFT|PCRE2_JIT_PARTIAL_HARD|PCRE2_JIT_INVALID_UTF)
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_jit_compile(pcre2_code *code, uint32_t options)
+{
+pcre2_real_code *re = (pcre2_real_code *)code;
+#ifdef SUPPORT_JIT
+executable_functions *functions;
+static int executable_allocator_is_working = -1;
+#endif
+
+if (code == NULL)
+ return PCRE2_ERROR_NULL;
+
+if ((options & ~PUBLIC_JIT_COMPILE_OPTIONS) != 0)
+ return PCRE2_ERROR_JIT_BADOPTION;
+
+/* Support for invalid UTF was first introduced in JIT, with the option
+PCRE2_JIT_INVALID_UTF. Later, support was added to the interpreter, and the
+compile-time option PCRE2_MATCH_INVALID_UTF was created. This is now the
+preferred feature, with the earlier option deprecated. However, for backward
+compatibility, if the earlier option is set, it forces the new option so that
+if JIT matching falls back to the interpreter, there is still support for
+invalid UTF. However, if this function has already been successfully called
+without PCRE2_JIT_INVALID_UTF and without PCRE2_MATCH_INVALID_UTF (meaning that
+non-invalid-supporting JIT code was compiled), give an error.
+
+If in the future support for PCRE2_JIT_INVALID_UTF is withdrawn, the following
+actions are needed:
+
+ 1. Remove the definition from pcre2.h.in and from the list in
+ PUBLIC_JIT_COMPILE_OPTIONS above.
+
+ 2. Replace PCRE2_JIT_INVALID_UTF with a local flag in this module.
+
+ 3. Replace PCRE2_JIT_INVALID_UTF in pcre2_jit_test.c.
+
+ 4. Delete the following short block of code. The setting of "re" and
+ "functions" can be moved into the JIT-only block below, but if that is
+ done, (void)re and (void)functions will be needed in the non-JIT case, to
+ avoid compiler warnings.
+*/
+
+#ifdef SUPPORT_JIT
+functions = (executable_functions *)re->executable_jit;
+#endif
+
+if ((options & PCRE2_JIT_INVALID_UTF) != 0)
+ {
+ if ((re->overall_options & PCRE2_MATCH_INVALID_UTF) == 0)
+ {
+#ifdef SUPPORT_JIT
+ if (functions != NULL) return PCRE2_ERROR_JIT_BADOPTION;
+#endif
+ re->overall_options |= PCRE2_MATCH_INVALID_UTF;
+ }
+ }
+
+/* The above tests are run with and without JIT support. This means that
+PCRE2_JIT_INVALID_UTF propagates back into the regex options (ensuring
+interpreter support) even in the absence of JIT. But now, if there is no JIT
+support, give an error return. */
+
+#ifndef SUPPORT_JIT
+return PCRE2_ERROR_JIT_BADOPTION;
+#else /* SUPPORT_JIT */
+
+/* There is JIT support. Do the necessary. */
+
+if ((re->flags & PCRE2_NOJIT) != 0) return 0;
+
+if (executable_allocator_is_working == -1)
+ {
+ /* Checks whether the executable allocator is working. This check
+ might run multiple times in multi-threaded environments, but the
+ result should not be affected by it. */
+ void *ptr = SLJIT_MALLOC_EXEC(32, NULL);
+ if (ptr != NULL)
+ {
+ SLJIT_FREE_EXEC(((sljit_u8*)(ptr)) + SLJIT_EXEC_OFFSET(ptr), NULL);
+ executable_allocator_is_working = 1;
+ }
+ else executable_allocator_is_working = 0;
+ }
+
+if (!executable_allocator_is_working)
+ return PCRE2_ERROR_NOMEMORY;
+
+if ((re->overall_options & PCRE2_MATCH_INVALID_UTF) != 0)
+ options |= PCRE2_JIT_INVALID_UTF;
+
+if ((options & PCRE2_JIT_COMPLETE) != 0 && (functions == NULL
+ || functions->executable_funcs[0] == NULL)) {
+ uint32_t excluded_options = (PCRE2_JIT_PARTIAL_SOFT | PCRE2_JIT_PARTIAL_HARD);
+ int result = jit_compile(code, options & ~excluded_options);
+ if (result != 0)
+ return result;
+ }
+
+if ((options & PCRE2_JIT_PARTIAL_SOFT) != 0 && (functions == NULL
+ || functions->executable_funcs[1] == NULL)) {
+ uint32_t excluded_options = (PCRE2_JIT_COMPLETE | PCRE2_JIT_PARTIAL_HARD);
+ int result = jit_compile(code, options & ~excluded_options);
+ if (result != 0)
+ return result;
+ }
+
+if ((options & PCRE2_JIT_PARTIAL_HARD) != 0 && (functions == NULL
+ || functions->executable_funcs[2] == NULL)) {
+ uint32_t excluded_options = (PCRE2_JIT_COMPLETE | PCRE2_JIT_PARTIAL_SOFT);
+ int result = jit_compile(code, options & ~excluded_options);
+ if (result != 0)
+ return result;
+ }
+
+return 0;
+
+#endif /* SUPPORT_JIT */
+}
+
+/* JIT compiler uses an all-in-one approach. This improves security,
+ since the code generator functions are not exported. */
+
+#define INCLUDED_FROM_PCRE2_JIT_COMPILE
+
+#include "pcre2_jit_match.c"
+#include "pcre2_jit_misc.c"
+
+/* End of pcre2_jit_compile.c */
diff --git a/contrib/libs/pcre2/src/pcre2_jit_match.c b/contrib/libs/pcre2/src/pcre2_jit_match.c
new file mode 100644
index 00000000000..1ab3af073e6
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_jit_match.c
@@ -0,0 +1,186 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2018 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#ifndef INCLUDED_FROM_PCRE2_JIT_COMPILE
+#error This file must be included from pcre2_jit_compile.c.
+#endif
+
+#ifdef SUPPORT_JIT
+
+static SLJIT_NOINLINE int jit_machine_stack_exec(jit_arguments *arguments, jit_function executable_func)
+{
+sljit_u8 local_space[MACHINE_STACK_SIZE];
+struct sljit_stack local_stack;
+
+local_stack.min_start = local_space;
+local_stack.start = local_space;
+local_stack.end = local_space + MACHINE_STACK_SIZE;
+local_stack.top = local_space + MACHINE_STACK_SIZE;
+arguments->stack = &local_stack;
+return executable_func(arguments);
+}
+
+#endif
+
+
+/*************************************************
+* Do a JIT pattern match *
+*************************************************/
+
+/* This function runs a JIT pattern match.
+
+Arguments:
+ code points to the compiled expression
+ subject points to the subject string
+ length length of subject string (may contain binary zeros)
+ start_offset where to start in the subject string
+ options option bits
+ match_data points to a match_data block
+ mcontext points to a match context
+
+Returns: > 0 => success; value is the number of ovector pairs filled
+ = 0 => success, but ovector is not big enough
+ -1 => failed to match (PCRE_ERROR_NOMATCH)
+ < -1 => some kind of unexpected problem
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_jit_match(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length,
+ PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data,
+ pcre2_match_context *mcontext)
+{
+#ifndef SUPPORT_JIT
+
+(void)code;
+(void)subject;
+(void)length;
+(void)start_offset;
+(void)options;
+(void)match_data;
+(void)mcontext;
+return PCRE2_ERROR_JIT_BADOPTION;
+
+#else /* SUPPORT_JIT */
+
+pcre2_real_code *re = (pcre2_real_code *)code;
+executable_functions *functions = (executable_functions *)re->executable_jit;
+pcre2_jit_stack *jit_stack;
+uint32_t oveccount = match_data->oveccount;
+uint32_t max_oveccount;
+union {
+ void *executable_func;
+ jit_function call_executable_func;
+} convert_executable_func;
+jit_arguments arguments;
+int rc;
+int index = 0;
+
+if ((options & PCRE2_PARTIAL_HARD) != 0)
+ index = 2;
+else if ((options & PCRE2_PARTIAL_SOFT) != 0)
+ index = 1;
+
+if (functions == NULL || functions->executable_funcs[index] == NULL)
+ return PCRE2_ERROR_JIT_BADOPTION;
+
+/* Sanity checks should be handled by pcre2_match. */
+arguments.str = subject + start_offset;
+arguments.begin = subject;
+arguments.end = subject + length;
+arguments.match_data = match_data;
+arguments.startchar_ptr = subject;
+arguments.mark_ptr = NULL;
+arguments.options = options;
+
+if (mcontext != NULL)
+ {
+ arguments.callout = mcontext->callout;
+ arguments.callout_data = mcontext->callout_data;
+ arguments.offset_limit = mcontext->offset_limit;
+ arguments.limit_match = (mcontext->match_limit < re->limit_match)?
+ mcontext->match_limit : re->limit_match;
+ if (mcontext->jit_callback != NULL)
+ jit_stack = mcontext->jit_callback(mcontext->jit_callback_data);
+ else
+ jit_stack = (pcre2_jit_stack *)mcontext->jit_callback_data;
+ }
+else
+ {
+ arguments.callout = NULL;
+ arguments.callout_data = NULL;
+ arguments.offset_limit = PCRE2_UNSET;
+ arguments.limit_match = (MATCH_LIMIT < re->limit_match)?
+ MATCH_LIMIT : re->limit_match;
+ jit_stack = NULL;
+ }
+
+
+max_oveccount = functions->top_bracket;
+if (oveccount > max_oveccount)
+ oveccount = max_oveccount;
+arguments.oveccount = oveccount << 1;
+
+
+convert_executable_func.executable_func = functions->executable_funcs[index];
+if (jit_stack != NULL)
+ {
+ arguments.stack = (struct sljit_stack *)(jit_stack->stack);
+ rc = convert_executable_func.call_executable_func(&arguments);
+ }
+else
+ rc = jit_machine_stack_exec(&arguments, convert_executable_func.call_executable_func);
+
+if (rc > (int)oveccount)
+ rc = 0;
+match_data->code = re;
+match_data->subject = (rc >= 0 || rc == PCRE2_ERROR_PARTIAL)? subject : NULL;
+match_data->rc = rc;
+match_data->startchar = arguments.startchar_ptr - subject;
+match_data->leftchar = 0;
+match_data->rightchar = 0;
+match_data->mark = arguments.mark_ptr;
+match_data->matchedby = PCRE2_MATCHEDBY_JIT;
+
+return match_data->rc;
+
+#endif /* SUPPORT_JIT */
+}
+
+/* End of pcre2_jit_match.c */
diff --git a/contrib/libs/pcre2/src/pcre2_jit_misc.c b/contrib/libs/pcre2/src/pcre2_jit_misc.c
new file mode 100644
index 00000000000..bb6a5589cb7
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_jit_misc.c
@@ -0,0 +1,234 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifndef INCLUDED_FROM_PCRE2_JIT_COMPILE
+#error This file must be included from pcre2_jit_compile.c.
+#endif
+
+
+
+/*************************************************
+* Free JIT read-only data *
+*************************************************/
+
+void
+PRIV(jit_free_rodata)(void *current, void *allocator_data)
+{
+#ifndef SUPPORT_JIT
+(void)current;
+(void)allocator_data;
+#else /* SUPPORT_JIT */
+void *next;
+
+SLJIT_UNUSED_ARG(allocator_data);
+
+while (current != NULL)
+ {
+ next = *(void**)current;
+ SLJIT_FREE(current, allocator_data);
+ current = next;
+ }
+
+#endif /* SUPPORT_JIT */
+}
+
+/*************************************************
+* Free JIT compiled code *
+*************************************************/
+
+void
+PRIV(jit_free)(void *executable_jit, pcre2_memctl *memctl)
+{
+#ifndef SUPPORT_JIT
+(void)executable_jit;
+(void)memctl;
+#else /* SUPPORT_JIT */
+
+executable_functions *functions = (executable_functions *)executable_jit;
+void *allocator_data = memctl;
+int i;
+
+for (i = 0; i < JIT_NUMBER_OF_COMPILE_MODES; i++)
+ {
+ if (functions->executable_funcs[i] != NULL)
+ sljit_free_code(functions->executable_funcs[i], NULL);
+ PRIV(jit_free_rodata)(functions->read_only_data_heads[i], allocator_data);
+ }
+
+SLJIT_FREE(functions, allocator_data);
+
+#endif /* SUPPORT_JIT */
+}
+
+
+/*************************************************
+* Free unused JIT memory *
+*************************************************/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_jit_free_unused_memory(pcre2_general_context *gcontext)
+{
+#ifndef SUPPORT_JIT
+(void)gcontext; /* Suppress warning */
+#else /* SUPPORT_JIT */
+SLJIT_UNUSED_ARG(gcontext);
+#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
+sljit_free_unused_memory_exec();
+#endif /* SLJIT_EXECUTABLE_ALLOCATOR */
+#endif /* SUPPORT_JIT */
+}
+
+
+
+/*************************************************
+* Allocate a JIT stack *
+*************************************************/
+
+PCRE2_EXP_DEFN pcre2_jit_stack * PCRE2_CALL_CONVENTION
+pcre2_jit_stack_create(size_t startsize, size_t maxsize,
+ pcre2_general_context *gcontext)
+{
+#ifndef SUPPORT_JIT
+
+(void)gcontext;
+(void)startsize;
+(void)maxsize;
+return NULL;
+
+#else /* SUPPORT_JIT */
+
+pcre2_jit_stack *jit_stack;
+
+if (startsize == 0 || maxsize == 0 || maxsize > SIZE_MAX - STACK_GROWTH_RATE)
+ return NULL;
+if (startsize > maxsize)
+ startsize = maxsize;
+startsize = (startsize + STACK_GROWTH_RATE - 1) & ~(STACK_GROWTH_RATE - 1);
+maxsize = (maxsize + STACK_GROWTH_RATE - 1) & ~(STACK_GROWTH_RATE - 1);
+
+jit_stack = PRIV(memctl_malloc)(sizeof(pcre2_real_jit_stack), (pcre2_memctl *)gcontext);
+if (jit_stack == NULL) return NULL;
+jit_stack->stack = sljit_allocate_stack(startsize, maxsize, &jit_stack->memctl);
+if (jit_stack->stack == NULL)
+ {
+ jit_stack->memctl.free(jit_stack, jit_stack->memctl.memory_data);
+ return NULL;
+ }
+return jit_stack;
+
+#endif
+}
+
+
+/*************************************************
+* Assign a JIT stack to a pattern *
+*************************************************/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_jit_stack_assign(pcre2_match_context *mcontext, pcre2_jit_callback callback,
+ void *callback_data)
+{
+#ifndef SUPPORT_JIT
+(void)mcontext;
+(void)callback;
+(void)callback_data;
+#else /* SUPPORT_JIT */
+
+if (mcontext == NULL) return;
+mcontext->jit_callback = callback;
+mcontext->jit_callback_data = callback_data;
+
+#endif /* SUPPORT_JIT */
+}
+
+
+/*************************************************
+* Free a JIT stack *
+*************************************************/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_jit_stack_free(pcre2_jit_stack *jit_stack)
+{
+#ifndef SUPPORT_JIT
+(void)jit_stack;
+#else /* SUPPORT_JIT */
+if (jit_stack != NULL)
+ {
+ sljit_free_stack((struct sljit_stack *)(jit_stack->stack), &jit_stack->memctl);
+ jit_stack->memctl.free(jit_stack, jit_stack->memctl.memory_data);
+ }
+#endif /* SUPPORT_JIT */
+}
+
+
+/*************************************************
+* Get target CPU type *
+*************************************************/
+
+const char*
+PRIV(jit_get_target)(void)
+{
+#ifndef SUPPORT_JIT
+return "JIT is not supported";
+#else /* SUPPORT_JIT */
+return sljit_get_platform_name();
+#endif /* SUPPORT_JIT */
+}
+
+
+/*************************************************
+* Get size of JIT code *
+*************************************************/
+
+size_t
+PRIV(jit_get_size)(void *executable_jit)
+{
+#ifndef SUPPORT_JIT
+(void)executable_jit;
+return 0;
+#else /* SUPPORT_JIT */
+sljit_uw *executable_sizes = ((executable_functions *)executable_jit)->executable_sizes;
+SLJIT_COMPILE_ASSERT(JIT_NUMBER_OF_COMPILE_MODES == 3, number_of_compile_modes_changed);
+return executable_sizes[0] + executable_sizes[1] + executable_sizes[2];
+#endif
+}
+
+/* End of pcre2_jit_misc.c */
diff --git a/contrib/libs/pcre2/src/pcre2_jit_simd_inc.h b/contrib/libs/pcre2/src/pcre2_jit_simd_inc.h
new file mode 100644
index 00000000000..6fcfac8f13d
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_jit_simd_inc.h
@@ -0,0 +1,1858 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ This module by Zoltan Herczeg
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2019 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#if !(defined SUPPORT_VALGRIND)
+
+#if ((defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
+ || (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X))
+
+typedef enum {
+ vector_compare_match1,
+ vector_compare_match1i,
+ vector_compare_match2,
+} vector_compare_type;
+
+static SLJIT_INLINE sljit_u32 max_fast_forward_char_pair_offset(void)
+{
+#if PCRE2_CODE_UNIT_WIDTH == 8
+return 15;
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+return 7;
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+return 3;
+#else
+#error "Unsupported unit width"
+#endif
+}
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+static struct sljit_jump *jump_if_utf_char_start(struct sljit_compiler *compiler, sljit_s32 reg)
+{
+#if PCRE2_CODE_UNIT_WIDTH == 8
+OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xc0);
+return CMP(SLJIT_NOT_EQUAL, reg, 0, SLJIT_IMM, 0x80);
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xfc00);
+return CMP(SLJIT_NOT_EQUAL, reg, 0, SLJIT_IMM, 0xdc00);
+#else
+#error "Unknown code width"
+#endif
+}
+#endif
+
+#endif /* SLJIT_CONFIG_X86 || SLJIT_CONFIG_S390X */
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+
+static sljit_s32 character_to_int32(PCRE2_UCHAR chr)
+{
+sljit_u32 value = chr;
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#define SSE2_COMPARE_TYPE_INDEX 0
+return (sljit_s32)((value << 24) | (value << 16) | (value << 8) | value);
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+#define SSE2_COMPARE_TYPE_INDEX 1
+return (sljit_s32)((value << 16) | value);
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+#define SSE2_COMPARE_TYPE_INDEX 2
+return (sljit_s32)(value);
+#else
+#error "Unsupported unit width"
+#endif
+}
+
+static void load_from_mem_sse2(struct sljit_compiler *compiler, sljit_s32 dst_xmm_reg, sljit_s32 src_general_reg, sljit_s8 offset)
+{
+sljit_u8 instruction[5];
+
+SLJIT_ASSERT(dst_xmm_reg < 8);
+SLJIT_ASSERT(src_general_reg < 8);
+
+/* MOVDQA xmm1, xmm2/m128 */
+instruction[0] = ((sljit_u8)offset & 0xf) == 0 ? 0x66 : 0xf3;
+instruction[1] = 0x0f;
+instruction[2] = 0x6f;
+
+if (offset == 0)
+ {
+ instruction[3] = (dst_xmm_reg << 3) | src_general_reg;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ return;
+ }
+
+instruction[3] = 0x40 | (dst_xmm_reg << 3) | src_general_reg;
+instruction[4] = (sljit_u8)offset;
+sljit_emit_op_custom(compiler, instruction, 5);
+}
+
+static void fast_forward_char_pair_sse2_compare(struct sljit_compiler *compiler, vector_compare_type compare_type,
+ int step, sljit_s32 dst_ind, sljit_s32 cmp1_ind, sljit_s32 cmp2_ind, sljit_s32 tmp_ind)
+{
+sljit_u8 instruction[4];
+instruction[0] = 0x66;
+instruction[1] = 0x0f;
+
+SLJIT_ASSERT(step >= 0 && step <= 3);
+
+if (compare_type != vector_compare_match2)
+ {
+ if (step == 0)
+ {
+ if (compare_type == vector_compare_match1i)
+ {
+ /* POR xmm1, xmm2/m128 */
+ /* instruction[0] = 0x66; */
+ /* instruction[1] = 0x0f; */
+ instruction[2] = 0xeb;
+ instruction[3] = 0xc0 | (dst_ind << 3) | cmp2_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+ return;
+ }
+
+ if (step != 2)
+ return;
+
+ /* PCMPEQB/W/D xmm1, xmm2/m128 */
+ /* instruction[0] = 0x66; */
+ /* instruction[1] = 0x0f; */
+ instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX;
+ instruction[3] = 0xc0 | (dst_ind << 3) | cmp1_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ return;
+ }
+
+switch (step)
+ {
+ case 0:
+ /* MOVDQA xmm1, xmm2/m128 */
+ /* instruction[0] = 0x66; */
+ /* instruction[1] = 0x0f; */
+ instruction[2] = 0x6f;
+ instruction[3] = 0xc0 | (tmp_ind << 3) | dst_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ return;
+
+ case 1:
+ /* PCMPEQB/W/D xmm1, xmm2/m128 */
+ /* instruction[0] = 0x66; */
+ /* instruction[1] = 0x0f; */
+ instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX;
+ instruction[3] = 0xc0 | (dst_ind << 3) | cmp1_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ return;
+
+ case 2:
+ /* PCMPEQB/W/D xmm1, xmm2/m128 */
+ /* instruction[0] = 0x66; */
+ /* instruction[1] = 0x0f; */
+ instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX;
+ instruction[3] = 0xc0 | (tmp_ind << 3) | cmp2_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ return;
+
+ case 3:
+ /* POR xmm1, xmm2/m128 */
+ /* instruction[0] = 0x66; */
+ /* instruction[1] = 0x0f; */
+ instruction[2] = 0xeb;
+ instruction[3] = 0xc0 | (dst_ind << 3) | tmp_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ return;
+ }
+}
+
+#define JIT_HAS_FAST_FORWARD_CHAR_SIMD (sljit_has_cpu_feature(SLJIT_HAS_SSE2))
+
+static void fast_forward_char_simd(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2, sljit_s32 offset)
+{
+DEFINE_COMPILER;
+sljit_u8 instruction[8];
+struct sljit_label *start;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_label *restart;
+#endif
+struct sljit_jump *quit;
+struct sljit_jump *partial_quit[2];
+vector_compare_type compare_type = vector_compare_match1;
+sljit_s32 tmp1_reg_ind = sljit_get_register_index(TMP1);
+sljit_s32 str_ptr_reg_ind = sljit_get_register_index(STR_PTR);
+sljit_s32 data_ind = 0;
+sljit_s32 tmp_ind = 1;
+sljit_s32 cmp1_ind = 2;
+sljit_s32 cmp2_ind = 3;
+sljit_u32 bit = 0;
+int i;
+
+SLJIT_UNUSED_ARG(offset);
+
+if (char1 != char2)
+ {
+ bit = char1 ^ char2;
+ compare_type = vector_compare_match1i;
+
+ if (!is_powerof2(bit))
+ {
+ bit = 0;
+ compare_type = vector_compare_match2;
+ }
+ }
+
+partial_quit[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit[0]);
+
+/* First part (unaligned start) */
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1 | bit));
+
+SLJIT_ASSERT(tmp1_reg_ind < 8);
+
+/* MOVD xmm, r/m32 */
+instruction[0] = 0x66;
+instruction[1] = 0x0f;
+instruction[2] = 0x6e;
+instruction[3] = 0xc0 | (cmp1_ind << 3) | tmp1_reg_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+if (char1 != char2)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(bit != 0 ? bit : char2));
+
+ /* MOVD xmm, r/m32 */
+ instruction[3] = 0xc0 | (cmp2_ind << 3) | tmp1_reg_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+
+OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0);
+
+/* PSHUFD xmm1, xmm2/m128, imm8 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x70;
+instruction[3] = 0xc0 | (cmp1_ind << 3) | cmp1_ind;
+instruction[4] = 0;
+sljit_emit_op_custom(compiler, instruction, 5);
+
+if (char1 != char2)
+ {
+ /* PSHUFD xmm1, xmm2/m128, imm8 */
+ instruction[3] = 0xc0 | (cmp2_ind << 3) | cmp2_ind;
+ sljit_emit_op_custom(compiler, instruction, 5);
+ }
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+restart = LABEL();
+#endif
+OP2(SLJIT_AND, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, ~0xf);
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf);
+
+load_from_mem_sse2(compiler, data_ind, str_ptr_reg_ind, 0);
+for (i = 0; i < 4; i++)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, i, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+/* PMOVMSKB reg, xmm */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xd7;
+instruction[3] = 0xc0 | (tmp1_reg_ind << 3) | data_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+quit = CMP(SLJIT_NOT_ZERO, TMP1, 0, SLJIT_IMM, 0);
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+
+/* Second part (aligned) */
+start = LABEL();
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16);
+
+partial_quit[1] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit[1]);
+
+load_from_mem_sse2(compiler, data_ind, str_ptr_reg_ind, 0);
+for (i = 0; i < 4; i++)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, i, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+/* PMOVMSKB reg, xmm */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xd7;
+instruction[3] = 0xc0 | (tmp1_reg_ind << 3) | data_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+CMPTO(SLJIT_ZERO, TMP1, 0, SLJIT_IMM, 0, start);
+
+JUMPHERE(quit);
+
+/* BSF r32, r/m32 */
+instruction[0] = 0x0f;
+instruction[1] = 0xbc;
+instruction[2] = 0xc0 | (tmp1_reg_ind << 3) | tmp1_reg_ind;
+sljit_emit_op_custom(compiler, instruction, 3);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+
+if (common->mode != PCRE2_JIT_COMPLETE)
+ {
+ JUMPHERE(partial_quit[0]);
+ JUMPHERE(partial_quit[1]);
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, STR_PTR, 0, STR_END, 0);
+ CMOV(SLJIT_GREATER, STR_PTR, STR_END, 0);
+ }
+else
+ add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+if (common->utf && offset > 0)
+ {
+ SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE);
+
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offset));
+
+ quit = jump_if_utf_char_start(compiler, TMP1);
+
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+ OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, restart);
+
+ JUMPHERE(quit);
+ }
+#endif
+}
+
+#define JIT_HAS_FAST_REQUESTED_CHAR_SIMD (sljit_has_cpu_feature(SLJIT_HAS_SSE2))
+
+static jump_list *fast_requested_char_simd(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2)
+{
+DEFINE_COMPILER;
+sljit_u8 instruction[8];
+struct sljit_label *start;
+struct sljit_jump *quit;
+jump_list *not_found = NULL;
+vector_compare_type compare_type = vector_compare_match1;
+sljit_s32 tmp1_reg_ind = sljit_get_register_index(TMP1);
+sljit_s32 str_ptr_reg_ind = sljit_get_register_index(STR_PTR);
+sljit_s32 data_ind = 0;
+sljit_s32 tmp_ind = 1;
+sljit_s32 cmp1_ind = 2;
+sljit_s32 cmp2_ind = 3;
+sljit_u32 bit = 0;
+int i;
+
+if (char1 != char2)
+ {
+ bit = char1 ^ char2;
+ compare_type = vector_compare_match1i;
+
+ if (!is_powerof2(bit))
+ {
+ bit = 0;
+ compare_type = vector_compare_match2;
+ }
+ }
+
+add_jump(compiler, &not_found, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, STR_END, 0));
+OP1(SLJIT_MOV, TMP2, 0, TMP1, 0);
+OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0);
+
+/* First part (unaligned start) */
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1 | bit));
+
+SLJIT_ASSERT(tmp1_reg_ind < 8);
+
+/* MOVD xmm, r/m32 */
+instruction[0] = 0x66;
+instruction[1] = 0x0f;
+instruction[2] = 0x6e;
+instruction[3] = 0xc0 | (cmp1_ind << 3) | tmp1_reg_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+if (char1 != char2)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(bit != 0 ? bit : char2));
+
+ /* MOVD xmm, r/m32 */
+ instruction[3] = 0xc0 | (cmp2_ind << 3) | tmp1_reg_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+
+OP1(SLJIT_MOV, STR_PTR, 0, TMP2, 0);
+
+/* PSHUFD xmm1, xmm2/m128, imm8 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x70;
+instruction[3] = 0xc0 | (cmp1_ind << 3) | cmp1_ind;
+instruction[4] = 0;
+sljit_emit_op_custom(compiler, instruction, 5);
+
+if (char1 != char2)
+ {
+ /* PSHUFD xmm1, xmm2/m128, imm8 */
+ instruction[3] = 0xc0 | (cmp2_ind << 3) | cmp2_ind;
+ sljit_emit_op_custom(compiler, instruction, 5);
+ }
+
+OP2(SLJIT_AND, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, ~0xf);
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf);
+
+load_from_mem_sse2(compiler, data_ind, str_ptr_reg_ind, 0);
+for (i = 0; i < 4; i++)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, i, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+/* PMOVMSKB reg, xmm */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xd7;
+instruction[3] = 0xc0 | (tmp1_reg_ind << 3) | data_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+quit = CMP(SLJIT_NOT_ZERO, TMP1, 0, SLJIT_IMM, 0);
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+
+/* Second part (aligned) */
+start = LABEL();
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16);
+
+add_jump(compiler, &not_found, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+load_from_mem_sse2(compiler, data_ind, str_ptr_reg_ind, 0);
+for (i = 0; i < 4; i++)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, i, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+/* PMOVMSKB reg, xmm */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xd7;
+instruction[3] = 0xc0 | (tmp1_reg_ind << 3) | data_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+CMPTO(SLJIT_ZERO, TMP1, 0, SLJIT_IMM, 0, start);
+
+JUMPHERE(quit);
+
+/* BSF r32, r/m32 */
+instruction[0] = 0x0f;
+instruction[1] = 0xbc;
+instruction[2] = 0xc0 | (tmp1_reg_ind << 3) | tmp1_reg_ind;
+sljit_emit_op_custom(compiler, instruction, 3);
+
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, STR_PTR, 0);
+add_jump(compiler, &not_found, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, STR_END, 0));
+
+OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0);
+return not_found;
+}
+
+#ifndef _WIN64
+
+#define JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD (sljit_has_cpu_feature(SLJIT_HAS_SSE2))
+
+static void fast_forward_char_pair_simd(compiler_common *common, sljit_s32 offs1,
+ PCRE2_UCHAR char1a, PCRE2_UCHAR char1b, sljit_s32 offs2, PCRE2_UCHAR char2a, PCRE2_UCHAR char2b)
+{
+DEFINE_COMPILER;
+sljit_u8 instruction[8];
+vector_compare_type compare1_type = vector_compare_match1;
+vector_compare_type compare2_type = vector_compare_match1;
+sljit_u32 bit1 = 0;
+sljit_u32 bit2 = 0;
+sljit_u32 diff = IN_UCHARS(offs1 - offs2);
+sljit_s32 tmp1_reg_ind = sljit_get_register_index(TMP1);
+sljit_s32 tmp2_reg_ind = sljit_get_register_index(TMP2);
+sljit_s32 str_ptr_reg_ind = sljit_get_register_index(STR_PTR);
+sljit_s32 data1_ind = 0;
+sljit_s32 data2_ind = 1;
+sljit_s32 tmp1_ind = 2;
+sljit_s32 tmp2_ind = 3;
+sljit_s32 cmp1a_ind = 4;
+sljit_s32 cmp1b_ind = 5;
+sljit_s32 cmp2a_ind = 6;
+sljit_s32 cmp2b_ind = 7;
+struct sljit_label *start;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_label *restart;
+#endif
+struct sljit_jump *jump[2];
+int i;
+
+SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE && offs1 > offs2);
+SLJIT_ASSERT(diff <= IN_UCHARS(max_fast_forward_char_pair_offset()));
+SLJIT_ASSERT(tmp1_reg_ind < 8 && tmp2_reg_ind == 1);
+
+/* Initialize. */
+if (common->match_end_ptr != 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(offs1 + 1));
+
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, STR_END, 0);
+ CMOV(SLJIT_LESS, STR_END, TMP1, 0);
+ }
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offs1));
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+/* MOVD xmm, r/m32 */
+instruction[0] = 0x66;
+instruction[1] = 0x0f;
+instruction[2] = 0x6e;
+
+if (char1a == char1b)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1a));
+else
+ {
+ bit1 = char1a ^ char1b;
+ if (is_powerof2(bit1))
+ {
+ compare1_type = vector_compare_match1i;
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1a | bit1));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(bit1));
+ }
+ else
+ {
+ compare1_type = vector_compare_match2;
+ bit1 = 0;
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1a));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(char1b));
+ }
+ }
+
+instruction[3] = 0xc0 | (cmp1a_ind << 3) | tmp1_reg_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+if (char1a != char1b)
+ {
+ instruction[3] = 0xc0 | (cmp1b_ind << 3) | tmp2_reg_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+
+if (char2a == char2b)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char2a));
+else
+ {
+ bit2 = char2a ^ char2b;
+ if (is_powerof2(bit2))
+ {
+ compare2_type = vector_compare_match1i;
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char2a | bit2));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(bit2));
+ }
+ else
+ {
+ compare2_type = vector_compare_match2;
+ bit2 = 0;
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char2a));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(char2b));
+ }
+ }
+
+instruction[3] = 0xc0 | (cmp2a_ind << 3) | tmp1_reg_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+if (char2a != char2b)
+ {
+ instruction[3] = 0xc0 | (cmp2b_ind << 3) | tmp2_reg_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+
+/* PSHUFD xmm1, xmm2/m128, imm8 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x70;
+instruction[4] = 0;
+
+instruction[3] = 0xc0 | (cmp1a_ind << 3) | cmp1a_ind;
+sljit_emit_op_custom(compiler, instruction, 5);
+
+if (char1a != char1b)
+ {
+ instruction[3] = 0xc0 | (cmp1b_ind << 3) | cmp1b_ind;
+ sljit_emit_op_custom(compiler, instruction, 5);
+ }
+
+instruction[3] = 0xc0 | (cmp2a_ind << 3) | cmp2a_ind;
+sljit_emit_op_custom(compiler, instruction, 5);
+
+if (char2a != char2b)
+ {
+ instruction[3] = 0xc0 | (cmp2b_ind << 3) | cmp2b_ind;
+ sljit_emit_op_custom(compiler, instruction, 5);
+ }
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+restart = LABEL();
+#endif
+
+OP2(SLJIT_SUB, TMP1, 0, STR_PTR, 0, SLJIT_IMM, diff);
+OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0);
+OP2(SLJIT_AND, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, ~0xf);
+
+load_from_mem_sse2(compiler, data1_ind, str_ptr_reg_ind, 0);
+
+jump[0] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, STR_PTR, 0);
+
+load_from_mem_sse2(compiler, data2_ind, str_ptr_reg_ind, -(sljit_s8)diff);
+jump[1] = JUMP(SLJIT_JUMP);
+
+JUMPHERE(jump[0]);
+
+/* MOVDQA xmm1, xmm2/m128 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x6f;
+instruction[3] = 0xc0 | (data2_ind << 3) | data1_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+/* PSLLDQ xmm1, imm8 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x73;
+instruction[3] = 0xc0 | (7 << 3) | data2_ind;
+instruction[4] = diff;
+sljit_emit_op_custom(compiler, instruction, 5);
+
+JUMPHERE(jump[1]);
+
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf);
+
+for (i = 0; i < 4; i++)
+ {
+ fast_forward_char_pair_sse2_compare(compiler, compare2_type, i, data2_ind, cmp2a_ind, cmp2b_ind, tmp2_ind);
+ fast_forward_char_pair_sse2_compare(compiler, compare1_type, i, data1_ind, cmp1a_ind, cmp1b_ind, tmp1_ind);
+ }
+
+/* PAND xmm1, xmm2/m128 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xdb;
+instruction[3] = 0xc0 | (data1_ind << 3) | data2_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+/* PMOVMSKB reg, xmm */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xd7;
+instruction[3] = 0xc0 | (tmp1_reg_ind << 3) | 0;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+/* Ignore matches before the first STR_PTR. */
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+jump[0] = CMP(SLJIT_NOT_ZERO, TMP1, 0, SLJIT_IMM, 0);
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+
+/* Main loop. */
+start = LABEL();
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16);
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+load_from_mem_sse2(compiler, data1_ind, str_ptr_reg_ind, 0);
+load_from_mem_sse2(compiler, data2_ind, str_ptr_reg_ind, -(sljit_s8)diff);
+
+for (i = 0; i < 4; i++)
+ {
+ fast_forward_char_pair_sse2_compare(compiler, compare1_type, i, data1_ind, cmp1a_ind, cmp1b_ind, tmp2_ind);
+ fast_forward_char_pair_sse2_compare(compiler, compare2_type, i, data2_ind, cmp2a_ind, cmp2b_ind, tmp1_ind);
+ }
+
+/* PAND xmm1, xmm2/m128 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xdb;
+instruction[3] = 0xc0 | (data1_ind << 3) | data2_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+/* PMOVMSKB reg, xmm */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xd7;
+instruction[3] = 0xc0 | (tmp1_reg_ind << 3) | 0;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+CMPTO(SLJIT_ZERO, TMP1, 0, SLJIT_IMM, 0, start);
+
+JUMPHERE(jump[0]);
+
+/* BSF r32, r/m32 */
+instruction[0] = 0x0f;
+instruction[1] = 0xbc;
+instruction[2] = 0xc0 | (tmp1_reg_ind << 3) | tmp1_reg_ind;
+sljit_emit_op_custom(compiler, instruction, 3);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+if (common->utf)
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offs1));
+
+ jump[0] = jump_if_utf_char_start(compiler, TMP1);
+
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, restart);
+
+ add_jump(compiler, &common->failed_match, JUMP(SLJIT_JUMP));
+
+ JUMPHERE(jump[0]);
+ }
+#endif
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offs1));
+
+if (common->match_end_ptr != 0)
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+}
+
+#endif /* !_WIN64 */
+
+#undef SSE2_COMPARE_TYPE_INDEX
+
+#endif /* SLJIT_CONFIG_X86 */
+
+#if (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64 && (defined __ARM_NEON || defined __ARM_NEON__))
+
+#include <arm_neon.h>
+
+typedef union {
+ unsigned int x;
+ struct { unsigned char c1, c2, c3, c4; } c;
+} int_char;
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+static SLJIT_INLINE int utf_continue(PCRE2_SPTR s)
+{
+#if PCRE2_CODE_UNIT_WIDTH == 8
+return (*s & 0xc0) == 0x80;
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+return (*s & 0xfc00) == 0xdc00;
+#else
+#error "Unknown code width"
+#endif
+}
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+# define VECTOR_FACTOR 16
+# define vect_t uint8x16_t
+# define VLD1Q(X) vld1q_u8((sljit_u8 *)(X))
+# define VCEQQ vceqq_u8
+# define VORRQ vorrq_u8
+# define VST1Q vst1q_u8
+# define VDUPQ vdupq_n_u8
+# define VEXTQ vextq_u8
+# define VANDQ vandq_u8
+typedef union {
+ uint8_t mem[16];
+ uint64_t dw[2];
+} quad_word;
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+# define VECTOR_FACTOR 8
+# define vect_t uint16x8_t
+# define VLD1Q(X) vld1q_u16((sljit_u16 *)(X))
+# define VCEQQ vceqq_u16
+# define VORRQ vorrq_u16
+# define VST1Q vst1q_u16
+# define VDUPQ vdupq_n_u16
+# define VEXTQ vextq_u16
+# define VANDQ vandq_u16
+typedef union {
+ uint16_t mem[8];
+ uint64_t dw[2];
+} quad_word;
+#else
+# define VECTOR_FACTOR 4
+# define vect_t uint32x4_t
+# define VLD1Q(X) vld1q_u32((sljit_u32 *)(X))
+# define VCEQQ vceqq_u32
+# define VORRQ vorrq_u32
+# define VST1Q vst1q_u32
+# define VDUPQ vdupq_n_u32
+# define VEXTQ vextq_u32
+# define VANDQ vandq_u32
+typedef union {
+ uint32_t mem[4];
+ uint64_t dw[2];
+} quad_word;
+#endif
+
+#define FFCS
+#error #include "pcre2_jit_neon_inc.h"
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+# define FF_UTF
+# error #include "pcre2_jit_neon_inc.h"
+# undef FF_UTF
+#endif
+#undef FFCS
+
+#define FFCS_2
+#error #include "pcre2_jit_neon_inc.h"
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+# define FF_UTF
+# error #include "pcre2_jit_neon_inc.h"
+# undef FF_UTF
+#endif
+#undef FFCS_2
+
+#define FFCS_MASK
+#error #include "pcre2_jit_neon_inc.h"
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+# define FF_UTF
+# error #include "pcre2_jit_neon_inc.h"
+# undef FF_UTF
+#endif
+#undef FFCS_MASK
+
+#define JIT_HAS_FAST_FORWARD_CHAR_SIMD 1
+
+static void fast_forward_char_simd(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2, sljit_s32 offset)
+{
+DEFINE_COMPILER;
+int_char ic;
+struct sljit_jump *partial_quit;
+/* Save temporary registers. */
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STR_PTR, 0);
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP3, 0);
+
+/* Prepare function arguments */
+OP1(SLJIT_MOV, SLJIT_R0, 0, STR_END, 0);
+OP1(SLJIT_MOV, SLJIT_R1, 0, STR_PTR, 0);
+OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, offset);
+
+if (char1 == char2)
+ {
+ ic.c.c1 = char1;
+ ic.c.c2 = char2;
+ OP1(SLJIT_MOV, SLJIT_R4, 0, SLJIT_IMM, ic.x);
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf && offset > 0)
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs_utf));
+ else
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs));
+#else
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs));
+#endif
+ }
+else
+ {
+ PCRE2_UCHAR mask = char1 ^ char2;
+ if (is_powerof2(mask))
+ {
+ ic.c.c1 = char1 | mask;
+ ic.c.c2 = mask;
+ OP1(SLJIT_MOV, SLJIT_R4, 0, SLJIT_IMM, ic.x);
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf && offset > 0)
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs_mask_utf));
+ else
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs_mask));
+#else
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs_mask));
+#endif
+ }
+ else
+ {
+ ic.c.c1 = char1;
+ ic.c.c2 = char2;
+ OP1(SLJIT_MOV, SLJIT_R4, 0, SLJIT_IMM, ic.x);
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf && offset > 0)
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs_2_utf));
+ else
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs_2));
+#else
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs_2));
+#endif
+ }
+ }
+/* Restore registers. */
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
+
+/* Check return value. */
+partial_quit = CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit);
+
+/* Fast forward STR_PTR to the result of memchr. */
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0);
+
+if (common->mode != PCRE2_JIT_COMPLETE)
+ JUMPHERE(partial_quit);
+}
+
+typedef enum {
+ compare_match1,
+ compare_match1i,
+ compare_match2,
+} compare_type;
+
+static inline vect_t fast_forward_char_pair_compare(compare_type ctype, vect_t dst, vect_t cmp1, vect_t cmp2)
+{
+if (ctype == compare_match2)
+ {
+ vect_t tmp = dst;
+ dst = VCEQQ(dst, cmp1);
+ tmp = VCEQQ(tmp, cmp2);
+ dst = VORRQ(dst, tmp);
+ return dst;
+ }
+
+if (ctype == compare_match1i)
+ dst = VORRQ(dst, cmp2);
+dst = VCEQQ(dst, cmp1);
+return dst;
+}
+
+static SLJIT_INLINE sljit_u32 max_fast_forward_char_pair_offset(void)
+{
+#if PCRE2_CODE_UNIT_WIDTH == 8
+return 15;
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+return 7;
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+return 3;
+#else
+#error "Unsupported unit width"
+#endif
+}
+
+/* ARM doesn't have a shift left across lanes. */
+static SLJIT_INLINE vect_t shift_left_n_lanes(vect_t a, sljit_u8 n)
+{
+vect_t zero = VDUPQ(0);
+SLJIT_ASSERT(0 < n && n < VECTOR_FACTOR);
+/* VEXTQ takes an immediate as last argument. */
+#define C(X) case X: return VEXTQ(zero, a, VECTOR_FACTOR - X);
+switch (n)
+ {
+ C(1); C(2); C(3);
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ C(4); C(5); C(6); C(7);
+# if PCRE2_CODE_UNIT_WIDTH != 16
+ C(8); C(9); C(10); C(11); C(12); C(13); C(14); C(15);
+# endif
+#endif
+ default:
+ /* Based on the ASSERT(0 < n && n < VECTOR_FACTOR) above, this won't
+ happen. The return is still here for compilers to not warn. */
+ return a;
+ }
+}
+
+#define FFCPS
+#define FFCPS_DIFF1
+#define FFCPS_CHAR1A2A
+
+#define FFCPS_0
+#error #include "pcre2_jit_neon_inc.h"
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+# define FF_UTF
+# error #include "pcre2_jit_neon_inc.h"
+# undef FF_UTF
+#endif
+#undef FFCPS_0
+
+#undef FFCPS_CHAR1A2A
+
+#define FFCPS_1
+#error #include "pcre2_jit_neon_inc.h"
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+# define FF_UTF
+# error #include "pcre2_jit_neon_inc.h"
+# undef FF_UTF
+#endif
+#undef FFCPS_1
+
+#undef FFCPS_DIFF1
+
+#define FFCPS_DEFAULT
+#error #include "pcre2_jit_neon_inc.h"
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+# define FF_UTF
+# error #include "pcre2_jit_neon_inc.h"
+# undef FF_UTF
+#endif
+#undef FFCPS
+
+#define JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD 1
+
+static void fast_forward_char_pair_simd(compiler_common *common, sljit_s32 offs1,
+ PCRE2_UCHAR char1a, PCRE2_UCHAR char1b, sljit_s32 offs2, PCRE2_UCHAR char2a, PCRE2_UCHAR char2b)
+{
+DEFINE_COMPILER;
+sljit_u32 diff = IN_UCHARS(offs1 - offs2);
+struct sljit_jump *partial_quit;
+int_char ic;
+SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE && offs1 > offs2);
+SLJIT_ASSERT(diff <= IN_UCHARS(max_fast_forward_char_pair_offset()));
+SLJIT_ASSERT(compiler->scratches == 5);
+
+/* Save temporary register STR_PTR. */
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STR_PTR, 0);
+
+/* Prepare arguments for the function call. */
+if (common->match_end_ptr == 0)
+ OP1(SLJIT_MOV, SLJIT_R0, 0, STR_END, 0);
+else
+ {
+ OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ OP2(SLJIT_ADD, SLJIT_R0, 0, SLJIT_R0, 0, SLJIT_IMM, IN_UCHARS(offs1 + 1));
+
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, STR_END, 0, SLJIT_R0, 0);
+ CMOV(SLJIT_LESS, SLJIT_R0, STR_END, 0);
+ }
+
+OP1(SLJIT_MOV, SLJIT_R1, 0, STR_PTR, 0);
+OP1(SLJIT_MOV_S32, SLJIT_R2, 0, SLJIT_IMM, offs1);
+OP1(SLJIT_MOV_S32, SLJIT_R3, 0, SLJIT_IMM, offs2);
+ic.c.c1 = char1a;
+ic.c.c2 = char1b;
+ic.c.c3 = char2a;
+ic.c.c4 = char2b;
+OP1(SLJIT_MOV_U32, SLJIT_R4, 0, SLJIT_IMM, ic.x);
+
+if (diff == 1) {
+ if (char1a == char1b && char2a == char2b) {
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf)
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcps_0_utf));
+ else
+#endif
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcps_0));
+ } else {
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf)
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcps_1_utf));
+ else
+#endif
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcps_1));
+ }
+} else {
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf)
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcps_default_utf));
+ else
+#endif
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcps_default));
+}
+
+/* Restore STR_PTR register. */
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+
+/* Check return value. */
+partial_quit = CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0);
+add_jump(compiler, &common->failed_match, partial_quit);
+
+/* Fast forward STR_PTR to the result of memchr. */
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0);
+
+JUMPHERE(partial_quit);
+}
+
+#endif /* SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64 */
+
+#if (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#define VECTOR_ELEMENT_SIZE 0
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+#define VECTOR_ELEMENT_SIZE 1
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+#define VECTOR_ELEMENT_SIZE 2
+#else
+#error "Unsupported unit width"
+#endif
+
+static void load_from_mem_vector(struct sljit_compiler *compiler, BOOL vlbb, sljit_s32 dst_vreg,
+ sljit_s32 base_reg, sljit_s32 index_reg)
+{
+sljit_u16 instruction[3];
+
+instruction[0] = (sljit_u16)(0xe700 | (dst_vreg << 4) | index_reg);
+instruction[1] = (sljit_u16)(base_reg << 12);
+instruction[2] = (sljit_u16)((0x8 << 8) | (vlbb ? 0x07 : 0x06));
+
+sljit_emit_op_custom(compiler, instruction, 6);
+}
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+
+static void replicate_imm_vector(struct sljit_compiler *compiler, int step, sljit_s32 dst_vreg,
+ PCRE2_UCHAR chr, sljit_s32 tmp_general_reg)
+{
+sljit_u16 instruction[3];
+
+SLJIT_ASSERT(step >= 0 && step <= 1);
+
+if (chr < 0x7fff)
+ {
+ if (step == 1)
+ return;
+
+ /* VREPI */
+ instruction[0] = (sljit_u16)(0xe700 | (dst_vreg << 4));
+ instruction[1] = (sljit_u16)chr;
+ instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ return;
+ }
+
+if (step == 0)
+ {
+ OP1(SLJIT_MOV, tmp_general_reg, 0, SLJIT_IMM, chr);
+
+ /* VLVG */
+ instruction[0] = (sljit_u16)(0xe700 | (dst_vreg << 4) | sljit_get_register_index(tmp_general_reg));
+ instruction[1] = 0;
+ instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x22);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ return;
+ }
+
+/* VREP */
+instruction[0] = (sljit_u16)(0xe700 | (dst_vreg << 4) | dst_vreg);
+instruction[1] = 0;
+instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0xc << 8) | 0x4d);
+sljit_emit_op_custom(compiler, instruction, 6);
+}
+
+#endif
+
+static void fast_forward_char_pair_sse2_compare(struct sljit_compiler *compiler, vector_compare_type compare_type,
+ int step, sljit_s32 dst_ind, sljit_s32 cmp1_ind, sljit_s32 cmp2_ind, sljit_s32 tmp_ind)
+{
+sljit_u16 instruction[3];
+
+SLJIT_ASSERT(step >= 0 && step <= 2);
+
+if (step == 1)
+ {
+ /* VCEQ */
+ instruction[0] = (sljit_u16)(0xe700 | (dst_ind << 4) | dst_ind);
+ instruction[1] = (sljit_u16)(cmp1_ind << 12);
+ instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0xe << 8) | 0xf8);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ return;
+ }
+
+if (compare_type != vector_compare_match2)
+ {
+ if (step == 0 && compare_type == vector_compare_match1i)
+ {
+ /* VO */
+ instruction[0] = (sljit_u16)(0xe700 | (dst_ind << 4) | dst_ind);
+ instruction[1] = (sljit_u16)(cmp2_ind << 12);
+ instruction[2] = (sljit_u16)((0xe << 8) | 0x6a);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+ return;
+ }
+
+switch (step)
+ {
+ case 0:
+ /* VCEQ */
+ instruction[0] = (sljit_u16)(0xe700 | (tmp_ind << 4) | dst_ind);
+ instruction[1] = (sljit_u16)(cmp2_ind << 12);
+ instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0xe << 8) | 0xf8);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ return;
+
+ case 2:
+ /* VO */
+ instruction[0] = (sljit_u16)(0xe700 | (dst_ind << 4) | dst_ind);
+ instruction[1] = (sljit_u16)(tmp_ind << 12);
+ instruction[2] = (sljit_u16)((0xe << 8) | 0x6a);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ return;
+ }
+}
+
+#define JIT_HAS_FAST_FORWARD_CHAR_SIMD 1
+
+static void fast_forward_char_simd(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2, sljit_s32 offset)
+{
+DEFINE_COMPILER;
+sljit_u16 instruction[3];
+struct sljit_label *start;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_label *restart;
+#endif
+struct sljit_jump *quit;
+struct sljit_jump *partial_quit[2];
+vector_compare_type compare_type = vector_compare_match1;
+sljit_s32 tmp1_reg_ind = sljit_get_register_index(TMP1);
+sljit_s32 str_ptr_reg_ind = sljit_get_register_index(STR_PTR);
+sljit_s32 data_ind = 0;
+sljit_s32 tmp_ind = 1;
+sljit_s32 cmp1_ind = 2;
+sljit_s32 cmp2_ind = 3;
+sljit_s32 zero_ind = 4;
+sljit_u32 bit = 0;
+int i;
+
+SLJIT_UNUSED_ARG(offset);
+
+if (char1 != char2)
+ {
+ bit = char1 ^ char2;
+ compare_type = vector_compare_match1i;
+
+ if (!is_powerof2(bit))
+ {
+ bit = 0;
+ compare_type = vector_compare_match2;
+ }
+ }
+
+partial_quit[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit[0]);
+
+/* First part (unaligned start) */
+
+OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, 16);
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+
+/* VREPI */
+instruction[0] = (sljit_u16)(0xe700 | (cmp1_ind << 4));
+instruction[1] = (sljit_u16)(char1 | bit);
+instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+if (char1 != char2)
+ {
+ /* VREPI */
+ instruction[0] = (sljit_u16)(0xe700 | (cmp2_ind << 4));
+ instruction[1] = (sljit_u16)(bit != 0 ? bit : char2);
+ /* instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45); */
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+#else /* PCRE2_CODE_UNIT_WIDTH == 32 */
+
+for (int i = 0; i < 2; i++)
+ {
+ replicate_imm_vector(compiler, i, cmp1_ind, char1 | bit, TMP1);
+
+ if (char1 != char2)
+ replicate_imm_vector(compiler, i, cmp2_ind, bit != 0 ? bit : char2, TMP1);
+ }
+
+#endif /* PCRE2_CODE_UNIT_WIDTH != 32 */
+
+if (compare_type == vector_compare_match2)
+ {
+ /* VREPI */
+ instruction[0] = (sljit_u16)(0xe700 | (zero_ind << 4));
+ instruction[1] = 0;
+ instruction[2] = (sljit_u16)((0x8 << 8) | 0x45);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+restart = LABEL();
+#endif
+
+load_from_mem_vector(compiler, TRUE, data_ind, str_ptr_reg_ind, 0);
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, ~15);
+
+if (compare_type != vector_compare_match2)
+ {
+ if (compare_type == vector_compare_match1i)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, 0, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+ /* VFEE */
+ instruction[0] = (sljit_u16)(0xe700 | (data_ind << 4) | data_ind);
+ instruction[1] = (sljit_u16)((cmp1_ind << 12) | (1 << 4));
+ instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0xe << 8) | 0x80);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+else
+ {
+ for (i = 0; i < 3; i++)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, i, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+ /* VFENE */
+ instruction[0] = (sljit_u16)(0xe700 | (data_ind << 4) | data_ind);
+ instruction[1] = (sljit_u16)((zero_ind << 12) | (1 << 4));
+ instruction[2] = (sljit_u16)((0xe << 8) | 0x81);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+/* VLGVB */
+instruction[0] = (sljit_u16)(0xe700 | (tmp1_reg_ind << 4) | data_ind);
+instruction[1] = 7;
+instruction[2] = (sljit_u16)((0x4 << 8) | 0x21);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+quit = CMP(SLJIT_LESS, STR_PTR, 0, TMP2, 0);
+
+OP2(SLJIT_SUB, STR_PTR, 0, TMP2, 0, SLJIT_IMM, 16);
+
+/* Second part (aligned) */
+start = LABEL();
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16);
+
+partial_quit[1] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit[1]);
+
+load_from_mem_vector(compiler, TRUE, data_ind, str_ptr_reg_ind, 0);
+
+if (compare_type != vector_compare_match2)
+ {
+ if (compare_type == vector_compare_match1i)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, 0, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+ /* VFEE */
+ instruction[0] = (sljit_u16)(0xe700 | (data_ind << 4) | data_ind);
+ instruction[1] = (sljit_u16)((cmp1_ind << 12) | (1 << 4));
+ instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0xe << 8) | 0x80);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+else
+ {
+ for (i = 0; i < 3; i++)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, i, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+ /* VFENE */
+ instruction[0] = (sljit_u16)(0xe700 | (data_ind << 4) | data_ind);
+ instruction[1] = (sljit_u16)((zero_ind << 12) | (1 << 4));
+ instruction[2] = (sljit_u16)((0xe << 8) | 0x81);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+sljit_set_current_flags(compiler, SLJIT_SET_OVERFLOW);
+JUMPTO(SLJIT_OVERFLOW, start);
+
+/* VLGVB */
+instruction[0] = (sljit_u16)(0xe700 | (tmp1_reg_ind << 4) | data_ind);
+instruction[1] = 7;
+instruction[2] = (sljit_u16)((0x4 << 8) | 0x21);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+
+JUMPHERE(quit);
+
+if (common->mode != PCRE2_JIT_COMPLETE)
+ {
+ JUMPHERE(partial_quit[0]);
+ JUMPHERE(partial_quit[1]);
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, STR_PTR, 0, STR_END, 0);
+ CMOV(SLJIT_GREATER, STR_PTR, STR_END, 0);
+ }
+else
+ add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+if (common->utf && offset > 0)
+ {
+ SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE);
+
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offset));
+
+ quit = jump_if_utf_char_start(compiler, TMP1);
+
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, 16);
+ JUMPTO(SLJIT_JUMP, restart);
+
+ JUMPHERE(quit);
+ }
+#endif
+}
+
+#define JIT_HAS_FAST_REQUESTED_CHAR_SIMD 1
+
+static jump_list *fast_requested_char_simd(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2)
+{
+DEFINE_COMPILER;
+sljit_u16 instruction[3];
+struct sljit_label *start;
+struct sljit_jump *quit;
+jump_list *not_found = NULL;
+vector_compare_type compare_type = vector_compare_match1;
+sljit_s32 tmp1_reg_ind = sljit_get_register_index(TMP1);
+sljit_s32 tmp3_reg_ind = sljit_get_register_index(TMP3);
+sljit_s32 data_ind = 0;
+sljit_s32 tmp_ind = 1;
+sljit_s32 cmp1_ind = 2;
+sljit_s32 cmp2_ind = 3;
+sljit_s32 zero_ind = 4;
+sljit_u32 bit = 0;
+int i;
+
+if (char1 != char2)
+ {
+ bit = char1 ^ char2;
+ compare_type = vector_compare_match1i;
+
+ if (!is_powerof2(bit))
+ {
+ bit = 0;
+ compare_type = vector_compare_match2;
+ }
+ }
+
+add_jump(compiler, &not_found, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, STR_END, 0));
+
+/* First part (unaligned start) */
+
+OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, 16);
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+
+/* VREPI */
+instruction[0] = (sljit_u16)(0xe700 | (cmp1_ind << 4));
+instruction[1] = (sljit_u16)(char1 | bit);
+instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+if (char1 != char2)
+ {
+ /* VREPI */
+ instruction[0] = (sljit_u16)(0xe700 | (cmp2_ind << 4));
+ instruction[1] = (sljit_u16)(bit != 0 ? bit : char2);
+ /* instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45); */
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+#else /* PCRE2_CODE_UNIT_WIDTH == 32 */
+
+for (int i = 0; i < 2; i++)
+ {
+ replicate_imm_vector(compiler, i, cmp1_ind, char1 | bit, TMP3);
+
+ if (char1 != char2)
+ replicate_imm_vector(compiler, i, cmp2_ind, bit != 0 ? bit : char2, TMP3);
+ }
+
+#endif /* PCRE2_CODE_UNIT_WIDTH != 32 */
+
+if (compare_type == vector_compare_match2)
+ {
+ /* VREPI */
+ instruction[0] = (sljit_u16)(0xe700 | (zero_ind << 4));
+ instruction[1] = 0;
+ instruction[2] = (sljit_u16)((0x8 << 8) | 0x45);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+load_from_mem_vector(compiler, TRUE, data_ind, tmp1_reg_ind, 0);
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, ~15);
+
+if (compare_type != vector_compare_match2)
+ {
+ if (compare_type == vector_compare_match1i)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, 0, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+ /* VFEE */
+ instruction[0] = (sljit_u16)(0xe700 | (data_ind << 4) | data_ind);
+ instruction[1] = (sljit_u16)((cmp1_ind << 12) | (1 << 4));
+ instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0xe << 8) | 0x80);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+else
+ {
+ for (i = 0; i < 3; i++)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, i, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+ /* VFENE */
+ instruction[0] = (sljit_u16)(0xe700 | (data_ind << 4) | data_ind);
+ instruction[1] = (sljit_u16)((zero_ind << 12) | (1 << 4));
+ instruction[2] = (sljit_u16)((0xe << 8) | 0x81);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+/* VLGVB */
+instruction[0] = (sljit_u16)(0xe700 | (tmp3_reg_ind << 4) | data_ind);
+instruction[1] = 7;
+instruction[2] = (sljit_u16)((0x4 << 8) | 0x21);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP3, 0);
+quit = CMP(SLJIT_LESS, TMP1, 0, TMP2, 0);
+
+OP2(SLJIT_SUB, TMP1, 0, TMP2, 0, SLJIT_IMM, 16);
+
+/* Second part (aligned) */
+start = LABEL();
+
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 16);
+
+add_jump(compiler, &not_found, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, STR_END, 0));
+
+load_from_mem_vector(compiler, TRUE, data_ind, tmp1_reg_ind, 0);
+
+if (compare_type != vector_compare_match2)
+ {
+ if (compare_type == vector_compare_match1i)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, 0, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+ /* VFEE */
+ instruction[0] = (sljit_u16)(0xe700 | (data_ind << 4) | data_ind);
+ instruction[1] = (sljit_u16)((cmp1_ind << 12) | (1 << 4));
+ instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0xe << 8) | 0x80);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+else
+ {
+ for (i = 0; i < 3; i++)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, i, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+ /* VFENE */
+ instruction[0] = (sljit_u16)(0xe700 | (data_ind << 4) | data_ind);
+ instruction[1] = (sljit_u16)((zero_ind << 12) | (1 << 4));
+ instruction[2] = (sljit_u16)((0xe << 8) | 0x81);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+sljit_set_current_flags(compiler, SLJIT_SET_OVERFLOW);
+JUMPTO(SLJIT_OVERFLOW, start);
+
+/* VLGVB */
+instruction[0] = (sljit_u16)(0xe700 | (tmp3_reg_ind << 4) | data_ind);
+instruction[1] = 7;
+instruction[2] = (sljit_u16)((0x4 << 8) | 0x21);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP3, 0);
+
+JUMPHERE(quit);
+add_jump(compiler, &not_found, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, STR_END, 0));
+
+return not_found;
+}
+
+#define JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD 1
+
+static void fast_forward_char_pair_simd(compiler_common *common, sljit_s32 offs1,
+ PCRE2_UCHAR char1a, PCRE2_UCHAR char1b, sljit_s32 offs2, PCRE2_UCHAR char2a, PCRE2_UCHAR char2b)
+{
+DEFINE_COMPILER;
+sljit_u16 instruction[3];
+struct sljit_label *start;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_label *restart;
+#endif
+struct sljit_jump *quit;
+struct sljit_jump *jump[2];
+vector_compare_type compare1_type = vector_compare_match1;
+vector_compare_type compare2_type = vector_compare_match1;
+sljit_u32 bit1 = 0;
+sljit_u32 bit2 = 0;
+sljit_s32 diff = IN_UCHARS(offs2 - offs1);
+sljit_s32 tmp1_reg_ind = sljit_get_register_index(TMP1);
+sljit_s32 tmp2_reg_ind = sljit_get_register_index(TMP2);
+sljit_s32 str_ptr_reg_ind = sljit_get_register_index(STR_PTR);
+sljit_s32 data1_ind = 0;
+sljit_s32 data2_ind = 1;
+sljit_s32 tmp1_ind = 2;
+sljit_s32 tmp2_ind = 3;
+sljit_s32 cmp1a_ind = 4;
+sljit_s32 cmp1b_ind = 5;
+sljit_s32 cmp2a_ind = 6;
+sljit_s32 cmp2b_ind = 7;
+sljit_s32 zero_ind = 8;
+int i;
+
+SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE && offs1 > offs2);
+SLJIT_ASSERT(-diff <= (sljit_s32)IN_UCHARS(max_fast_forward_char_pair_offset()));
+SLJIT_ASSERT(tmp1_reg_ind != 0 && tmp2_reg_ind != 0);
+
+if (char1a != char1b)
+ {
+ bit1 = char1a ^ char1b;
+ compare1_type = vector_compare_match1i;
+
+ if (!is_powerof2(bit1))
+ {
+ bit1 = 0;
+ compare1_type = vector_compare_match2;
+ }
+ }
+
+if (char2a != char2b)
+ {
+ bit2 = char2a ^ char2b;
+ compare2_type = vector_compare_match1i;
+
+ if (!is_powerof2(bit2))
+ {
+ bit2 = 0;
+ compare2_type = vector_compare_match2;
+ }
+ }
+
+/* Initialize. */
+if (common->match_end_ptr != 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(offs1 + 1));
+
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, STR_END, 0);
+ CMOV(SLJIT_LESS, STR_END, TMP1, 0);
+ }
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offs1));
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+OP2(SLJIT_AND, TMP2, 0, STR_PTR, 0, SLJIT_IMM, ~15);
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+
+OP2(SLJIT_SUB, TMP1, 0, STR_PTR, 0, SLJIT_IMM, -diff);
+
+/* VREPI */
+instruction[0] = (sljit_u16)(0xe700 | (cmp1a_ind << 4));
+instruction[1] = (sljit_u16)(char1a | bit1);
+instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+if (char1a != char1b)
+ {
+ /* VREPI */
+ instruction[0] = (sljit_u16)(0xe700 | (cmp1b_ind << 4));
+ instruction[1] = (sljit_u16)(bit1 != 0 ? bit1 : char1b);
+ /* instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45); */
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+/* VREPI */
+instruction[0] = (sljit_u16)(0xe700 | (cmp2a_ind << 4));
+instruction[1] = (sljit_u16)(char2a | bit2);
+/* instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45); */
+sljit_emit_op_custom(compiler, instruction, 6);
+
+if (char2a != char2b)
+ {
+ /* VREPI */
+ instruction[0] = (sljit_u16)(0xe700 | (cmp2b_ind << 4));
+ instruction[1] = (sljit_u16)(bit2 != 0 ? bit2 : char2b);
+ /* instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45); */
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+#else /* PCRE2_CODE_UNIT_WIDTH == 32 */
+
+for (int i = 0; i < 2; i++)
+ {
+ replicate_imm_vector(compiler, i, cmp1a_ind, char1a | bit1, TMP1);
+
+ if (char1a != char1b)
+ replicate_imm_vector(compiler, i, cmp1b_ind, bit1 != 0 ? bit1 : char1b, TMP1);
+
+ replicate_imm_vector(compiler, i, cmp2a_ind, char2a | bit2, TMP1);
+
+ if (char2a != char2b)
+ replicate_imm_vector(compiler, i, cmp2b_ind, bit2 != 0 ? bit2 : char2b, TMP1);
+ }
+
+OP2(SLJIT_SUB, TMP1, 0, STR_PTR, 0, SLJIT_IMM, -diff);
+
+#endif /* PCRE2_CODE_UNIT_WIDTH != 32 */
+
+/* VREPI */
+instruction[0] = (sljit_u16)(0xe700 | (zero_ind << 4));
+instruction[1] = 0;
+instruction[2] = (sljit_u16)((0x8 << 8) | 0x45);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+restart = LABEL();
+#endif
+
+jump[0] = CMP(SLJIT_LESS, TMP1, 0, TMP2, 0);
+load_from_mem_vector(compiler, TRUE, data2_ind, tmp1_reg_ind, 0);
+jump[1] = JUMP(SLJIT_JUMP);
+JUMPHERE(jump[0]);
+load_from_mem_vector(compiler, FALSE, data2_ind, tmp1_reg_ind, 0);
+JUMPHERE(jump[1]);
+
+load_from_mem_vector(compiler, TRUE, data1_ind, str_ptr_reg_ind, 0);
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, 16);
+
+for (i = 0; i < 3; i++)
+ {
+ fast_forward_char_pair_sse2_compare(compiler, compare1_type, i, data1_ind, cmp1a_ind, cmp1b_ind, tmp1_ind);
+ fast_forward_char_pair_sse2_compare(compiler, compare2_type, i, data2_ind, cmp2a_ind, cmp2b_ind, tmp2_ind);
+ }
+
+/* VN */
+instruction[0] = (sljit_u16)(0xe700 | (data1_ind << 4) | data1_ind);
+instruction[1] = (sljit_u16)(data2_ind << 12);
+instruction[2] = (sljit_u16)((0xe << 8) | 0x68);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+/* VFENE */
+instruction[0] = (sljit_u16)(0xe700 | (data1_ind << 4) | data1_ind);
+instruction[1] = (sljit_u16)((zero_ind << 12) | (1 << 4));
+instruction[2] = (sljit_u16)((0xe << 8) | 0x81);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+/* VLGVB */
+instruction[0] = (sljit_u16)(0xe700 | (tmp1_reg_ind << 4) | data1_ind);
+instruction[1] = 7;
+instruction[2] = (sljit_u16)((0x4 << 8) | 0x21);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+quit = CMP(SLJIT_LESS, STR_PTR, 0, TMP2, 0);
+
+OP2(SLJIT_SUB, STR_PTR, 0, TMP2, 0, SLJIT_IMM, 16);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, diff);
+
+/* Main loop. */
+start = LABEL();
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16);
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+load_from_mem_vector(compiler, FALSE, data1_ind, str_ptr_reg_ind, 0);
+load_from_mem_vector(compiler, FALSE, data2_ind, str_ptr_reg_ind, tmp1_reg_ind);
+
+for (i = 0; i < 3; i++)
+ {
+ fast_forward_char_pair_sse2_compare(compiler, compare1_type, i, data1_ind, cmp1a_ind, cmp1b_ind, tmp1_ind);
+ fast_forward_char_pair_sse2_compare(compiler, compare2_type, i, data2_ind, cmp2a_ind, cmp2b_ind, tmp2_ind);
+ }
+
+/* VN */
+instruction[0] = (sljit_u16)(0xe700 | (data1_ind << 4) | data1_ind);
+instruction[1] = (sljit_u16)(data2_ind << 12);
+instruction[2] = (sljit_u16)((0xe << 8) | 0x68);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+/* VFENE */
+instruction[0] = (sljit_u16)(0xe700 | (data1_ind << 4) | data1_ind);
+instruction[1] = (sljit_u16)((zero_ind << 12) | (1 << 4));
+instruction[2] = (sljit_u16)((0xe << 8) | 0x81);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+sljit_set_current_flags(compiler, SLJIT_SET_OVERFLOW);
+JUMPTO(SLJIT_OVERFLOW, start);
+
+/* VLGVB */
+instruction[0] = (sljit_u16)(0xe700 | (tmp2_reg_ind << 4) | data1_ind);
+instruction[1] = 7;
+instruction[2] = (sljit_u16)((0x4 << 8) | 0x21);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+
+JUMPHERE(quit);
+
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+if (common->utf)
+ {
+ SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE);
+
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offs1));
+
+ quit = jump_if_utf_char_start(compiler, TMP1);
+
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+ /* TMP1 contains diff. */
+ OP2(SLJIT_AND, TMP2, 0, STR_PTR, 0, SLJIT_IMM, ~15);
+ OP2(SLJIT_SUB, TMP1, 0, STR_PTR, 0, SLJIT_IMM, -diff);
+ JUMPTO(SLJIT_JUMP, restart);
+
+ JUMPHERE(quit);
+ }
+#endif
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offs1));
+
+if (common->match_end_ptr != 0)
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+}
+
+#endif /* SLJIT_CONFIG_S390X */
+
+#endif /* !SUPPORT_VALGRIND */
diff --git a/contrib/libs/pcre2/src/pcre2_maketables.c b/contrib/libs/pcre2/src/pcre2_maketables.c
new file mode 100644
index 00000000000..3a4c47d00a3
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_maketables.c
@@ -0,0 +1,163 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2020 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This module contains the external function pcre2_maketables(), which builds
+character tables for PCRE2 in the current locale. The file is compiled on its
+own as part of the PCRE2 library. It is also included in the compilation of
+pcre2_dftables.c as a freestanding program, in which case the macro
+PCRE2_DFTABLES is defined. */
+
+#ifndef PCRE2_DFTABLES /* Compiling the library */
+# ifdef HAVE_CONFIG_H
+# include <pcre2_config.h>
+# endif
+# include "pcre2_internal.h"
+#endif
+
+
+
+/*************************************************
+* Create PCRE2 character tables *
+*************************************************/
+
+/* This function builds a set of character tables for use by PCRE2 and returns
+a pointer to them. They are build using the ctype functions, and consequently
+their contents will depend upon the current locale setting. When compiled as
+part of the library, the store is obtained via a general context malloc, if
+supplied, but when PCRE2_DFTABLES is defined (when compiling the pcre2_dftables
+freestanding auxiliary program) malloc() is used, and the function has a
+different name so as not to clash with the prototype in pcre2.h.
+
+Arguments: none when PCRE2_DFTABLES is defined
+ else a PCRE2 general context or NULL
+Returns: pointer to the contiguous block of data
+ else NULL if memory allocation failed
+*/
+
+#ifdef PCRE2_DFTABLES /* Included in freestanding pcre2_dftables program */
+static const uint8_t *maketables(void)
+{
+uint8_t *yield = (uint8_t *)malloc(TABLES_LENGTH);
+
+#else /* Not PCRE2_DFTABLES, that is, compiling the library */
+PCRE2_EXP_DEFN const uint8_t * PCRE2_CALL_CONVENTION
+pcre2_maketables(pcre2_general_context *gcontext)
+{
+uint8_t *yield = (uint8_t *)((gcontext != NULL)?
+ gcontext->memctl.malloc(TABLES_LENGTH, gcontext->memctl.memory_data) :
+ malloc(TABLES_LENGTH));
+#endif /* PCRE2_DFTABLES */
+
+int i;
+uint8_t *p;
+
+if (yield == NULL) return NULL;
+p = yield;
+
+/* First comes the lower casing table */
+
+for (i = 0; i < 256; i++) *p++ = tolower(i);
+
+/* Next the case-flipping table */
+
+for (i = 0; i < 256; i++) *p++ = islower(i)? toupper(i) : tolower(i);
+
+/* Then the character class tables. Don't try to be clever and save effort on
+exclusive ones - in some locales things may be different.
+
+Note that the table for "space" includes everything "isspace" gives, including
+VT in the default locale. This makes it work for the POSIX class [:space:].
+From PCRE1 release 8.34 and for all PCRE2 releases it is also correct for Perl
+space, because Perl added VT at release 5.18.
+
+Note also that it is possible for a character to be alnum or alpha without
+being lower or upper, such as "male and female ordinals" (\xAA and \xBA) in the
+fr_FR locale (at least under Debian Linux's locales as of 12/2005). So we must
+test for alnum specially. */
+
+memset(p, 0, cbit_length);
+for (i = 0; i < 256; i++)
+ {
+ if (isdigit(i)) p[cbit_digit + i/8] |= 1u << (i&7);
+ if (isupper(i)) p[cbit_upper + i/8] |= 1u << (i&7);
+ if (islower(i)) p[cbit_lower + i/8] |= 1u << (i&7);
+ if (isalnum(i)) p[cbit_word + i/8] |= 1u << (i&7);
+ if (i == '_') p[cbit_word + i/8] |= 1u << (i&7);
+ if (isspace(i)) p[cbit_space + i/8] |= 1u << (i&7);
+ if (isxdigit(i)) p[cbit_xdigit + i/8] |= 1u << (i&7);
+ if (isgraph(i)) p[cbit_graph + i/8] |= 1u << (i&7);
+ if (isprint(i)) p[cbit_print + i/8] |= 1u << (i&7);
+ if (ispunct(i)) p[cbit_punct + i/8] |= 1u << (i&7);
+ if (iscntrl(i)) p[cbit_cntrl + i/8] |= 1u << (i&7);
+ }
+p += cbit_length;
+
+/* Finally, the character type table. In this, we used to exclude VT from the
+white space chars, because Perl didn't recognize it as such for \s and for
+comments within regexes. However, Perl changed at release 5.18, so PCRE1
+changed at release 8.34 and it's always been this way for PCRE2. */
+
+for (i = 0; i < 256; i++)
+ {
+ int x = 0;
+ if (isspace(i)) x += ctype_space;
+ if (isalpha(i)) x += ctype_letter;
+ if (islower(i)) x += ctype_lcletter;
+ if (isdigit(i)) x += ctype_digit;
+ if (isalnum(i) || i == '_') x += ctype_word;
+ *p++ = x;
+ }
+
+return yield;
+}
+
+#ifndef PCRE2_DFTABLES /* Compiling the library */
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_maketables_free(pcre2_general_context *gcontext, const uint8_t *tables)
+{
+ if (gcontext)
+ gcontext->memctl.free((void *)tables, gcontext->memctl.memory_data);
+ else
+ free((void *)tables);
+}
+#endif
+
+/* End of pcre2_maketables.c */
diff --git a/contrib/libs/pcre2/src/pcre2_match.c b/contrib/libs/pcre2/src/pcre2_match.c
new file mode 100644
index 00000000000..80255bbe17b
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_match.c
@@ -0,0 +1,7544 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2015-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+/* These defines enable debugging code */
+
+/* #define DEBUG_FRAMES_DISPLAY */
+/* #define DEBUG_SHOW_OPS */
+/* #define DEBUG_SHOW_RMATCH */
+
+#ifdef DEBUG_FRAMES_DISPLAY
+#include <stdarg.h>
+#endif
+
+/* These defines identify the name of the block containing "static"
+information, and fields within it. */
+
+#define NLBLOCK mb /* Block containing newline information */
+#define PSSTART start_subject /* Field containing processed string start */
+#define PSEND end_subject /* Field containing processed string end */
+
+#include "pcre2_internal.h"
+
+#define RECURSE_UNSET 0xffffffffu /* Bigger than max group number */
+
+/* Masks for identifying the public options that are permitted at match time. */
+
+#define PUBLIC_MATCH_OPTIONS \
+ (PCRE2_ANCHORED|PCRE2_ENDANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \
+ PCRE2_NOTEMPTY_ATSTART|PCRE2_NO_UTF_CHECK|PCRE2_PARTIAL_HARD| \
+ PCRE2_PARTIAL_SOFT|PCRE2_NO_JIT|PCRE2_COPY_MATCHED_SUBJECT)
+
+#define PUBLIC_JIT_MATCH_OPTIONS \
+ (PCRE2_NO_UTF_CHECK|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY|\
+ PCRE2_NOTEMPTY_ATSTART|PCRE2_PARTIAL_SOFT|PCRE2_PARTIAL_HARD|\
+ PCRE2_COPY_MATCHED_SUBJECT)
+
+/* Non-error returns from and within the match() function. Error returns are
+externally defined PCRE2_ERROR_xxx codes, which are all negative. */
+
+#define MATCH_MATCH 1
+#define MATCH_NOMATCH 0
+
+/* Special internal returns used in the match() function. Make them
+sufficiently negative to avoid the external error codes. */
+
+#define MATCH_ACCEPT (-999)
+#define MATCH_KETRPOS (-998)
+/* The next 5 must be kept together and in sequence so that a test that checks
+for any one of them can use a range. */
+#define MATCH_COMMIT (-997)
+#define MATCH_PRUNE (-996)
+#define MATCH_SKIP (-995)
+#define MATCH_SKIP_ARG (-994)
+#define MATCH_THEN (-993)
+#define MATCH_BACKTRACK_MAX MATCH_THEN
+#define MATCH_BACKTRACK_MIN MATCH_COMMIT
+
+/* Group frame type values. Zero means the frame is not a group frame. The
+lower 16 bits are used for data (e.g. the capture number). Group frames are
+used for most groups so that information about the start is easily available at
+the end without having to scan back through intermediate frames (backtrack
+points). */
+
+#define GF_CAPTURE 0x00010000u
+#define GF_NOCAPTURE 0x00020000u
+#define GF_CONDASSERT 0x00030000u
+#define GF_RECURSE 0x00040000u
+
+/* Masks for the identity and data parts of the group frame type. */
+
+#define GF_IDMASK(a) ((a) & 0xffff0000u)
+#define GF_DATAMASK(a) ((a) & 0x0000ffffu)
+
+/* Repetition types */
+
+enum { REPTYPE_MIN, REPTYPE_MAX, REPTYPE_POS };
+
+/* Min and max values for the common repeats; a maximum of UINT32_MAX =>
+infinity. */
+
+static const uint32_t rep_min[] = {
+ 0, 0, /* * and *? */
+ 1, 1, /* + and +? */
+ 0, 0, /* ? and ?? */
+ 0, 0, /* dummy placefillers for OP_CR[MIN]RANGE */
+ 0, 1, 0 }; /* OP_CRPOS{STAR, PLUS, QUERY} */
+
+static const uint32_t rep_max[] = {
+ UINT32_MAX, UINT32_MAX, /* * and *? */
+ UINT32_MAX, UINT32_MAX, /* + and +? */
+ 1, 1, /* ? and ?? */
+ 0, 0, /* dummy placefillers for OP_CR[MIN]RANGE */
+ UINT32_MAX, UINT32_MAX, 1 }; /* OP_CRPOS{STAR, PLUS, QUERY} */
+
+/* Repetition types - must include OP_CRPOSRANGE (not needed above) */
+
+static const uint32_t rep_typ[] = {
+ REPTYPE_MAX, REPTYPE_MIN, /* * and *? */
+ REPTYPE_MAX, REPTYPE_MIN, /* + and +? */
+ REPTYPE_MAX, REPTYPE_MIN, /* ? and ?? */
+ REPTYPE_MAX, REPTYPE_MIN, /* OP_CRRANGE and OP_CRMINRANGE */
+ REPTYPE_POS, REPTYPE_POS, /* OP_CRPOSSTAR, OP_CRPOSPLUS */
+ REPTYPE_POS, REPTYPE_POS }; /* OP_CRPOSQUERY, OP_CRPOSRANGE */
+
+/* Numbers for RMATCH calls at backtracking points. When these lists are
+changed, the code at RETURN_SWITCH below must be updated in sync. */
+
+enum { RM1=1, RM2, RM3, RM4, RM5, RM6, RM7, RM8, RM9, RM10,
+ RM11, RM12, RM13, RM14, RM15, RM16, RM17, RM18, RM19, RM20,
+ RM21, RM22, RM23, RM24, RM25, RM26, RM27, RM28, RM29, RM30,
+ RM31, RM32, RM33, RM34, RM35, RM36 };
+
+#ifdef SUPPORT_WIDE_CHARS
+enum { RM100=100, RM101 };
+#endif
+
+#ifdef SUPPORT_UNICODE
+enum { RM200=200, RM201, RM202, RM203, RM204, RM205, RM206, RM207,
+ RM208, RM209, RM210, RM211, RM212, RM213, RM214, RM215,
+ RM216, RM217, RM218, RM219, RM220, RM221, RM222, RM223,
+ RM224, RM225 };
+#endif
+
+/* Define short names for general fields in the current backtrack frame, which
+is always pointed to by the F variable. Occasional references to fields in
+other frames are written out explicitly. There are also some fields in the
+current frame whose names start with "temp" that are used for short-term,
+localised backtracking memory. These are #defined with Lxxx names at the point
+of use and undefined afterwards. */
+
+#define Fback_frame F->back_frame
+#define Fcapture_last F->capture_last
+#define Fcurrent_recurse F->current_recurse
+#define Fecode F->ecode
+#define Feptr F->eptr
+#define Fgroup_frame_type F->group_frame_type
+#define Flast_group_offset F->last_group_offset
+#define Flength F->length
+#define Fmark F->mark
+#define Frdepth F->rdepth
+#define Fstart_match F->start_match
+#define Foffset_top F->offset_top
+#define Foccu F->occu
+#define Fop F->op
+#define Fovector F->ovector
+#define Freturn_id F->return_id
+
+
+#ifdef DEBUG_FRAMES_DISPLAY
+/*************************************************
+* Display current frames and contents *
+*************************************************/
+
+/* This debugging function displays the current set of frames and their
+contents. It is not called automatically from anywhere, the intention being
+that calls can be inserted where necessary when debugging frame-related
+problems.
+
+Arguments:
+ f the file to write to
+ F the current top frame
+ P a previous frame of interest
+ frame_size the frame size
+ mb points to the match block
+ match_data points to the match data block
+ s identification text
+
+Returns: nothing
+*/
+
+static void
+display_frames(FILE *f, heapframe *F, heapframe *P, PCRE2_SIZE frame_size,
+ match_block *mb, pcre2_match_data *match_data, const char *s, ...)
+{
+uint32_t i;
+heapframe *Q;
+va_list ap;
+va_start(ap, s);
+
+fprintf(f, "FRAMES ");
+vfprintf(f, s, ap);
+va_end(ap);
+
+if (P != NULL) fprintf(f, " P=%lu",
+ ((char *)P - (char *)(match_data->heapframes))/frame_size);
+fprintf(f, "\n");
+
+for (i = 0, Q = match_data->heapframes;
+ Q <= F;
+ i++, Q = (heapframe *)((char *)Q + frame_size))
+ {
+ fprintf(f, "Frame %d type=%x subj=%lu code=%d back=%lu id=%d",
+ i, Q->group_frame_type, Q->eptr - mb->start_subject, *(Q->ecode),
+ Q->back_frame, Q->return_id);
+
+ if (Q->last_group_offset == PCRE2_UNSET)
+ fprintf(f, " lgoffset=unset\n");
+ else
+ fprintf(f, " lgoffset=%lu\n", Q->last_group_offset/frame_size);
+ }
+}
+
+#endif
+
+
+
+/*************************************************
+* Process a callout *
+*************************************************/
+
+/* This function is called for all callouts, whether "standalone" or at the
+start of a conditional group. Feptr will be pointing to either OP_CALLOUT or
+OP_CALLOUT_STR. A callout block is allocated in pcre2_match() and initialized
+with fixed values.
+
+Arguments:
+ F points to the current backtracking frame
+ mb points to the match block
+ lengthptr where to return the length of the callout item
+
+Returns: the return from the callout
+ or 0 if no callout function exists
+*/
+
+static int
+do_callout(heapframe *F, match_block *mb, PCRE2_SIZE *lengthptr)
+{
+int rc;
+PCRE2_SIZE save0, save1;
+PCRE2_SIZE *callout_ovector;
+pcre2_callout_block *cb;
+
+*lengthptr = (*Fecode == OP_CALLOUT)?
+ PRIV(OP_lengths)[OP_CALLOUT] : GET(Fecode, 1 + 2*LINK_SIZE);
+
+if (mb->callout == NULL) return 0; /* No callout function provided */
+
+/* The original matching code (pre 10.30) worked directly with the ovector
+passed by the user, and this was passed to callouts. Now that the working
+ovector is in the backtracking frame, it no longer needs to reserve space for
+the overall match offsets (which would waste space in the frame). For backward
+compatibility, however, we pass capture_top and offset_vector to the callout as
+if for the extended ovector, and we ensure that the first two slots are unset
+by preserving and restoring their current contents. Picky compilers complain if
+references such as Fovector[-2] are use directly, so we set up a separate
+pointer. */
+
+callout_ovector = (PCRE2_SIZE *)(Fovector) - 2;
+
+/* The cb->version, cb->subject, cb->subject_length, and cb->start_match fields
+are set externally. The first 3 never change; the last is updated for each
+bumpalong. */
+
+cb = mb->cb;
+cb->capture_top = (uint32_t)Foffset_top/2 + 1;
+cb->capture_last = Fcapture_last;
+cb->offset_vector = callout_ovector;
+cb->mark = mb->nomatch_mark;
+cb->current_position = (PCRE2_SIZE)(Feptr - mb->start_subject);
+cb->pattern_position = GET(Fecode, 1);
+cb->next_item_length = GET(Fecode, 1 + LINK_SIZE);
+
+if (*Fecode == OP_CALLOUT) /* Numerical callout */
+ {
+ cb->callout_number = Fecode[1 + 2*LINK_SIZE];
+ cb->callout_string_offset = 0;
+ cb->callout_string = NULL;
+ cb->callout_string_length = 0;
+ }
+else /* String callout */
+ {
+ cb->callout_number = 0;
+ cb->callout_string_offset = GET(Fecode, 1 + 3*LINK_SIZE);
+ cb->callout_string = Fecode + (1 + 4*LINK_SIZE) + 1;
+ cb->callout_string_length =
+ *lengthptr - (1 + 4*LINK_SIZE) - 2;
+ }
+
+save0 = callout_ovector[0];
+save1 = callout_ovector[1];
+callout_ovector[0] = callout_ovector[1] = PCRE2_UNSET;
+rc = mb->callout(cb, mb->callout_data);
+callout_ovector[0] = save0;
+callout_ovector[1] = save1;
+cb->callout_flags = 0;
+return rc;
+}
+
+
+
+/*************************************************
+* Match a back-reference *
+*************************************************/
+
+/* This function is called only when it is known that the offset lies within
+the offsets that have so far been used in the match. Note that in caseless
+UTF-8 mode, the number of subject bytes matched may be different to the number
+of reference bytes. (In theory this could also happen in UTF-16 mode, but it
+seems unlikely.)
+
+Arguments:
+ offset index into the offset vector
+ caseless TRUE if caseless
+ F the current backtracking frame pointer
+ mb points to match block
+ lengthptr pointer for returning the length matched
+
+Returns: = 0 sucessful match; number of code units matched is set
+ < 0 no match
+ > 0 partial match
+*/
+
+static int
+match_ref(PCRE2_SIZE offset, BOOL caseless, heapframe *F, match_block *mb,
+ PCRE2_SIZE *lengthptr)
+{
+PCRE2_SPTR p;
+PCRE2_SIZE length;
+PCRE2_SPTR eptr;
+PCRE2_SPTR eptr_start;
+
+/* Deal with an unset group. The default is no match, but there is an option to
+match an empty string. */
+
+if (offset >= Foffset_top || Fovector[offset] == PCRE2_UNSET)
+ {
+ if ((mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0)
+ {
+ *lengthptr = 0;
+ return 0; /* Match */
+ }
+ else return -1; /* No match */
+ }
+
+/* Separate the caseless and UTF cases for speed. */
+
+eptr = eptr_start = Feptr;
+p = mb->start_subject + Fovector[offset];
+length = Fovector[offset+1] - Fovector[offset];
+
+if (caseless)
+ {
+#if defined SUPPORT_UNICODE
+ BOOL utf = (mb->poptions & PCRE2_UTF) != 0;
+
+ if (utf || (mb->poptions & PCRE2_UCP) != 0)
+ {
+ PCRE2_SPTR endptr = p + length;
+
+ /* Match characters up to the end of the reference. NOTE: the number of
+ code units matched may differ, because in UTF-8 there are some characters
+ whose upper and lower case codes have different numbers of bytes. For
+ example, U+023A (2 bytes in UTF-8) is the upper case version of U+2C65 (3
+ bytes in UTF-8); a sequence of 3 of the former uses 6 bytes, as does a
+ sequence of two of the latter. It is important, therefore, to check the
+ length along the reference, not along the subject (earlier code did this
+ wrong). UCP without uses Unicode properties but without UTF encoding. */
+
+ while (p < endptr)
+ {
+ uint32_t c, d;
+ const ucd_record *ur;
+ if (eptr >= mb->end_subject) return 1; /* Partial match */
+
+ if (utf)
+ {
+ GETCHARINC(c, eptr);
+ GETCHARINC(d, p);
+ }
+ else
+ {
+ c = *eptr++;
+ d = *p++;
+ }
+
+ ur = GET_UCD(d);
+ if (c != d && c != (uint32_t)((int)d + ur->other_case))
+ {
+ const uint32_t *pp = PRIV(ucd_caseless_sets) + ur->caseset;
+ for (;;)
+ {
+ if (c < *pp) return -1; /* No match */
+ if (c == *pp++) break;
+ }
+ }
+ }
+ }
+ else
+#endif
+
+ /* Not in UTF or UCP mode */
+ {
+ for (; length > 0; length--)
+ {
+ uint32_t cc, cp;
+ if (eptr >= mb->end_subject) return 1; /* Partial match */
+ cc = UCHAR21TEST(eptr);
+ cp = UCHAR21TEST(p);
+ if (TABLE_GET(cp, mb->lcc, cp) != TABLE_GET(cc, mb->lcc, cc))
+ return -1; /* No match */
+ p++;
+ eptr++;
+ }
+ }
+ }
+
+/* In the caseful case, we can just compare the code units, whether or not we
+are in UTF and/or UCP mode. When partial matching, we have to do this unit by
+unit. */
+
+else
+ {
+ if (mb->partial != 0)
+ {
+ for (; length > 0; length--)
+ {
+ if (eptr >= mb->end_subject) return 1; /* Partial match */
+ if (UCHAR21INCTEST(p) != UCHAR21INCTEST(eptr)) return -1; /* No match */
+ }
+ }
+
+ /* Not partial matching */
+
+ else
+ {
+ if ((PCRE2_SIZE)(mb->end_subject - eptr) < length) return 1; /* Partial */
+ if (memcmp(p, eptr, CU2BYTES(length)) != 0) return -1; /* No match */
+ eptr += length;
+ }
+ }
+
+*lengthptr = eptr - eptr_start;
+return 0; /* Match */
+}
+
+
+
+/******************************************************************************
+*******************************************************************************
+ "Recursion" in the match() function
+
+The original match() function was highly recursive, but this proved to be the
+source of a number of problems over the years, mostly because of the relatively
+small system stacks that are commonly found. As new features were added to
+patterns, various kludges were invented to reduce the amount of stack used,
+making the code hard to understand in places.
+
+A version did exist that used individual frames on the heap instead of calling
+match() recursively, but this ran substantially slower. The current version is
+a refactoring that uses a vector of frames to remember backtracking points.
+This runs no slower, and possibly even a bit faster than the original recursive
+implementation.
+
+At first, an initial vector of size START_FRAMES_SIZE (enough for maybe 50
+frames) was allocated on the system stack. If this was not big enough, the heap
+was used for a larger vector. However, it turns out that there are environments
+where taking as little as 20KiB from the system stack is an embarrassment.
+After another refactoring, the heap is used exclusively, but a pointer the
+frames vector and its size are cached in the match_data block, so that there is
+no new memory allocation if the same match_data block is used for multiple
+matches (unless the frames vector has to be extended).
+*******************************************************************************
+******************************************************************************/
+
+
+
+
+/*************************************************
+* Macros for the match() function *
+*************************************************/
+
+/* These macros pack up tests that are used for partial matching several times
+in the code. The second one is used when we already know we are past the end of
+the subject. We set the "hit end" flag if the pointer is at the end of the
+subject and either (a) the pointer is past the earliest inspected character
+(i.e. something has been matched, even if not part of the actual matched
+string), or (b) the pattern contains a lookbehind. These are the conditions for
+which adding more characters may allow the current match to continue.
+
+For hard partial matching, we immediately return a partial match. Otherwise,
+carrying on means that a complete match on the current subject will be sought.
+A partial match is returned only if no complete match can be found. */
+
+#define CHECK_PARTIAL()\
+ if (Feptr >= mb->end_subject) \
+ { \
+ SCHECK_PARTIAL(); \
+ }
+
+#define SCHECK_PARTIAL()\
+ if (mb->partial != 0 && \
+ (Feptr > mb->start_used_ptr || mb->allowemptypartial)) \
+ { \
+ mb->hitend = TRUE; \
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; \
+ }
+
+
+/* These macros are used to implement backtracking. They simulate a recursive
+call to the match() function by means of a local vector of frames which
+remember the backtracking points. */
+
+#define RMATCH(ra,rb)\
+ {\
+ start_ecode = ra;\
+ Freturn_id = rb;\
+ goto MATCH_RECURSE;\
+ L_##rb:;\
+ }
+
+#define RRETURN(ra)\
+ {\
+ rrc = ra;\
+ goto RETURN_SWITCH;\
+ }
+
+
+
+/*************************************************
+* Match from current position *
+*************************************************/
+
+/* This function is called to run one match attempt at a single starting point
+in the subject.
+
+Performance note: It might be tempting to extract commonly used fields from the
+mb structure (e.g. end_subject) into individual variables to improve
+performance. Tests using gcc on a SPARC disproved this; in the first case, it
+made performance worse.
+
+Arguments:
+ start_eptr starting character in subject
+ start_ecode starting position in compiled code
+ top_bracket number of capturing parentheses in the pattern
+ frame_size size of each backtracking frame
+ match_data pointer to the match_data block
+ mb pointer to "static" variables block
+
+Returns: MATCH_MATCH if matched ) these values are >= 0
+ MATCH_NOMATCH if failed to match )
+ negative MATCH_xxx value for PRUNE, SKIP, etc
+ negative PCRE2_ERROR_xxx value if aborted by an error condition
+ (e.g. stopped by repeated call or depth limit)
+*/
+
+static int
+match(PCRE2_SPTR start_eptr, PCRE2_SPTR start_ecode, uint16_t top_bracket,
+ PCRE2_SIZE frame_size, pcre2_match_data *match_data, match_block *mb)
+{
+/* Frame-handling variables */
+
+heapframe *F; /* Current frame pointer */
+heapframe *N = NULL; /* Temporary frame pointers */
+heapframe *P = NULL;
+
+heapframe *frames_top; /* End of frames vector */
+heapframe *assert_accept_frame = NULL; /* For passing back a frame with captures */
+PCRE2_SIZE heapframes_size; /* Usable size of frames vector */
+PCRE2_SIZE frame_copy_size; /* Amount to copy when creating a new frame */
+
+/* Local variables that do not need to be preserved over calls to RRMATCH(). */
+
+PCRE2_SPTR bracode; /* Temp pointer to start of group */
+PCRE2_SIZE offset; /* Used for group offsets */
+PCRE2_SIZE length; /* Used for various length calculations */
+
+int rrc; /* Return from functions & backtracking "recursions" */
+#ifdef SUPPORT_UNICODE
+int proptype; /* Type of character property */
+#endif
+
+uint32_t i; /* Used for local loops */
+uint32_t fc; /* Character values */
+uint32_t number; /* Used for group and other numbers */
+uint32_t reptype = 0; /* Type of repetition (0 to avoid compiler warning) */
+uint32_t group_frame_type; /* Specifies type for new group frames */
+
+BOOL condition; /* Used in conditional groups */
+BOOL cur_is_word; /* Used in "word" tests */
+BOOL prev_is_word; /* Used in "word" tests */
+
+/* UTF and UCP flags */
+
+#ifdef SUPPORT_UNICODE
+BOOL utf = (mb->poptions & PCRE2_UTF) != 0;
+BOOL ucp = (mb->poptions & PCRE2_UCP) != 0;
+#else
+BOOL utf = FALSE; /* Required for convenience even when no Unicode support */
+#endif
+
+/* This is the length of the last part of a backtracking frame that must be
+copied when a new frame is created. */
+
+frame_copy_size = frame_size - offsetof(heapframe, eptr);
+
+/* Set up the first frame and the end of the frames vector. We set the local
+heapframes_size to the usuable amount of the vector, that is, a whole number of
+frames. */
+
+F = match_data->heapframes;
+heapframes_size = (match_data->heapframes_size / frame_size) * frame_size;
+frames_top = (heapframe *)((char *)F + heapframes_size);
+
+Frdepth = 0; /* "Recursion" depth */
+Fcapture_last = 0; /* Number of most recent capture */
+Fcurrent_recurse = RECURSE_UNSET; /* Not pattern recursing. */
+Fstart_match = Feptr = start_eptr; /* Current data pointer and start match */
+Fmark = NULL; /* Most recent mark */
+Foffset_top = 0; /* End of captures within the frame */
+Flast_group_offset = PCRE2_UNSET; /* Saved frame of most recent group */
+group_frame_type = 0; /* Not a start of group frame */
+goto NEW_FRAME; /* Start processing with this frame */
+
+/* Come back here when we want to create a new frame for remembering a
+backtracking point. */
+
+MATCH_RECURSE:
+
+/* Set up a new backtracking frame. If the vector is full, get a new one,
+doubling the size, but constrained by the heap limit (which is in KiB). */
+
+N = (heapframe *)((char *)F + frame_size);
+if (N >= frames_top)
+ {
+ heapframe *new;
+ PCRE2_SIZE newsize = match_data->heapframes_size * 2;
+
+ if (newsize > mb->heap_limit)
+ {
+ PCRE2_SIZE maxsize = (mb->heap_limit/frame_size) * frame_size;
+ if (match_data->heapframes_size >= maxsize) return PCRE2_ERROR_HEAPLIMIT;
+ newsize = maxsize;
+ }
+
+ new = match_data->memctl.malloc(newsize, match_data->memctl.memory_data);
+ if (new == NULL) return PCRE2_ERROR_NOMEMORY;
+ memcpy(new, match_data->heapframes, heapframes_size);
+
+ F = (heapframe *)((char *)new + ((char *)F - (char *)match_data->heapframes));
+ N = (heapframe *)((char *)F + frame_size);
+
+ match_data->memctl.free(match_data->heapframes, match_data->memctl.memory_data);
+ match_data->heapframes = new;
+ match_data->heapframes_size = newsize;
+
+ heapframes_size = (newsize / frame_size) * frame_size;
+ frames_top = (heapframe *)((char *)new + heapframes_size);
+ }
+
+#ifdef DEBUG_SHOW_RMATCH
+fprintf(stderr, "++ RMATCH %2d frame=%d", Freturn_id, Frdepth + 1);
+if (group_frame_type != 0)
+ {
+ fprintf(stderr, " type=%x ", group_frame_type);
+ switch (GF_IDMASK(group_frame_type))
+ {
+ case GF_CAPTURE:
+ fprintf(stderr, "capture=%d", GF_DATAMASK(group_frame_type));
+ break;
+
+ case GF_NOCAPTURE:
+ fprintf(stderr, "nocapture op=%d", GF_DATAMASK(group_frame_type));
+ break;
+
+ case GF_CONDASSERT:
+ fprintf(stderr, "condassert op=%d", GF_DATAMASK(group_frame_type));
+ break;
+
+ case GF_RECURSE:
+ fprintf(stderr, "recurse=%d", GF_DATAMASK(group_frame_type));
+ break;
+
+ default:
+ fprintf(stderr, "*** unknown ***");
+ break;
+ }
+ }
+fprintf(stderr, "\n");
+#endif
+
+/* Copy those fields that must be copied into the new frame, increase the
+"recursion" depth (i.e. the new frame's index) and then make the new frame
+current. */
+
+memcpy((char *)N + offsetof(heapframe, eptr),
+ (char *)F + offsetof(heapframe, eptr),
+ frame_copy_size);
+
+N->rdepth = Frdepth + 1;
+F = N;
+
+/* Carry on processing with a new frame. */
+
+NEW_FRAME:
+Fgroup_frame_type = group_frame_type;
+Fecode = start_ecode; /* Starting code pointer */
+Fback_frame = frame_size; /* Default is go back one frame */
+
+/* If this is a special type of group frame, remember its offset for quick
+access at the end of the group. If this is a recursion, set a new current
+recursion value. */
+
+if (group_frame_type != 0)
+ {
+ Flast_group_offset = (char *)F - (char *)match_data->heapframes;
+ if (GF_IDMASK(group_frame_type) == GF_RECURSE)
+ Fcurrent_recurse = GF_DATAMASK(group_frame_type);
+ group_frame_type = 0;
+ }
+
+
+/* ========================================================================= */
+/* This is the main processing loop. First check that we haven't recorded too
+many backtracks (search tree is too large), or that we haven't exceeded the
+recursive depth limit (used too many backtracking frames). If not, process the
+opcodes. */
+
+if (mb->match_call_count++ >= mb->match_limit) return PCRE2_ERROR_MATCHLIMIT;
+if (Frdepth >= mb->match_limit_depth) return PCRE2_ERROR_DEPTHLIMIT;
+
+for (;;)
+ {
+#ifdef DEBUG_SHOW_OPS
+fprintf(stderr, "++ op=%d\n", *Fecode);
+#endif
+
+ Fop = (uint8_t)(*Fecode); /* Cast needed for 16-bit and 32-bit modes */
+ switch(Fop)
+ {
+ /* ===================================================================== */
+ /* Before OP_ACCEPT there may be any number of OP_CLOSE opcodes, to close
+ any currently open capturing brackets. Unlike reaching the end of a group,
+ where we know the starting frame is at the top of the chained frames, in
+ this case we have to search back for the relevant frame in case other types
+ of group that use chained frames have intervened. Multiple OP_CLOSEs always
+ come innermost first, which matches the chain order. We can ignore this in
+ a recursion, because captures are not passed out of recursions. */
+
+ case OP_CLOSE:
+ if (Fcurrent_recurse == RECURSE_UNSET)
+ {
+ number = GET2(Fecode, 1);
+ offset = Flast_group_offset;
+ for(;;)
+ {
+ if (offset == PCRE2_UNSET) return PCRE2_ERROR_INTERNAL;
+ N = (heapframe *)((char *)match_data->heapframes + offset);
+ P = (heapframe *)((char *)N - frame_size);
+ if (N->group_frame_type == (GF_CAPTURE | number)) break;
+ offset = P->last_group_offset;
+ }
+ offset = (number << 1) - 2;
+ Fcapture_last = number;
+ Fovector[offset] = P->eptr - mb->start_subject;
+ Fovector[offset+1] = Feptr - mb->start_subject;
+ if (offset >= Foffset_top) Foffset_top = offset + 2;
+ }
+ Fecode += PRIV(OP_lengths)[*Fecode];
+ break;
+
+
+ /* ===================================================================== */
+ /* Real or forced end of the pattern, assertion, or recursion. In an
+ assertion ACCEPT, update the last used pointer and remember the current
+ frame so that the captures and mark can be fished out of it. */
+
+ case OP_ASSERT_ACCEPT:
+ if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;
+ assert_accept_frame = F;
+ RRETURN(MATCH_ACCEPT);
+
+ /* If recursing, we have to find the most recent recursion. */
+
+ case OP_ACCEPT:
+ case OP_END:
+
+ /* Handle end of a recursion. */
+
+ if (Fcurrent_recurse != RECURSE_UNSET)
+ {
+ offset = Flast_group_offset;
+ for(;;)
+ {
+ if (offset == PCRE2_UNSET) return PCRE2_ERROR_INTERNAL;
+ N = (heapframe *)((char *)match_data->heapframes + offset);
+ P = (heapframe *)((char *)N - frame_size);
+ if (GF_IDMASK(N->group_frame_type) == GF_RECURSE) break;
+ offset = P->last_group_offset;
+ }
+
+ /* N is now the frame of the recursion; the previous frame is at the
+ OP_RECURSE position. Go back there, copying the current subject position
+ and mark, and the start_match position (\K might have changed it), and
+ then move on past the OP_RECURSE. */
+
+ P->eptr = Feptr;
+ P->mark = Fmark;
+ P->start_match = Fstart_match;
+ F = P;
+ Fecode += 1 + LINK_SIZE;
+ continue;
+ }
+
+ /* Not a recursion. Fail for an empty string match if either PCRE2_NOTEMPTY
+ is set, or if PCRE2_NOTEMPTY_ATSTART is set and we have matched at the
+ start of the subject. In both cases, backtracking will then try other
+ alternatives, if any. */
+
+ if (Feptr == Fstart_match &&
+ ((mb->moptions & PCRE2_NOTEMPTY) != 0 ||
+ ((mb->moptions & PCRE2_NOTEMPTY_ATSTART) != 0 &&
+ Fstart_match == mb->start_subject + mb->start_offset)))
+ RRETURN(MATCH_NOMATCH);
+
+ /* Also fail if PCRE2_ENDANCHORED is set and the end of the match is not
+ the end of the subject. After (*ACCEPT) we fail the entire match (at this
+ position) but backtrack on reaching the end of the pattern. */
+
+ if (Feptr < mb->end_subject &&
+ ((mb->moptions | mb->poptions) & PCRE2_ENDANCHORED) != 0)
+ {
+ if (Fop == OP_END) RRETURN(MATCH_NOMATCH);
+ return MATCH_NOMATCH;
+ }
+
+ /* We have a successful match of the whole pattern. Record the result and
+ then do a direct return from the function. If there is space in the offset
+ vector, set any pairs that follow the highest-numbered captured string but
+ are less than the number of capturing groups in the pattern to PCRE2_UNSET.
+ It is documented that this happens. "Gaps" are set to PCRE2_UNSET
+ dynamically. It is only those at the end that need setting here. */
+
+ mb->end_match_ptr = Feptr; /* Record where we ended */
+ mb->end_offset_top = Foffset_top; /* and how many extracts were taken */
+ mb->mark = Fmark; /* and the last success mark */
+ if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;
+
+ match_data->ovector[0] = Fstart_match - mb->start_subject;
+ match_data->ovector[1] = Feptr - mb->start_subject;
+
+ /* Set i to the smaller of the sizes of the external and frame ovectors. */
+
+ i = 2 * ((top_bracket + 1 > match_data->oveccount)?
+ match_data->oveccount : top_bracket + 1);
+ memcpy(match_data->ovector + 2, Fovector, (i - 2) * sizeof(PCRE2_SIZE));
+ while (--i >= Foffset_top + 2) match_data->ovector[i] = PCRE2_UNSET;
+ return MATCH_MATCH; /* Note: NOT RRETURN */
+
+
+ /*===================================================================== */
+ /* Match any single character type except newline; have to take care with
+ CRLF newlines and partial matching. */
+
+ case OP_ANY:
+ if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH);
+ if (mb->partial != 0 &&
+ Feptr == mb->end_subject - 1 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ UCHAR21TEST(Feptr) == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ /* Fall through */
+
+ /* Match any single character whatsoever. */
+
+ case OP_ALLANY:
+ if (Feptr >= mb->end_subject) /* DO NOT merge the Feptr++ here; it must */
+ { /* not be updated before SCHECK_PARTIAL. */
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ Feptr++;
+#ifdef SUPPORT_UNICODE
+ if (utf) ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
+#endif
+ Fecode++;
+ break;
+
+
+ /* ===================================================================== */
+ /* Match a single code unit, even in UTF mode. This opcode really does
+ match any code unit, even newline. (It really should be called ANYCODEUNIT,
+ of course - the byte name is from pre-16 bit days.) */
+
+ case OP_ANYBYTE:
+ if (Feptr >= mb->end_subject) /* DO NOT merge the Feptr++ here; it must */
+ { /* not be updated before SCHECK_PARTIAL. */
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ Feptr++;
+ Fecode++;
+ break;
+
+
+ /* ===================================================================== */
+ /* Match a single character, casefully */
+
+ case OP_CHAR:
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ Flength = 1;
+ Fecode++;
+ GETCHARLEN(fc, Fecode, Flength);
+ if (Flength > (PCRE2_SIZE)(mb->end_subject - Feptr))
+ {
+ CHECK_PARTIAL(); /* Not SCHECK_PARTIAL() */
+ RRETURN(MATCH_NOMATCH);
+ }
+ for (; Flength > 0; Flength--)
+ {
+ if (*Fecode++ != UCHAR21INC(Feptr)) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif
+
+ /* Not UTF mode */
+ {
+ if (mb->end_subject - Feptr < 1)
+ {
+ SCHECK_PARTIAL(); /* This one can use SCHECK_PARTIAL() */
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Fecode[1] != *Feptr++) RRETURN(MATCH_NOMATCH);
+ Fecode += 2;
+ }
+ break;
+
+
+ /* ===================================================================== */
+ /* Match a single character, caselessly. If we are at the end of the
+ subject, give up immediately. We get here only when the pattern character
+ has at most one other case. Characters with more than two cases are coded
+ as OP_PROP with the pseudo-property PT_CLIST. */
+
+ case OP_CHARI:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ Flength = 1;
+ Fecode++;
+ GETCHARLEN(fc, Fecode, Flength);
+
+ /* If the pattern character's value is < 128, we know that its other case
+ (if any) is also < 128 (and therefore only one code unit long in all
+ code-unit widths), so we can use the fast lookup table. We checked above
+ that there is at least one character left in the subject. */
+
+ if (fc < 128)
+ {
+ uint32_t cc = UCHAR21(Feptr);
+ if (mb->lcc[fc] != TABLE_GET(cc, mb->lcc, cc)) RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ Feptr++;
+ }
+
+ /* Otherwise we must pick up the subject character and use Unicode
+ property support to test its other case. Note that we cannot use the
+ value of "Flength" to check for sufficient bytes left, because the other
+ case of the character may have more or fewer code units. */
+
+ else
+ {
+ uint32_t dc;
+ GETCHARINC(dc, Feptr);
+ Fecode += Flength;
+ if (dc != fc && dc != UCD_OTHERCASE(fc)) RRETURN(MATCH_NOMATCH);
+ }
+ }
+
+ /* If UCP is set without UTF we must do the same as above, but with one
+ character per code unit. */
+
+ else if (ucp)
+ {
+ uint32_t cc = UCHAR21(Feptr);
+ fc = Fecode[1];
+ if (fc < 128)
+ {
+ if (mb->lcc[fc] != TABLE_GET(cc, mb->lcc, cc)) RRETURN(MATCH_NOMATCH);
+ }
+ else
+ {
+ if (cc != fc && cc != UCD_OTHERCASE(fc)) RRETURN(MATCH_NOMATCH);
+ }
+ Feptr++;
+ Fecode += 2;
+ }
+
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Not UTF or UCP mode; use the table for characters < 256. */
+ {
+ if (TABLE_GET(Fecode[1], mb->lcc, Fecode[1])
+ != TABLE_GET(*Feptr, mb->lcc, *Feptr)) RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ Fecode += 2;
+ }
+ break;
+
+
+ /* ===================================================================== */
+ /* Match not a single character. */
+
+ case OP_NOT:
+ case OP_NOTI:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ uint32_t ch;
+ Fecode++;
+ GETCHARINC(ch, Fecode);
+ GETCHARINC(fc, Feptr);
+ if (ch == fc)
+ {
+ RRETURN(MATCH_NOMATCH); /* Caseful match */
+ }
+ else if (Fop == OP_NOTI) /* If caseless */
+ {
+ if (ch > 127)
+ ch = UCD_OTHERCASE(ch);
+ else
+ ch = (mb->fcc)[ch];
+ if (ch == fc) RRETURN(MATCH_NOMATCH);
+ }
+ }
+
+ /* UCP without UTF is as above, but with one character per code unit. */
+
+ else if (ucp)
+ {
+ uint32_t ch;
+ fc = UCHAR21INC(Feptr);
+ ch = Fecode[1];
+ Fecode += 2;
+
+ if (ch == fc)
+ {
+ RRETURN(MATCH_NOMATCH); /* Caseful match */
+ }
+ else if (Fop == OP_NOTI) /* If caseless */
+ {
+ if (ch > 127)
+ ch = UCD_OTHERCASE(ch);
+ else
+ ch = (mb->fcc)[ch];
+ if (ch == fc) RRETURN(MATCH_NOMATCH);
+ }
+ }
+
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Neither UTF nor UCP is set */
+
+ {
+ uint32_t ch = Fecode[1];
+ fc = UCHAR21INC(Feptr);
+ if (ch == fc || (Fop == OP_NOTI && TABLE_GET(ch, mb->fcc, ch) == fc))
+ RRETURN(MATCH_NOMATCH);
+ Fecode += 2;
+ }
+ break;
+
+
+ /* ===================================================================== */
+ /* Match a single character repeatedly. */
+
+#define Loclength F->temp_size
+#define Lstart_eptr F->temp_sptr[0]
+#define Lcharptr F->temp_sptr[1]
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+#define Lc F->temp_32[2]
+#define Loc F->temp_32[3]
+
+ case OP_EXACT:
+ case OP_EXACTI:
+ Lmin = Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATCHAR;
+
+ case OP_POSUPTO:
+ case OP_POSUPTOI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATCHAR;
+
+ case OP_UPTO:
+ case OP_UPTOI:
+ reptype = REPTYPE_MAX;
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATCHAR;
+
+ case OP_MINUPTO:
+ case OP_MINUPTOI:
+ reptype = REPTYPE_MIN;
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATCHAR;
+
+ case OP_POSSTAR:
+ case OP_POSSTARI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = UINT32_MAX;
+ Fecode++;
+ goto REPEATCHAR;
+
+ case OP_POSPLUS:
+ case OP_POSPLUSI:
+ reptype = REPTYPE_POS;
+ Lmin = 1;
+ Lmax = UINT32_MAX;
+ Fecode++;
+ goto REPEATCHAR;
+
+ case OP_POSQUERY:
+ case OP_POSQUERYI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = 1;
+ Fecode++;
+ goto REPEATCHAR;
+
+ case OP_STAR:
+ case OP_STARI:
+ case OP_MINSTAR:
+ case OP_MINSTARI:
+ case OP_PLUS:
+ case OP_PLUSI:
+ case OP_MINPLUS:
+ case OP_MINPLUSI:
+ case OP_QUERY:
+ case OP_QUERYI:
+ case OP_MINQUERY:
+ case OP_MINQUERYI:
+ fc = *Fecode++ - ((Fop < OP_STARI)? OP_STAR : OP_STARI);
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
+
+ /* Common code for all repeated single-character matches. We first check
+ for the minimum number of characters. If the minimum equals the maximum, we
+ are done. Otherwise, if minimizing, check the rest of the pattern for a
+ match; if there isn't one, advance up to the maximum, one character at a
+ time.
+
+ If maximizing, advance up to the maximum number of matching characters,
+ until Feptr is past the end of the maximum run. If possessive, we are
+ then done (no backing up). Otherwise, match at this position; anything
+ other than no match is immediately returned. For nomatch, back up one
+ character, unless we are matching \R and the last thing matched was
+ \r\n, in which case, back up two code units until we reach the first
+ optional character position.
+
+ The various UTF/non-UTF and caseful/caseless cases are handled separately,
+ for speed. */
+
+ REPEATCHAR:
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ Flength = 1;
+ Lcharptr = Fecode;
+ GETCHARLEN(fc, Fecode, Flength);
+ Fecode += Flength;
+
+ /* Handle multi-code-unit character matching, caseful and caseless. */
+
+ if (Flength > 1)
+ {
+ uint32_t othercase;
+
+ if (Fop >= OP_STARI && /* Caseless */
+ (othercase = UCD_OTHERCASE(fc)) != fc)
+ Loclength = PRIV(ord2utf)(othercase, Foccu);
+ else Loclength = 0;
+
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr <= mb->end_subject - Flength &&
+ memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0) Feptr += Flength;
+ else if (Loclength > 0 &&
+ Feptr <= mb->end_subject - Loclength &&
+ memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0)
+ Feptr += Loclength;
+ else
+ {
+ CHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+
+ if (Lmin == Lmax) continue;
+
+ if (reptype == REPTYPE_MIN)
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM202);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr <= mb->end_subject - Flength &&
+ memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0) Feptr += Flength;
+ else if (Loclength > 0 &&
+ Feptr <= mb->end_subject - Loclength &&
+ memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0)
+ Feptr += Loclength;
+ else
+ {
+ CHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ /* Control never gets here */
+ }
+
+ else /* Maximize */
+ {
+ Lstart_eptr = Feptr;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr <= mb->end_subject - Flength &&
+ memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0)
+ Feptr += Flength;
+ else if (Loclength > 0 &&
+ Feptr <= mb->end_subject - Loclength &&
+ memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0)
+ Feptr += Loclength;
+ else
+ {
+ CHECK_PARTIAL();
+ break;
+ }
+ }
+
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't
+ go too far. */
+
+ if (reptype != REPTYPE_POS) for(;;)
+ {
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM203);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ BACKCHAR(Feptr);
+ }
+ }
+ break; /* End of repeated wide character handling */
+ }
+
+ /* Length of UTF character is 1. Put it into the preserved variable and
+ fall through to the non-UTF code. */
+
+ Lc = fc;
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* When not in UTF mode, load a single-code-unit character. Then proceed as
+ above, using Unicode casing if either UTF or UCP is set. */
+
+ Lc = *Fecode++;
+
+ /* Caseless comparison */
+
+ if (Fop >= OP_STARI)
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#ifdef SUPPORT_UNICODE
+ if (ucp && !utf && Lc > 127) Loc = UCD_OTHERCASE(Lc);
+ else
+#endif /* SUPPORT_UNICODE */
+ /* Lc will be < 128 in UTF-8 mode. */
+ Loc = mb->fcc[Lc];
+#else /* 16-bit & 32-bit */
+#ifdef SUPPORT_UNICODE
+ if ((utf || ucp) && Lc > 127) Loc = UCD_OTHERCASE(Lc);
+ else
+#endif /* SUPPORT_UNICODE */
+ Loc = TABLE_GET(Lc, mb->fcc, Lc);
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+
+ for (i = 1; i <= Lmin; i++)
+ {
+ uint32_t cc; /* Faster than PCRE2_UCHAR */
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ cc = UCHAR21TEST(Feptr);
+ if (Lc != cc && Loc != cc) RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ if (Lmin == Lmax) continue;
+
+ if (reptype == REPTYPE_MIN)
+ {
+ for (;;)
+ {
+ uint32_t cc; /* Faster than PCRE2_UCHAR */
+ RMATCH(Fecode, RM25);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ cc = UCHAR21TEST(Feptr);
+ if (Lc != cc && Loc != cc) RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ /* Control never gets here */
+ }
+
+ else /* Maximize */
+ {
+ Lstart_eptr = Feptr;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ uint32_t cc; /* Faster than PCRE2_UCHAR */
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ cc = UCHAR21TEST(Feptr);
+ if (Lc != cc && Loc != cc) break;
+ Feptr++;
+ }
+ if (reptype != REPTYPE_POS) for (;;)
+ {
+ if (Feptr == Lstart_eptr) break;
+ RMATCH(Fecode, RM26);
+ Feptr--;
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ }
+ }
+ }
+
+ /* Caseful comparisons (includes all multi-byte characters) */
+
+ else
+ {
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lc != UCHAR21INCTEST(Feptr)) RRETURN(MATCH_NOMATCH);
+ }
+
+ if (Lmin == Lmax) continue;
+
+ if (reptype == REPTYPE_MIN)
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM27);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lc != UCHAR21INCTEST(Feptr)) RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+ }
+ else /* Maximize */
+ {
+ Lstart_eptr = Feptr;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+
+ if (Lc != UCHAR21TEST(Feptr)) break;
+ Feptr++;
+ }
+
+ if (reptype != REPTYPE_POS) for (;;)
+ {
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM28);
+ Feptr--;
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ }
+ }
+ }
+ break;
+
+#undef Loclength
+#undef Lstart_eptr
+#undef Lcharptr
+#undef Lmin
+#undef Lmax
+#undef Lc
+#undef Loc
+
+
+ /* ===================================================================== */
+ /* Match a negated single one-byte character repeatedly. This is almost a
+ repeat of the code for a repeated single character, but I haven't found a
+ nice way of commoning these up that doesn't require a test of the
+ positive/negative option for each character match. Maybe that wouldn't add
+ very much to the time taken, but character matching *is* what this is all
+ about... */
+
+#define Lstart_eptr F->temp_sptr[0]
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+#define Lc F->temp_32[2]
+#define Loc F->temp_32[3]
+
+ case OP_NOTEXACT:
+ case OP_NOTEXACTI:
+ Lmin = Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATNOTCHAR;
+
+ case OP_NOTUPTO:
+ case OP_NOTUPTOI:
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ reptype = REPTYPE_MAX;
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATNOTCHAR;
+
+ case OP_NOTMINUPTO:
+ case OP_NOTMINUPTOI:
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ reptype = REPTYPE_MIN;
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATNOTCHAR;
+
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSSTARI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = UINT32_MAX;
+ Fecode++;
+ goto REPEATNOTCHAR;
+
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSPLUSI:
+ reptype = REPTYPE_POS;
+ Lmin = 1;
+ Lmax = UINT32_MAX;
+ Fecode++;
+ goto REPEATNOTCHAR;
+
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSQUERYI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = 1;
+ Fecode++;
+ goto REPEATNOTCHAR;
+
+ case OP_NOTPOSUPTO:
+ case OP_NOTPOSUPTOI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATNOTCHAR;
+
+ case OP_NOTSTAR:
+ case OP_NOTSTARI:
+ case OP_NOTMINSTAR:
+ case OP_NOTMINSTARI:
+ case OP_NOTPLUS:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUS:
+ case OP_NOTMINPLUSI:
+ case OP_NOTQUERY:
+ case OP_NOTQUERYI:
+ case OP_NOTMINQUERY:
+ case OP_NOTMINQUERYI:
+ fc = *Fecode++ - ((Fop >= OP_NOTSTARI)? OP_NOTSTARI: OP_NOTSTAR);
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
+
+ /* Common code for all repeated single-character non-matches. */
+
+ REPEATNOTCHAR:
+ GETCHARINCTEST(Lc, Fecode);
+
+ /* The code is duplicated for the caseless and caseful cases, for speed,
+ since matching characters is likely to be quite common. First, ensure the
+ minimum number of matches are present. If Lmin = Lmax, we are done.
+ Otherwise, if minimizing, keep trying the rest of the expression and
+ advancing one matching character if failing, up to the maximum.
+ Alternatively, if maximizing, find the maximum number of characters and
+ work backwards. */
+
+ if (Fop >= OP_NOTSTARI) /* Caseless */
+ {
+#ifdef SUPPORT_UNICODE
+ if ((utf || ucp) && Lc > 127)
+ Loc = UCD_OTHERCASE(Lc);
+ else
+#endif /* SUPPORT_UNICODE */
+
+ Loc = TABLE_GET(Lc, mb->fcc, Lc); /* Other case from table */
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ uint32_t d;
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(d, Feptr);
+ if (Lc == d || Loc == d) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Not UTF mode */
+ {
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lc == *Feptr || Loc == *Feptr) RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ }
+
+ if (Lmin == Lmax) continue; /* Finished for exact count */
+
+ if (reptype == REPTYPE_MIN)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ uint32_t d;
+ for (;;)
+ {
+ RMATCH(Fecode, RM204);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(d, Feptr);
+ if (Lc == d || Loc == d) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif /*SUPPORT_UNICODE */
+
+ /* Not UTF mode */
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM29);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lc == *Feptr || Loc == *Feptr) RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ }
+ /* Control never gets here */
+ }
+
+ /* Maximize case */
+
+ else
+ {
+ Lstart_eptr = Feptr;
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ uint32_t d;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(d, Feptr, len);
+ if (Lc == d || Loc == d) break;
+ Feptr += len;
+ }
+
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't
+ go too far. */
+
+ if (reptype != REPTYPE_POS) for(;;)
+ {
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM205);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ BACKCHAR(Feptr);
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Not UTF mode */
+ {
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (Lc == *Feptr || Loc == *Feptr) break;
+ Feptr++;
+ }
+ if (reptype != REPTYPE_POS) for (;;)
+ {
+ if (Feptr == Lstart_eptr) break;
+ RMATCH(Fecode, RM30);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ }
+ }
+ }
+ }
+
+ /* Caseful comparisons */
+
+ else
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ uint32_t d;
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(d, Feptr);
+ if (Lc == d) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif
+ /* Not UTF mode */
+ {
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lc == *Feptr++) RRETURN(MATCH_NOMATCH);
+ }
+ }
+
+ if (Lmin == Lmax) continue;
+
+ if (reptype == REPTYPE_MIN)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ uint32_t d;
+ for (;;)
+ {
+ RMATCH(Fecode, RM206);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(d, Feptr);
+ if (Lc == d) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif
+ /* Not UTF mode */
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM31);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lc == *Feptr++) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ /* Control never gets here */
+ }
+
+ /* Maximize case */
+
+ else
+ {
+ Lstart_eptr = Feptr;
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ uint32_t d;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(d, Feptr, len);
+ if (Lc == d) break;
+ Feptr += len;
+ }
+
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't
+ go too far. */
+
+ if (reptype != REPTYPE_POS) for(;;)
+ {
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM207);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ BACKCHAR(Feptr);
+ }
+ }
+ else
+#endif
+ /* Not UTF mode */
+ {
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (Lc == *Feptr) break;
+ Feptr++;
+ }
+ if (reptype != REPTYPE_POS) for (;;)
+ {
+ if (Feptr == Lstart_eptr) break;
+ RMATCH(Fecode, RM32);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ }
+ }
+ }
+ }
+ break;
+
+#undef Lstart_eptr
+#undef Lmin
+#undef Lmax
+#undef Lc
+#undef Loc
+
+
+ /* ===================================================================== */
+ /* Match a bit-mapped character class, possibly repeatedly. These opcodes
+ are used when all the characters in the class have values in the range
+ 0-255, and either the matching is caseful, or the characters are in the
+ range 0-127 when UTF processing is enabled. The only difference between
+ OP_CLASS and OP_NCLASS occurs when a data character outside the range is
+ encountered. */
+
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+#define Lstart_eptr F->temp_sptr[0]
+#define Lbyte_map_address F->temp_sptr[1]
+#define Lbyte_map ((unsigned char *)Lbyte_map_address)
+
+ case OP_NCLASS:
+ case OP_CLASS:
+ {
+ Lbyte_map_address = Fecode + 1; /* Save for matching */
+ Fecode += 1 + (32 / sizeof(PCRE2_UCHAR)); /* Advance past the item */
+
+ /* Look past the end of the item to see if there is repeat information
+ following. Then obey similar code to character type repeats. */
+
+ switch (*Fecode)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSSTAR:
+ case OP_CRPOSPLUS:
+ case OP_CRPOSQUERY:
+ fc = *Fecode++ - OP_CRSTAR;
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ Lmin = GET2(Fecode, 1);
+ Lmax = GET2(Fecode, 1 + IMM2_SIZE);
+ if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */
+ reptype = rep_typ[*Fecode - OP_CRSTAR];
+ Fecode += 1 + 2 * IMM2_SIZE;
+ break;
+
+ default: /* No repeat follows */
+ Lmin = Lmax = 1;
+ break;
+ }
+
+ /* First, ensure the minimum number of matches are present. */
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(fc, Feptr);
+ if (fc > 255)
+ {
+ if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH);
+ }
+ else
+ if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif
+ /* Not UTF mode */
+ {
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ fc = *Feptr++;
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (fc > 255)
+ {
+ if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH);
+ }
+ else
+#endif
+ if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) RRETURN(MATCH_NOMATCH);
+ }
+ }
+
+ /* If Lmax == Lmin we are done. Continue with main loop. */
+
+ if (Lmin == Lmax) continue;
+
+ /* If minimizing, keep testing the rest of the expression and advancing
+ the pointer while it matches the class. */
+
+ if (reptype == REPTYPE_MIN)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM200);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(fc, Feptr);
+ if (fc > 255)
+ {
+ if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH);
+ }
+ else
+ if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif
+ /* Not UTF mode */
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM23);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ fc = *Feptr++;
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (fc > 255)
+ {
+ if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH);
+ }
+ else
+#endif
+ if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ /* Control never gets here */
+ }
+
+ /* If maximizing, find the longest possible run, then work backwards. */
+
+ else
+ {
+ Lstart_eptr = Feptr;
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ if (fc > 255)
+ {
+ if (Fop == OP_CLASS) break;
+ }
+ else
+ if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) break;
+ Feptr += len;
+ }
+
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
+
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't
+ go too far. */
+
+ for (;;)
+ {
+ RMATCH(Fecode, RM201);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Feptr-- <= Lstart_eptr) break; /* Tried at original position */
+ BACKCHAR(Feptr);
+ }
+ }
+ else
+#endif
+ /* Not UTF mode */
+ {
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ fc = *Feptr;
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (fc > 255)
+ {
+ if (Fop == OP_CLASS) break;
+ }
+ else
+#endif
+ if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) break;
+ Feptr++;
+ }
+
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
+
+ while (Feptr >= Lstart_eptr)
+ {
+ RMATCH(Fecode, RM24);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ }
+ }
+
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ /* Control never gets here */
+
+#undef Lbyte_map_address
+#undef Lbyte_map
+#undef Lstart_eptr
+#undef Lmin
+#undef Lmax
+
+
+ /* ===================================================================== */
+ /* Match an extended character class. In the 8-bit library, this opcode is
+ encountered only when UTF-8 mode mode is supported. In the 16-bit and
+ 32-bit libraries, codepoints greater than 255 may be encountered even when
+ UTF is not supported. */
+
+#define Lstart_eptr F->temp_sptr[0]
+#define Lxclass_data F->temp_sptr[1]
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+ {
+ Lxclass_data = Fecode + 1 + LINK_SIZE; /* Save for matching */
+ Fecode += GET(Fecode, 1); /* Advance past the item */
+
+ switch (*Fecode)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSSTAR:
+ case OP_CRPOSPLUS:
+ case OP_CRPOSQUERY:
+ fc = *Fecode++ - OP_CRSTAR;
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ Lmin = GET2(Fecode, 1);
+ Lmax = GET2(Fecode, 1 + IMM2_SIZE);
+ if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */
+ reptype = rep_typ[*Fecode - OP_CRSTAR];
+ Fecode += 1 + 2 * IMM2_SIZE;
+ break;
+
+ default: /* No repeat follows */
+ Lmin = Lmax = 1;
+ break;
+ }
+
+ /* First, ensure the minimum number of matches are present. */
+
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (!PRIV(xclass)(fc, Lxclass_data, utf)) RRETURN(MATCH_NOMATCH);
+ }
+
+ /* If Lmax == Lmin we can just continue with the main loop. */
+
+ if (Lmin == Lmax) continue;
+
+ /* If minimizing, keep testing the rest of the expression and advancing
+ the pointer while it matches the class. */
+
+ if (reptype == REPTYPE_MIN)
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM100);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (!PRIV(xclass)(fc, Lxclass_data, utf)) RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+ }
+
+ /* If maximizing, find the longest possible run, then work backwards. */
+
+ else
+ {
+ Lstart_eptr = Feptr;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+#ifdef SUPPORT_UNICODE
+ GETCHARLENTEST(fc, Feptr, len);
+#else
+ fc = *Feptr;
+#endif
+ if (!PRIV(xclass)(fc, Lxclass_data, utf)) break;
+ Feptr += len;
+ }
+
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
+
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't
+ go too far. */
+
+ for(;;)
+ {
+ RMATCH(Fecode, RM101);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Feptr-- <= Lstart_eptr) break; /* Tried at original position */
+#ifdef SUPPORT_UNICODE
+ if (utf) BACKCHAR(Feptr);
+#endif
+ }
+ RRETURN(MATCH_NOMATCH);
+ }
+
+ /* Control never gets here */
+ }
+#endif /* SUPPORT_WIDE_CHARS: end of XCLASS */
+
+#undef Lstart_eptr
+#undef Lxclass_data
+#undef Lmin
+#undef Lmax
+
+
+ /* ===================================================================== */
+ /* Match various character types when PCRE2_UCP is not set. These opcodes
+ are not generated when PCRE2_UCP is set - instead appropriate property
+ tests are compiled. */
+
+ case OP_NOT_DIGIT:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_digit) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ case OP_DIGIT:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_digit) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ case OP_NOT_WHITESPACE:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_space) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ case OP_WHITESPACE:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_space) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ case OP_NOT_WORDCHAR:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ case OP_WORDCHAR:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_word) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ case OP_ANYNL:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+
+ case CHAR_CR:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ }
+ else if (UCHAR21TEST(Feptr) == CHAR_LF) Feptr++;
+ break;
+
+ case CHAR_LF:
+ break;
+
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#ifndef EBCDIC
+ case 0x2028:
+ case 0x2029:
+#endif /* Not EBCDIC */
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ Fecode++;
+ break;
+
+ case OP_NOT_HSPACE:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
+ {
+ HSPACE_CASES: RRETURN(MATCH_NOMATCH); /* Byte and multibyte cases */
+ default: break;
+ }
+ Fecode++;
+ break;
+
+ case OP_HSPACE:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
+ {
+ HSPACE_CASES: break; /* Byte and multibyte cases */
+ default: RRETURN(MATCH_NOMATCH);
+ }
+ Fecode++;
+ break;
+
+ case OP_NOT_VSPACE:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
+ {
+ VSPACE_CASES: RRETURN(MATCH_NOMATCH);
+ default: break;
+ }
+ Fecode++;
+ break;
+
+ case OP_VSPACE:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
+ {
+ VSPACE_CASES: break;
+ default: RRETURN(MATCH_NOMATCH);
+ }
+ Fecode++;
+ break;
+
+
+#ifdef SUPPORT_UNICODE
+
+ /* ===================================================================== */
+ /* Check the next character by Unicode property. We will get here only
+ if the support is in the binary; otherwise a compile-time error occurs. */
+
+ case OP_PROP:
+ case OP_NOTPROP:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ {
+ const uint32_t *cp;
+ const ucd_record *prop = GET_UCD(fc);
+ BOOL notmatch = Fop == OP_NOTPROP;
+
+ switch(Fecode[1])
+ {
+ case PT_ANY:
+ if (notmatch) RRETURN(MATCH_NOMATCH);
+ break;
+
+ case PT_LAMP:
+ if ((prop->chartype == ucp_Lu ||
+ prop->chartype == ucp_Ll ||
+ prop->chartype == ucp_Lt) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case PT_GC:
+ if ((Fecode[2] == PRIV(ucp_gentype)[prop->chartype]) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case PT_PC:
+ if ((Fecode[2] == prop->chartype) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case PT_SC:
+ if ((Fecode[2] == prop->script) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case PT_SCX:
+ {
+ BOOL ok = (Fecode[2] == prop->script ||
+ MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), Fecode[2]) != 0);
+ if (ok == notmatch) RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ /* These are specials */
+
+ case PT_ALNUM:
+ if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ switch(fc)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ if (notmatch) RRETURN(MATCH_NOMATCH);
+ break;
+
+ default:
+ if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ break;
+
+ case PT_WORD:
+ if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N ||
+ fc == CHAR_UNDERSCORE) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case PT_CLIST:
+ cp = PRIV(ucd_caseless_sets) + Fecode[2];
+ for (;;)
+ {
+ if (fc < *cp)
+ { if (notmatch) break; else { RRETURN(MATCH_NOMATCH); } }
+ if (fc == *cp++)
+ { if (notmatch) { RRETURN(MATCH_NOMATCH); } else break; }
+ }
+ break;
+
+ case PT_UCNC:
+ if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT ||
+ fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) ||
+ fc >= 0xe000) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case PT_BIDICL:
+ if ((UCD_BIDICLASS_PROP(prop) == Fecode[2]) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case PT_BOOL:
+ {
+ BOOL ok = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), Fecode[2]) != 0;
+ if (ok == notmatch) RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ /* This should never occur */
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+
+ Fecode += 3;
+ }
+ break;
+
+
+ /* ===================================================================== */
+ /* Match an extended Unicode sequence. We will get here only if the support
+ is in the binary; otherwise a compile-time error occurs. */
+
+ case OP_EXTUNI:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ else
+ {
+ GETCHARINCTEST(fc, Feptr);
+ Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject, utf,
+ NULL);
+ }
+ CHECK_PARTIAL();
+ Fecode++;
+ break;
+
+#endif /* SUPPORT_UNICODE */
+
+
+ /* ===================================================================== */
+ /* Match a single character type repeatedly. Note that the property type
+ does not need to be in a stack frame as it is not used within an RMATCH()
+ loop. */
+
+#define Lstart_eptr F->temp_sptr[0]
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+#define Lctype F->temp_32[2]
+#define Lpropvalue F->temp_32[3]
+
+ case OP_TYPEEXACT:
+ Lmin = Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATTYPE;
+
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ reptype = (*Fecode == OP_TYPEMINUPTO)? REPTYPE_MIN : REPTYPE_MAX;
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATTYPE;
+
+ case OP_TYPEPOSSTAR:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = UINT32_MAX;
+ Fecode++;
+ goto REPEATTYPE;
+
+ case OP_TYPEPOSPLUS:
+ reptype = REPTYPE_POS;
+ Lmin = 1;
+ Lmax = UINT32_MAX;
+ Fecode++;
+ goto REPEATTYPE;
+
+ case OP_TYPEPOSQUERY:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = 1;
+ Fecode++;
+ goto REPEATTYPE;
+
+ case OP_TYPEPOSUPTO:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATTYPE;
+
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ fc = *Fecode++ - OP_TYPESTAR;
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
+
+ /* Common code for all repeated character type matches. */
+
+ REPEATTYPE:
+ Lctype = *Fecode++; /* Code for the character type */
+
+#ifdef SUPPORT_UNICODE
+ if (Lctype == OP_PROP || Lctype == OP_NOTPROP)
+ {
+ proptype = *Fecode++;
+ Lpropvalue = *Fecode++;
+ }
+ else proptype = -1;
+#endif
+
+ /* First, ensure the minimum number of matches are present. Use inline
+ code for maximizing the speed, and do the type test once at the start
+ (i.e. keep it out of the loops). As there are no calls to RMATCH in the
+ loops, we can use an ordinary variable for "notmatch". The code for UTF
+ mode is separated out for tidiness, except for Unicode property tests. */
+
+ if (Lmin > 0)
+ {
+#ifdef SUPPORT_UNICODE
+ if (proptype >= 0) /* Property tests in all modes */
+ {
+ BOOL notmatch = Lctype == OP_NOTPROP;
+ switch(proptype)
+ {
+ case PT_ANY:
+ if (notmatch) RRETURN(MATCH_NOMATCH);
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ }
+ break;
+
+ case PT_LAMP:
+ for (i = 1; i <= Lmin; i++)
+ {
+ int chartype;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ chartype = UCD_CHARTYPE(fc);
+ if ((chartype == ucp_Lu ||
+ chartype == ucp_Ll ||
+ chartype == ucp_Lt) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case PT_GC:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_CATEGORY(fc) == Lpropvalue) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case PT_PC:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_CHARTYPE(fc) == Lpropvalue) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case PT_SC:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_SCRIPT(fc) == Lpropvalue) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case PT_SCX:
+ for (i = 1; i <= Lmin; i++)
+ {
+ BOOL ok;
+ const ucd_record *prop;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ prop = GET_UCD(fc);
+ ok = (prop->script == Lpropvalue ||
+ MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), Lpropvalue) != 0);
+ if (ok == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case PT_ALNUM:
+ for (i = 1; i <= Lmin; i++)
+ {
+ int category;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ category = UCD_CATEGORY(fc);
+ if ((category == ucp_L || category == ucp_N) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ if (notmatch) RRETURN(MATCH_NOMATCH);
+ break;
+
+ default:
+ if ((UCD_CATEGORY(fc) == ucp_Z) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ }
+ break;
+
+ case PT_WORD:
+ for (i = 1; i <= Lmin; i++)
+ {
+ int category;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ category = UCD_CATEGORY(fc);
+ if ((category == ucp_L || category == ucp_N ||
+ fc == CHAR_UNDERSCORE) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case PT_CLIST:
+ for (i = 1; i <= Lmin; i++)
+ {
+ const uint32_t *cp;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ cp = PRIV(ucd_caseless_sets) + Lpropvalue;
+ for (;;)
+ {
+ if (fc < *cp)
+ {
+ if (notmatch) break;
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (fc == *cp++)
+ {
+ if (notmatch) RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ }
+ }
+ break;
+
+ case PT_UCNC:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT ||
+ fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) ||
+ fc >= 0xe000) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case PT_BIDICL:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_BIDICLASS(fc) == Lpropvalue) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case PT_BOOL:
+ for (i = 1; i <= Lmin; i++)
+ {
+ BOOL ok;
+ const ucd_record *prop;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ prop = GET_UCD(fc);
+ ok = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), Lpropvalue) != 0;
+ if (ok == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ /* This should not occur */
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+ }
+
+ /* Match extended Unicode sequences. We will get here only if the
+ support is in the binary; otherwise a compile-time error occurs. */
+
+ else if (Lctype == OP_EXTUNI)
+ {
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ else
+ {
+ GETCHARINCTEST(fc, Feptr);
+ Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject,
+ mb->end_subject, utf, NULL);
+ }
+ CHECK_PARTIAL();
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+/* Handle all other cases in UTF mode */
+
+#ifdef SUPPORT_UNICODE
+ if (utf) switch(Lctype)
+ {
+ case OP_ANY:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH);
+ if (mb->partial != 0 &&
+ Feptr + 1 >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ UCHAR21(Feptr) == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
+ }
+ break;
+
+ case OP_ALLANY:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
+ }
+ break;
+
+ case OP_ANYBYTE:
+ if (Feptr > mb->end_subject - Lmin) RRETURN(MATCH_NOMATCH);
+ Feptr += Lmin;
+ break;
+
+ case OP_ANYNL:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(fc, Feptr);
+ switch(fc)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+
+ case CHAR_CR:
+ if (Feptr < mb->end_subject && UCHAR21(Feptr) == CHAR_LF) Feptr++;
+ break;
+
+ case CHAR_LF:
+ break;
+
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#ifndef EBCDIC
+ case 0x2028:
+ case 0x2029:
+#endif /* Not EBCDIC */
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ }
+ break;
+
+ case OP_NOT_HSPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(fc, Feptr);
+ switch(fc)
+ {
+ HSPACE_CASES: RRETURN(MATCH_NOMATCH);
+ default: break;
+ }
+ }
+ break;
+
+ case OP_HSPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(fc, Feptr);
+ switch(fc)
+ {
+ HSPACE_CASES: break;
+ default: RRETURN(MATCH_NOMATCH);
+ }
+ }
+ break;
+
+ case OP_NOT_VSPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(fc, Feptr);
+ switch(fc)
+ {
+ VSPACE_CASES: RRETURN(MATCH_NOMATCH);
+ default: break;
+ }
+ }
+ break;
+
+ case OP_VSPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(fc, Feptr);
+ switch(fc)
+ {
+ VSPACE_CASES: break;
+ default: RRETURN(MATCH_NOMATCH);
+ }
+ }
+ break;
+
+ case OP_NOT_DIGIT:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(fc, Feptr);
+ if (fc < 128 && (mb->ctypes[fc] & ctype_digit) != 0)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case OP_DIGIT:
+ for (i = 1; i <= Lmin; i++)
+ {
+ uint32_t cc;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ cc = UCHAR21(Feptr);
+ if (cc >= 128 || (mb->ctypes[cc] & ctype_digit) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ /* No need to skip more code units - we know it has only one. */
+ }
+ break;
+
+ case OP_NOT_WHITESPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ uint32_t cc;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ cc = UCHAR21(Feptr);
+ if (cc < 128 && (mb->ctypes[cc] & ctype_space) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
+ }
+ break;
+
+ case OP_WHITESPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ uint32_t cc;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ cc = UCHAR21(Feptr);
+ if (cc >= 128 || (mb->ctypes[cc] & ctype_space) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ /* No need to skip more code units - we know it has only one. */
+ }
+ break;
+
+ case OP_NOT_WORDCHAR:
+ for (i = 1; i <= Lmin; i++)
+ {
+ uint32_t cc;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ cc = UCHAR21(Feptr);
+ if (cc < 128 && (mb->ctypes[cc] & ctype_word) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
+ }
+ break;
+
+ case OP_WORDCHAR:
+ for (i = 1; i <= Lmin; i++)
+ {
+ uint32_t cc;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ cc = UCHAR21(Feptr);
+ if (cc >= 128 || (mb->ctypes[cc] & ctype_word) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ /* No need to skip more code units - we know it has only one. */
+ }
+ break;
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ } /* End switch(Lctype) */
+
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Code for the non-UTF case for minimum matching of operators other
+ than OP_PROP and OP_NOTPROP. */
+
+ switch(Lctype)
+ {
+ case OP_ANY:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH);
+ if (mb->partial != 0 &&
+ Feptr + 1 >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ *Feptr == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ Feptr++;
+ }
+ break;
+
+ case OP_ALLANY:
+ if (Feptr > mb->end_subject - Lmin)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ Feptr += Lmin;
+ break;
+
+ /* This OP_ANYBYTE case will never be reached because \C gets turned
+ into OP_ALLANY in non-UTF mode. Cut out the code so that coverage
+ reports don't complain about it's never being used. */
+
+/* case OP_ANYBYTE:
+* if (Feptr > mb->end_subject - Lmin)
+* {
+* SCHECK_PARTIAL();
+* RRETURN(MATCH_NOMATCH);
+* }
+* Feptr += Lmin;
+* break;
+*/
+ case OP_ANYNL:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ switch(*Feptr++)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+
+ case CHAR_CR:
+ if (Feptr < mb->end_subject && *Feptr == CHAR_LF) Feptr++;
+ break;
+
+ case CHAR_LF:
+ break;
+
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ case 0x2028:
+ case 0x2029:
+#endif
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ }
+ break;
+
+ case OP_NOT_HSPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ switch(*Feptr++)
+ {
+ default: break;
+ HSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ HSPACE_MULTIBYTE_CASES:
+#endif
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ break;
+
+ case OP_HSPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ switch(*Feptr++)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+ HSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ HSPACE_MULTIBYTE_CASES:
+#endif
+ break;
+ }
+ }
+ break;
+
+ case OP_NOT_VSPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ switch(*Feptr++)
+ {
+ VSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ VSPACE_MULTIBYTE_CASES:
+#endif
+ RRETURN(MATCH_NOMATCH);
+ default: break;
+ }
+ }
+ break;
+
+ case OP_VSPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ switch(*Feptr++)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+ VSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ VSPACE_MULTIBYTE_CASES:
+#endif
+ break;
+ }
+ }
+ break;
+
+ case OP_NOT_DIGIT:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_digit) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ break;
+
+ case OP_DIGIT:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_digit) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ break;
+
+ case OP_NOT_WHITESPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_space) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ break;
+
+ case OP_WHITESPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_space) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ break;
+
+ case OP_NOT_WORDCHAR:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_word) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ break;
+
+ case OP_WORDCHAR:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_word) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ break;
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+ }
+
+ /* If Lmin = Lmax we are done. Continue with the main loop. */
+
+ if (Lmin == Lmax) continue;
+
+ /* If minimizing, we have to test the rest of the pattern before each
+ subsequent match. This means we cannot use a local "notmatch" variable as
+ in the other cases. As all 4 temporary 32-bit values in the frame are
+ already in use, just test the type each time. */
+
+ if (reptype == REPTYPE_MIN)
+ {
+#ifdef SUPPORT_UNICODE
+ if (proptype >= 0)
+ {
+ switch(proptype)
+ {
+ case PT_ANY:
+ for (;;)
+ {
+ RMATCH(Fecode, RM208);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_LAMP:
+ for (;;)
+ {
+ int chartype;
+ RMATCH(Fecode, RM209);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ chartype = UCD_CHARTYPE(fc);
+ if ((chartype == ucp_Lu ||
+ chartype == ucp_Ll ||
+ chartype == ucp_Lt) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_GC:
+ for (;;)
+ {
+ RMATCH(Fecode, RM210);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_CATEGORY(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_PC:
+ for (;;)
+ {
+ RMATCH(Fecode, RM211);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_CHARTYPE(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_SC:
+ for (;;)
+ {
+ RMATCH(Fecode, RM212);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_SCRIPT(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_SCX:
+ for (;;)
+ {
+ BOOL ok;
+ const ucd_record *prop;
+ RMATCH(Fecode, RM225);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ prop = GET_UCD(fc);
+ ok = (prop->script == Lpropvalue
+ || MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), Lpropvalue) != 0);
+ if (ok == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_ALNUM:
+ for (;;)
+ {
+ int category;
+ RMATCH(Fecode, RM213);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ category = UCD_CATEGORY(fc);
+ if ((category == ucp_L || category == ucp_N) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ for (;;)
+ {
+ RMATCH(Fecode, RM214);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH);
+ break;
+
+ default:
+ if ((UCD_CATEGORY(fc) == ucp_Z) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ }
+ /* Control never gets here */
+
+ case PT_WORD:
+ for (;;)
+ {
+ int category;
+ RMATCH(Fecode, RM215);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ category = UCD_CATEGORY(fc);
+ if ((category == ucp_L ||
+ category == ucp_N ||
+ fc == CHAR_UNDERSCORE) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_CLIST:
+ for (;;)
+ {
+ const uint32_t *cp;
+ RMATCH(Fecode, RM216);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ cp = PRIV(ucd_caseless_sets) + Lpropvalue;
+ for (;;)
+ {
+ if (fc < *cp)
+ {
+ if (Lctype == OP_NOTPROP) break;
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (fc == *cp++)
+ {
+ if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ }
+ }
+ /* Control never gets here */
+
+ case PT_UCNC:
+ for (;;)
+ {
+ RMATCH(Fecode, RM217);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT ||
+ fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) ||
+ fc >= 0xe000) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_BIDICL:
+ for (;;)
+ {
+ RMATCH(Fecode, RM224);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_BIDICLASS(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_BOOL:
+ for (;;)
+ {
+ BOOL ok;
+ const ucd_record *prop;
+ RMATCH(Fecode, RM223);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ prop = GET_UCD(fc);
+ ok = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), Lpropvalue) != 0;
+ if (ok == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ /* This should never occur */
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+ }
+
+ /* Match extended Unicode sequences. We will get here only if the
+ support is in the binary; otherwise a compile-time error occurs. */
+
+ else if (Lctype == OP_EXTUNI)
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM218);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ else
+ {
+ GETCHARINCTEST(fc, Feptr);
+ Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject,
+ utf, NULL);
+ }
+ CHECK_PARTIAL();
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* UTF mode for non-property testing character types. */
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM219);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lctype == OP_ANY && IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH);
+ GETCHARINC(fc, Feptr);
+ switch(Lctype)
+ {
+ case OP_ANY: /* This is the non-NL case */
+ if (mb->partial != 0 && /* Take care with CRLF partial */
+ Feptr >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ fc == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ break;
+
+ case OP_ALLANY:
+ case OP_ANYBYTE:
+ break;
+
+ case OP_ANYNL:
+ switch(fc)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+
+ case CHAR_CR:
+ if (Feptr < mb->end_subject && UCHAR21(Feptr) == CHAR_LF) Feptr++;
+ break;
+
+ case CHAR_LF:
+ break;
+
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#ifndef EBCDIC
+ case 0x2028:
+ case 0x2029:
+#endif /* Not EBCDIC */
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF)
+ RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ break;
+
+ case OP_NOT_HSPACE:
+ switch(fc)
+ {
+ HSPACE_CASES: RRETURN(MATCH_NOMATCH);
+ default: break;
+ }
+ break;
+
+ case OP_HSPACE:
+ switch(fc)
+ {
+ HSPACE_CASES: break;
+ default: RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case OP_NOT_VSPACE:
+ switch(fc)
+ {
+ VSPACE_CASES: RRETURN(MATCH_NOMATCH);
+ default: break;
+ }
+ break;
+
+ case OP_VSPACE:
+ switch(fc)
+ {
+ VSPACE_CASES: break;
+ default: RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case OP_NOT_DIGIT:
+ if (fc < 256 && (mb->ctypes[fc] & ctype_digit) != 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_DIGIT:
+ if (fc >= 256 || (mb->ctypes[fc] & ctype_digit) == 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_NOT_WHITESPACE:
+ if (fc < 256 && (mb->ctypes[fc] & ctype_space) != 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_WHITESPACE:
+ if (fc >= 256 || (mb->ctypes[fc] & ctype_space) == 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_NOT_WORDCHAR:
+ if (fc < 256 && (mb->ctypes[fc] & ctype_word) != 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_WORDCHAR:
+ if (fc >= 256 || (mb->ctypes[fc] & ctype_word) == 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Not UTF mode */
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM33);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lctype == OP_ANY && IS_NEWLINE(Feptr))
+ RRETURN(MATCH_NOMATCH);
+ fc = *Feptr++;
+ switch(Lctype)
+ {
+ case OP_ANY: /* This is the non-NL case */
+ if (mb->partial != 0 && /* Take care with CRLF partial */
+ Feptr >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ fc == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ break;
+
+ case OP_ALLANY:
+ case OP_ANYBYTE:
+ break;
+
+ case OP_ANYNL:
+ switch(fc)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+
+ case CHAR_CR:
+ if (Feptr < mb->end_subject && *Feptr == CHAR_LF) Feptr++;
+ break;
+
+ case CHAR_LF:
+ break;
+
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ case 0x2028:
+ case 0x2029:
+#endif
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF)
+ RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ break;
+
+ case OP_NOT_HSPACE:
+ switch(fc)
+ {
+ default: break;
+ HSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ HSPACE_MULTIBYTE_CASES:
+#endif
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case OP_HSPACE:
+ switch(fc)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+ HSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ HSPACE_MULTIBYTE_CASES:
+#endif
+ break;
+ }
+ break;
+
+ case OP_NOT_VSPACE:
+ switch(fc)
+ {
+ default: break;
+ VSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ VSPACE_MULTIBYTE_CASES:
+#endif
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case OP_VSPACE:
+ switch(fc)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+ VSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ VSPACE_MULTIBYTE_CASES:
+#endif
+ break;
+ }
+ break;
+
+ case OP_NOT_DIGIT:
+ if (MAX_255(fc) && (mb->ctypes[fc] & ctype_digit) != 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_DIGIT:
+ if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_digit) == 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_NOT_WHITESPACE:
+ if (MAX_255(fc) && (mb->ctypes[fc] & ctype_space) != 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_WHITESPACE:
+ if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_space) == 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_NOT_WORDCHAR:
+ if (MAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_WORDCHAR:
+ if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_word) == 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+ }
+ }
+ /* Control never gets here */
+ }
+
+ /* If maximizing, it is worth using inline code for speed, doing the type
+ test once at the start (i.e. keep it out of the loops). Once again,
+ "notmatch" can be an ordinary local variable because the loops do not call
+ RMATCH. */
+
+ else
+ {
+ Lstart_eptr = Feptr; /* Remember where we started */
+
+#ifdef SUPPORT_UNICODE
+ if (proptype >= 0)
+ {
+ BOOL notmatch = Lctype == OP_NOTPROP;
+ switch(proptype)
+ {
+ case PT_ANY:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ if (notmatch) break;
+ Feptr+= len;
+ }
+ break;
+
+ case PT_LAMP:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int chartype;
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ chartype = UCD_CHARTYPE(fc);
+ if ((chartype == ucp_Lu ||
+ chartype == ucp_Ll ||
+ chartype == ucp_Lt) == notmatch)
+ break;
+ Feptr+= len;
+ }
+ break;
+
+ case PT_GC:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ if ((UCD_CATEGORY(fc) == Lpropvalue) == notmatch) break;
+ Feptr+= len;
+ }
+ break;
+
+ case PT_PC:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ if ((UCD_CHARTYPE(fc) == Lpropvalue) == notmatch) break;
+ Feptr+= len;
+ }
+ break;
+
+ case PT_SC:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ if ((UCD_SCRIPT(fc) == Lpropvalue) == notmatch) break;
+ Feptr+= len;
+ }
+ break;
+
+ case PT_SCX:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ BOOL ok;
+ const ucd_record *prop;
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ prop = GET_UCD(fc);
+ ok = (prop->script == Lpropvalue ||
+ MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), Lpropvalue) != 0);
+ if (ok == notmatch) break;
+ Feptr+= len;
+ }
+ break;
+
+ case PT_ALNUM:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int category;
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ category = UCD_CATEGORY(fc);
+ if ((category == ucp_L || category == ucp_N) == notmatch)
+ break;
+ Feptr+= len;
+ }
+ break;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ switch(fc)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ if (notmatch) goto ENDLOOP99; /* Break the loop */
+ break;
+
+ default:
+ if ((UCD_CATEGORY(fc) == ucp_Z) == notmatch)
+ goto ENDLOOP99; /* Break the loop */
+ break;
+ }
+ Feptr+= len;
+ }
+ ENDLOOP99:
+ break;
+
+ case PT_WORD:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int category;
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ category = UCD_CATEGORY(fc);
+ if ((category == ucp_L || category == ucp_N ||
+ fc == CHAR_UNDERSCORE) == notmatch)
+ break;
+ Feptr+= len;
+ }
+ break;
+
+ case PT_CLIST:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ const uint32_t *cp;
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ cp = PRIV(ucd_caseless_sets) + Lpropvalue;
+ for (;;)
+ {
+ if (fc < *cp)
+ { if (notmatch) break; else goto GOT_MAX; }
+ if (fc == *cp++)
+ { if (notmatch) goto GOT_MAX; else break; }
+ }
+ Feptr += len;
+ }
+ GOT_MAX:
+ break;
+
+ case PT_UCNC:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT ||
+ fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) ||
+ fc >= 0xe000) == notmatch)
+ break;
+ Feptr += len;
+ }
+ break;
+
+ case PT_BIDICL:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ if ((UCD_BIDICLASS(fc) == Lpropvalue) == notmatch) break;
+ Feptr+= len;
+ }
+ break;
+
+ case PT_BOOL:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ BOOL ok;
+ const ucd_record *prop;
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ prop = GET_UCD(fc);
+ ok = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), Lpropvalue) != 0;
+ if (ok == notmatch) break;
+ Feptr+= len;
+ }
+ break;
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+
+ /* Feptr is now past the end of the maximum run */
+
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
+
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't
+ go too far. */
+
+ for(;;)
+ {
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM222);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ if (utf) BACKCHAR(Feptr);
+ }
+ }
+
+ /* Match extended Unicode grapheme clusters. We will get here only if the
+ support is in the binary; otherwise a compile-time error occurs. */
+
+ else if (Lctype == OP_EXTUNI)
+ {
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ else
+ {
+ GETCHARINCTEST(fc, Feptr);
+ Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject,
+ utf, NULL);
+ }
+ CHECK_PARTIAL();
+ }
+
+ /* Feptr is now past the end of the maximum run */
+
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
+
+ /* We use <= Lstart_eptr rather than == Lstart_eptr to detect the start
+ of the run while backtracking because the use of \C in UTF mode can
+ cause BACKCHAR to move back past Lstart_eptr. This is just palliative;
+ the use of \C in UTF mode is fraught with danger. */
+
+ for(;;)
+ {
+ int lgb, rgb;
+ PCRE2_SPTR fptr;
+
+ if (Feptr <= Lstart_eptr) break; /* At start of char run */
+ RMATCH(Fecode, RM220);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+
+ /* Backtracking over an extended grapheme cluster involves inspecting
+ the previous two characters (if present) to see if a break is
+ permitted between them. */
+
+ Feptr--;
+ if (!utf) fc = *Feptr; else
+ {
+ BACKCHAR(Feptr);
+ GETCHAR(fc, Feptr);
+ }
+ rgb = UCD_GRAPHBREAK(fc);
+
+ for (;;)
+ {
+ if (Feptr <= Lstart_eptr) break; /* At start of char run */
+ fptr = Feptr - 1;
+ if (!utf) fc = *fptr; else
+ {
+ BACKCHAR(fptr);
+ GETCHAR(fc, fptr);
+ }
+ lgb = UCD_GRAPHBREAK(fc);
+ if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break;
+ Feptr = fptr;
+ rgb = lgb;
+ }
+ }
+ }
+
+ else
+#endif /* SUPPORT_UNICODE */
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ switch(Lctype)
+ {
+ case OP_ANY:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (IS_NEWLINE(Feptr)) break;
+ if (mb->partial != 0 && /* Take care with CRLF partial */
+ Feptr + 1 >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ UCHAR21(Feptr) == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
+ }
+ break;
+
+ case OP_ALLANY:
+ if (Lmax < UINT32_MAX)
+ {
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
+ }
+ }
+ else
+ {
+ Feptr = mb->end_subject; /* Unlimited UTF-8 repeat */
+ SCHECK_PARTIAL();
+ }
+ break;
+
+ /* The "byte" (i.e. "code unit") case is the same as non-UTF */
+
+ case OP_ANYBYTE:
+ fc = Lmax - Lmin;
+ if (fc > (uint32_t)(mb->end_subject - Feptr))
+ {
+ Feptr = mb->end_subject;
+ SCHECK_PARTIAL();
+ }
+ else Feptr += fc;
+ break;
+
+ case OP_ANYNL:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ if (fc == CHAR_CR)
+ {
+ if (++Feptr >= mb->end_subject) break;
+ if (UCHAR21(Feptr) == CHAR_LF) Feptr++;
+ }
+ else
+ {
+ if (fc != CHAR_LF &&
+ (mb->bsr_convention == PCRE2_BSR_ANYCRLF ||
+ (fc != CHAR_VT && fc != CHAR_FF && fc != CHAR_NEL
+#ifndef EBCDIC
+ && fc != 0x2028 && fc != 0x2029
+#endif /* Not EBCDIC */
+ )))
+ break;
+ Feptr += len;
+ }
+ }
+ break;
+
+ case OP_NOT_HSPACE:
+ case OP_HSPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ BOOL gotspace;
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ switch(fc)
+ {
+ HSPACE_CASES: gotspace = TRUE; break;
+ default: gotspace = FALSE; break;
+ }
+ if (gotspace == (Lctype == OP_NOT_HSPACE)) break;
+ Feptr += len;
+ }
+ break;
+
+ case OP_NOT_VSPACE:
+ case OP_VSPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ BOOL gotspace;
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ switch(fc)
+ {
+ VSPACE_CASES: gotspace = TRUE; break;
+ default: gotspace = FALSE; break;
+ }
+ if (gotspace == (Lctype == OP_NOT_VSPACE)) break;
+ Feptr += len;
+ }
+ break;
+
+ case OP_NOT_DIGIT:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ if (fc < 256 && (mb->ctypes[fc] & ctype_digit) != 0) break;
+ Feptr+= len;
+ }
+ break;
+
+ case OP_DIGIT:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ if (fc >= 256 ||(mb->ctypes[fc] & ctype_digit) == 0) break;
+ Feptr+= len;
+ }
+ break;
+
+ case OP_NOT_WHITESPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ if (fc < 256 && (mb->ctypes[fc] & ctype_space) != 0) break;
+ Feptr+= len;
+ }
+ break;
+
+ case OP_WHITESPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ if (fc >= 256 ||(mb->ctypes[fc] & ctype_space) == 0) break;
+ Feptr+= len;
+ }
+ break;
+
+ case OP_NOT_WORDCHAR:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ if (fc < 256 && (mb->ctypes[fc] & ctype_word) != 0) break;
+ Feptr+= len;
+ }
+ break;
+
+ case OP_WORDCHAR:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ if (fc >= 256 || (mb->ctypes[fc] & ctype_word) == 0) break;
+ Feptr+= len;
+ }
+ break;
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
+
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't go
+ too far. */
+
+ for(;;)
+ {
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM221);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ BACKCHAR(Feptr);
+ if (Lctype == OP_ANYNL && Feptr > Lstart_eptr &&
+ UCHAR21(Feptr) == CHAR_NL && UCHAR21(Feptr - 1) == CHAR_CR)
+ Feptr--;
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Not UTF mode */
+ {
+ switch(Lctype)
+ {
+ case OP_ANY:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (IS_NEWLINE(Feptr)) break;
+ if (mb->partial != 0 && /* Take care with CRLF partial */
+ Feptr + 1 >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ *Feptr == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ Feptr++;
+ }
+ break;
+
+ case OP_ALLANY:
+ case OP_ANYBYTE:
+ fc = Lmax - Lmin;
+ if (fc > (uint32_t)(mb->end_subject - Feptr))
+ {
+ Feptr = mb->end_subject;
+ SCHECK_PARTIAL();
+ }
+ else Feptr += fc;
+ break;
+
+ case OP_ANYNL:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ fc = *Feptr;
+ if (fc == CHAR_CR)
+ {
+ if (++Feptr >= mb->end_subject) break;
+ if (*Feptr == CHAR_LF) Feptr++;
+ }
+ else
+ {
+ if (fc != CHAR_LF && (mb->bsr_convention == PCRE2_BSR_ANYCRLF ||
+ (fc != CHAR_VT && fc != CHAR_FF && fc != CHAR_NEL
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ && fc != 0x2028 && fc != 0x2029
+#endif
+ ))) break;
+ Feptr++;
+ }
+ }
+ break;
+
+ case OP_NOT_HSPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ switch(*Feptr)
+ {
+ default: Feptr++; break;
+ HSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ HSPACE_MULTIBYTE_CASES:
+#endif
+ goto ENDLOOP00;
+ }
+ }
+ ENDLOOP00:
+ break;
+
+ case OP_HSPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ switch(*Feptr)
+ {
+ default: goto ENDLOOP01;
+ HSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ HSPACE_MULTIBYTE_CASES:
+#endif
+ Feptr++; break;
+ }
+ }
+ ENDLOOP01:
+ break;
+
+ case OP_NOT_VSPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ switch(*Feptr)
+ {
+ default: Feptr++; break;
+ VSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ VSPACE_MULTIBYTE_CASES:
+#endif
+ goto ENDLOOP02;
+ }
+ }
+ ENDLOOP02:
+ break;
+
+ case OP_VSPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ switch(*Feptr)
+ {
+ default: goto ENDLOOP03;
+ VSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ VSPACE_MULTIBYTE_CASES:
+#endif
+ Feptr++; break;
+ }
+ }
+ ENDLOOP03:
+ break;
+
+ case OP_NOT_DIGIT:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_digit) != 0)
+ break;
+ Feptr++;
+ }
+ break;
+
+ case OP_DIGIT:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_digit) == 0)
+ break;
+ Feptr++;
+ }
+ break;
+
+ case OP_NOT_WHITESPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_space) != 0)
+ break;
+ Feptr++;
+ }
+ break;
+
+ case OP_WHITESPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_space) == 0)
+ break;
+ Feptr++;
+ }
+ break;
+
+ case OP_NOT_WORDCHAR:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_word) != 0)
+ break;
+ Feptr++;
+ }
+ break;
+
+ case OP_WORDCHAR:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_word) == 0)
+ break;
+ Feptr++;
+ }
+ break;
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
+
+ for (;;)
+ {
+ if (Feptr == Lstart_eptr) break;
+ RMATCH(Fecode, RM34);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ if (Lctype == OP_ANYNL && Feptr > Lstart_eptr && *Feptr == CHAR_LF &&
+ Feptr[-1] == CHAR_CR) Feptr--;
+ }
+ }
+ }
+ break; /* End of repeat character type processing */
+
+#undef Lstart_eptr
+#undef Lmin
+#undef Lmax
+#undef Lctype
+#undef Lpropvalue
+
+
+ /* ===================================================================== */
+ /* Match a back reference, possibly repeatedly. Look past the end of the
+ item to see if there is repeat information following. The OP_REF and
+ OP_REFI opcodes are used for a reference to a numbered group or to a
+ non-duplicated named group. For a duplicated named group, OP_DNREF and
+ OP_DNREFI are used. In this case we must scan the list of groups to which
+ the name refers, and use the first one that is set. */
+
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+#define Lcaseless F->temp_32[2]
+#define Lstart F->temp_sptr[0]
+#define Loffset F->temp_size
+
+ case OP_DNREF:
+ case OP_DNREFI:
+ Lcaseless = (Fop == OP_DNREFI);
+ {
+ int count = GET2(Fecode, 1+IMM2_SIZE);
+ PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size;
+ Fecode += 1 + 2*IMM2_SIZE;
+
+ while (count-- > 0)
+ {
+ Loffset = (GET2(slot, 0) << 1) - 2;
+ if (Loffset < Foffset_top && Fovector[Loffset] != PCRE2_UNSET) break;
+ slot += mb->name_entry_size;
+ }
+ }
+ goto REF_REPEAT;
+
+ case OP_REF:
+ case OP_REFI:
+ Lcaseless = (Fop == OP_REFI);
+ Loffset = (GET2(Fecode, 1) << 1) - 2;
+ Fecode += 1 + IMM2_SIZE;
+
+ /* Set up for repetition, or handle the non-repeated case. The maximum and
+ minimum must be in the heap frame, but as they are short-term values, we
+ use temporary fields. */
+
+ REF_REPEAT:
+ switch (*Fecode)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ fc = *Fecode++ - OP_CRSTAR;
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ Lmin = GET2(Fecode, 1);
+ Lmax = GET2(Fecode, 1 + IMM2_SIZE);
+ reptype = rep_typ[*Fecode - OP_CRSTAR];
+ if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */
+ Fecode += 1 + 2 * IMM2_SIZE;
+ break;
+
+ default: /* No repeat follows */
+ {
+ rrc = match_ref(Loffset, Lcaseless, F, mb, &length);
+ if (rrc != 0)
+ {
+ if (rrc > 0) Feptr = mb->end_subject; /* Partial match */
+ CHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ Feptr += length;
+ continue; /* With the main loop */
+ }
+
+ /* Handle repeated back references. If a set group has length zero, just
+ continue with the main loop, because it matches however many times. For an
+ unset reference, if the minimum is zero, we can also just continue. We can
+ also continue if PCRE2_MATCH_UNSET_BACKREF is set, because this makes unset
+ group behave as a zero-length group. For any other unset cases, carrying
+ on will result in NOMATCH. */
+
+ if (Loffset < Foffset_top && Fovector[Loffset] != PCRE2_UNSET)
+ {
+ if (Fovector[Loffset] == Fovector[Loffset + 1]) continue;
+ }
+ else /* Group is not set */
+ {
+ if (Lmin == 0 || (mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0)
+ continue;
+ }
+
+ /* First, ensure the minimum number of matches are present. */
+
+ for (i = 1; i <= Lmin; i++)
+ {
+ PCRE2_SIZE slength;
+ rrc = match_ref(Loffset, Lcaseless, F, mb, &slength);
+ if (rrc != 0)
+ {
+ if (rrc > 0) Feptr = mb->end_subject; /* Partial match */
+ CHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ Feptr += slength;
+ }
+
+ /* If min = max, we are done. They are not both allowed to be zero. */
+
+ if (Lmin == Lmax) continue;
+
+ /* If minimizing, keep trying and advancing the pointer. */
+
+ if (reptype == REPTYPE_MIN)
+ {
+ for (;;)
+ {
+ PCRE2_SIZE slength;
+ RMATCH(Fecode, RM20);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ rrc = match_ref(Loffset, Lcaseless, F, mb, &slength);
+ if (rrc != 0)
+ {
+ if (rrc > 0) Feptr = mb->end_subject; /* Partial match */
+ CHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ Feptr += slength;
+ }
+ /* Control never gets here */
+ }
+
+ /* If maximizing, find the longest string and work backwards, as long as
+ the matched lengths for each iteration are the same. */
+
+ else
+ {
+ BOOL samelengths = TRUE;
+ Lstart = Feptr; /* Starting position */
+ Flength = Fovector[Loffset+1] - Fovector[Loffset];
+
+ for (i = Lmin; i < Lmax; i++)
+ {
+ PCRE2_SIZE slength;
+ rrc = match_ref(Loffset, Lcaseless, F, mb, &slength);
+ if (rrc != 0)
+ {
+ /* Can't use CHECK_PARTIAL because we don't want to update Feptr in
+ the soft partial matching case. */
+
+ if (rrc > 0 && mb->partial != 0 &&
+ mb->end_subject > mb->start_used_ptr)
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ break;
+ }
+
+ if (slength != Flength) samelengths = FALSE;
+ Feptr += slength;
+ }
+
+ /* If the length matched for each repetition is the same as the length of
+ the captured group, we can easily work backwards. This is the normal
+ case. However, in caseless UTF-8 mode there are pairs of case-equivalent
+ characters whose lengths (in terms of code units) differ. However, this
+ is very rare, so we handle it by re-matching fewer and fewer times. */
+
+ if (samelengths)
+ {
+ while (Feptr >= Lstart)
+ {
+ RMATCH(Fecode, RM21);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr -= Flength;
+ }
+ }
+
+ /* The rare case of non-matching lengths. Re-scan the repetition for each
+ iteration. We know that match_ref() will succeed every time. */
+
+ else
+ {
+ Lmax = i;
+ for (;;)
+ {
+ RMATCH(Fecode, RM22);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Feptr == Lstart) break; /* Failed after minimal repetition */
+ Feptr = Lstart;
+ Lmax--;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ PCRE2_SIZE slength;
+ (void)match_ref(Loffset, Lcaseless, F, mb, &slength);
+ Feptr += slength;
+ }
+ }
+ }
+
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+#undef Lcaseless
+#undef Lmin
+#undef Lmax
+#undef Lstart
+#undef Loffset
+
+
+
+/* ========================================================================= */
+/* Opcodes for the start of various parenthesized items */
+/* ========================================================================= */
+
+ /* In all cases, if the result of RMATCH() is MATCH_THEN, check whether the
+ (*THEN) is within the current branch by comparing the address of OP_THEN
+ that is passed back with the end of the branch. If (*THEN) is within the
+ current branch, and the branch is one of two or more alternatives (it
+ either starts or ends with OP_ALT), we have reached the limit of THEN's
+ action, so convert the return code to NOMATCH, which will cause normal
+ backtracking to happen from now on. Otherwise, THEN is passed back to an
+ outer alternative. This implements Perl's treatment of parenthesized
+ groups, where a group not containing | does not affect the current
+ alternative, that is, (X) is NOT the same as (X|(*F)). */
+
+
+ /* ===================================================================== */
+ /* BRAZERO, BRAMINZERO and SKIPZERO occur just before a non-possessive
+ bracket group, indicating that it may occur zero times. It may repeat
+ infinitely, or not at all - i.e. it could be ()* or ()? or even (){0} in
+ the pattern. Brackets with fixed upper repeat limits are compiled as a
+ number of copies, with the optional ones preceded by BRAZERO or BRAMINZERO.
+ Possessive groups with possible zero repeats are preceded by BRAPOSZERO. */
+
+#define Lnext_ecode F->temp_sptr[0]
+
+ case OP_BRAZERO:
+ Lnext_ecode = Fecode + 1;
+ RMATCH(Lnext_ecode, RM9);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ do Lnext_ecode += GET(Lnext_ecode, 1); while (*Lnext_ecode == OP_ALT);
+ Fecode = Lnext_ecode + 1 + LINK_SIZE;
+ break;
+
+ case OP_BRAMINZERO:
+ Lnext_ecode = Fecode + 1;
+ do Lnext_ecode += GET(Lnext_ecode, 1); while (*Lnext_ecode == OP_ALT);
+ RMATCH(Lnext_ecode + 1 + LINK_SIZE, RM10);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Fecode++;
+ break;
+
+#undef Lnext_ecode
+
+ case OP_SKIPZERO:
+ Fecode++;
+ do Fecode += GET(Fecode,1); while (*Fecode == OP_ALT);
+ Fecode += 1 + LINK_SIZE;
+ break;
+
+
+ /* ===================================================================== */
+ /* Handle possessive brackets with an unlimited repeat. The end of these
+ brackets will always be OP_KETRPOS, which returns MATCH_KETRPOS without
+ going further in the pattern. */
+
+#define Lframe_type F->temp_32[0]
+#define Lmatched_once F->temp_32[1]
+#define Lzero_allowed F->temp_32[2]
+#define Lstart_eptr F->temp_sptr[0]
+#define Lstart_group F->temp_sptr[1]
+
+ case OP_BRAPOSZERO:
+ Lzero_allowed = TRUE; /* Zero repeat is allowed */
+ Fecode += 1;
+ if (*Fecode == OP_CBRAPOS || *Fecode == OP_SCBRAPOS)
+ goto POSSESSIVE_CAPTURE;
+ goto POSSESSIVE_NON_CAPTURE;
+
+ case OP_BRAPOS:
+ case OP_SBRAPOS:
+ Lzero_allowed = FALSE; /* Zero repeat not allowed */
+
+ POSSESSIVE_NON_CAPTURE:
+ Lframe_type = GF_NOCAPTURE; /* Remembered frame type */
+ goto POSSESSIVE_GROUP;
+
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ Lzero_allowed = FALSE; /* Zero repeat not allowed */
+
+ POSSESSIVE_CAPTURE:
+ number = GET2(Fecode, 1+LINK_SIZE);
+ Lframe_type = GF_CAPTURE | number; /* Remembered frame type */
+
+ POSSESSIVE_GROUP:
+ Lmatched_once = FALSE; /* Never matched */
+ Lstart_group = Fecode; /* Start of this group */
+
+ for (;;)
+ {
+ Lstart_eptr = Feptr; /* Position at group start */
+ group_frame_type = Lframe_type;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM8);
+ if (rrc == MATCH_KETRPOS)
+ {
+ Lmatched_once = TRUE; /* Matched at least once */
+ if (Feptr == Lstart_eptr) /* Empty match; skip to end */
+ {
+ do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT);
+ break;
+ }
+
+ Fecode = Lstart_group;
+ continue;
+ }
+
+ /* See comment above about handling THEN. */
+
+ if (rrc == MATCH_THEN)
+ {
+ PCRE2_SPTR next_ecode = Fecode + GET(Fecode,1);
+ if (mb->verb_ecode_ptr < next_ecode &&
+ (*Fecode == OP_ALT || *next_ecode == OP_ALT))
+ rrc = MATCH_NOMATCH;
+ }
+
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Fecode += GET(Fecode, 1);
+ if (*Fecode != OP_ALT) break;
+ }
+
+ /* Success if matched something or zero repeat allowed */
+
+ if (Lmatched_once || Lzero_allowed)
+ {
+ Fecode += 1 + LINK_SIZE;
+ break;
+ }
+
+ RRETURN(MATCH_NOMATCH);
+
+#undef Lmatched_once
+#undef Lzero_allowed
+#undef Lframe_type
+#undef Lstart_eptr
+#undef Lstart_group
+
+
+ /* ===================================================================== */
+ /* Handle non-capturing brackets that cannot match an empty string. When we
+ get to the final alternative within the brackets, as long as there are no
+ THEN's in the pattern, we can optimize by not recording a new backtracking
+ point. (Ideally we should test for a THEN within this group, but we don't
+ have that information.) Don't do this if we are at the very top level,
+ however, because that would make handling assertions and once-only brackets
+ messier when there is nothing to go back to. */
+
+#define Lframe_type F->temp_32[0] /* Set for all that use GROUPLOOP */
+#define Lnext_branch F->temp_sptr[0] /* Used only in OP_BRA handling */
+
+ case OP_BRA:
+ if (mb->hasthen || Frdepth == 0)
+ {
+ Lframe_type = 0;
+ goto GROUPLOOP;
+ }
+
+ for (;;)
+ {
+ Lnext_branch = Fecode + GET(Fecode, 1);
+ if (*Lnext_branch != OP_ALT) break;
+
+ /* This is never the final branch. We do not need to test for MATCH_THEN
+ here because this code is not used when there is a THEN in the pattern. */
+
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM1);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Fecode = Lnext_branch;
+ }
+
+ /* Hit the start of the final branch. Continue at this level. */
+
+ Fecode += PRIV(OP_lengths)[*Fecode];
+ break;
+
+#undef Lnext_branch
+
+
+ /* ===================================================================== */
+ /* Handle a capturing bracket, other than those that are possessive with an
+ unlimited repeat. */
+
+ case OP_CBRA:
+ case OP_SCBRA:
+ Lframe_type = GF_CAPTURE | GET2(Fecode, 1+LINK_SIZE);
+ goto GROUPLOOP;
+
+
+ /* ===================================================================== */
+ /* Atomic groups and non-capturing brackets that can match an empty string
+ must record a backtracking point and also set up a chained frame. */
+
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_SBRA:
+ Lframe_type = GF_NOCAPTURE | Fop;
+
+ GROUPLOOP:
+ for (;;)
+ {
+ group_frame_type = Lframe_type;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM2);
+ if (rrc == MATCH_THEN)
+ {
+ PCRE2_SPTR next_ecode = Fecode + GET(Fecode,1);
+ if (mb->verb_ecode_ptr < next_ecode &&
+ (*Fecode == OP_ALT || *next_ecode == OP_ALT))
+ rrc = MATCH_NOMATCH;
+ }
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Fecode += GET(Fecode, 1);
+ if (*Fecode != OP_ALT) RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never reaches here. */
+
+#undef Lframe_type
+
+
+ /* ===================================================================== */
+ /* Recursion either matches the current regex, or some subexpression. The
+ offset data is the offset to the starting bracket from the start of the
+ whole pattern. (This is so that it works from duplicated subpatterns.) */
+
+#define Lframe_type F->temp_32[0]
+#define Lstart_branch F->temp_sptr[0]
+
+ case OP_RECURSE:
+ bracode = mb->start_code + GET(Fecode, 1);
+ number = (bracode == mb->start_code)? 0 : GET2(bracode, 1 + LINK_SIZE);
+
+ /* If we are already in a recursion, check for repeating the same one
+ without advancing the subject pointer. This should catch convoluted mutual
+ recursions. (Some simple cases are caught at compile time.) */
+
+ if (Fcurrent_recurse != RECURSE_UNSET)
+ {
+ offset = Flast_group_offset;
+ while (offset != PCRE2_UNSET)
+ {
+ N = (heapframe *)((char *)match_data->heapframes + offset);
+ P = (heapframe *)((char *)N - frame_size);
+ if (N->group_frame_type == (GF_RECURSE | number))
+ {
+ if (Feptr == P->eptr) return PCRE2_ERROR_RECURSELOOP;
+ break;
+ }
+ offset = P->last_group_offset;
+ }
+ }
+
+ /* Now run the recursion, branch by branch. */
+
+ Lstart_branch = bracode;
+ Lframe_type = GF_RECURSE | number;
+
+ for (;;)
+ {
+ PCRE2_SPTR next_ecode;
+
+ group_frame_type = Lframe_type;
+ RMATCH(Lstart_branch + PRIV(OP_lengths)[*Lstart_branch], RM11);
+ next_ecode = Lstart_branch + GET(Lstart_branch,1);
+
+ /* Handle backtracking verbs, which are defined in a range that can
+ easily be tested for. PCRE does not allow THEN, SKIP, PRUNE or COMMIT to
+ escape beyond a recursion; they cause a NOMATCH for the entire recursion.
+
+ When one of these verbs triggers, the current recursion group number is
+ recorded. If it matches the recursion we are processing, the verb
+ happened within the recursion and we must deal with it. Otherwise it must
+ have happened after the recursion completed, and so has to be passed
+ back. See comment above about handling THEN. */
+
+ if (rrc >= MATCH_BACKTRACK_MIN && rrc <= MATCH_BACKTRACK_MAX &&
+ mb->verb_current_recurse == (Lframe_type ^ GF_RECURSE))
+ {
+ if (rrc == MATCH_THEN && mb->verb_ecode_ptr < next_ecode &&
+ (*Lstart_branch == OP_ALT || *next_ecode == OP_ALT))
+ rrc = MATCH_NOMATCH;
+ else RRETURN(MATCH_NOMATCH);
+ }
+
+ /* Note that carrying on after (*ACCEPT) in a recursion is handled in the
+ OP_ACCEPT code. Nothing needs to be done here. */
+
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Lstart_branch = next_ecode;
+ if (*Lstart_branch != OP_ALT) RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never reaches here. */
+
+#undef Lframe_type
+#undef Lstart_branch
+
+
+ /* ===================================================================== */
+ /* Positive assertions are like other groups except that PCRE doesn't allow
+ the effect of (*THEN) to escape beyond an assertion; it is therefore
+ treated as NOMATCH. (*ACCEPT) is treated as successful assertion, with its
+ captures and mark retained. Any other return is an error. */
+
+#define Lframe_type F->temp_32[0]
+
+ case OP_ASSERT:
+ case OP_ASSERTBACK:
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ Lframe_type = GF_NOCAPTURE | Fop;
+ for (;;)
+ {
+ group_frame_type = Lframe_type;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM3);
+ if (rrc == MATCH_ACCEPT)
+ {
+ memcpy(Fovector,
+ (char *)assert_accept_frame + offsetof(heapframe, ovector),
+ assert_accept_frame->offset_top * sizeof(PCRE2_SIZE));
+ Foffset_top = assert_accept_frame->offset_top;
+ Fmark = assert_accept_frame->mark;
+ break;
+ }
+ if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc);
+ Fecode += GET(Fecode, 1);
+ if (*Fecode != OP_ALT) RRETURN(MATCH_NOMATCH);
+ }
+
+ do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT);
+ Fecode += 1 + LINK_SIZE;
+ break;
+
+#undef Lframe_type
+
+
+ /* ===================================================================== */
+ /* Handle negative assertions. Loop for each non-matching branch as for
+ positive assertions. */
+
+#define Lframe_type F->temp_32[0]
+
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK_NOT:
+ Lframe_type = GF_NOCAPTURE | Fop;
+
+ for (;;)
+ {
+ group_frame_type = Lframe_type;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM4);
+ switch(rrc)
+ {
+ case MATCH_ACCEPT: /* Assertion matched, therefore it fails. */
+ case MATCH_MATCH:
+ RRETURN (MATCH_NOMATCH);
+
+ case MATCH_NOMATCH: /* Branch failed, try next if present. */
+ case MATCH_THEN:
+ Fecode += GET(Fecode, 1);
+ if (*Fecode != OP_ALT) goto ASSERT_NOT_FAILED;
+ break;
+
+ case MATCH_COMMIT: /* Assertion forced to fail, therefore continue. */
+ case MATCH_SKIP:
+ case MATCH_PRUNE:
+ do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT);
+ goto ASSERT_NOT_FAILED;
+
+ default: /* Pass back any other return */
+ RRETURN(rrc);
+ }
+ }
+
+ /* None of the branches have matched or there was a backtrack to (*COMMIT),
+ (*SKIP), (*PRUNE), or (*THEN) in the last branch. This is success for a
+ negative assertion, so carry on. */
+
+ ASSERT_NOT_FAILED:
+ Fecode += 1 + LINK_SIZE;
+ break;
+
+#undef Lframe_type
+
+
+ /* ===================================================================== */
+ /* The callout item calls an external function, if one is provided, passing
+ details of the match so far. This is mainly for debugging, though the
+ function is able to force a failure. */
+
+ case OP_CALLOUT:
+ case OP_CALLOUT_STR:
+ rrc = do_callout(F, mb, &length);
+ if (rrc > 0) RRETURN(MATCH_NOMATCH);
+ if (rrc < 0) RRETURN(rrc);
+ Fecode += length;
+ break;
+
+
+ /* ===================================================================== */
+ /* Conditional group: compilation checked that there are no more than two
+ branches. If the condition is false, skipping the first branch takes us
+ past the end of the item if there is only one branch, but that's exactly
+ what we want. */
+
+ case OP_COND:
+ case OP_SCOND:
+
+ /* The variable Flength will be added to Fecode when the condition is
+ false, to get to the second branch. Setting it to the offset to the ALT or
+ KET, then incrementing Fecode achieves this effect. However, if the second
+ branch is non-existent, we must point to the KET so that the end of the
+ group is correctly processed. We now have Fecode pointing to the condition
+ or callout. */
+
+ Flength = GET(Fecode, 1); /* Offset to the second branch */
+ if (Fecode[Flength] != OP_ALT) Flength -= 1 + LINK_SIZE;
+ Fecode += 1 + LINK_SIZE; /* From this opcode */
+
+ /* Because of the way auto-callout works during compile, a callout item is
+ inserted between OP_COND and an assertion condition. Such a callout can
+ also be inserted manually. */
+
+ if (*Fecode == OP_CALLOUT || *Fecode == OP_CALLOUT_STR)
+ {
+ rrc = do_callout(F, mb, &length);
+ if (rrc > 0) RRETURN(MATCH_NOMATCH);
+ if (rrc < 0) RRETURN(rrc);
+
+ /* Advance Fecode past the callout, so it now points to the condition. We
+ must adjust Flength so that the value of Fecode+Flength is unchanged. */
+
+ Fecode += length;
+ Flength -= length;
+ }
+
+ /* Test the various possible conditions */
+
+ condition = FALSE;
+ switch(*Fecode)
+ {
+ case OP_RREF: /* Group recursion test */
+ if (Fcurrent_recurse != RECURSE_UNSET)
+ {
+ number = GET2(Fecode, 1);
+ condition = (number == RREF_ANY || number == Fcurrent_recurse);
+ }
+ break;
+
+ case OP_DNRREF: /* Duplicate named group recursion test */
+ if (Fcurrent_recurse != RECURSE_UNSET)
+ {
+ int count = GET2(Fecode, 1 + IMM2_SIZE);
+ PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size;
+ while (count-- > 0)
+ {
+ number = GET2(slot, 0);
+ condition = number == Fcurrent_recurse;
+ if (condition) break;
+ slot += mb->name_entry_size;
+ }
+ }
+ break;
+
+ case OP_CREF: /* Numbered group used test */
+ offset = (GET2(Fecode, 1) << 1) - 2; /* Doubled ref number */
+ condition = offset < Foffset_top && Fovector[offset] != PCRE2_UNSET;
+ break;
+
+ case OP_DNCREF: /* Duplicate named group used test */
+ {
+ int count = GET2(Fecode, 1 + IMM2_SIZE);
+ PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size;
+ while (count-- > 0)
+ {
+ offset = (GET2(slot, 0) << 1) - 2;
+ condition = offset < Foffset_top && Fovector[offset] != PCRE2_UNSET;
+ if (condition) break;
+ slot += mb->name_entry_size;
+ }
+ }
+ break;
+
+ case OP_FALSE:
+ case OP_FAIL: /* The assertion (?!) becomes OP_FAIL */
+ break;
+
+ case OP_TRUE:
+ condition = TRUE;
+ break;
+
+ /* The condition is an assertion. Run code similar to the assertion code
+ above. */
+
+#define Lpositive F->temp_32[0]
+#define Lstart_branch F->temp_sptr[0]
+
+ default:
+ Lpositive = (*Fecode == OP_ASSERT || *Fecode == OP_ASSERTBACK);
+ Lstart_branch = Fecode;
+
+ for (;;)
+ {
+ group_frame_type = GF_CONDASSERT | *Fecode;
+ RMATCH(Lstart_branch + PRIV(OP_lengths)[*Lstart_branch], RM5);
+
+ switch(rrc)
+ {
+ case MATCH_ACCEPT: /* Save captures */
+ memcpy(Fovector,
+ (char *)assert_accept_frame + offsetof(heapframe, ovector),
+ assert_accept_frame->offset_top * sizeof(PCRE2_SIZE));
+ Foffset_top = assert_accept_frame->offset_top;
+
+ /* Fall through */
+ /* In the case of a match, the captures have already been put into
+ the current frame. */
+
+ case MATCH_MATCH:
+ condition = Lpositive; /* TRUE for positive assertion */
+ break;
+
+ /* PCRE doesn't allow the effect of (*THEN) to escape beyond an
+ assertion; it is therefore always treated as NOMATCH. */
+
+ case MATCH_NOMATCH:
+ case MATCH_THEN:
+ Lstart_branch += GET(Lstart_branch, 1);
+ if (*Lstart_branch == OP_ALT) continue; /* Try next branch */
+ condition = !Lpositive; /* TRUE for negative assertion */
+ break;
+
+ /* These force no match without checking other branches. */
+
+ case MATCH_COMMIT:
+ case MATCH_SKIP:
+ case MATCH_PRUNE:
+ condition = !Lpositive;
+ break;
+
+ default:
+ RRETURN(rrc);
+ }
+ break; /* Out of the branch loop */
+ }
+
+ /* If the condition is true, find the end of the assertion so that
+ advancing past it gets us to the start of the first branch. */
+
+ if (condition)
+ {
+ do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT);
+ }
+ break; /* End of assertion condition */
+ }
+
+#undef Lpositive
+#undef Lstart_branch
+
+ /* Choose branch according to the condition. */
+
+ Fecode += condition? PRIV(OP_lengths)[*Fecode] : Flength;
+
+ /* If the opcode is OP_SCOND it means we are at a repeated conditional
+ group that might match an empty string. We must therefore descend a level
+ so that the start is remembered for checking. For OP_COND we can just
+ continue at this level. */
+
+ if (Fop == OP_SCOND)
+ {
+ group_frame_type = GF_NOCAPTURE | Fop;
+ RMATCH(Fecode, RM35);
+ RRETURN(rrc);
+ }
+ break;
+
+
+
+/* ========================================================================= */
+/* End of start of parenthesis opcodes */
+/* ========================================================================= */
+
+
+ /* ===================================================================== */
+ /* Move the subject pointer back. This occurs only at the start of each
+ branch of a lookbehind assertion. If we are too close to the start to move
+ back, fail. When working with UTF-8 we move back a number of characters,
+ not bytes. */
+
+ case OP_REVERSE:
+ number = GET(Fecode, 1);
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ while (number-- > 0)
+ {
+ if (Feptr <= mb->check_subject) RRETURN(MATCH_NOMATCH);
+ Feptr--;
+ BACKCHAR(Feptr);
+ }
+ }
+ else
+#endif
+
+ /* No UTF-8 support, or not in UTF-8 mode: count is code unit count */
+
+ {
+ if ((ptrdiff_t)number > Feptr - mb->start_subject) RRETURN(MATCH_NOMATCH);
+ Feptr -= number;
+ }
+
+ /* Save the earliest consulted character, then skip to next opcode */
+
+ if (Feptr < mb->start_used_ptr) mb->start_used_ptr = Feptr;
+ Fecode += 1 + LINK_SIZE;
+ break;
+
+
+ /* ===================================================================== */
+ /* An alternation is the end of a branch; scan along to find the end of the
+ bracketed group. */
+
+ case OP_ALT:
+ do Fecode += GET(Fecode,1); while (*Fecode == OP_ALT);
+ break;
+
+
+ /* ===================================================================== */
+ /* The end of a parenthesized group. For all but OP_BRA and OP_COND, the
+ starting frame was added to the chained frames in order to remember the
+ starting subject position for the group. */
+
+ case OP_KET:
+ case OP_KETRMIN:
+ case OP_KETRMAX:
+ case OP_KETRPOS:
+
+ bracode = Fecode - GET(Fecode, 1);
+
+ /* Point N to the frame at the start of the most recent group.
+ Remember the subject pointer at the start of the group. */
+
+ if (*bracode != OP_BRA && *bracode != OP_COND)
+ {
+ N = (heapframe *)((char *)match_data->heapframes + Flast_group_offset);
+ P = (heapframe *)((char *)N - frame_size);
+ Flast_group_offset = P->last_group_offset;
+
+#ifdef DEBUG_SHOW_RMATCH
+ fprintf(stderr, "++ KET for frame=%d type=%x prev char offset=%lu\n",
+ N->rdepth, N->group_frame_type,
+ (char *)P->eptr - (char *)mb->start_subject);
+#endif
+
+ /* If we are at the end of an assertion that is a condition, return a
+ match, discarding any intermediate backtracking points. Copy back the
+ mark setting and the captures into the frame before N so that they are
+ set on return. Doing this for all assertions, both positive and negative,
+ seems to match what Perl does. */
+
+ if (GF_IDMASK(N->group_frame_type) == GF_CONDASSERT)
+ {
+ memcpy((char *)P + offsetof(heapframe, ovector), Fovector,
+ Foffset_top * sizeof(PCRE2_SIZE));
+ P->offset_top = Foffset_top;
+ P->mark = Fmark;
+ Fback_frame = (char *)F - (char *)P;
+ RRETURN(MATCH_MATCH);
+ }
+ }
+ else P = NULL; /* Indicates starting frame not recorded */
+
+ /* The group was not a conditional assertion. */
+
+ switch (*bracode)
+ {
+ case OP_BRA: /* No need to do anything for these */
+ case OP_COND:
+ case OP_SCOND:
+ break;
+
+ /* Non-atomic positive assertions are like OP_BRA, except that the
+ subject pointer must be put back to where it was at the start of the
+ assertion. */
+
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;
+ Feptr = P->eptr;
+ break;
+
+ /* Atomic positive assertions are like OP_ONCE, except that in addition
+ the subject pointer must be put back to where it was at the start of the
+ assertion. */
+
+ case OP_ASSERT:
+ case OP_ASSERTBACK:
+ if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;
+ Feptr = P->eptr;
+ /* Fall through */
+
+ /* For an atomic group, discard internal backtracking points. We must
+ also ensure that any remaining branches within the top-level of the group
+ are not tried. Do this by adjusting the code pointer within the backtrack
+ frame so that it points to the final branch. */
+
+ case OP_ONCE:
+ Fback_frame = ((char *)F - (char *)P);
+ for (;;)
+ {
+ uint32_t y = GET(P->ecode,1);
+ if ((P->ecode)[y] != OP_ALT) break;
+ P->ecode += y;
+ }
+ break;
+
+ /* A matching negative assertion returns MATCH, which is turned into
+ NOMATCH at the assertion level. */
+
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK_NOT:
+ RRETURN(MATCH_MATCH);
+
+ /* At the end of a script run, apply the script-checking rules. This code
+ will never by exercised if Unicode support it not compiled, because in
+ that environment script runs cause an error at compile time. */
+
+ case OP_SCRIPT_RUN:
+ if (!PRIV(script_run)(P->eptr, Feptr, utf)) RRETURN(MATCH_NOMATCH);
+ break;
+
+ /* Whole-pattern recursion is coded as a recurse into group 0, so it
+ won't be picked up here. Instead, we catch it when the OP_END is reached.
+ Other recursion is handled here. */
+
+ case OP_CBRA:
+ case OP_CBRAPOS:
+ case OP_SCBRA:
+ case OP_SCBRAPOS:
+ number = GET2(bracode, 1+LINK_SIZE);
+
+ /* Handle a recursively called group. We reinstate the previous set of
+ captures and then carry on after the recursion call. */
+
+ if (Fcurrent_recurse == number)
+ {
+ P = (heapframe *)((char *)N - frame_size);
+ memcpy((char *)F + offsetof(heapframe, ovector), P->ovector,
+ P->offset_top * sizeof(PCRE2_SIZE));
+ Foffset_top = P->offset_top;
+ Fcapture_last = P->capture_last;
+ Fcurrent_recurse = P->current_recurse;
+ Fecode = P->ecode + 1 + LINK_SIZE;
+ continue; /* With next opcode */
+ }
+
+ /* Deal with actual capturing. */
+
+ offset = (number << 1) - 2;
+ Fcapture_last = number;
+ Fovector[offset] = P->eptr - mb->start_subject;
+ Fovector[offset+1] = Feptr - mb->start_subject;
+ if (offset >= Foffset_top) Foffset_top = offset + 2;
+ break;
+ } /* End actions relating to the starting opcode */
+
+ /* OP_KETRPOS is a possessive repeating ket. Remember the current position,
+ and return the MATCH_KETRPOS. This makes it possible to do the repeats one
+ at a time from the outer level. This must precede the empty string test -
+ in this case that test is done at the outer level. */
+
+ if (*Fecode == OP_KETRPOS)
+ {
+ memcpy((char *)P + offsetof(heapframe, eptr),
+ (char *)F + offsetof(heapframe, eptr),
+ frame_copy_size);
+ RRETURN(MATCH_KETRPOS);
+ }
+
+ /* Handle the different kinds of closing brackets. A non-repeating ket
+ needs no special action, just continuing at this level. This also happens
+ for the repeating kets if the group matched no characters, in order to
+ forcibly break infinite loops. Otherwise, the repeating kets try the rest
+ of the pattern or restart from the preceding bracket, in the appropriate
+ order. */
+
+ if (Fop != OP_KET && (P == NULL || Feptr != P->eptr))
+ {
+ if (Fop == OP_KETRMIN)
+ {
+ RMATCH(Fecode + 1 + LINK_SIZE, RM6);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Fecode -= GET(Fecode, 1);
+ break; /* End of ket processing */
+ }
+
+ /* Repeat the maximum number of times (KETRMAX) */
+
+ RMATCH(bracode, RM7);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ }
+
+ /* Carry on at this level for a non-repeating ket, or after matching an
+ empty string, or after repeating for a maximum number of times. */
+
+ Fecode += 1 + LINK_SIZE;
+ break;
+
+
+ /* ===================================================================== */
+ /* Start and end of line assertions, not multiline mode. */
+
+ case OP_CIRC: /* Start of line, unless PCRE2_NOTBOL is set. */
+ if (Feptr != mb->start_subject || (mb->moptions & PCRE2_NOTBOL) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ case OP_SOD: /* Unconditional start of subject */
+ if (Feptr != mb->start_subject) RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ /* When PCRE2_NOTEOL is unset, assert before the subject end, or a
+ terminating newline unless PCRE2_DOLLAR_ENDONLY is set. */
+
+ case OP_DOLL:
+ if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH);
+ if ((mb->poptions & PCRE2_DOLLAR_ENDONLY) == 0) goto ASSERT_NL_OR_EOS;
+
+ /* Fall through */
+ /* Unconditional end of subject assertion (\z) */
+
+ case OP_EOD:
+ if (Feptr < mb->end_subject) RRETURN(MATCH_NOMATCH);
+ if (mb->partial != 0)
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ Fecode++;
+ break;
+
+ /* End of subject or ending \n assertion (\Z) */
+
+ case OP_EODN:
+ ASSERT_NL_OR_EOS:
+ if (Feptr < mb->end_subject &&
+ (!IS_NEWLINE(Feptr) || Feptr != mb->end_subject - mb->nllen))
+ {
+ if (mb->partial != 0 &&
+ Feptr + 1 >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ UCHAR21TEST(Feptr) == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ RRETURN(MATCH_NOMATCH);
+ }
+
+ /* Either at end of string or \n before end. */
+
+ if (mb->partial != 0)
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ Fecode++;
+ break;
+
+
+ /* ===================================================================== */
+ /* Start and end of line assertions, multiline mode. */
+
+ /* Start of subject unless notbol, or after any newline except for one at
+ the very end, unless PCRE2_ALT_CIRCUMFLEX is set. */
+
+ case OP_CIRCM:
+ if ((mb->moptions & PCRE2_NOTBOL) != 0 && Feptr == mb->start_subject)
+ RRETURN(MATCH_NOMATCH);
+ if (Feptr != mb->start_subject &&
+ ((Feptr == mb->end_subject &&
+ (mb->poptions & PCRE2_ALT_CIRCUMFLEX) == 0) ||
+ !WAS_NEWLINE(Feptr)))
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ /* Assert before any newline, or before end of subject unless noteol is
+ set. */
+
+ case OP_DOLLM:
+ if (Feptr < mb->end_subject)
+ {
+ if (!IS_NEWLINE(Feptr))
+ {
+ if (mb->partial != 0 &&
+ Feptr + 1 >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ UCHAR21TEST(Feptr) == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+ {
+ if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH);
+ SCHECK_PARTIAL();
+ }
+ Fecode++;
+ break;
+
+
+ /* ===================================================================== */
+ /* Start of match assertion */
+
+ case OP_SOM:
+ if (Feptr != mb->start_subject + mb->start_offset) RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+
+ /* ===================================================================== */
+ /* Reset the start of match point */
+
+ case OP_SET_SOM:
+ Fstart_match = Feptr;
+ Fecode++;
+ break;
+
+
+ /* ===================================================================== */
+ /* Word boundary assertions. Find out if the previous and current
+ characters are "word" characters. It takes a bit more work in UTF mode.
+ Characters > 255 are assumed to be "non-word" characters when PCRE2_UCP is
+ not set. When it is set, use Unicode properties if available, even when not
+ in UTF mode. Remember the earliest and latest consulted characters. */
+
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ if (Feptr == mb->check_subject) prev_is_word = FALSE; else
+ {
+ PCRE2_SPTR lastptr = Feptr - 1;
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ BACKCHAR(lastptr);
+ GETCHAR(fc, lastptr);
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+ fc = *lastptr;
+ if (lastptr < mb->start_used_ptr) mb->start_used_ptr = lastptr;
+#ifdef SUPPORT_UNICODE
+ if ((mb->poptions & PCRE2_UCP) != 0)
+ {
+ if (fc == '_') prev_is_word = TRUE; else
+ {
+ int cat = UCD_CATEGORY(fc);
+ prev_is_word = (cat == ucp_L || cat == ucp_N);
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+ prev_is_word = CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0;
+ }
+
+ /* Get status of next character */
+
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ cur_is_word = FALSE;
+ }
+ else
+ {
+ PCRE2_SPTR nextptr = Feptr + 1;
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ FORWARDCHARTEST(nextptr, mb->end_subject);
+ GETCHAR(fc, Feptr);
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+ fc = *Feptr;
+ if (nextptr > mb->last_used_ptr) mb->last_used_ptr = nextptr;
+#ifdef SUPPORT_UNICODE
+ if ((mb->poptions & PCRE2_UCP) != 0)
+ {
+ if (fc == '_') cur_is_word = TRUE; else
+ {
+ int cat = UCD_CATEGORY(fc);
+ cur_is_word = (cat == ucp_L || cat == ucp_N);
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+ cur_is_word = CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0;
+ }
+
+ /* Now see if the situation is what we want */
+
+ if ((*Fecode++ == OP_WORD_BOUNDARY)?
+ cur_is_word == prev_is_word : cur_is_word != prev_is_word)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+
+ /* ===================================================================== */
+ /* Backtracking (*VERB)s, with and without arguments. Note that if the
+ pattern is successfully matched, we do not come back from RMATCH. */
+
+ case OP_MARK:
+ Fmark = mb->nomatch_mark = Fecode + 2;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM12);
+
+ /* A return of MATCH_SKIP_ARG means that matching failed at SKIP with an
+ argument, and we must check whether that argument matches this MARK's
+ argument. It is passed back in mb->verb_skip_ptr. If it does match, we
+ return MATCH_SKIP with mb->verb_skip_ptr now pointing to the subject
+ position that corresponds to this mark. Otherwise, pass back the return
+ code unaltered. */
+
+ if (rrc == MATCH_SKIP_ARG &&
+ PRIV(strcmp)(Fecode + 2, mb->verb_skip_ptr) == 0)
+ {
+ mb->verb_skip_ptr = Feptr; /* Pass back current position */
+ RRETURN(MATCH_SKIP);
+ }
+ RRETURN(rrc);
+
+ case OP_FAIL:
+ RRETURN(MATCH_NOMATCH);
+
+ /* Record the current recursing group number in mb->verb_current_recurse
+ when a backtracking return such as MATCH_COMMIT is given. This enables the
+ recurse processing to catch verbs from within the recursion. */
+
+ case OP_COMMIT:
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM13);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_COMMIT);
+
+ case OP_COMMIT_ARG:
+ Fmark = mb->nomatch_mark = Fecode + 2;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM36);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_COMMIT);
+
+ case OP_PRUNE:
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM14);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_PRUNE);
+
+ case OP_PRUNE_ARG:
+ Fmark = mb->nomatch_mark = Fecode + 2;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM15);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_PRUNE);
+
+ case OP_SKIP:
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM16);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_skip_ptr = Feptr; /* Pass back current position */
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_SKIP);
+
+ /* Note that, for Perl compatibility, SKIP with an argument does NOT set
+ nomatch_mark. When a pattern match ends with a SKIP_ARG for which there was
+ not a matching mark, we have to re-run the match, ignoring the SKIP_ARG
+ that failed and any that precede it (either they also failed, or were not
+ triggered). To do this, we maintain a count of executed SKIP_ARGs. If a
+ SKIP_ARG gets to top level, the match is re-run with mb->ignore_skip_arg
+ set to the count of the one that failed. */
+
+ case OP_SKIP_ARG:
+ mb->skip_arg_count++;
+ if (mb->skip_arg_count <= mb->ignore_skip_arg)
+ {
+ Fecode += PRIV(OP_lengths)[*Fecode] + Fecode[1];
+ break;
+ }
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM17);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+
+ /* Pass back the current skip name and return the special MATCH_SKIP_ARG
+ return code. This will either be caught by a matching MARK, or get to the
+ top, where it causes a rematch with mb->ignore_skip_arg set to the value of
+ mb->skip_arg_count. */
+
+ mb->verb_skip_ptr = Fecode + 2;
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_SKIP_ARG);
+
+ /* For THEN (and THEN_ARG) we pass back the address of the opcode, so that
+ the branch in which it occurs can be determined. */
+
+ case OP_THEN:
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM18);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_ecode_ptr = Fecode;
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_THEN);
+
+ case OP_THEN_ARG:
+ Fmark = mb->nomatch_mark = Fecode + 2;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM19);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_ecode_ptr = Fecode;
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_THEN);
+
+
+ /* ===================================================================== */
+ /* There's been some horrible disaster. Arrival here can only mean there is
+ something seriously wrong in the code above or the OP_xxx definitions. */
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+
+ /* Do not insert any code in here without much thought; it is assumed
+ that "continue" in the code above comes out to here to repeat the main
+ loop. */
+
+ } /* End of main loop */
+/* Control never reaches here */
+
+
+/* ========================================================================= */
+/* The RRETURN() macro jumps here. The number that is saved in Freturn_id
+indicates which label we actually want to return to. The value in Frdepth is
+the index number of the frame in the vector. The return value has been placed
+in rrc. */
+
+#define LBL(val) case val: goto L_RM##val;
+
+RETURN_SWITCH:
+if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;
+if (Frdepth == 0) return rrc; /* Exit from the top level */
+F = (heapframe *)((char *)F - Fback_frame); /* Backtrack */
+mb->cb->callout_flags |= PCRE2_CALLOUT_BACKTRACK; /* Note for callouts */
+
+#ifdef DEBUG_SHOW_RMATCH
+fprintf(stderr, "++ RETURN %d to %d\n", rrc, Freturn_id);
+#endif
+
+switch (Freturn_id)
+ {
+ LBL( 1) LBL( 2) LBL( 3) LBL( 4) LBL( 5) LBL( 6) LBL( 7) LBL( 8)
+ LBL( 9) LBL(10) LBL(11) LBL(12) LBL(13) LBL(14) LBL(15) LBL(16)
+ LBL(17) LBL(18) LBL(19) LBL(20) LBL(21) LBL(22) LBL(23) LBL(24)
+ LBL(25) LBL(26) LBL(27) LBL(28) LBL(29) LBL(30) LBL(31) LBL(32)
+ LBL(33) LBL(34) LBL(35) LBL(36)
+
+#ifdef SUPPORT_WIDE_CHARS
+ LBL(100) LBL(101)
+#endif
+
+#ifdef SUPPORT_UNICODE
+ LBL(200) LBL(201) LBL(202) LBL(203) LBL(204) LBL(205) LBL(206)
+ LBL(207) LBL(208) LBL(209) LBL(210) LBL(211) LBL(212) LBL(213)
+ LBL(214) LBL(215) LBL(216) LBL(217) LBL(218) LBL(219) LBL(220)
+ LBL(221) LBL(222) LBL(223) LBL(224) LBL(225)
+#endif
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+#undef LBL
+}
+
+
+/*************************************************
+* Match a Regular Expression *
+*************************************************/
+
+/* This function applies a compiled pattern to a subject string and picks out
+portions of the string if it matches. Two elements in the vector are set for
+each substring: the offsets to the start and end of the substring.
+
+Arguments:
+ code points to the compiled expression
+ subject points to the subject string
+ length length of subject string (may contain binary zeros)
+ start_offset where to start in the subject string
+ options option bits
+ match_data points to a match_data block
+ mcontext points a PCRE2 context
+
+Returns: > 0 => success; value is the number of ovector pairs filled
+ = 0 => success, but ovector is not big enough
+ = -1 => failed to match (PCRE2_ERROR_NOMATCH)
+ = -2 => partial match (PCRE2_ERROR_PARTIAL)
+ < -2 => some kind of unexpected problem
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_match(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length,
+ PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data,
+ pcre2_match_context *mcontext)
+{
+int rc;
+int was_zero_terminated = 0;
+const uint8_t *start_bits = NULL;
+const pcre2_real_code *re = (const pcre2_real_code *)code;
+
+BOOL anchored;
+BOOL firstline;
+BOOL has_first_cu = FALSE;
+BOOL has_req_cu = FALSE;
+BOOL startline;
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+PCRE2_SPTR memchr_found_first_cu;
+PCRE2_SPTR memchr_found_first_cu2;
+#endif
+
+PCRE2_UCHAR first_cu = 0;
+PCRE2_UCHAR first_cu2 = 0;
+PCRE2_UCHAR req_cu = 0;
+PCRE2_UCHAR req_cu2 = 0;
+
+PCRE2_SPTR bumpalong_limit;
+PCRE2_SPTR end_subject;
+PCRE2_SPTR true_end_subject;
+PCRE2_SPTR start_match;
+PCRE2_SPTR req_cu_ptr;
+PCRE2_SPTR start_partial;
+PCRE2_SPTR match_partial;
+
+#ifdef SUPPORT_JIT
+BOOL use_jit;
+#endif
+
+/* This flag is needed even when Unicode is not supported for convenience
+(it is used by the IS_NEWLINE macro). */
+
+BOOL utf = FALSE;
+
+#ifdef SUPPORT_UNICODE
+BOOL ucp = FALSE;
+BOOL allow_invalid;
+uint32_t fragment_options = 0;
+#ifdef SUPPORT_JIT
+BOOL jit_checked_utf = FALSE;
+#endif
+#endif /* SUPPORT_UNICODE */
+
+PCRE2_SIZE frame_size;
+PCRE2_SIZE heapframes_size;
+
+/* We need to have mb as a pointer to a match block, because the IS_NEWLINE
+macro is used below, and it expects NLBLOCK to be defined as a pointer. */
+
+pcre2_callout_block cb;
+match_block actual_match_block;
+match_block *mb = &actual_match_block;
+
+/* Recognize NULL, length 0 as an empty string. */
+
+if (subject == NULL && length == 0) subject = (PCRE2_SPTR)"";
+
+/* Plausibility checks */
+
+if ((options & ~PUBLIC_MATCH_OPTIONS) != 0) return PCRE2_ERROR_BADOPTION;
+if (code == NULL || subject == NULL || match_data == NULL)
+ return PCRE2_ERROR_NULL;
+
+start_match = subject + start_offset;
+req_cu_ptr = start_match - 1;
+if (length == PCRE2_ZERO_TERMINATED)
+ {
+ length = PRIV(strlen)(subject);
+ was_zero_terminated = 1;
+ }
+true_end_subject = end_subject = subject + length;
+
+if (start_offset > length) return PCRE2_ERROR_BADOFFSET;
+
+/* Check that the first field in the block is the magic number. */
+
+if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC;
+
+/* Check the code unit width. */
+
+if ((re->flags & PCRE2_MODE_MASK) != PCRE2_CODE_UNIT_WIDTH/8)
+ return PCRE2_ERROR_BADMODE;
+
+/* PCRE2_NOTEMPTY and PCRE2_NOTEMPTY_ATSTART are match-time flags in the
+options variable for this function. Users of PCRE2 who are not calling the
+function directly would like to have a way of setting these flags, in the same
+way that they can set pcre2_compile() flags like PCRE2_NO_AUTOPOSSESS with
+constructions like (*NO_AUTOPOSSESS). To enable this, (*NOTEMPTY) and
+(*NOTEMPTY_ATSTART) set bits in the pattern's "flag" function which we now
+transfer to the options for this function. The bits are guaranteed to be
+adjacent, but do not have the same values. This bit of Boolean trickery assumes
+that the match-time bits are not more significant than the flag bits. If by
+accident this is not the case, a compile-time division by zero error will
+occur. */
+
+#define FF (PCRE2_NOTEMPTY_SET|PCRE2_NE_ATST_SET)
+#define OO (PCRE2_NOTEMPTY|PCRE2_NOTEMPTY_ATSTART)
+options |= (re->flags & FF) / ((FF & (~FF+1)) / (OO & (~OO+1)));
+#undef FF
+#undef OO
+
+/* If the pattern was successfully studied with JIT support, we will run the
+JIT executable instead of the rest of this function. Most options must be set
+at compile time for the JIT code to be usable. */
+
+#ifdef SUPPORT_JIT
+use_jit = (re->executable_jit != NULL &&
+ (options & ~PUBLIC_JIT_MATCH_OPTIONS) == 0);
+#endif
+
+/* Initialize UTF/UCP parameters. */
+
+#ifdef SUPPORT_UNICODE
+utf = (re->overall_options & PCRE2_UTF) != 0;
+allow_invalid = (re->overall_options & PCRE2_MATCH_INVALID_UTF) != 0;
+ucp = (re->overall_options & PCRE2_UCP) != 0;
+#endif /* SUPPORT_UNICODE */
+
+/* Convert the partial matching flags into an integer. */
+
+mb->partial = ((options & PCRE2_PARTIAL_HARD) != 0)? 2 :
+ ((options & PCRE2_PARTIAL_SOFT) != 0)? 1 : 0;
+
+/* Partial matching and PCRE2_ENDANCHORED are currently not allowed at the same
+time. */
+
+if (mb->partial != 0 &&
+ ((re->overall_options | options) & PCRE2_ENDANCHORED) != 0)
+ return PCRE2_ERROR_BADOPTION;
+
+/* It is an error to set an offset limit without setting the flag at compile
+time. */
+
+if (mcontext != NULL && mcontext->offset_limit != PCRE2_UNSET &&
+ (re->overall_options & PCRE2_USE_OFFSET_LIMIT) == 0)
+ return PCRE2_ERROR_BADOFFSETLIMIT;
+
+/* If the match data block was previously used with PCRE2_COPY_MATCHED_SUBJECT,
+free the memory that was obtained. Set the field to NULL for no match cases. */
+
+if ((match_data->flags & PCRE2_MD_COPIED_SUBJECT) != 0)
+ {
+ match_data->memctl.free((void *)match_data->subject,
+ match_data->memctl.memory_data);
+ match_data->flags &= ~PCRE2_MD_COPIED_SUBJECT;
+ }
+match_data->subject = NULL;
+
+/* Zero the error offset in case the first code unit is invalid UTF. */
+
+match_data->startchar = 0;
+
+
+/* ============================= JIT matching ============================== */
+
+/* Prepare for JIT matching. Check a UTF string for validity unless no check is
+requested or invalid UTF can be handled. We check only the portion of the
+subject that might be be inspected during matching - from the offset minus the
+maximum lookbehind to the given length. This saves time when a small part of a
+large subject is being matched by the use of a starting offset. Note that the
+maximum lookbehind is a number of characters, not code units. */
+
+#ifdef SUPPORT_JIT
+if (use_jit)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf && (options & PCRE2_NO_UTF_CHECK) == 0 && !allow_invalid)
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ unsigned int i;
+#endif
+
+ /* For 8-bit and 16-bit UTF, check that the first code unit is a valid
+ character start. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ if (start_match < end_subject && NOT_FIRSTCU(*start_match))
+ {
+ if (start_offset > 0) return PCRE2_ERROR_BADUTFOFFSET;
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ return PCRE2_ERROR_UTF8_ERR20; /* Isolated 0x80 byte */
+#else
+ return PCRE2_ERROR_UTF16_ERR3; /* Isolated low surrogate */
+#endif
+ }
+#endif /* WIDTH != 32 */
+
+ /* Move back by the maximum lookbehind, just in case it happens at the very
+ start of matching. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ for (i = re->max_lookbehind; i > 0 && start_match > subject; i--)
+ {
+ start_match--;
+ while (start_match > subject &&
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ (*start_match & 0xc0) == 0x80)
+#else /* 16-bit */
+ (*start_match & 0xfc00) == 0xdc00)
+#endif
+ start_match--;
+ }
+#else /* PCRE2_CODE_UNIT_WIDTH != 32 */
+
+ /* In the 32-bit library, one code unit equals one character. However,
+ we cannot just subtract the lookbehind and then compare pointers, because
+ a very large lookbehind could create an invalid pointer. */
+
+ if (start_offset >= re->max_lookbehind)
+ start_match -= re->max_lookbehind;
+ else
+ start_match = subject;
+#endif /* PCRE2_CODE_UNIT_WIDTH != 32 */
+
+ /* Validate the relevant portion of the subject. Adjust the offset of an
+ invalid code point to be an absolute offset in the whole string. */
+
+ match_data->rc = PRIV(valid_utf)(start_match,
+ length - (start_match - subject), &(match_data->startchar));
+ if (match_data->rc != 0)
+ {
+ match_data->startchar += start_match - subject;
+ return match_data->rc;
+ }
+ jit_checked_utf = TRUE;
+ }
+#endif /* SUPPORT_UNICODE */
+
+ /* If JIT returns BADOPTION, which means that the selected complete or
+ partial matching mode was not compiled, fall through to the interpreter. */
+
+ rc = pcre2_jit_match(code, subject, length, start_offset, options,
+ match_data, mcontext);
+ if (rc != PCRE2_ERROR_JIT_BADOPTION)
+ {
+ if (rc >= 0 && (options & PCRE2_COPY_MATCHED_SUBJECT) != 0)
+ {
+ length = CU2BYTES(length + was_zero_terminated);
+ match_data->subject = match_data->memctl.malloc(length,
+ match_data->memctl.memory_data);
+ if (match_data->subject == NULL) return PCRE2_ERROR_NOMEMORY;
+ memcpy((void *)match_data->subject, subject, length);
+ match_data->flags |= PCRE2_MD_COPIED_SUBJECT;
+ }
+ return rc;
+ }
+ }
+#endif /* SUPPORT_JIT */
+
+/* ========================= End of JIT matching ========================== */
+
+
+/* Proceed with non-JIT matching. The default is to allow lookbehinds to the
+start of the subject. A UTF check when there is a non-zero offset may change
+this. */
+
+mb->check_subject = subject;
+
+/* If a UTF subject string was not checked for validity in the JIT code above,
+check it here, and handle support for invalid UTF strings. The check above
+happens only when invalid UTF is not supported and PCRE2_NO_CHECK_UTF is unset.
+If we get here in those circumstances, it means the subject string is valid,
+but for some reason JIT matching was not successful. There is no need to check
+the subject again.
+
+We check only the portion of the subject that might be be inspected during
+matching - from the offset minus the maximum lookbehind to the given length.
+This saves time when a small part of a large subject is being matched by the
+use of a starting offset. Note that the maximum lookbehind is a number of
+characters, not code units.
+
+Note also that support for invalid UTF forces a check, overriding the setting
+of PCRE2_NO_CHECK_UTF. */
+
+#ifdef SUPPORT_UNICODE
+if (utf &&
+#ifdef SUPPORT_JIT
+ !jit_checked_utf &&
+#endif
+ ((options & PCRE2_NO_UTF_CHECK) == 0 || allow_invalid))
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ BOOL skipped_bad_start = FALSE;
+#endif
+
+ /* For 8-bit and 16-bit UTF, check that the first code unit is a valid
+ character start. If we are handling invalid UTF, just skip over such code
+ units. Otherwise, give an appropriate error. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ if (allow_invalid)
+ {
+ while (start_match < end_subject && NOT_FIRSTCU(*start_match))
+ {
+ start_match++;
+ skipped_bad_start = TRUE;
+ }
+ }
+ else if (start_match < end_subject && NOT_FIRSTCU(*start_match))
+ {
+ if (start_offset > 0) return PCRE2_ERROR_BADUTFOFFSET;
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ return PCRE2_ERROR_UTF8_ERR20; /* Isolated 0x80 byte */
+#else
+ return PCRE2_ERROR_UTF16_ERR3; /* Isolated low surrogate */
+#endif
+ }
+#endif /* WIDTH != 32 */
+
+ /* The mb->check_subject field points to the start of UTF checking;
+ lookbehinds can go back no further than this. */
+
+ mb->check_subject = start_match;
+
+ /* Move back by the maximum lookbehind, just in case it happens at the very
+ start of matching, but don't do this if we skipped bad 8-bit or 16-bit code
+ units above. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ if (!skipped_bad_start)
+ {
+ unsigned int i;
+ for (i = re->max_lookbehind; i > 0 && mb->check_subject > subject; i--)
+ {
+ mb->check_subject--;
+ while (mb->check_subject > subject &&
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ (*mb->check_subject & 0xc0) == 0x80)
+#else /* 16-bit */
+ (*mb->check_subject & 0xfc00) == 0xdc00)
+#endif
+ mb->check_subject--;
+ }
+ }
+#else /* PCRE2_CODE_UNIT_WIDTH != 32 */
+
+ /* In the 32-bit library, one code unit equals one character. However,
+ we cannot just subtract the lookbehind and then compare pointers, because
+ a very large lookbehind could create an invalid pointer. */
+
+ if (start_offset >= re->max_lookbehind)
+ mb->check_subject -= re->max_lookbehind;
+ else
+ mb->check_subject = subject;
+#endif /* PCRE2_CODE_UNIT_WIDTH != 32 */
+
+ /* Validate the relevant portion of the subject. There's a loop in case we
+ encounter bad UTF in the characters preceding start_match which we are
+ scanning because of a lookbehind. */
+
+ for (;;)
+ {
+ match_data->rc = PRIV(valid_utf)(mb->check_subject,
+ length - (mb->check_subject - subject), &(match_data->startchar));
+
+ if (match_data->rc == 0) break; /* Valid UTF string */
+
+ /* Invalid UTF string. Adjust the offset to be an absolute offset in the
+ whole string. If we are handling invalid UTF strings, set end_subject to
+ stop before the bad code unit, and set the options to "not end of line".
+ Otherwise return the error. */
+
+ match_data->startchar += mb->check_subject - subject;
+ if (!allow_invalid || match_data->rc > 0) return match_data->rc;
+ end_subject = subject + match_data->startchar;
+
+ /* If the end precedes start_match, it means there is invalid UTF in the
+ extra code units we reversed over because of a lookbehind. Advance past the
+ first bad code unit, and then skip invalid character starting code units in
+ 8-bit and 16-bit modes, and try again with the original end point. */
+
+ if (end_subject < start_match)
+ {
+ mb->check_subject = end_subject + 1;
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ while (mb->check_subject < start_match && NOT_FIRSTCU(*mb->check_subject))
+ mb->check_subject++;
+#endif
+ end_subject = true_end_subject;
+ }
+
+ /* Otherwise, set the not end of line option, and do the match. */
+
+ else
+ {
+ fragment_options = PCRE2_NOTEOL;
+ break;
+ }
+ }
+ }
+#endif /* SUPPORT_UNICODE */
+
+/* A NULL match context means "use a default context", but we take the memory
+control functions from the pattern. */
+
+if (mcontext == NULL)
+ {
+ mcontext = (pcre2_match_context *)(&PRIV(default_match_context));
+ mb->memctl = re->memctl;
+ }
+else mb->memctl = mcontext->memctl;
+
+anchored = ((re->overall_options | options) & PCRE2_ANCHORED) != 0;
+firstline = (re->overall_options & PCRE2_FIRSTLINE) != 0;
+startline = (re->flags & PCRE2_STARTLINE) != 0;
+bumpalong_limit = (mcontext->offset_limit == PCRE2_UNSET)?
+ true_end_subject : subject + mcontext->offset_limit;
+
+/* Initialize and set up the fixed fields in the callout block, with a pointer
+in the match block. */
+
+mb->cb = &cb;
+cb.version = 2;
+cb.subject = subject;
+cb.subject_length = (PCRE2_SIZE)(end_subject - subject);
+cb.callout_flags = 0;
+
+/* Fill in the remaining fields in the match block, except for moptions, which
+gets set later. */
+
+mb->callout = mcontext->callout;
+mb->callout_data = mcontext->callout_data;
+
+mb->start_subject = subject;
+mb->start_offset = start_offset;
+mb->end_subject = end_subject;
+mb->hasthen = (re->flags & PCRE2_HASTHEN) != 0;
+mb->allowemptypartial = (re->max_lookbehind > 0) ||
+ (re->flags & PCRE2_MATCH_EMPTY) != 0;
+mb->poptions = re->overall_options; /* Pattern options */
+mb->ignore_skip_arg = 0;
+mb->mark = mb->nomatch_mark = NULL; /* In case never set */
+
+/* The name table is needed for finding all the numbers associated with a
+given name, for condition testing. The code follows the name table. */
+
+mb->name_table = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code));
+mb->name_count = re->name_count;
+mb->name_entry_size = re->name_entry_size;
+mb->start_code = mb->name_table + re->name_count * re->name_entry_size;
+
+/* Process the \R and newline settings. */
+
+mb->bsr_convention = re->bsr_convention;
+mb->nltype = NLTYPE_FIXED;
+switch(re->newline_convention)
+ {
+ case PCRE2_NEWLINE_CR:
+ mb->nllen = 1;
+ mb->nl[0] = CHAR_CR;
+ break;
+
+ case PCRE2_NEWLINE_LF:
+ mb->nllen = 1;
+ mb->nl[0] = CHAR_NL;
+ break;
+
+ case PCRE2_NEWLINE_NUL:
+ mb->nllen = 1;
+ mb->nl[0] = CHAR_NUL;
+ break;
+
+ case PCRE2_NEWLINE_CRLF:
+ mb->nllen = 2;
+ mb->nl[0] = CHAR_CR;
+ mb->nl[1] = CHAR_NL;
+ break;
+
+ case PCRE2_NEWLINE_ANY:
+ mb->nltype = NLTYPE_ANY;
+ break;
+
+ case PCRE2_NEWLINE_ANYCRLF:
+ mb->nltype = NLTYPE_ANYCRLF;
+ break;
+
+ default: return PCRE2_ERROR_INTERNAL;
+ }
+
+/* The backtracking frames have fixed data at the front, and a PCRE2_SIZE
+vector at the end, whose size depends on the number of capturing parentheses in
+the pattern. It is not used at all if there are no capturing parentheses.
+
+ frame_size is the total size of each frame
+ match_data->heapframes is the pointer to the frames vector
+ match_data->heapframes_size is the total size of the vector
+
+We must pad the frame_size for alignment to ensure subsequent frames are as
+aligned as heapframe. Whilst ovector is word-aligned due to being a PCRE2_SIZE
+array, that does not guarantee it is suitably aligned for pointers, as some
+architectures have pointers that are larger than a size_t. */
+
+frame_size = (offsetof(heapframe, ovector) +
+ re->top_bracket * 2 * sizeof(PCRE2_SIZE) + HEAPFRAME_ALIGNMENT - 1) &
+ ~(HEAPFRAME_ALIGNMENT - 1);
+
+/* Limits set in the pattern override the match context only if they are
+smaller. */
+
+mb->heap_limit = ((mcontext->heap_limit < re->limit_heap)?
+ mcontext->heap_limit : re->limit_heap) * 1024;
+
+mb->match_limit = (mcontext->match_limit < re->limit_match)?
+ mcontext->match_limit : re->limit_match;
+
+mb->match_limit_depth = (mcontext->depth_limit < re->limit_depth)?
+ mcontext->depth_limit : re->limit_depth;
+
+/* If a pattern has very many capturing parentheses, the frame size may be very
+large. Set the initial frame vector size to ensure that there are at least 10
+available frames, but enforce a minimum of START_FRAMES_SIZE. If this is
+greater than the heap limit, get as large a vector as possible. Always round
+the size to a multiple of the frame size. */
+
+heapframes_size = frame_size * 10;
+if (heapframes_size < START_FRAMES_SIZE) heapframes_size = START_FRAMES_SIZE;
+if (heapframes_size > mb->heap_limit)
+ {
+ if (frame_size > mb->heap_limit ) return PCRE2_ERROR_HEAPLIMIT;
+ heapframes_size = mb->heap_limit;
+ }
+
+/* If an existing frame vector in the match_data block is large enough, we can
+use it.Otherwise, free any pre-existing vector and get a new one. */
+
+if (match_data->heapframes_size < heapframes_size)
+ {
+ match_data->memctl.free(match_data->heapframes,
+ match_data->memctl.memory_data);
+ match_data->heapframes = match_data->memctl.malloc(heapframes_size,
+ match_data->memctl.memory_data);
+ if (match_data->heapframes == NULL)
+ {
+ match_data->heapframes_size = 0;
+ return PCRE2_ERROR_NOMEMORY;
+ }
+ match_data->heapframes_size = heapframes_size;
+ }
+
+/* Write to the ovector within the first frame to mark every capture unset and
+to avoid uninitialized memory read errors when it is copied to a new frame. */
+
+memset((char *)(match_data->heapframes) + offsetof(heapframe, ovector), 0xff,
+ frame_size - offsetof(heapframe, ovector));
+
+/* Pointers to the individual character tables */
+
+mb->lcc = re->tables + lcc_offset;
+mb->fcc = re->tables + fcc_offset;
+mb->ctypes = re->tables + ctypes_offset;
+
+/* Set up the first code unit to match, if available. If there's no first code
+unit there may be a bitmap of possible first characters. */
+
+if ((re->flags & PCRE2_FIRSTSET) != 0)
+ {
+ has_first_cu = TRUE;
+ first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit);
+ if ((re->flags & PCRE2_FIRSTCASELESS) != 0)
+ {
+ first_cu2 = TABLE_GET(first_cu, mb->fcc, first_cu);
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (first_cu > 127 && ucp && !utf) first_cu2 = UCD_OTHERCASE(first_cu);
+#else
+ if (first_cu > 127 && (utf || ucp)) first_cu2 = UCD_OTHERCASE(first_cu);
+#endif
+#endif /* SUPPORT_UNICODE */
+ }
+ }
+else
+ if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0)
+ start_bits = re->start_bitmap;
+
+/* There may also be a "last known required character" set. */
+
+if ((re->flags & PCRE2_LASTSET) != 0)
+ {
+ has_req_cu = TRUE;
+ req_cu = req_cu2 = (PCRE2_UCHAR)(re->last_codeunit);
+ if ((re->flags & PCRE2_LASTCASELESS) != 0)
+ {
+ req_cu2 = TABLE_GET(req_cu, mb->fcc, req_cu);
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (req_cu > 127 && ucp && !utf) req_cu2 = UCD_OTHERCASE(req_cu);
+#else
+ if (req_cu > 127 && (utf || ucp)) req_cu2 = UCD_OTHERCASE(req_cu);
+#endif
+#endif /* SUPPORT_UNICODE */
+ }
+ }
+
+
+/* ==========================================================================*/
+
+/* Loop for handling unanchored repeated matching attempts; for anchored regexs
+the loop runs just once. */
+
+#ifdef SUPPORT_UNICODE
+FRAGMENT_RESTART:
+#endif
+
+start_partial = match_partial = NULL;
+mb->hitend = FALSE;
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+memchr_found_first_cu = NULL;
+memchr_found_first_cu2 = NULL;
+#endif
+
+for(;;)
+ {
+ PCRE2_SPTR new_start_match;
+
+ /* ----------------- Start of match optimizations ---------------- */
+
+ /* There are some optimizations that avoid running the match if a known
+ starting point is not found, or if a known later code unit is not present.
+ However, there is an option (settable at compile time) that disables these,
+ for testing and for ensuring that all callouts do actually occur. */
+
+ if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0)
+ {
+ /* If firstline is TRUE, the start of the match is constrained to the first
+ line of a multiline string. That is, the match must be before or at the
+ first newline following the start of matching. Temporarily adjust
+ end_subject so that we stop the scans for a first code unit at a newline.
+ If the match fails at the newline, later code breaks the loop. */
+
+ if (firstline)
+ {
+ PCRE2_SPTR t = start_match;
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ while (t < end_subject && !IS_NEWLINE(t))
+ {
+ t++;
+ ACROSSCHAR(t < end_subject, t, t++);
+ }
+ }
+ else
+#endif
+ while (t < end_subject && !IS_NEWLINE(t)) t++;
+ end_subject = t;
+ }
+
+ /* Anchored: check the first code unit if one is recorded. This may seem
+ pointless but it can help in detecting a no match case without scanning for
+ the required code unit. */
+
+ if (anchored)
+ {
+ if (has_first_cu || start_bits != NULL)
+ {
+ BOOL ok = start_match < end_subject;
+ if (ok)
+ {
+ PCRE2_UCHAR c = UCHAR21TEST(start_match);
+ ok = has_first_cu && (c == first_cu || c == first_cu2);
+ if (!ok && start_bits != NULL)
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (c > 255) c = 255;
+#endif
+ ok = (start_bits[c/8] & (1u << (c&7))) != 0;
+ }
+ }
+ if (!ok)
+ {
+ rc = MATCH_NOMATCH;
+ break;
+ }
+ }
+ }
+
+ /* Not anchored. Advance to a unique first code unit if there is one. */
+
+ else
+ {
+ if (has_first_cu)
+ {
+ if (first_cu != first_cu2) /* Caseless */
+ {
+ /* In 16-bit and 32_bit modes we have to do our own search, so can
+ look for both cases at once. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ PCRE2_UCHAR smc;
+ while (start_match < end_subject &&
+ (smc = UCHAR21TEST(start_match)) != first_cu &&
+ smc != first_cu2)
+ start_match++;
+#else
+ /* In 8-bit mode, the use of memchr() gives a big speed up, even
+ though we have to call it twice in order to find the earliest
+ occurrence of the code unit in either of its cases. Caching is used
+ to remember the positions of previously found code units. This can
+ make a huge difference when the strings are very long and only one
+ case is actually present. */
+
+ PCRE2_SPTR pp1 = NULL;
+ PCRE2_SPTR pp2 = NULL;
+ PCRE2_SIZE searchlength = end_subject - start_match;
+
+ /* If we haven't got a previously found position for first_cu, or if
+ the current starting position is later, we need to do a search. If
+ the code unit is not found, set it to the end. */
+
+ if (memchr_found_first_cu == NULL ||
+ start_match > memchr_found_first_cu)
+ {
+ pp1 = memchr(start_match, first_cu, searchlength);
+ memchr_found_first_cu = (pp1 == NULL)? end_subject : pp1;
+ }
+
+ /* If the start is before a previously found position, use the
+ previous position, or NULL if a previous search failed. */
+
+ else pp1 = (memchr_found_first_cu == end_subject)? NULL :
+ memchr_found_first_cu;
+
+ /* Do the same thing for the other case. */
+
+ if (memchr_found_first_cu2 == NULL ||
+ start_match > memchr_found_first_cu2)
+ {
+ pp2 = memchr(start_match, first_cu2, searchlength);
+ memchr_found_first_cu2 = (pp2 == NULL)? end_subject : pp2;
+ }
+
+ else pp2 = (memchr_found_first_cu2 == end_subject)? NULL :
+ memchr_found_first_cu2;
+
+ /* Set the start to the end of the subject if neither case was found.
+ Otherwise, use the earlier found point. */
+
+ if (pp1 == NULL)
+ start_match = (pp2 == NULL)? end_subject : pp2;
+ else
+ start_match = (pp2 == NULL || pp1 < pp2)? pp1 : pp2;
+
+#endif /* 8-bit handling */
+ }
+
+ /* The caseful case is much simpler. */
+
+ else
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ while (start_match < end_subject && UCHAR21TEST(start_match) !=
+ first_cu)
+ start_match++;
+#else
+ start_match = memchr(start_match, first_cu, end_subject - start_match);
+ if (start_match == NULL) start_match = end_subject;
+#endif
+ }
+
+ /* If we can't find the required first code unit, having reached the
+ true end of the subject, break the bumpalong loop, to force a match
+ failure, except when doing partial matching, when we let the next cycle
+ run at the end of the subject. To see why, consider the pattern
+ /(?<=abc)def/, which partially matches "abc", even though the string
+ does not contain the starting character "d". If we have not reached the
+ true end of the subject (PCRE2_FIRSTLINE caused end_subject to be
+ temporarily modified) we also let the cycle run, because the matching
+ string is legitimately allowed to start with the first code unit of a
+ newline. */
+
+ if (mb->partial == 0 && start_match >= mb->end_subject)
+ {
+ rc = MATCH_NOMATCH;
+ break;
+ }
+ }
+
+ /* If there's no first code unit, advance to just after a linebreak for a
+ multiline match if required. */
+
+ else if (startline)
+ {
+ if (start_match > mb->start_subject + start_offset)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ while (start_match < end_subject && !WAS_NEWLINE(start_match))
+ {
+ start_match++;
+ ACROSSCHAR(start_match < end_subject, start_match, start_match++);
+ }
+ }
+ else
+#endif
+ while (start_match < end_subject && !WAS_NEWLINE(start_match))
+ start_match++;
+
+ /* If we have just passed a CR and the newline option is ANY or
+ ANYCRLF, and we are now at a LF, advance the match position by one
+ more code unit. */
+
+ if (start_match[-1] == CHAR_CR &&
+ (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) &&
+ start_match < end_subject &&
+ UCHAR21TEST(start_match) == CHAR_NL)
+ start_match++;
+ }
+ }
+
+ /* If there's no first code unit or a requirement for a multiline line
+ start, advance to a non-unique first code unit if any have been
+ identified. The bitmap contains only 256 bits. When code units are 16 or
+ 32 bits wide, all code units greater than 254 set the 255 bit. */
+
+ else if (start_bits != NULL)
+ {
+ while (start_match < end_subject)
+ {
+ uint32_t c = UCHAR21TEST(start_match);
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (c > 255) c = 255;
+#endif
+ if ((start_bits[c/8] & (1u << (c&7))) != 0) break;
+ start_match++;
+ }
+
+ /* See comment above in first_cu checking about the next few lines. */
+
+ if (mb->partial == 0 && start_match >= mb->end_subject)
+ {
+ rc = MATCH_NOMATCH;
+ break;
+ }
+ }
+ } /* End first code unit handling */
+
+ /* Restore fudged end_subject */
+
+ end_subject = mb->end_subject;
+
+ /* The following two optimizations must be disabled for partial matching. */
+
+ if (mb->partial == 0)
+ {
+ PCRE2_SPTR p;
+
+ /* The minimum matching length is a lower bound; no string of that length
+ may actually match the pattern. Although the value is, strictly, in
+ characters, we treat it as code units to avoid spending too much time in
+ this optimization. */
+
+ if (end_subject - start_match < re->minlength)
+ {
+ rc = MATCH_NOMATCH;
+ break;
+ }
+
+ /* If req_cu is set, we know that that code unit must appear in the
+ subject for the (non-partial) match to succeed. If the first code unit is
+ set, req_cu must be later in the subject; otherwise the test starts at
+ the match point. This optimization can save a huge amount of backtracking
+ in patterns with nested unlimited repeats that aren't going to match.
+ Writing separate code for caseful/caseless versions makes it go faster,
+ as does using an autoincrement and backing off on a match. As in the case
+ of the first code unit, using memchr() in the 8-bit library gives a big
+ speed up. Unlike the first_cu check above, we do not need to call
+ memchr() twice in the caseless case because we only need to check for the
+ presence of the character in either case, not find the first occurrence.
+
+ The search can be skipped if the code unit was found later than the
+ current starting point in a previous iteration of the bumpalong loop.
+
+ HOWEVER: when the subject string is very, very long, searching to its end
+ can take a long time, and give bad performance on quite ordinary
+ anchored patterns. This showed up when somebody was matching something
+ like /^\d+C/ on a 32-megabyte string... so we don't do this when the
+ string is sufficiently long, but it's worth searching a lot more for
+ unanchored patterns. */
+
+ p = start_match + (has_first_cu? 1:0);
+ if (has_req_cu && p > req_cu_ptr)
+ {
+ PCRE2_SIZE check_length = end_subject - start_match;
+
+ if (check_length < REQ_CU_MAX ||
+ (!anchored && check_length < REQ_CU_MAX * 1000))
+ {
+ if (req_cu != req_cu2) /* Caseless */
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ while (p < end_subject)
+ {
+ uint32_t pp = UCHAR21INCTEST(p);
+ if (pp == req_cu || pp == req_cu2) { p--; break; }
+ }
+#else /* 8-bit code units */
+ PCRE2_SPTR pp = p;
+ p = memchr(pp, req_cu, end_subject - pp);
+ if (p == NULL)
+ {
+ p = memchr(pp, req_cu2, end_subject - pp);
+ if (p == NULL) p = end_subject;
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH != 8 */
+ }
+
+ /* The caseful case */
+
+ else
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ while (p < end_subject)
+ {
+ if (UCHAR21INCTEST(p) == req_cu) { p--; break; }
+ }
+
+#else /* 8-bit code units */
+ p = memchr(p, req_cu, end_subject - p);
+ if (p == NULL) p = end_subject;
+#endif
+ }
+
+ /* If we can't find the required code unit, break the bumpalong loop,
+ forcing a match failure. */
+
+ if (p >= end_subject)
+ {
+ rc = MATCH_NOMATCH;
+ break;
+ }
+
+ /* If we have found the required code unit, save the point where we
+ found it, so that we don't search again next time round the bumpalong
+ loop if the start hasn't yet passed this code unit. */
+
+ req_cu_ptr = p;
+ }
+ }
+ }
+ }
+
+ /* ------------ End of start of match optimizations ------------ */
+
+ /* Give no match if we have passed the bumpalong limit. */
+
+ if (start_match > bumpalong_limit)
+ {
+ rc = MATCH_NOMATCH;
+ break;
+ }
+
+ /* OK, we can now run the match. If "hitend" is set afterwards, remember the
+ first starting point for which a partial match was found. */
+
+ cb.start_match = (PCRE2_SIZE)(start_match - subject);
+ cb.callout_flags |= PCRE2_CALLOUT_STARTMATCH;
+
+ mb->start_used_ptr = start_match;
+ mb->last_used_ptr = start_match;
+#ifdef SUPPORT_UNICODE
+ mb->moptions = options | fragment_options;
+#else
+ mb->moptions = options;
+#endif
+ mb->match_call_count = 0;
+ mb->end_offset_top = 0;
+ mb->skip_arg_count = 0;
+
+ rc = match(start_match, mb->start_code, re->top_bracket, frame_size,
+ match_data, mb);
+
+ if (mb->hitend && start_partial == NULL)
+ {
+ start_partial = mb->start_used_ptr;
+ match_partial = start_match;
+ }
+
+ switch(rc)
+ {
+ /* If MATCH_SKIP_ARG reaches this level it means that a MARK that matched
+ the SKIP's arg was not found. In this circumstance, Perl ignores the SKIP
+ entirely. The only way we can do that is to re-do the match at the same
+ point, with a flag to force SKIP with an argument to be ignored. Just
+ treating this case as NOMATCH does not work because it does not check other
+ alternatives in patterns such as A(*SKIP:A)B|AC when the subject is AC. */
+
+ case MATCH_SKIP_ARG:
+ new_start_match = start_match;
+ mb->ignore_skip_arg = mb->skip_arg_count;
+ break;
+
+ /* SKIP passes back the next starting point explicitly, but if it is no
+ greater than the match we have just done, treat it as NOMATCH. */
+
+ case MATCH_SKIP:
+ if (mb->verb_skip_ptr > start_match)
+ {
+ new_start_match = mb->verb_skip_ptr;
+ break;
+ }
+ /* Fall through */
+
+ /* NOMATCH and PRUNE advance by one character. THEN at this level acts
+ exactly like PRUNE. Unset ignore SKIP-with-argument. */
+
+ case MATCH_NOMATCH:
+ case MATCH_PRUNE:
+ case MATCH_THEN:
+ mb->ignore_skip_arg = 0;
+ new_start_match = start_match + 1;
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ ACROSSCHAR(new_start_match < end_subject, new_start_match,
+ new_start_match++);
+#endif
+ break;
+
+ /* COMMIT disables the bumpalong, but otherwise behaves as NOMATCH. */
+
+ case MATCH_COMMIT:
+ rc = MATCH_NOMATCH;
+ goto ENDLOOP;
+
+ /* Any other return is either a match, or some kind of error. */
+
+ default:
+ goto ENDLOOP;
+ }
+
+ /* Control reaches here for the various types of "no match at this point"
+ result. Reset the code to MATCH_NOMATCH for subsequent checking. */
+
+ rc = MATCH_NOMATCH;
+
+ /* If PCRE2_FIRSTLINE is set, the match must happen before or at the first
+ newline in the subject (though it may continue over the newline). Therefore,
+ if we have just failed to match, starting at a newline, do not continue. */
+
+ if (firstline && IS_NEWLINE(start_match)) break;
+
+ /* Advance to new matching position */
+
+ start_match = new_start_match;
+
+ /* Break the loop if the pattern is anchored or if we have passed the end of
+ the subject. */
+
+ if (anchored || start_match > end_subject) break;
+
+ /* If we have just passed a CR and we are now at a LF, and the pattern does
+ not contain any explicit matches for \r or \n, and the newline option is CRLF
+ or ANY or ANYCRLF, advance the match position by one more code unit. In
+ normal matching start_match will aways be greater than the first position at
+ this stage, but a failed *SKIP can cause a return at the same point, which is
+ why the first test exists. */
+
+ if (start_match > subject + start_offset &&
+ start_match[-1] == CHAR_CR &&
+ start_match < end_subject &&
+ *start_match == CHAR_NL &&
+ (re->flags & PCRE2_HASCRORLF) == 0 &&
+ (mb->nltype == NLTYPE_ANY ||
+ mb->nltype == NLTYPE_ANYCRLF ||
+ mb->nllen == 2))
+ start_match++;
+
+ mb->mark = NULL; /* Reset for start of next match attempt */
+ } /* End of for(;;) "bumpalong" loop */
+
+/* ==========================================================================*/
+
+/* When we reach here, one of the following stopping conditions is true:
+
+(1) The match succeeded, either completely, or partially;
+
+(2) The pattern is anchored or the match was failed after (*COMMIT);
+
+(3) We are past the end of the subject or the bumpalong limit;
+
+(4) PCRE2_FIRSTLINE is set and we have failed to match at a newline, because
+ this option requests that a match occur at or before the first newline in
+ the subject.
+
+(5) Some kind of error occurred.
+
+*/
+
+ENDLOOP:
+
+/* If end_subject != true_end_subject, it means we are handling invalid UTF,
+and have just processed a non-terminal fragment. If this resulted in no match
+or a partial match we must carry on to the next fragment (a partial match is
+returned to the caller only at the very end of the subject). A loop is used to
+avoid trying to match against empty fragments; if the pattern can match an
+empty string it would have done so already. */
+
+#ifdef SUPPORT_UNICODE
+if (utf && end_subject != true_end_subject &&
+ (rc == MATCH_NOMATCH || rc == PCRE2_ERROR_PARTIAL))
+ {
+ for (;;)
+ {
+ /* Advance past the first bad code unit, and then skip invalid character
+ starting code units in 8-bit and 16-bit modes. */
+
+ start_match = end_subject + 1;
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ while (start_match < true_end_subject && NOT_FIRSTCU(*start_match))
+ start_match++;
+#endif
+
+ /* If we have hit the end of the subject, there isn't another non-empty
+ fragment, so give up. */
+
+ if (start_match >= true_end_subject)
+ {
+ rc = MATCH_NOMATCH; /* In case it was partial */
+ break;
+ }
+
+ /* Check the rest of the subject */
+
+ mb->check_subject = start_match;
+ rc = PRIV(valid_utf)(start_match, length - (start_match - subject),
+ &(match_data->startchar));
+
+ /* The rest of the subject is valid UTF. */
+
+ if (rc == 0)
+ {
+ mb->end_subject = end_subject = true_end_subject;
+ fragment_options = PCRE2_NOTBOL;
+ goto FRAGMENT_RESTART;
+ }
+
+ /* A subsequent UTF error has been found; if the next fragment is
+ non-empty, set up to process it. Otherwise, let the loop advance. */
+
+ else if (rc < 0)
+ {
+ mb->end_subject = end_subject = start_match + match_data->startchar;
+ if (end_subject > start_match)
+ {
+ fragment_options = PCRE2_NOTBOL|PCRE2_NOTEOL;
+ goto FRAGMENT_RESTART;
+ }
+ }
+ }
+ }
+#endif /* SUPPORT_UNICODE */
+
+/* Fill in fields that are always returned in the match data. */
+
+match_data->code = re;
+match_data->mark = mb->mark;
+match_data->matchedby = PCRE2_MATCHEDBY_INTERPRETER;
+
+/* Handle a fully successful match. Set the return code to the number of
+captured strings, or 0 if there were too many to fit into the ovector, and then
+set the remaining returned values before returning. Make a copy of the subject
+string if requested. */
+
+if (rc == MATCH_MATCH)
+ {
+ match_data->rc = ((int)mb->end_offset_top >= 2 * match_data->oveccount)?
+ 0 : (int)mb->end_offset_top/2 + 1;
+ match_data->startchar = start_match - subject;
+ match_data->leftchar = mb->start_used_ptr - subject;
+ match_data->rightchar = ((mb->last_used_ptr > mb->end_match_ptr)?
+ mb->last_used_ptr : mb->end_match_ptr) - subject;
+ if ((options & PCRE2_COPY_MATCHED_SUBJECT) != 0)
+ {
+ length = CU2BYTES(length + was_zero_terminated);
+ match_data->subject = match_data->memctl.malloc(length,
+ match_data->memctl.memory_data);
+ if (match_data->subject == NULL) return PCRE2_ERROR_NOMEMORY;
+ memcpy((void *)match_data->subject, subject, length);
+ match_data->flags |= PCRE2_MD_COPIED_SUBJECT;
+ }
+ else match_data->subject = subject;
+ return match_data->rc;
+ }
+
+/* Control gets here if there has been a partial match, an error, or if the
+overall match attempt has failed at all permitted starting positions. Any mark
+data is in the nomatch_mark field. */
+
+match_data->mark = mb->nomatch_mark;
+
+/* For anything other than nomatch or partial match, just return the code. */
+
+if (rc != MATCH_NOMATCH && rc != PCRE2_ERROR_PARTIAL) match_data->rc = rc;
+
+/* Handle a partial match. If a "soft" partial match was requested, searching
+for a complete match will have continued, and the value of rc at this point
+will be MATCH_NOMATCH. For a "hard" partial match, it will already be
+PCRE2_ERROR_PARTIAL. */
+
+else if (match_partial != NULL)
+ {
+ match_data->subject = subject;
+ match_data->ovector[0] = match_partial - subject;
+ match_data->ovector[1] = end_subject - subject;
+ match_data->startchar = match_partial - subject;
+ match_data->leftchar = start_partial - subject;
+ match_data->rightchar = end_subject - subject;
+ match_data->rc = PCRE2_ERROR_PARTIAL;
+ }
+
+/* Else this is the classic nomatch case. */
+
+else match_data->rc = PCRE2_ERROR_NOMATCH;
+
+return match_data->rc;
+}
+
+/* These #undefs are here to enable unity builds with CMake. */
+
+#undef NLBLOCK /* Block containing newline information */
+#undef PSSTART /* Field containing processed string start */
+#undef PSEND /* Field containing processed string end */
+
+/* End of pcre2_match.c */
diff --git a/contrib/libs/pcre2/src/pcre2_match_data.c b/contrib/libs/pcre2/src/pcre2_match_data.c
new file mode 100644
index 00000000000..1b663596201
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_match_data.c
@@ -0,0 +1,173 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+
+/*************************************************
+* Create a match data block given ovector size *
+*************************************************/
+
+/* A minimum of 1 is imposed on the number of ovector pairs. A maximum is also
+imposed because the oveccount field in a match data block is uintt6_t. */
+
+PCRE2_EXP_DEFN pcre2_match_data * PCRE2_CALL_CONVENTION
+pcre2_match_data_create(uint32_t oveccount, pcre2_general_context *gcontext)
+{
+pcre2_match_data *yield;
+if (oveccount < 1) oveccount = 1;
+if (oveccount > UINT16_MAX) oveccount = UINT16_MAX;
+yield = PRIV(memctl_malloc)(
+ offsetof(pcre2_match_data, ovector) + 2*oveccount*sizeof(PCRE2_SIZE),
+ (pcre2_memctl *)gcontext);
+if (yield == NULL) return NULL;
+yield->oveccount = oveccount;
+yield->flags = 0;
+yield->heapframes = NULL;
+yield->heapframes_size = 0;
+return yield;
+}
+
+
+
+/*************************************************
+* Create a match data block using pattern data *
+*************************************************/
+
+/* If no context is supplied, use the memory allocator from the code. */
+
+PCRE2_EXP_DEFN pcre2_match_data * PCRE2_CALL_CONVENTION
+pcre2_match_data_create_from_pattern(const pcre2_code *code,
+ pcre2_general_context *gcontext)
+{
+if (gcontext == NULL) gcontext = (pcre2_general_context *)code;
+return pcre2_match_data_create(((pcre2_real_code *)code)->top_bracket + 1,
+ gcontext);
+}
+
+
+
+/*************************************************
+* Free a match data block *
+*************************************************/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_match_data_free(pcre2_match_data *match_data)
+{
+if (match_data != NULL)
+ {
+ if (match_data->heapframes != NULL)
+ match_data->memctl.free(match_data->heapframes,
+ match_data->memctl.memory_data);
+ if ((match_data->flags & PCRE2_MD_COPIED_SUBJECT) != 0)
+ match_data->memctl.free((void *)match_data->subject,
+ match_data->memctl.memory_data);
+ match_data->memctl.free(match_data, match_data->memctl.memory_data);
+ }
+}
+
+
+
+/*************************************************
+* Get last mark in match *
+*************************************************/
+
+PCRE2_EXP_DEFN PCRE2_SPTR PCRE2_CALL_CONVENTION
+pcre2_get_mark(pcre2_match_data *match_data)
+{
+return match_data->mark;
+}
+
+
+
+/*************************************************
+* Get pointer to ovector *
+*************************************************/
+
+PCRE2_EXP_DEFN PCRE2_SIZE * PCRE2_CALL_CONVENTION
+pcre2_get_ovector_pointer(pcre2_match_data *match_data)
+{
+return match_data->ovector;
+}
+
+
+
+/*************************************************
+* Get number of ovector slots *
+*************************************************/
+
+PCRE2_EXP_DEFN uint32_t PCRE2_CALL_CONVENTION
+pcre2_get_ovector_count(pcre2_match_data *match_data)
+{
+return match_data->oveccount;
+}
+
+
+
+/*************************************************
+* Get starting code unit in match *
+*************************************************/
+
+PCRE2_EXP_DEFN PCRE2_SIZE PCRE2_CALL_CONVENTION
+pcre2_get_startchar(pcre2_match_data *match_data)
+{
+return match_data->startchar;
+}
+
+
+
+/*************************************************
+* Get size of match data block *
+*************************************************/
+
+PCRE2_EXP_DEFN PCRE2_SIZE PCRE2_CALL_CONVENTION
+pcre2_get_match_data_size(pcre2_match_data *match_data)
+{
+return offsetof(pcre2_match_data, ovector) +
+ 2 * (match_data->oveccount) * sizeof(PCRE2_SIZE);
+}
+
+/* End of pcre2_match_data.c */
diff --git a/contrib/libs/pcre2/src/pcre2_newline.c b/contrib/libs/pcre2/src/pcre2_newline.c
new file mode 100644
index 00000000000..ae830b38df3
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_newline.c
@@ -0,0 +1,243 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This module contains internal functions for testing newlines when more than
+one kind of newline is to be recognized. When a newline is found, its length is
+returned. In principle, we could implement several newline "types", each
+referring to a different set of newline characters. At present, PCRE2 supports
+only NLTYPE_FIXED, which gets handled without these functions, NLTYPE_ANYCRLF,
+and NLTYPE_ANY. The full list of Unicode newline characters is taken from
+http://unicode.org/unicode/reports/tr18/. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+
+/*************************************************
+* Check for newline at given position *
+*************************************************/
+
+/* This function is called only via the IS_NEWLINE macro, which does so only
+when the newline type is NLTYPE_ANY or NLTYPE_ANYCRLF. The case of a fixed
+newline (NLTYPE_FIXED) is handled inline. It is guaranteed that the code unit
+pointed to by ptr is less than the end of the string.
+
+Arguments:
+ ptr pointer to possible newline
+ type the newline type
+ endptr pointer to the end of the string
+ lenptr where to return the length
+ utf TRUE if in utf mode
+
+Returns: TRUE or FALSE
+*/
+
+BOOL
+PRIV(is_newline)(PCRE2_SPTR ptr, uint32_t type, PCRE2_SPTR endptr,
+ uint32_t *lenptr, BOOL utf)
+{
+uint32_t c;
+
+#ifdef SUPPORT_UNICODE
+if (utf) { GETCHAR(c, ptr); } else c = *ptr;
+#else
+(void)utf;
+c = *ptr;
+#endif /* SUPPORT_UNICODE */
+
+if (type == NLTYPE_ANYCRLF) switch(c)
+ {
+ case CHAR_LF:
+ *lenptr = 1;
+ return TRUE;
+
+ case CHAR_CR:
+ *lenptr = (ptr < endptr - 1 && ptr[1] == CHAR_LF)? 2 : 1;
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+
+/* NLTYPE_ANY */
+
+else switch(c)
+ {
+#ifdef EBCDIC
+ case CHAR_NEL:
+#endif
+ case CHAR_LF:
+ case CHAR_VT:
+ case CHAR_FF:
+ *lenptr = 1;
+ return TRUE;
+
+ case CHAR_CR:
+ *lenptr = (ptr < endptr - 1 && ptr[1] == CHAR_LF)? 2 : 1;
+ return TRUE;
+
+#ifndef EBCDIC
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ case CHAR_NEL:
+ *lenptr = utf? 2 : 1;
+ return TRUE;
+
+ case 0x2028: /* LS */
+ case 0x2029: /* PS */
+ *lenptr = 3;
+ return TRUE;
+
+#else /* 16-bit or 32-bit code units */
+ case CHAR_NEL:
+ case 0x2028: /* LS */
+ case 0x2029: /* PS */
+ *lenptr = 1;
+ return TRUE;
+#endif
+#endif /* Not EBCDIC */
+
+ default:
+ return FALSE;
+ }
+}
+
+
+
+/*************************************************
+* Check for newline at previous position *
+*************************************************/
+
+/* This function is called only via the WAS_NEWLINE macro, which does so only
+when the newline type is NLTYPE_ANY or NLTYPE_ANYCRLF. The case of a fixed
+newline (NLTYPE_FIXED) is handled inline. It is guaranteed that the initial
+value of ptr is greater than the start of the string that is being processed.
+
+Arguments:
+ ptr pointer to possible newline
+ type the newline type
+ startptr pointer to the start of the string
+ lenptr where to return the length
+ utf TRUE if in utf mode
+
+Returns: TRUE or FALSE
+*/
+
+BOOL
+PRIV(was_newline)(PCRE2_SPTR ptr, uint32_t type, PCRE2_SPTR startptr,
+ uint32_t *lenptr, BOOL utf)
+{
+uint32_t c;
+ptr--;
+
+#ifdef SUPPORT_UNICODE
+if (utf)
+ {
+ BACKCHAR(ptr);
+ GETCHAR(c, ptr);
+ }
+else c = *ptr;
+#else
+(void)utf;
+c = *ptr;
+#endif /* SUPPORT_UNICODE */
+
+if (type == NLTYPE_ANYCRLF) switch(c)
+ {
+ case CHAR_LF:
+ *lenptr = (ptr > startptr && ptr[-1] == CHAR_CR)? 2 : 1;
+ return TRUE;
+
+ case CHAR_CR:
+ *lenptr = 1;
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+
+/* NLTYPE_ANY */
+
+else switch(c)
+ {
+ case CHAR_LF:
+ *lenptr = (ptr > startptr && ptr[-1] == CHAR_CR)? 2 : 1;
+ return TRUE;
+
+#ifdef EBCDIC
+ case CHAR_NEL:
+#endif
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_CR:
+ *lenptr = 1;
+ return TRUE;
+
+#ifndef EBCDIC
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ case CHAR_NEL:
+ *lenptr = utf? 2 : 1;
+ return TRUE;
+
+ case 0x2028: /* LS */
+ case 0x2029: /* PS */
+ *lenptr = 3;
+ return TRUE;
+
+#else /* 16-bit or 32-bit code units */
+ case CHAR_NEL:
+ case 0x2028: /* LS */
+ case 0x2029: /* PS */
+ *lenptr = 1;
+ return TRUE;
+#endif
+#endif /* Not EBCDIC */
+
+ default:
+ return FALSE;
+ }
+}
+
+/* End of pcre2_newline.c */
diff --git a/contrib/libs/pcre2/src/pcre2_ord2utf.c b/contrib/libs/pcre2/src/pcre2_ord2utf.c
new file mode 100644
index 00000000000..c2cf6e9856b
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_ord2utf.c
@@ -0,0 +1,120 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This file contains a function that converts a Unicode character code point
+into a UTF string. The behaviour is different for each code unit width. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+/* If SUPPORT_UNICODE is not defined, this function will never be called.
+Supply a dummy function because some compilers do not like empty source
+modules. */
+
+#ifndef SUPPORT_UNICODE
+unsigned int
+PRIV(ord2utf)(uint32_t cvalue, PCRE2_UCHAR *buffer)
+{
+(void)(cvalue);
+(void)(buffer);
+return 0;
+}
+#else /* SUPPORT_UNICODE */
+
+
+/*************************************************
+* Convert code point to UTF *
+*************************************************/
+
+/*
+Arguments:
+ cvalue the character value
+ buffer pointer to buffer for result
+
+Returns: number of code units placed in the buffer
+*/
+
+unsigned int
+PRIV(ord2utf)(uint32_t cvalue, PCRE2_UCHAR *buffer)
+{
+/* Convert to UTF-8 */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+int i, j;
+for (i = 0; i < PRIV(utf8_table1_size); i++)
+ if ((int)cvalue <= PRIV(utf8_table1)[i]) break;
+buffer += i;
+for (j = i; j > 0; j--)
+ {
+ *buffer-- = 0x80 | (cvalue & 0x3f);
+ cvalue >>= 6;
+ }
+*buffer = PRIV(utf8_table2)[i] | cvalue;
+return i + 1;
+
+/* Convert to UTF-16 */
+
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+if (cvalue <= 0xffff)
+ {
+ *buffer = (PCRE2_UCHAR)cvalue;
+ return 1;
+ }
+cvalue -= 0x10000;
+*buffer++ = 0xd800 | (cvalue >> 10);
+*buffer = 0xdc00 | (cvalue & 0x3ff);
+return 2;
+
+/* Convert to UTF-32 */
+
+#else
+*buffer = (PCRE2_UCHAR)cvalue;
+return 1;
+#endif
+}
+#endif /* SUPPORT_UNICODE */
+
+/* End of pcre_ord2utf.c */
diff --git a/contrib/libs/pcre2/src/pcre2_pattern_info.c b/contrib/libs/pcre2/src/pcre2_pattern_info.c
new file mode 100644
index 00000000000..6d3ad3c1ec4
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_pattern_info.c
@@ -0,0 +1,432 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2018 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+/*************************************************
+* Return info about compiled pattern *
+*************************************************/
+
+/*
+Arguments:
+ code points to compiled code
+ what what information is required
+ where where to put the information; if NULL, return length
+
+Returns: 0 when data returned
+ > 0 when length requested
+ < 0 on error or unset value
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_pattern_info(const pcre2_code *code, uint32_t what, void *where)
+{
+const pcre2_real_code *re = (pcre2_real_code *)code;
+
+if (where == NULL) /* Requests field length */
+ {
+ switch(what)
+ {
+ case PCRE2_INFO_ALLOPTIONS:
+ case PCRE2_INFO_ARGOPTIONS:
+ case PCRE2_INFO_BACKREFMAX:
+ case PCRE2_INFO_BSR:
+ case PCRE2_INFO_CAPTURECOUNT:
+ case PCRE2_INFO_DEPTHLIMIT:
+ case PCRE2_INFO_EXTRAOPTIONS:
+ case PCRE2_INFO_FIRSTCODETYPE:
+ case PCRE2_INFO_FIRSTCODEUNIT:
+ case PCRE2_INFO_HASBACKSLASHC:
+ case PCRE2_INFO_HASCRORLF:
+ case PCRE2_INFO_HEAPLIMIT:
+ case PCRE2_INFO_JCHANGED:
+ case PCRE2_INFO_LASTCODETYPE:
+ case PCRE2_INFO_LASTCODEUNIT:
+ case PCRE2_INFO_MATCHEMPTY:
+ case PCRE2_INFO_MATCHLIMIT:
+ case PCRE2_INFO_MAXLOOKBEHIND:
+ case PCRE2_INFO_MINLENGTH:
+ case PCRE2_INFO_NAMEENTRYSIZE:
+ case PCRE2_INFO_NAMECOUNT:
+ case PCRE2_INFO_NEWLINE:
+ return sizeof(uint32_t);
+
+ case PCRE2_INFO_FIRSTBITMAP:
+ return sizeof(const uint8_t *);
+
+ case PCRE2_INFO_JITSIZE:
+ case PCRE2_INFO_SIZE:
+ case PCRE2_INFO_FRAMESIZE:
+ return sizeof(size_t);
+
+ case PCRE2_INFO_NAMETABLE:
+ return sizeof(PCRE2_SPTR);
+ }
+ }
+
+if (re == NULL) return PCRE2_ERROR_NULL;
+
+/* Check that the first field in the block is the magic number. If it is not,
+return with PCRE2_ERROR_BADMAGIC. */
+
+if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC;
+
+/* Check that this pattern was compiled in the correct bit mode */
+
+if ((re->flags & (PCRE2_CODE_UNIT_WIDTH/8)) == 0) return PCRE2_ERROR_BADMODE;
+
+switch(what)
+ {
+ case PCRE2_INFO_ALLOPTIONS:
+ *((uint32_t *)where) = re->overall_options;
+ break;
+
+ case PCRE2_INFO_ARGOPTIONS:
+ *((uint32_t *)where) = re->compile_options;
+ break;
+
+ case PCRE2_INFO_BACKREFMAX:
+ *((uint32_t *)where) = re->top_backref;
+ break;
+
+ case PCRE2_INFO_BSR:
+ *((uint32_t *)where) = re->bsr_convention;
+ break;
+
+ case PCRE2_INFO_CAPTURECOUNT:
+ *((uint32_t *)where) = re->top_bracket;
+ break;
+
+ case PCRE2_INFO_DEPTHLIMIT:
+ *((uint32_t *)where) = re->limit_depth;
+ if (re->limit_depth == UINT32_MAX) return PCRE2_ERROR_UNSET;
+ break;
+
+ case PCRE2_INFO_EXTRAOPTIONS:
+ *((uint32_t *)where) = re->extra_options;
+ break;
+
+ case PCRE2_INFO_FIRSTCODETYPE:
+ *((uint32_t *)where) = ((re->flags & PCRE2_FIRSTSET) != 0)? 1 :
+ ((re->flags & PCRE2_STARTLINE) != 0)? 2 : 0;
+ break;
+
+ case PCRE2_INFO_FIRSTCODEUNIT:
+ *((uint32_t *)where) = ((re->flags & PCRE2_FIRSTSET) != 0)?
+ re->first_codeunit : 0;
+ break;
+
+ case PCRE2_INFO_FIRSTBITMAP:
+ *((const uint8_t **)where) = ((re->flags & PCRE2_FIRSTMAPSET) != 0)?
+ &(re->start_bitmap[0]) : NULL;
+ break;
+
+ case PCRE2_INFO_FRAMESIZE:
+ *((size_t *)where) = offsetof(heapframe, ovector) +
+ re->top_bracket * 2 * sizeof(PCRE2_SIZE);
+ break;
+
+ case PCRE2_INFO_HASBACKSLASHC:
+ *((uint32_t *)where) = (re->flags & PCRE2_HASBKC) != 0;
+ break;
+
+ case PCRE2_INFO_HASCRORLF:
+ *((uint32_t *)where) = (re->flags & PCRE2_HASCRORLF) != 0;
+ break;
+
+ case PCRE2_INFO_HEAPLIMIT:
+ *((uint32_t *)where) = re->limit_heap;
+ if (re->limit_heap == UINT32_MAX) return PCRE2_ERROR_UNSET;
+ break;
+
+ case PCRE2_INFO_JCHANGED:
+ *((uint32_t *)where) = (re->flags & PCRE2_JCHANGED) != 0;
+ break;
+
+ case PCRE2_INFO_JITSIZE:
+#ifdef SUPPORT_JIT
+ *((size_t *)where) = (re->executable_jit != NULL)?
+ PRIV(jit_get_size)(re->executable_jit) : 0;
+#else
+ *((size_t *)where) = 0;
+#endif
+ break;
+
+ case PCRE2_INFO_LASTCODETYPE:
+ *((uint32_t *)where) = ((re->flags & PCRE2_LASTSET) != 0)? 1 : 0;
+ break;
+
+ case PCRE2_INFO_LASTCODEUNIT:
+ *((uint32_t *)where) = ((re->flags & PCRE2_LASTSET) != 0)?
+ re->last_codeunit : 0;
+ break;
+
+ case PCRE2_INFO_MATCHEMPTY:
+ *((uint32_t *)where) = (re->flags & PCRE2_MATCH_EMPTY) != 0;
+ break;
+
+ case PCRE2_INFO_MATCHLIMIT:
+ *((uint32_t *)where) = re->limit_match;
+ if (re->limit_match == UINT32_MAX) return PCRE2_ERROR_UNSET;
+ break;
+
+ case PCRE2_INFO_MAXLOOKBEHIND:
+ *((uint32_t *)where) = re->max_lookbehind;
+ break;
+
+ case PCRE2_INFO_MINLENGTH:
+ *((uint32_t *)where) = re->minlength;
+ break;
+
+ case PCRE2_INFO_NAMEENTRYSIZE:
+ *((uint32_t *)where) = re->name_entry_size;
+ break;
+
+ case PCRE2_INFO_NAMECOUNT:
+ *((uint32_t *)where) = re->name_count;
+ break;
+
+ case PCRE2_INFO_NAMETABLE:
+ *((PCRE2_SPTR *)where) = (PCRE2_SPTR)((char *)re + sizeof(pcre2_real_code));
+ break;
+
+ case PCRE2_INFO_NEWLINE:
+ *((uint32_t *)where) = re->newline_convention;
+ break;
+
+ case PCRE2_INFO_SIZE:
+ *((size_t *)where) = re->blocksize;
+ break;
+
+ default: return PCRE2_ERROR_BADOPTION;
+ }
+
+return 0;
+}
+
+
+
+/*************************************************
+* Callout enumerator *
+*************************************************/
+
+/*
+Arguments:
+ code points to compiled code
+ callback function called for each callout block
+ callout_data user data passed to the callback
+
+Returns: 0 when successfully completed
+ < 0 on local error
+ != 0 for callback error
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_callout_enumerate(const pcre2_code *code,
+ int (*callback)(pcre2_callout_enumerate_block *, void *), void *callout_data)
+{
+pcre2_real_code *re = (pcre2_real_code *)code;
+pcre2_callout_enumerate_block cb;
+PCRE2_SPTR cc;
+#ifdef SUPPORT_UNICODE
+BOOL utf;
+#endif
+
+if (re == NULL) return PCRE2_ERROR_NULL;
+
+#ifdef SUPPORT_UNICODE
+utf = (re->overall_options & PCRE2_UTF) != 0;
+#endif
+
+/* Check that the first field in the block is the magic number. If it is not,
+return with PCRE2_ERROR_BADMAGIC. */
+
+if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC;
+
+/* Check that this pattern was compiled in the correct bit mode */
+
+if ((re->flags & (PCRE2_CODE_UNIT_WIDTH/8)) == 0) return PCRE2_ERROR_BADMODE;
+
+cb.version = 0;
+cc = (PCRE2_SPTR)((uint8_t *)re + sizeof(pcre2_real_code))
+ + re->name_count * re->name_entry_size;
+
+while (TRUE)
+ {
+ int rc;
+ switch (*cc)
+ {
+ case OP_END:
+ return 0;
+
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_EXACT:
+ case OP_POSSTAR:
+ case OP_POSPLUS:
+ case OP_POSQUERY:
+ case OP_POSUPTO:
+ case OP_STARI:
+ case OP_MINSTARI:
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_UPTOI:
+ case OP_MINUPTOI:
+ case OP_EXACTI:
+ case OP_POSSTARI:
+ case OP_POSPLUSI:
+ case OP_POSQUERYI:
+ case OP_POSUPTOI:
+ case OP_NOTSTAR:
+ case OP_NOTMINSTAR:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTQUERY:
+ case OP_NOTMINQUERY:
+ case OP_NOTUPTO:
+ case OP_NOTMINUPTO:
+ case OP_NOTEXACT:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSUPTO:
+ case OP_NOTSTARI:
+ case OP_NOTMINSTARI:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUSI:
+ case OP_NOTQUERYI:
+ case OP_NOTMINQUERYI:
+ case OP_NOTUPTOI:
+ case OP_NOTMINUPTOI:
+ case OP_NOTEXACTI:
+ case OP_NOTPOSSTARI:
+ case OP_NOTPOSPLUSI:
+ case OP_NOTPOSQUERYI:
+ case OP_NOTPOSUPTOI:
+ cc += PRIV(OP_lengths)[*cc];
+#ifdef SUPPORT_UNICODE
+ if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEEXACT:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ case OP_TYPEPOSQUERY:
+ case OP_TYPEPOSUPTO:
+ cc += PRIV(OP_lengths)[*cc];
+#ifdef SUPPORT_UNICODE
+ if (cc[-1] == OP_PROP || cc[-1] == OP_NOTPROP) cc += 2;
+#endif
+ break;
+
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ case OP_XCLASS:
+ cc += GET(cc, 1);
+ break;
+#endif
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_SKIP_ARG:
+ case OP_THEN_ARG:
+ cc += PRIV(OP_lengths)[*cc] + cc[1];
+ break;
+
+ case OP_CALLOUT:
+ cb.pattern_position = GET(cc, 1);
+ cb.next_item_length = GET(cc, 1 + LINK_SIZE);
+ cb.callout_number = cc[1 + 2*LINK_SIZE];
+ cb.callout_string_offset = 0;
+ cb.callout_string_length = 0;
+ cb.callout_string = NULL;
+ rc = callback(&cb, callout_data);
+ if (rc != 0) return rc;
+ cc += PRIV(OP_lengths)[*cc];
+ break;
+
+ case OP_CALLOUT_STR:
+ cb.pattern_position = GET(cc, 1);
+ cb.next_item_length = GET(cc, 1 + LINK_SIZE);
+ cb.callout_number = 0;
+ cb.callout_string_offset = GET(cc, 1 + 3*LINK_SIZE);
+ cb.callout_string_length =
+ GET(cc, 1 + 2*LINK_SIZE) - (1 + 4*LINK_SIZE) - 2;
+ cb.callout_string = cc + (1 + 4*LINK_SIZE) + 1;
+ rc = callback(&cb, callout_data);
+ if (rc != 0) return rc;
+ cc += GET(cc, 1 + 2*LINK_SIZE);
+ break;
+
+ default:
+ cc += PRIV(OP_lengths)[*cc];
+ break;
+ }
+ }
+}
+
+/* End of pcre2_pattern_info.c */
diff --git a/contrib/libs/pcre2/src/pcre2_script_run.c b/contrib/libs/pcre2/src/pcre2_script_run.c
new file mode 100644
index 00000000000..1ac2985b498
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_script_run.c
@@ -0,0 +1,344 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2021 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains the function for checking a script run. */
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+/*************************************************
+* Check script run *
+*************************************************/
+
+/* A script run is conceptually a sequence of characters all in the same
+Unicode script. However, it isn't quite that simple. There are special rules
+for scripts that are commonly used together, and also special rules for digits.
+This function implements the appropriate checks, which is possible only when
+PCRE2 is compiled with Unicode support. The function returns TRUE if there is
+no Unicode support; however, it should never be called in that circumstance
+because an error is given by pcre2_compile() if a script run is called for in a
+version of PCRE2 compiled without Unicode support.
+
+Arguments:
+ pgr point to the first character
+ endptr point after the last character
+ utf TRUE if in UTF mode
+
+Returns: TRUE if this is a valid script run
+*/
+
+/* These are states in the checking process. */
+
+enum { SCRIPT_UNSET, /* Requirement as yet unknown */
+ SCRIPT_MAP, /* Bitmap contains acceptable scripts */
+ SCRIPT_HANPENDING, /* Have had only Han characters */
+ SCRIPT_HANHIRAKATA, /* Expect Han or Hirikata */
+ SCRIPT_HANBOPOMOFO, /* Expect Han or Bopomofo */
+ SCRIPT_HANHANGUL /* Expect Han or Hangul */
+ };
+
+#define UCD_MAPSIZE (ucp_Unknown/32 + 1)
+#define FULL_MAPSIZE (ucp_Script_Count/32 + 1)
+
+BOOL
+PRIV(script_run)(PCRE2_SPTR ptr, PCRE2_SPTR endptr, BOOL utf)
+{
+#ifdef SUPPORT_UNICODE
+uint32_t require_state = SCRIPT_UNSET;
+uint32_t require_map[FULL_MAPSIZE];
+uint32_t map[FULL_MAPSIZE];
+uint32_t require_digitset = 0;
+uint32_t c;
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+(void)utf; /* Avoid compiler warning */
+#endif
+
+/* Any string containing fewer than 2 characters is a valid script run. */
+
+if (ptr >= endptr) return TRUE;
+GETCHARINCTEST(c, ptr);
+if (ptr >= endptr) return TRUE;
+
+/* Initialize the require map. This is a full-size bitmap that has a bit for
+every script, as opposed to the maps in ucd_script_sets, which only have bits
+for scripts less than ucp_Unknown - those that appear in script extension
+lists. */
+
+for (int i = 0; i < FULL_MAPSIZE; i++) require_map[i] = 0;
+
+/* Scan strings of two or more characters, checking the Unicode characteristics
+of each code point. There is special code for scripts that can be combined with
+characters from the Han Chinese script. This may be used in conjunction with
+four other scripts in these combinations:
+
+. Han with Hiragana and Katakana is allowed (for Japanese).
+. Han with Bopomofo is allowed (for Taiwanese Mandarin).
+. Han with Hangul is allowed (for Korean).
+
+If the first significant character's script is one of the four, the required
+script type is immediately known. However, if the first significant
+character's script is Han, we have to keep checking for a non-Han character.
+Hence the SCRIPT_HANPENDING state. */
+
+for (;;)
+ {
+ const ucd_record *ucd = GET_UCD(c);
+ uint32_t script = ucd->script;
+
+ /* If the script is Unknown, the string is not a valid script run. Such
+ characters can only form script runs of length one (see test above). */
+
+ if (script == ucp_Unknown) return FALSE;
+
+ /* A character without any script extensions whose script is Inherited or
+ Common is always accepted with any script. If there are extensions, the
+ following processing happens for all scripts. */
+
+ if (UCD_SCRIPTX_PROP(ucd) != 0 || (script != ucp_Inherited && script != ucp_Common))
+ {
+ BOOL OK;
+
+ /* Set up a full-sized map for this character that can include bits for all
+ scripts. Copy the scriptx map for this character (which covers those
+ scripts that appear in script extension lists), set the remaining values to
+ zero, and then, except for Common or Inherited, add this script's bit to
+ the map. */
+
+ memcpy(map, PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(ucd), UCD_MAPSIZE * sizeof(uint32_t));
+ memset(map + UCD_MAPSIZE, 0, (FULL_MAPSIZE - UCD_MAPSIZE) * sizeof(uint32_t));
+ if (script != ucp_Common && script != ucp_Inherited) MAPSET(map, script);
+
+ /* Handle the different checking states */
+
+ switch(require_state)
+ {
+ /* First significant character - it might follow Common or Inherited
+ characters that do not have any script extensions. */
+
+ case SCRIPT_UNSET:
+ switch(script)
+ {
+ case ucp_Han:
+ require_state = SCRIPT_HANPENDING;
+ break;
+
+ case ucp_Hiragana:
+ case ucp_Katakana:
+ require_state = SCRIPT_HANHIRAKATA;
+ break;
+
+ case ucp_Bopomofo:
+ require_state = SCRIPT_HANBOPOMOFO;
+ break;
+
+ case ucp_Hangul:
+ require_state = SCRIPT_HANHANGUL;
+ break;
+
+ default:
+ memcpy(require_map, map, FULL_MAPSIZE * sizeof(uint32_t));
+ require_state = SCRIPT_MAP;
+ break;
+ }
+ break;
+
+ /* The first significant character was Han. An inspection of the Unicode
+ 11.0.0 files shows that there are the following types of Script Extension
+ list that involve the Han, Bopomofo, Hiragana, Katakana, and Hangul
+ scripts:
+
+ . Bopomofo + Han
+ . Han + Hiragana + Katakana
+ . Hiragana + Katakana
+ . Bopopmofo + Hangul + Han + Hiragana + Katakana
+
+ The following code tries to make sense of this. */
+
+#define FOUND_BOPOMOFO 1
+#define FOUND_HIRAGANA 2
+#define FOUND_KATAKANA 4
+#define FOUND_HANGUL 8
+
+ case SCRIPT_HANPENDING:
+ if (script != ucp_Han) /* Another Han does nothing */
+ {
+ uint32_t chspecial = 0;
+
+ if (MAPBIT(map, ucp_Bopomofo) != 0) chspecial |= FOUND_BOPOMOFO;
+ if (MAPBIT(map, ucp_Hiragana) != 0) chspecial |= FOUND_HIRAGANA;
+ if (MAPBIT(map, ucp_Katakana) != 0) chspecial |= FOUND_KATAKANA;
+ if (MAPBIT(map, ucp_Hangul) != 0) chspecial |= FOUND_HANGUL;
+
+ if (chspecial == 0) return FALSE; /* Not allowed with Han */
+
+ if (chspecial == FOUND_BOPOMOFO)
+ require_state = SCRIPT_HANBOPOMOFO;
+ else if (chspecial == (FOUND_HIRAGANA|FOUND_KATAKANA))
+ require_state = SCRIPT_HANHIRAKATA;
+
+ /* Otherwise this character must be allowed with all of them, so remain
+ in the pending state. */
+ }
+ break;
+
+ /* Previously encountered one of the "with Han" scripts. Check that
+ this character is appropriate. */
+
+ case SCRIPT_HANHIRAKATA:
+ if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hiragana) +
+ MAPBIT(map, ucp_Katakana) == 0) return FALSE;
+ break;
+
+ case SCRIPT_HANBOPOMOFO:
+ if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Bopomofo) == 0) return FALSE;
+ break;
+
+ case SCRIPT_HANHANGUL:
+ if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hangul) == 0) return FALSE;
+ break;
+
+ /* Previously encountered one or more characters that are allowed with a
+ list of scripts. */
+
+ case SCRIPT_MAP:
+ OK = FALSE;
+
+ for (int i = 0; i < FULL_MAPSIZE; i++)
+ {
+ if ((require_map[i] & map[i]) != 0)
+ {
+ OK = TRUE;
+ break;
+ }
+ }
+
+ if (!OK) return FALSE;
+
+ /* The rest of the string must be in this script, but we have to
+ allow for the Han complications. */
+
+ switch(script)
+ {
+ case ucp_Han:
+ require_state = SCRIPT_HANPENDING;
+ break;
+
+ case ucp_Hiragana:
+ case ucp_Katakana:
+ require_state = SCRIPT_HANHIRAKATA;
+ break;
+
+ case ucp_Bopomofo:
+ require_state = SCRIPT_HANBOPOMOFO;
+ break;
+
+ case ucp_Hangul:
+ require_state = SCRIPT_HANHANGUL;
+ break;
+
+ /* Compute the intersection of the required list of scripts and the
+ allowed scripts for this character. */
+
+ default:
+ for (int i = 0; i < FULL_MAPSIZE; i++) require_map[i] &= map[i];
+ break;
+ }
+
+ break;
+ }
+ } /* End checking character's script and extensions. */
+
+ /* The character is in an acceptable script. We must now ensure that all
+ decimal digits in the string come from the same set. Some scripts (e.g.
+ Common, Arabic) have more than one set of decimal digits. This code does
+ not allow mixing sets, even within the same script. The vector called
+ PRIV(ucd_digit_sets)[] contains, in its first element, the number of
+ following elements, and then, in ascending order, the code points of the
+ '9' characters in every set of 10 digits. Each set is identified by the
+ offset in the vector of its '9' character. An initial check of the first
+ value picks up ASCII digits quickly. Otherwise, a binary chop is used. */
+
+ if (ucd->chartype == ucp_Nd)
+ {
+ uint32_t digitset;
+
+ if (c <= PRIV(ucd_digit_sets)[1]) digitset = 1; else
+ {
+ int mid;
+ int bot = 1;
+ int top = PRIV(ucd_digit_sets)[0];
+ for (;;)
+ {
+ if (top <= bot + 1) /* <= rather than == is paranoia */
+ {
+ digitset = top;
+ break;
+ }
+ mid = (top + bot) / 2;
+ if (c <= PRIV(ucd_digit_sets)[mid]) top = mid; else bot = mid;
+ }
+ }
+
+ /* A required value of 0 means "unset". */
+
+ if (require_digitset == 0) require_digitset = digitset;
+ else if (digitset != require_digitset) return FALSE;
+ } /* End digit handling */
+
+ /* If we haven't yet got to the end, pick up the next character. */
+
+ if (ptr >= endptr) return TRUE;
+ GETCHARINCTEST(c, ptr);
+ } /* End checking loop */
+
+#else /* NOT SUPPORT_UNICODE */
+(void)ptr;
+(void)endptr;
+(void)utf;
+return TRUE;
+#endif /* SUPPORT_UNICODE */
+}
+
+/* End of pcre2_script_run.c */
diff --git a/contrib/libs/pcre2/src/pcre2_serialize.c b/contrib/libs/pcre2/src/pcre2_serialize.c
new file mode 100644
index 00000000000..6d5c44b038b
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_serialize.c
@@ -0,0 +1,286 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2020 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains functions for serializing and deserializing
+a sequence of compiled codes. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+
+#include "pcre2_internal.h"
+
+/* Magic number to provide a small check against being handed junk. */
+
+#define SERIALIZED_DATA_MAGIC 0x50523253u
+
+/* Deserialization is limited to the current PCRE version and
+character width. */
+
+#define SERIALIZED_DATA_VERSION \
+ ((PCRE2_MAJOR) | ((PCRE2_MINOR) << 16))
+
+#define SERIALIZED_DATA_CONFIG \
+ (sizeof(PCRE2_UCHAR) | ((sizeof(void*)) << 8) | ((sizeof(PCRE2_SIZE)) << 16))
+
+
+
+/*************************************************
+* Serialize compiled patterns *
+*************************************************/
+
+PCRE2_EXP_DEFN int32_t PCRE2_CALL_CONVENTION
+pcre2_serialize_encode(const pcre2_code **codes, int32_t number_of_codes,
+ uint8_t **serialized_bytes, PCRE2_SIZE *serialized_size,
+ pcre2_general_context *gcontext)
+{
+uint8_t *bytes;
+uint8_t *dst_bytes;
+int32_t i;
+PCRE2_SIZE total_size;
+const pcre2_real_code *re;
+const uint8_t *tables;
+pcre2_serialized_data *data;
+
+const pcre2_memctl *memctl = (gcontext != NULL) ?
+ &gcontext->memctl : &PRIV(default_compile_context).memctl;
+
+if (codes == NULL || serialized_bytes == NULL || serialized_size == NULL)
+ return PCRE2_ERROR_NULL;
+
+if (number_of_codes <= 0) return PCRE2_ERROR_BADDATA;
+
+/* Compute total size. */
+total_size = sizeof(pcre2_serialized_data) + TABLES_LENGTH;
+tables = NULL;
+
+for (i = 0; i < number_of_codes; i++)
+ {
+ if (codes[i] == NULL) return PCRE2_ERROR_NULL;
+ re = (const pcre2_real_code *)(codes[i]);
+ if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC;
+ if (tables == NULL)
+ tables = re->tables;
+ else if (tables != re->tables)
+ return PCRE2_ERROR_MIXEDTABLES;
+ total_size += re->blocksize;
+ }
+
+/* Initialize the byte stream. */
+bytes = memctl->malloc(total_size + sizeof(pcre2_memctl), memctl->memory_data);
+if (bytes == NULL) return PCRE2_ERROR_NOMEMORY;
+
+/* The controller is stored as a hidden parameter. */
+memcpy(bytes, memctl, sizeof(pcre2_memctl));
+bytes += sizeof(pcre2_memctl);
+
+data = (pcre2_serialized_data *)bytes;
+data->magic = SERIALIZED_DATA_MAGIC;
+data->version = SERIALIZED_DATA_VERSION;
+data->config = SERIALIZED_DATA_CONFIG;
+data->number_of_codes = number_of_codes;
+
+/* Copy all compiled code data. */
+dst_bytes = bytes + sizeof(pcre2_serialized_data);
+memcpy(dst_bytes, tables, TABLES_LENGTH);
+dst_bytes += TABLES_LENGTH;
+
+for (i = 0; i < number_of_codes; i++)
+ {
+ re = (const pcre2_real_code *)(codes[i]);
+ (void)memcpy(dst_bytes, (char *)re, re->blocksize);
+
+ /* Certain fields in the compiled code block are re-set during
+ deserialization. In order to ensure that the serialized data stream is always
+ the same for the same pattern, set them to zero here. We can't assume the
+ copy of the pattern is correctly aligned for accessing the fields as part of
+ a structure. Note the use of sizeof(void *) in the second of these, to
+ specify the size of a pointer. If sizeof(uint8_t *) is used (tables is a
+ pointer to uint8_t), gcc gives a warning because the first argument is also a
+ pointer to uint8_t. Casting the first argument to (void *) can stop this, but
+ it didn't stop Coverity giving the same complaint. */
+
+ (void)memset(dst_bytes + offsetof(pcre2_real_code, memctl), 0,
+ sizeof(pcre2_memctl));
+ (void)memset(dst_bytes + offsetof(pcre2_real_code, tables), 0,
+ sizeof(void *));
+ (void)memset(dst_bytes + offsetof(pcre2_real_code, executable_jit), 0,
+ sizeof(void *));
+
+ dst_bytes += re->blocksize;
+ }
+
+*serialized_bytes = bytes;
+*serialized_size = total_size;
+return number_of_codes;
+}
+
+
+/*************************************************
+* Deserialize compiled patterns *
+*************************************************/
+
+PCRE2_EXP_DEFN int32_t PCRE2_CALL_CONVENTION
+pcre2_serialize_decode(pcre2_code **codes, int32_t number_of_codes,
+ const uint8_t *bytes, pcre2_general_context *gcontext)
+{
+const pcre2_serialized_data *data = (const pcre2_serialized_data *)bytes;
+const pcre2_memctl *memctl = (gcontext != NULL) ?
+ &gcontext->memctl : &PRIV(default_compile_context).memctl;
+
+const uint8_t *src_bytes;
+pcre2_real_code *dst_re;
+uint8_t *tables;
+int32_t i, j;
+
+/* Sanity checks. */
+
+if (data == NULL || codes == NULL) return PCRE2_ERROR_NULL;
+if (number_of_codes <= 0) return PCRE2_ERROR_BADDATA;
+if (data->number_of_codes <= 0) return PCRE2_ERROR_BADSERIALIZEDDATA;
+if (data->magic != SERIALIZED_DATA_MAGIC) return PCRE2_ERROR_BADMAGIC;
+if (data->version != SERIALIZED_DATA_VERSION) return PCRE2_ERROR_BADMODE;
+if (data->config != SERIALIZED_DATA_CONFIG) return PCRE2_ERROR_BADMODE;
+
+if (number_of_codes > data->number_of_codes)
+ number_of_codes = data->number_of_codes;
+
+src_bytes = bytes + sizeof(pcre2_serialized_data);
+
+/* Decode tables. The reference count for the tables is stored immediately
+following them. */
+
+tables = memctl->malloc(TABLES_LENGTH + sizeof(PCRE2_SIZE), memctl->memory_data);
+if (tables == NULL) return PCRE2_ERROR_NOMEMORY;
+
+memcpy(tables, src_bytes, TABLES_LENGTH);
+*(PCRE2_SIZE *)(tables + TABLES_LENGTH) = number_of_codes;
+src_bytes += TABLES_LENGTH;
+
+/* Decode the byte stream. We must not try to read the size from the compiled
+code block in the stream, because it might be unaligned, which causes errors on
+hardware such as Sparc-64 that doesn't like unaligned memory accesses. The type
+of the blocksize field is given its own name to ensure that it is the same here
+as in the block. */
+
+for (i = 0; i < number_of_codes; i++)
+ {
+ CODE_BLOCKSIZE_TYPE blocksize;
+ memcpy(&blocksize, src_bytes + offsetof(pcre2_real_code, blocksize),
+ sizeof(CODE_BLOCKSIZE_TYPE));
+ if (blocksize <= sizeof(pcre2_real_code))
+ return PCRE2_ERROR_BADSERIALIZEDDATA;
+
+ /* The allocator provided by gcontext replaces the original one. */
+
+ dst_re = (pcre2_real_code *)PRIV(memctl_malloc)(blocksize,
+ (pcre2_memctl *)gcontext);
+ if (dst_re == NULL)
+ {
+ memctl->free(tables, memctl->memory_data);
+ for (j = 0; j < i; j++)
+ {
+ memctl->free(codes[j], memctl->memory_data);
+ codes[j] = NULL;
+ }
+ return PCRE2_ERROR_NOMEMORY;
+ }
+
+ /* The new allocator must be preserved. */
+
+ memcpy(((uint8_t *)dst_re) + sizeof(pcre2_memctl),
+ src_bytes + sizeof(pcre2_memctl), blocksize - sizeof(pcre2_memctl));
+ if (dst_re->magic_number != MAGIC_NUMBER ||
+ dst_re->name_entry_size > MAX_NAME_SIZE + IMM2_SIZE + 1 ||
+ dst_re->name_count > MAX_NAME_COUNT)
+ {
+ memctl->free(dst_re, memctl->memory_data);
+ return PCRE2_ERROR_BADSERIALIZEDDATA;
+ }
+
+ /* At the moment only one table is supported. */
+
+ dst_re->tables = tables;
+ dst_re->executable_jit = NULL;
+ dst_re->flags |= PCRE2_DEREF_TABLES;
+
+ codes[i] = dst_re;
+ src_bytes += blocksize;
+ }
+
+return number_of_codes;
+}
+
+
+/*************************************************
+* Get the number of serialized patterns *
+*************************************************/
+
+PCRE2_EXP_DEFN int32_t PCRE2_CALL_CONVENTION
+pcre2_serialize_get_number_of_codes(const uint8_t *bytes)
+{
+const pcre2_serialized_data *data = (const pcre2_serialized_data *)bytes;
+
+if (data == NULL) return PCRE2_ERROR_NULL;
+if (data->magic != SERIALIZED_DATA_MAGIC) return PCRE2_ERROR_BADMAGIC;
+if (data->version != SERIALIZED_DATA_VERSION) return PCRE2_ERROR_BADMODE;
+if (data->config != SERIALIZED_DATA_CONFIG) return PCRE2_ERROR_BADMODE;
+
+return data->number_of_codes;
+}
+
+
+/*************************************************
+* Free the allocated stream *
+*************************************************/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_serialize_free(uint8_t *bytes)
+{
+if (bytes != NULL)
+ {
+ pcre2_memctl *memctl = (pcre2_memctl *)(bytes - sizeof(pcre2_memctl));
+ memctl->free(memctl, memctl->memory_data);
+ }
+}
+
+/* End of pcre2_serialize.c */
diff --git a/contrib/libs/pcre2/src/pcre2_string_utils.c b/contrib/libs/pcre2/src/pcre2_string_utils.c
new file mode 100644
index 00000000000..3230919d519
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_string_utils.c
@@ -0,0 +1,237 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2018-2021 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains internal functions for comparing and finding the length
+of strings. These are used instead of strcmp() etc because the standard
+functions work only on 8-bit data. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+/*************************************************
+* Emulated memmove() for systems without it *
+*************************************************/
+
+/* This function can make use of bcopy() if it is available. Otherwise do it by
+steam, as there some non-Unix environments that lack both memmove() and
+bcopy(). */
+
+#if !defined(VPCOMPAT) && !defined(HAVE_MEMMOVE)
+void *
+PRIV(memmove)(void *d, const void *s, size_t n)
+{
+#ifdef HAVE_BCOPY
+bcopy(s, d, n);
+return d;
+#else
+size_t i;
+unsigned char *dest = (unsigned char *)d;
+const unsigned char *src = (const unsigned char *)s;
+if (dest > src)
+ {
+ dest += n;
+ src += n;
+ for (i = 0; i < n; ++i) *(--dest) = *(--src);
+ return (void *)dest;
+ }
+else
+ {
+ for (i = 0; i < n; ++i) *dest++ = *src++;
+ return (void *)(dest - n);
+ }
+#endif /* not HAVE_BCOPY */
+}
+#endif /* not VPCOMPAT && not HAVE_MEMMOVE */
+
+
+/*************************************************
+* Compare two zero-terminated PCRE2 strings *
+*************************************************/
+
+/*
+Arguments:
+ str1 first string
+ str2 second string
+
+Returns: 0, 1, or -1
+*/
+
+int
+PRIV(strcmp)(PCRE2_SPTR str1, PCRE2_SPTR str2)
+{
+PCRE2_UCHAR c1, c2;
+while (*str1 != '\0' || *str2 != '\0')
+ {
+ c1 = *str1++;
+ c2 = *str2++;
+ if (c1 != c2) return ((c1 > c2) << 1) - 1;
+ }
+return 0;
+}
+
+
+/*************************************************
+* Compare zero-terminated PCRE2 & 8-bit strings *
+*************************************************/
+
+/* As the 8-bit string is almost always a literal, its type is specified as
+const char *.
+
+Arguments:
+ str1 first string
+ str2 second string
+
+Returns: 0, 1, or -1
+*/
+
+int
+PRIV(strcmp_c8)(PCRE2_SPTR str1, const char *str2)
+{
+PCRE2_UCHAR c1, c2;
+while (*str1 != '\0' || *str2 != '\0')
+ {
+ c1 = *str1++;
+ c2 = *str2++;
+ if (c1 != c2) return ((c1 > c2) << 1) - 1;
+ }
+return 0;
+}
+
+
+/*************************************************
+* Compare two PCRE2 strings, given a length *
+*************************************************/
+
+/*
+Arguments:
+ str1 first string
+ str2 second string
+ len the length
+
+Returns: 0, 1, or -1
+*/
+
+int
+PRIV(strncmp)(PCRE2_SPTR str1, PCRE2_SPTR str2, size_t len)
+{
+PCRE2_UCHAR c1, c2;
+for (; len > 0; len--)
+ {
+ c1 = *str1++;
+ c2 = *str2++;
+ if (c1 != c2) return ((c1 > c2) << 1) - 1;
+ }
+return 0;
+}
+
+
+/*************************************************
+* Compare PCRE2 string to 8-bit string by length *
+*************************************************/
+
+/* As the 8-bit string is almost always a literal, its type is specified as
+const char *.
+
+Arguments:
+ str1 first string
+ str2 second string
+ len the length
+
+Returns: 0, 1, or -1
+*/
+
+int
+PRIV(strncmp_c8)(PCRE2_SPTR str1, const char *str2, size_t len)
+{
+PCRE2_UCHAR c1, c2;
+for (; len > 0; len--)
+ {
+ c1 = *str1++;
+ c2 = *str2++;
+ if (c1 != c2) return ((c1 > c2) << 1) - 1;
+ }
+return 0;
+}
+
+
+/*************************************************
+* Find the length of a PCRE2 string *
+*************************************************/
+
+/*
+Argument: the string
+Returns: the length
+*/
+
+PCRE2_SIZE
+PRIV(strlen)(PCRE2_SPTR str)
+{
+PCRE2_SIZE c = 0;
+while (*str++ != 0) c++;
+return c;
+}
+
+
+/*************************************************
+* Copy 8-bit 0-terminated string to PCRE2 string *
+*************************************************/
+
+/* Arguments:
+ str1 buffer to receive the string
+ str2 8-bit string to be copied
+
+Returns: the number of code units used (excluding trailing zero)
+*/
+
+PCRE2_SIZE
+PRIV(strcpy_c8)(PCRE2_UCHAR *str1, const char *str2)
+{
+PCRE2_UCHAR *t = str1;
+while (*str2 != 0) *t++ = *str2++;
+*t = 0;
+return t - str1;
+}
+
+/* End of pcre2_string_utils.c */
diff --git a/contrib/libs/pcre2/src/pcre2_study.c b/contrib/libs/pcre2/src/pcre2_study.c
new file mode 100644
index 00000000000..b259d246ebc
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_study.c
@@ -0,0 +1,1825 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2021 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains functions for scanning a compiled pattern and
+collecting data (e.g. minimum matching length). */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+/* The maximum remembered capturing brackets minimum. */
+
+#define MAX_CACHE_BACKREF 128
+
+/* Set a bit in the starting code unit bit map. */
+
+#define SET_BIT(c) re->start_bitmap[(c)/8] |= (1u << ((c)&7))
+
+/* Returns from set_start_bits() */
+
+enum { SSB_FAIL, SSB_DONE, SSB_CONTINUE, SSB_UNKNOWN, SSB_TOODEEP };
+
+
+/*************************************************
+* Find the minimum subject length for a group *
+*************************************************/
+
+/* Scan a parenthesized group and compute the minimum length of subject that
+is needed to match it. This is a lower bound; it does not mean there is a
+string of that length that matches. In UTF mode, the result is in characters
+rather than code units. The field in a compiled pattern for storing the minimum
+length is 16-bits long (on the grounds that anything longer than that is
+pathological), so we give up when we reach that amount. This also means that
+integer overflow for really crazy patterns cannot happen.
+
+Backreference minimum lengths are cached to speed up multiple references. This
+function is called only when the highest back reference in the pattern is less
+than or equal to MAX_CACHE_BACKREF, which is one less than the size of the
+caching vector. The zeroth element contains the number of the highest set
+value.
+
+Arguments:
+ re compiled pattern block
+ code pointer to start of group (the bracket)
+ startcode pointer to start of the whole pattern's code
+ utf UTF flag
+ recurses chain of recurse_check to catch mutual recursion
+ countptr pointer to call count (to catch over complexity)
+ backref_cache vector for caching back references.
+
+This function is no longer called when the pattern contains (*ACCEPT); however,
+the old code for returning -1 is retained, just in case.
+
+Returns: the minimum length
+ -1 \C in UTF-8 mode
+ or (*ACCEPT)
+ or pattern too complicated
+ -2 internal error (missing capturing bracket)
+ -3 internal error (opcode not listed)
+*/
+
+static int
+find_minlength(const pcre2_real_code *re, PCRE2_SPTR code,
+ PCRE2_SPTR startcode, BOOL utf, recurse_check *recurses, int *countptr,
+ int *backref_cache)
+{
+int length = -1;
+int branchlength = 0;
+int prev_cap_recno = -1;
+int prev_cap_d = 0;
+int prev_recurse_recno = -1;
+int prev_recurse_d = 0;
+uint32_t once_fudge = 0;
+BOOL had_recurse = FALSE;
+BOOL dupcapused = (re->flags & PCRE2_DUPCAPUSED) != 0;
+PCRE2_SPTR nextbranch = code + GET(code, 1);
+PCRE2_UCHAR *cc = (PCRE2_UCHAR *)code + 1 + LINK_SIZE;
+recurse_check this_recurse;
+
+/* If this is a "could be empty" group, its minimum length is 0. */
+
+if (*code >= OP_SBRA && *code <= OP_SCOND) return 0;
+
+/* Skip over capturing bracket number */
+
+if (*code == OP_CBRA || *code == OP_CBRAPOS) cc += IMM2_SIZE;
+
+/* A large and/or complex regex can take too long to process. */
+
+if ((*countptr)++ > 1000) return -1;
+
+/* Scan along the opcodes for this branch. If we get to the end of the branch,
+check the length against that of the other branches. If the accumulated length
+passes 16-bits, reset to that value and skip the rest of the branch. */
+
+for (;;)
+ {
+ int d, min, recno;
+ PCRE2_UCHAR op, *cs, *ce;
+
+ if (branchlength >= UINT16_MAX)
+ {
+ branchlength = UINT16_MAX;
+ cc = (PCRE2_UCHAR *)nextbranch;
+ }
+
+ op = *cc;
+ switch (op)
+ {
+ case OP_COND:
+ case OP_SCOND:
+
+ /* If there is only one branch in a condition, the implied branch has zero
+ length, so we don't add anything. This covers the DEFINE "condition"
+ automatically. If there are two branches we can treat it the same as any
+ other non-capturing subpattern. */
+
+ cs = cc + GET(cc, 1);
+ if (*cs != OP_ALT)
+ {
+ cc = cs + 1 + LINK_SIZE;
+ break;
+ }
+ goto PROCESS_NON_CAPTURE;
+
+ case OP_BRA:
+ /* There's a special case of OP_BRA, when it is wrapped round a repeated
+ OP_RECURSE. We'd like to process the latter at this level so that
+ remembering the value works for repeated cases. So we do nothing, but
+ set a fudge value to skip over the OP_KET after the recurse. */
+
+ if (cc[1+LINK_SIZE] == OP_RECURSE && cc[2*(1+LINK_SIZE)] == OP_KET)
+ {
+ once_fudge = 1 + LINK_SIZE;
+ cc += 1 + LINK_SIZE;
+ break;
+ }
+ /* Fall through */
+
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_SBRA:
+ case OP_BRAPOS:
+ case OP_SBRAPOS:
+ PROCESS_NON_CAPTURE:
+ d = find_minlength(re, cc, startcode, utf, recurses, countptr,
+ backref_cache);
+ if (d < 0) return d;
+ branchlength += d;
+ do cc += GET(cc, 1); while (*cc == OP_ALT);
+ cc += 1 + LINK_SIZE;
+ break;
+
+ /* To save time for repeated capturing subpatterns, we remember the
+ length of the previous one. Unfortunately we can't do the same for
+ the unnumbered ones above. Nor can we do this if (?| is present in the
+ pattern because captures with the same number are not then identical. */
+
+ case OP_CBRA:
+ case OP_SCBRA:
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ recno = (int)GET2(cc, 1+LINK_SIZE);
+ if (dupcapused || recno != prev_cap_recno)
+ {
+ prev_cap_recno = recno;
+ prev_cap_d = find_minlength(re, cc, startcode, utf, recurses, countptr,
+ backref_cache);
+ if (prev_cap_d < 0) return prev_cap_d;
+ }
+ branchlength += prev_cap_d;
+ do cc += GET(cc, 1); while (*cc == OP_ALT);
+ cc += 1 + LINK_SIZE;
+ break;
+
+ /* ACCEPT makes things far too complicated; we have to give up. In fact,
+ from 10.34 onwards, if a pattern contains (*ACCEPT), this function is not
+ used. However, leave the code in place, just in case. */
+
+ case OP_ACCEPT:
+ case OP_ASSERT_ACCEPT:
+ return -1;
+
+ /* Reached end of a branch; if it's a ket it is the end of a nested
+ call. If it's ALT it is an alternation in a nested call. If it is END it's
+ the end of the outer call. All can be handled by the same code. If the
+ length of any branch is zero, there is no need to scan any subsequent
+ branches. */
+
+ case OP_ALT:
+ case OP_KET:
+ case OP_KETRMAX:
+ case OP_KETRMIN:
+ case OP_KETRPOS:
+ case OP_END:
+ if (length < 0 || (!had_recurse && branchlength < length))
+ length = branchlength;
+ if (op != OP_ALT || length == 0) return length;
+ nextbranch = cc + GET(cc, 1);
+ cc += 1 + LINK_SIZE;
+ branchlength = 0;
+ had_recurse = FALSE;
+ break;
+
+ /* Skip over assertive subpatterns */
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ do cc += GET(cc, 1); while (*cc == OP_ALT);
+ /* Fall through */
+
+ /* Skip over things that don't match chars */
+
+ case OP_REVERSE:
+ case OP_CREF:
+ case OP_DNCREF:
+ case OP_RREF:
+ case OP_DNRREF:
+ case OP_FALSE:
+ case OP_TRUE:
+ case OP_CALLOUT:
+ case OP_SOD:
+ case OP_SOM:
+ case OP_EOD:
+ case OP_EODN:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_DOLL:
+ case OP_DOLLM:
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ cc += PRIV(OP_lengths)[*cc];
+ break;
+
+ case OP_CALLOUT_STR:
+ cc += GET(cc, 1 + 2*LINK_SIZE);
+ break;
+
+ /* Skip over a subpattern that has a {0} or {0,x} quantifier */
+
+ case OP_BRAZERO:
+ case OP_BRAMINZERO:
+ case OP_BRAPOSZERO:
+ case OP_SKIPZERO:
+ cc += PRIV(OP_lengths)[*cc];
+ do cc += GET(cc, 1); while (*cc == OP_ALT);
+ cc += 1 + LINK_SIZE;
+ break;
+
+ /* Handle literal characters and + repetitions */
+
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ case OP_PLUS:
+ case OP_PLUSI:
+ case OP_MINPLUS:
+ case OP_MINPLUSI:
+ case OP_POSPLUS:
+ case OP_POSPLUSI:
+ case OP_NOTPLUS:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUS:
+ case OP_NOTMINPLUSI:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSPLUSI:
+ branchlength++;
+ cc += 2;
+#ifdef SUPPORT_UNICODE
+ if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEPOSPLUS:
+ branchlength++;
+ cc += (cc[1] == OP_PROP || cc[1] == OP_NOTPROP)? 4 : 2;
+ break;
+
+ /* Handle exact repetitions. The count is already in characters, but we
+ may need to skip over a multibyte character in UTF mode. */
+
+ case OP_EXACT:
+ case OP_EXACTI:
+ case OP_NOTEXACT:
+ case OP_NOTEXACTI:
+ branchlength += GET2(cc,1);
+ cc += 2 + IMM2_SIZE;
+#ifdef SUPPORT_UNICODE
+ if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ case OP_TYPEEXACT:
+ branchlength += GET2(cc,1);
+ cc += 2 + IMM2_SIZE + ((cc[1 + IMM2_SIZE] == OP_PROP
+ || cc[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0);
+ break;
+
+ /* Handle single-char non-literal matchers */
+
+ case OP_PROP:
+ case OP_NOTPROP:
+ cc += 2;
+ /* Fall through */
+
+ case OP_NOT_DIGIT:
+ case OP_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ case OP_WORDCHAR:
+ case OP_ANY:
+ case OP_ALLANY:
+ case OP_EXTUNI:
+ case OP_HSPACE:
+ case OP_NOT_HSPACE:
+ case OP_VSPACE:
+ case OP_NOT_VSPACE:
+ branchlength++;
+ cc++;
+ break;
+
+ /* "Any newline" might match two characters, but it also might match just
+ one. */
+
+ case OP_ANYNL:
+ branchlength += 1;
+ cc++;
+ break;
+
+ /* The single-byte matcher means we can't proceed in UTF mode. (In
+ non-UTF mode \C will actually be turned into OP_ALLANY, so won't ever
+ appear, but leave the code, just in case.) */
+
+ case OP_ANYBYTE:
+#ifdef SUPPORT_UNICODE
+ if (utf) return -1;
+#endif
+ branchlength++;
+ cc++;
+ break;
+
+ /* For repeated character types, we have to test for \p and \P, which have
+ an extra two bytes of parameters. */
+
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSQUERY:
+ if (cc[1] == OP_PROP || cc[1] == OP_NOTPROP) cc += 2;
+ cc += PRIV(OP_lengths)[op];
+ break;
+
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEPOSUPTO:
+ if (cc[1 + IMM2_SIZE] == OP_PROP
+ || cc[1 + IMM2_SIZE] == OP_NOTPROP) cc += 2;
+ cc += PRIV(OP_lengths)[op];
+ break;
+
+ /* Check a class for variable quantification */
+
+ case OP_CLASS:
+ case OP_NCLASS:
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+ /* The original code caused an unsigned overflow in 64 bit systems,
+ so now we use a conditional statement. */
+ if (op == OP_XCLASS)
+ cc += GET(cc, 1);
+ else
+ cc += PRIV(OP_lengths)[OP_CLASS];
+#else
+ cc += PRIV(OP_lengths)[OP_CLASS];
+#endif
+
+ switch (*cc)
+ {
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRPOSPLUS:
+ branchlength++;
+ /* Fall through */
+
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSSTAR:
+ case OP_CRPOSQUERY:
+ cc++;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ branchlength += GET2(cc,1);
+ cc += 1 + 2 * IMM2_SIZE;
+ break;
+
+ default:
+ branchlength++;
+ break;
+ }
+ break;
+
+ /* Backreferences and subroutine calls (OP_RECURSE) are treated in the same
+ way: we find the minimum length for the subpattern. A recursion
+ (backreference or subroutine) causes an a flag to be set that causes the
+ length of this branch to be ignored. The logic is that a recursion can only
+ make sense if there is another alternative that stops the recursing. That
+ will provide the minimum length (when no recursion happens).
+
+ If PCRE2_MATCH_UNSET_BACKREF is set, a backreference to an unset bracket
+ matches an empty string (by default it causes a matching failure), so in
+ that case we must set the minimum length to zero.
+
+ For backreferenes, if duplicate numbers are present in the pattern we check
+ for a reference to a duplicate. If it is, we don't know which version will
+ be referenced, so we have to set the minimum length to zero. */
+
+ /* Duplicate named pattern back reference. */
+
+ case OP_DNREF:
+ case OP_DNREFI:
+ if (!dupcapused && (re->overall_options & PCRE2_MATCH_UNSET_BACKREF) == 0)
+ {
+ int count = GET2(cc, 1+IMM2_SIZE);
+ PCRE2_UCHAR *slot =
+ (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)) +
+ GET2(cc, 1) * re->name_entry_size;
+
+ d = INT_MAX;
+
+ /* Scan all groups with the same name; find the shortest. */
+
+ while (count-- > 0)
+ {
+ int dd, i;
+ recno = GET2(slot, 0);
+
+ if (recno <= backref_cache[0] && backref_cache[recno] >= 0)
+ dd = backref_cache[recno];
+ else
+ {
+ ce = cs = (PCRE2_UCHAR *)PRIV(find_bracket)(startcode, utf, recno);
+ if (cs == NULL) return -2;
+ do ce += GET(ce, 1); while (*ce == OP_ALT);
+
+ dd = 0;
+ if (!dupcapused ||
+ (PCRE2_UCHAR *)PRIV(find_bracket)(ce, utf, recno) == NULL)
+ {
+ if (cc > cs && cc < ce) /* Simple recursion */
+ {
+ had_recurse = TRUE;
+ }
+ else
+ {
+ recurse_check *r = recurses;
+ for (r = recurses; r != NULL; r = r->prev)
+ if (r->group == cs) break;
+ if (r != NULL) /* Mutual recursion */
+ {
+ had_recurse = TRUE;
+ }
+ else
+ {
+ this_recurse.prev = recurses; /* No recursion */
+ this_recurse.group = cs;
+ dd = find_minlength(re, cs, startcode, utf, &this_recurse,
+ countptr, backref_cache);
+ if (dd < 0) return dd;
+ }
+ }
+ }
+
+ backref_cache[recno] = dd;
+ for (i = backref_cache[0] + 1; i < recno; i++) backref_cache[i] = -1;
+ backref_cache[0] = recno;
+ }
+
+ if (dd < d) d = dd;
+ if (d <= 0) break; /* No point looking at any more */
+ slot += re->name_entry_size;
+ }
+ }
+ else d = 0;
+ cc += 1 + 2*IMM2_SIZE;
+ goto REPEAT_BACK_REFERENCE;
+
+ /* Single back reference by number. References by name are converted to by
+ number when there is no duplication. */
+
+ case OP_REF:
+ case OP_REFI:
+ recno = GET2(cc, 1);
+ if (recno <= backref_cache[0] && backref_cache[recno] >= 0)
+ d = backref_cache[recno];
+ else
+ {
+ int i;
+ d = 0;
+
+ if ((re->overall_options & PCRE2_MATCH_UNSET_BACKREF) == 0)
+ {
+ ce = cs = (PCRE2_UCHAR *)PRIV(find_bracket)(startcode, utf, recno);
+ if (cs == NULL) return -2;
+ do ce += GET(ce, 1); while (*ce == OP_ALT);
+
+ if (!dupcapused ||
+ (PCRE2_UCHAR *)PRIV(find_bracket)(ce, utf, recno) == NULL)
+ {
+ if (cc > cs && cc < ce) /* Simple recursion */
+ {
+ had_recurse = TRUE;
+ }
+ else
+ {
+ recurse_check *r = recurses;
+ for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break;
+ if (r != NULL) /* Mutual recursion */
+ {
+ had_recurse = TRUE;
+ }
+ else /* No recursion */
+ {
+ this_recurse.prev = recurses;
+ this_recurse.group = cs;
+ d = find_minlength(re, cs, startcode, utf, &this_recurse, countptr,
+ backref_cache);
+ if (d < 0) return d;
+ }
+ }
+ }
+ }
+
+ backref_cache[recno] = d;
+ for (i = backref_cache[0] + 1; i < recno; i++) backref_cache[i] = -1;
+ backref_cache[0] = recno;
+ }
+
+ cc += 1 + IMM2_SIZE;
+
+ /* Handle repeated back references */
+
+ REPEAT_BACK_REFERENCE:
+ switch (*cc)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSSTAR:
+ case OP_CRPOSQUERY:
+ min = 0;
+ cc++;
+ break;
+
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRPOSPLUS:
+ min = 1;
+ cc++;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ min = GET2(cc, 1);
+ cc += 1 + 2 * IMM2_SIZE;
+ break;
+
+ default:
+ min = 1;
+ break;
+ }
+
+ /* Take care not to overflow: (1) min and d are ints, so check that their
+ product is not greater than INT_MAX. (2) branchlength is limited to
+ UINT16_MAX (checked at the top of the loop). */
+
+ if ((d > 0 && (INT_MAX/d) < min) || UINT16_MAX - branchlength < min*d)
+ branchlength = UINT16_MAX;
+ else branchlength += min * d;
+ break;
+
+ /* Recursion always refers to the first occurrence of a subpattern with a
+ given number. Therefore, we can always make use of caching, even when the
+ pattern contains multiple subpatterns with the same number. */
+
+ case OP_RECURSE:
+ cs = ce = (PCRE2_UCHAR *)startcode + GET(cc, 1);
+ recno = GET2(cs, 1+LINK_SIZE);
+ if (recno == prev_recurse_recno)
+ {
+ branchlength += prev_recurse_d;
+ }
+ else
+ {
+ do ce += GET(ce, 1); while (*ce == OP_ALT);
+ if (cc > cs && cc < ce) /* Simple recursion */
+ had_recurse = TRUE;
+ else
+ {
+ recurse_check *r = recurses;
+ for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break;
+ if (r != NULL) /* Mutual recursion */
+ had_recurse = TRUE;
+ else
+ {
+ this_recurse.prev = recurses;
+ this_recurse.group = cs;
+ prev_recurse_d = find_minlength(re, cs, startcode, utf, &this_recurse,
+ countptr, backref_cache);
+ if (prev_recurse_d < 0) return prev_recurse_d;
+ prev_recurse_recno = recno;
+ branchlength += prev_recurse_d;
+ }
+ }
+ }
+ cc += 1 + LINK_SIZE + once_fudge;
+ once_fudge = 0;
+ break;
+
+ /* Anything else does not or need not match a character. We can get the
+ item's length from the table, but for those that can match zero occurrences
+ of a character, we must take special action for UTF-8 characters. As it
+ happens, the "NOT" versions of these opcodes are used at present only for
+ ASCII characters, so they could be omitted from this list. However, in
+ future that may change, so we include them here so as not to leave a
+ gotcha for a future maintainer. */
+
+ case OP_UPTO:
+ case OP_UPTOI:
+ case OP_NOTUPTO:
+ case OP_NOTUPTOI:
+ case OP_MINUPTO:
+ case OP_MINUPTOI:
+ case OP_NOTMINUPTO:
+ case OP_NOTMINUPTOI:
+ case OP_POSUPTO:
+ case OP_POSUPTOI:
+ case OP_NOTPOSUPTO:
+ case OP_NOTPOSUPTOI:
+
+ case OP_STAR:
+ case OP_STARI:
+ case OP_NOTSTAR:
+ case OP_NOTSTARI:
+ case OP_MINSTAR:
+ case OP_MINSTARI:
+ case OP_NOTMINSTAR:
+ case OP_NOTMINSTARI:
+ case OP_POSSTAR:
+ case OP_POSSTARI:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSSTARI:
+
+ case OP_QUERY:
+ case OP_QUERYI:
+ case OP_NOTQUERY:
+ case OP_NOTQUERYI:
+ case OP_MINQUERY:
+ case OP_MINQUERYI:
+ case OP_NOTMINQUERY:
+ case OP_NOTMINQUERYI:
+ case OP_POSQUERY:
+ case OP_POSQUERYI:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSQUERYI:
+
+ cc += PRIV(OP_lengths)[op];
+#ifdef SUPPORT_UNICODE
+ if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ /* Skip these, but we need to add in the name length. */
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_SKIP_ARG:
+ case OP_THEN_ARG:
+ cc += PRIV(OP_lengths)[op] + cc[1];
+ break;
+
+ /* The remaining opcodes are just skipped over. */
+
+ case OP_CLOSE:
+ case OP_COMMIT:
+ case OP_FAIL:
+ case OP_PRUNE:
+ case OP_SET_SOM:
+ case OP_SKIP:
+ case OP_THEN:
+ cc += PRIV(OP_lengths)[op];
+ break;
+
+ /* This should not occur: we list all opcodes explicitly so that when
+ new ones get added they are properly considered. */
+
+ default:
+ return -3;
+ }
+ }
+/* Control never gets here */
+}
+
+
+
+/*************************************************
+* Set a bit and maybe its alternate case *
+*************************************************/
+
+/* Given a character, set its first code unit's bit in the table, and also the
+corresponding bit for the other version of a letter if we are caseless.
+
+Arguments:
+ re points to the regex block
+ p points to the first code unit of the character
+ caseless TRUE if caseless
+ utf TRUE for UTF mode
+ ucp TRUE for UCP mode
+
+Returns: pointer after the character
+*/
+
+static PCRE2_SPTR
+set_table_bit(pcre2_real_code *re, PCRE2_SPTR p, BOOL caseless, BOOL utf,
+ BOOL ucp)
+{
+uint32_t c = *p++; /* First code unit */
+
+(void)utf; /* Stop compiler warnings when UTF not supported */
+(void)ucp;
+
+/* In 16-bit and 32-bit modes, code units greater than 0xff set the bit for
+0xff. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+if (c > 0xff) SET_BIT(0xff); else
+#endif
+
+SET_BIT(c);
+
+/* In UTF-8 or UTF-16 mode, pick up the remaining code units in order to find
+the end of the character, even when caseless. */
+
+#ifdef SUPPORT_UNICODE
+if (utf)
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (c >= 0xc0) GETUTF8INC(c, p);
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+ if ((c & 0xfc00) == 0xd800) GETUTF16INC(c, p);
+#endif
+ }
+#endif /* SUPPORT_UNICODE */
+
+/* If caseless, handle the other case of the character. */
+
+if (caseless)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf || ucp)
+ {
+ c = UCD_OTHERCASE(c);
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (utf)
+ {
+ PCRE2_UCHAR buff[6];
+ (void)PRIV(ord2utf)(c, buff);
+ SET_BIT(buff[0]);
+ }
+ else if (c < 256) SET_BIT(c);
+#else /* 16-bit or 32-bit mode */
+ if (c > 0xff) SET_BIT(0xff); else SET_BIT(c);
+#endif
+ }
+
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Not UTF or UCP */
+
+ if (MAX_255(c)) SET_BIT(re->tables[fcc_offset + c]);
+ }
+
+return p;
+}
+
+
+
+/*************************************************
+* Set bits for a positive character type *
+*************************************************/
+
+/* This function sets starting bits for a character type. In UTF-8 mode, we can
+only do a direct setting for bytes less than 128, as otherwise there can be
+confusion with bytes in the middle of UTF-8 characters. In a "traditional"
+environment, the tables will only recognize ASCII characters anyway, but in at
+least one Windows environment, some higher bytes bits were set in the tables.
+So we deal with that case by considering the UTF-8 encoding.
+
+Arguments:
+ re the regex block
+ cbit type the type of character wanted
+ table_limit 32 for non-UTF-8; 16 for UTF-8
+
+Returns: nothing
+*/
+
+static void
+set_type_bits(pcre2_real_code *re, int cbit_type, unsigned int table_limit)
+{
+uint32_t c;
+for (c = 0; c < table_limit; c++)
+ re->start_bitmap[c] |= re->tables[c+cbits_offset+cbit_type];
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+if (table_limit == 32) return;
+for (c = 128; c < 256; c++)
+ {
+ if ((re->tables[cbits_offset + c/8] & (1u << (c&7))) != 0)
+ {
+ PCRE2_UCHAR buff[6];
+ (void)PRIV(ord2utf)(c, buff);
+ SET_BIT(buff[0]);
+ }
+ }
+#endif /* UTF-8 */
+}
+
+
+/*************************************************
+* Set bits for a negative character type *
+*************************************************/
+
+/* This function sets starting bits for a negative character type such as \D.
+In UTF-8 mode, we can only do a direct setting for bytes less than 128, as
+otherwise there can be confusion with bytes in the middle of UTF-8 characters.
+Unlike in the positive case, where we can set appropriate starting bits for
+specific high-valued UTF-8 characters, in this case we have to set the bits for
+all high-valued characters. The lowest is 0xc2, but we overkill by starting at
+0xc0 (192) for simplicity.
+
+Arguments:
+ re the regex block
+ cbit type the type of character wanted
+ table_limit 32 for non-UTF-8; 16 for UTF-8
+
+Returns: nothing
+*/
+
+static void
+set_nottype_bits(pcre2_real_code *re, int cbit_type, unsigned int table_limit)
+{
+uint32_t c;
+for (c = 0; c < table_limit; c++)
+ re->start_bitmap[c] |= (uint8_t)(~(re->tables[c+cbits_offset+cbit_type]));
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+if (table_limit != 32) for (c = 24; c < 32; c++) re->start_bitmap[c] = 0xff;
+#endif
+}
+
+
+
+/*************************************************
+* Create bitmap of starting code units *
+*************************************************/
+
+/* This function scans a compiled unanchored expression recursively and
+attempts to build a bitmap of the set of possible starting code units whose
+values are less than 256. In 16-bit and 32-bit mode, values above 255 all cause
+the 255 bit to be set. When calling set[_not]_type_bits() in UTF-8 (sic) mode
+we pass a value of 16 rather than 32 as the final argument. (See comments in
+those functions for the reason.)
+
+The SSB_CONTINUE return is useful for parenthesized groups in patterns such as
+(a*)b where the group provides some optional starting code units but scanning
+must continue at the outer level to find at least one mandatory code unit. At
+the outermost level, this function fails unless the result is SSB_DONE.
+
+We restrict recursion (for nested groups) to 1000 to avoid stack overflow
+issues.
+
+Arguments:
+ re points to the compiled regex block
+ code points to an expression
+ utf TRUE if in UTF mode
+ ucp TRUE if in UCP mode
+ depthptr pointer to recurse depth
+
+Returns: SSB_FAIL => Failed to find any starting code units
+ SSB_DONE => Found mandatory starting code units
+ SSB_CONTINUE => Found optional starting code units
+ SSB_UNKNOWN => Hit an unrecognized opcode
+ SSB_TOODEEP => Recursion is too deep
+*/
+
+static int
+set_start_bits(pcre2_real_code *re, PCRE2_SPTR code, BOOL utf, BOOL ucp,
+ int *depthptr)
+{
+uint32_t c;
+int yield = SSB_DONE;
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+int table_limit = utf? 16:32;
+#else
+int table_limit = 32;
+#endif
+
+*depthptr += 1;
+if (*depthptr > 1000) return SSB_TOODEEP;
+
+do
+ {
+ BOOL try_next = TRUE;
+ PCRE2_SPTR tcode = code + 1 + LINK_SIZE;
+
+ if (*code == OP_CBRA || *code == OP_SCBRA ||
+ *code == OP_CBRAPOS || *code == OP_SCBRAPOS) tcode += IMM2_SIZE;
+
+ while (try_next) /* Loop for items in this branch */
+ {
+ int rc;
+ uint8_t *classmap = NULL;
+#ifdef SUPPORT_WIDE_CHARS
+ PCRE2_UCHAR xclassflags;
+#endif
+
+ switch(*tcode)
+ {
+ /* If we reach something we don't understand, it means a new opcode has
+ been created that hasn't been added to this function. Hopefully this
+ problem will be discovered during testing. */
+
+ default:
+ return SSB_UNKNOWN;
+
+ /* Fail for a valid opcode that implies no starting bits. */
+
+ case OP_ACCEPT:
+ case OP_ASSERT_ACCEPT:
+ case OP_ALLANY:
+ case OP_ANY:
+ case OP_ANYBYTE:
+ case OP_CIRCM:
+ case OP_CLOSE:
+ case OP_COMMIT:
+ case OP_COMMIT_ARG:
+ case OP_COND:
+ case OP_CREF:
+ case OP_FALSE:
+ case OP_TRUE:
+ case OP_DNCREF:
+ case OP_DNREF:
+ case OP_DNREFI:
+ case OP_DNRREF:
+ case OP_DOLL:
+ case OP_DOLLM:
+ case OP_END:
+ case OP_EOD:
+ case OP_EODN:
+ case OP_EXTUNI:
+ case OP_FAIL:
+ case OP_MARK:
+ case OP_NOT:
+ case OP_NOTEXACT:
+ case OP_NOTEXACTI:
+ case OP_NOTI:
+ case OP_NOTMINPLUS:
+ case OP_NOTMINPLUSI:
+ case OP_NOTMINQUERY:
+ case OP_NOTMINQUERYI:
+ case OP_NOTMINSTAR:
+ case OP_NOTMINSTARI:
+ case OP_NOTMINUPTO:
+ case OP_NOTMINUPTOI:
+ case OP_NOTPLUS:
+ case OP_NOTPLUSI:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSPLUSI:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSQUERYI:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSSTARI:
+ case OP_NOTPOSUPTO:
+ case OP_NOTPOSUPTOI:
+ case OP_NOTPROP:
+ case OP_NOTQUERY:
+ case OP_NOTQUERYI:
+ case OP_NOTSTAR:
+ case OP_NOTSTARI:
+ case OP_NOTUPTO:
+ case OP_NOTUPTOI:
+ case OP_NOT_HSPACE:
+ case OP_NOT_VSPACE:
+ case OP_PRUNE:
+ case OP_PRUNE_ARG:
+ case OP_RECURSE:
+ case OP_REF:
+ case OP_REFI:
+ case OP_REVERSE:
+ case OP_RREF:
+ case OP_SCOND:
+ case OP_SET_SOM:
+ case OP_SKIP:
+ case OP_SKIP_ARG:
+ case OP_SOD:
+ case OP_SOM:
+ case OP_THEN:
+ case OP_THEN_ARG:
+ return SSB_FAIL;
+
+ /* OP_CIRC happens only at the start of an anchored branch (multiline ^
+ uses OP_CIRCM). Skip over it. */
+
+ case OP_CIRC:
+ tcode += PRIV(OP_lengths)[OP_CIRC];
+ break;
+
+ /* A "real" property test implies no starting bits, but the fake property
+ PT_CLIST identifies a list of characters. These lists are short, as they
+ are used for characters with more than one "other case", so there is no
+ point in recognizing them for OP_NOTPROP. */
+
+ case OP_PROP:
+ if (tcode[1] != PT_CLIST) return SSB_FAIL;
+ {
+ const uint32_t *p = PRIV(ucd_caseless_sets) + tcode[2];
+ while ((c = *p++) < NOTACHAR)
+ {
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (utf)
+ {
+ PCRE2_UCHAR buff[6];
+ (void)PRIV(ord2utf)(c, buff);
+ c = buff[0];
+ }
+#endif
+ if (c > 0xff) SET_BIT(0xff); else SET_BIT(c);
+ }
+ }
+ try_next = FALSE;
+ break;
+
+ /* We can ignore word boundary tests. */
+
+ case OP_WORD_BOUNDARY:
+ case OP_NOT_WORD_BOUNDARY:
+ tcode++;
+ break;
+
+ /* If we hit a bracket or a positive lookahead assertion, recurse to set
+ bits from within the subpattern. If it can't find anything, we have to
+ give up. If it finds some mandatory character(s), we are done for this
+ branch. Otherwise, carry on scanning after the subpattern. */
+
+ case OP_BRA:
+ case OP_SBRA:
+ case OP_CBRA:
+ case OP_SCBRA:
+ case OP_BRAPOS:
+ case OP_SBRAPOS:
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_ASSERT:
+ case OP_ASSERT_NA:
+ rc = set_start_bits(re, tcode, utf, ucp, depthptr);
+ if (rc == SSB_DONE)
+ {
+ try_next = FALSE;
+ }
+ else if (rc == SSB_CONTINUE)
+ {
+ do tcode += GET(tcode, 1); while (*tcode == OP_ALT);
+ tcode += 1 + LINK_SIZE;
+ }
+ else return rc; /* FAIL, UNKNOWN, or TOODEEP */
+ break;
+
+ /* If we hit ALT or KET, it means we haven't found anything mandatory in
+ this branch, though we might have found something optional. For ALT, we
+ continue with the next alternative, but we have to arrange that the final
+ result from subpattern is SSB_CONTINUE rather than SSB_DONE. For KET,
+ return SSB_CONTINUE: if this is the top level, that indicates failure,
+ but after a nested subpattern, it causes scanning to continue. */
+
+ case OP_ALT:
+ yield = SSB_CONTINUE;
+ try_next = FALSE;
+ break;
+
+ case OP_KET:
+ case OP_KETRMAX:
+ case OP_KETRMIN:
+ case OP_KETRPOS:
+ return SSB_CONTINUE;
+
+ /* Skip over callout */
+
+ case OP_CALLOUT:
+ tcode += PRIV(OP_lengths)[OP_CALLOUT];
+ break;
+
+ case OP_CALLOUT_STR:
+ tcode += GET(tcode, 1 + 2*LINK_SIZE);
+ break;
+
+ /* Skip over lookbehind and negative lookahead assertions */
+
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERTBACK_NA:
+ do tcode += GET(tcode, 1); while (*tcode == OP_ALT);
+ tcode += 1 + LINK_SIZE;
+ break;
+
+ /* BRAZERO does the bracket, but carries on. */
+
+ case OP_BRAZERO:
+ case OP_BRAMINZERO:
+ case OP_BRAPOSZERO:
+ rc = set_start_bits(re, ++tcode, utf, ucp, depthptr);
+ if (rc == SSB_FAIL || rc == SSB_UNKNOWN || rc == SSB_TOODEEP) return rc;
+ do tcode += GET(tcode,1); while (*tcode == OP_ALT);
+ tcode += 1 + LINK_SIZE;
+ break;
+
+ /* SKIPZERO skips the bracket. */
+
+ case OP_SKIPZERO:
+ tcode++;
+ do tcode += GET(tcode,1); while (*tcode == OP_ALT);
+ tcode += 1 + LINK_SIZE;
+ break;
+
+ /* Single-char * or ? sets the bit and tries the next item */
+
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_POSSTAR:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_POSQUERY:
+ tcode = set_table_bit(re, tcode + 1, FALSE, utf, ucp);
+ break;
+
+ case OP_STARI:
+ case OP_MINSTARI:
+ case OP_POSSTARI:
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_POSQUERYI:
+ tcode = set_table_bit(re, tcode + 1, TRUE, utf, ucp);
+ break;
+
+ /* Single-char upto sets the bit and tries the next */
+
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_POSUPTO:
+ tcode = set_table_bit(re, tcode + 1 + IMM2_SIZE, FALSE, utf, ucp);
+ break;
+
+ case OP_UPTOI:
+ case OP_MINUPTOI:
+ case OP_POSUPTOI:
+ tcode = set_table_bit(re, tcode + 1 + IMM2_SIZE, TRUE, utf, ucp);
+ break;
+
+ /* At least one single char sets the bit and stops */
+
+ case OP_EXACT:
+ tcode += IMM2_SIZE;
+ /* Fall through */
+ case OP_CHAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_POSPLUS:
+ (void)set_table_bit(re, tcode + 1, FALSE, utf, ucp);
+ try_next = FALSE;
+ break;
+
+ case OP_EXACTI:
+ tcode += IMM2_SIZE;
+ /* Fall through */
+ case OP_CHARI:
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_POSPLUSI:
+ (void)set_table_bit(re, tcode + 1, TRUE, utf, ucp);
+ try_next = FALSE;
+ break;
+
+ /* Special spacing and line-terminating items. These recognize specific
+ lists of characters. The difference between VSPACE and ANYNL is that the
+ latter can match the two-character CRLF sequence, but that is not
+ relevant for finding the first character, so their code here is
+ identical. */
+
+ case OP_HSPACE:
+ SET_BIT(CHAR_HT);
+ SET_BIT(CHAR_SPACE);
+
+ /* For the 16-bit and 32-bit libraries (which can never be EBCDIC), set
+ the bits for 0xA0 and for code units >= 255, independently of UTF. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ SET_BIT(0xA0);
+ SET_BIT(0xFF);
+#else
+ /* For the 8-bit library in UTF-8 mode, set the bits for the first code
+ units of horizontal space characters. */
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ SET_BIT(0xC2); /* For U+00A0 */
+ SET_BIT(0xE1); /* For U+1680, U+180E */
+ SET_BIT(0xE2); /* For U+2000 - U+200A, U+202F, U+205F */
+ SET_BIT(0xE3); /* For U+3000 */
+ }
+ else
+#endif
+ /* For the 8-bit library not in UTF-8 mode, set the bit for 0xA0, unless
+ the code is EBCDIC. */
+ {
+#ifndef EBCDIC
+ SET_BIT(0xA0);
+#endif /* Not EBCDIC */
+ }
+#endif /* 8-bit support */
+
+ try_next = FALSE;
+ break;
+
+ case OP_ANYNL:
+ case OP_VSPACE:
+ SET_BIT(CHAR_LF);
+ SET_BIT(CHAR_VT);
+ SET_BIT(CHAR_FF);
+ SET_BIT(CHAR_CR);
+
+ /* For the 16-bit and 32-bit libraries (which can never be EBCDIC), set
+ the bits for NEL and for code units >= 255, independently of UTF. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ SET_BIT(CHAR_NEL);
+ SET_BIT(0xFF);
+#else
+ /* For the 8-bit library in UTF-8 mode, set the bits for the first code
+ units of vertical space characters. */
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ SET_BIT(0xC2); /* For U+0085 (NEL) */
+ SET_BIT(0xE2); /* For U+2028, U+2029 */
+ }
+ else
+#endif
+ /* For the 8-bit library not in UTF-8 mode, set the bit for NEL. */
+ {
+ SET_BIT(CHAR_NEL);
+ }
+#endif /* 8-bit support */
+
+ try_next = FALSE;
+ break;
+
+ /* Single character types set the bits and stop. Note that if PCRE2_UCP
+ is set, we do not see these opcodes because \d etc are converted to
+ properties. Therefore, these apply in the case when only characters less
+ than 256 are recognized to match the types. */
+
+ case OP_NOT_DIGIT:
+ set_nottype_bits(re, cbit_digit, table_limit);
+ try_next = FALSE;
+ break;
+
+ case OP_DIGIT:
+ set_type_bits(re, cbit_digit, table_limit);
+ try_next = FALSE;
+ break;
+
+ case OP_NOT_WHITESPACE:
+ set_nottype_bits(re, cbit_space, table_limit);
+ try_next = FALSE;
+ break;
+
+ case OP_WHITESPACE:
+ set_type_bits(re, cbit_space, table_limit);
+ try_next = FALSE;
+ break;
+
+ case OP_NOT_WORDCHAR:
+ set_nottype_bits(re, cbit_word, table_limit);
+ try_next = FALSE;
+ break;
+
+ case OP_WORDCHAR:
+ set_type_bits(re, cbit_word, table_limit);
+ try_next = FALSE;
+ break;
+
+ /* One or more character type fudges the pointer and restarts, knowing
+ it will hit a single character type and stop there. */
+
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEPOSPLUS:
+ tcode++;
+ break;
+
+ case OP_TYPEEXACT:
+ tcode += 1 + IMM2_SIZE;
+ break;
+
+ /* Zero or more repeats of character types set the bits and then
+ try again. */
+
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEPOSUPTO:
+ tcode += IMM2_SIZE; /* Fall through */
+
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEPOSQUERY:
+ switch(tcode[1])
+ {
+ default:
+ case OP_ANY:
+ case OP_ALLANY:
+ return SSB_FAIL;
+
+ case OP_HSPACE:
+ SET_BIT(CHAR_HT);
+ SET_BIT(CHAR_SPACE);
+
+ /* For the 16-bit and 32-bit libraries (which can never be EBCDIC), set
+ the bits for 0xA0 and for code units >= 255, independently of UTF. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ SET_BIT(0xA0);
+ SET_BIT(0xFF);
+#else
+ /* For the 8-bit library in UTF-8 mode, set the bits for the first code
+ units of horizontal space characters. */
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ SET_BIT(0xC2); /* For U+00A0 */
+ SET_BIT(0xE1); /* For U+1680, U+180E */
+ SET_BIT(0xE2); /* For U+2000 - U+200A, U+202F, U+205F */
+ SET_BIT(0xE3); /* For U+3000 */
+ }
+ else
+#endif
+ /* For the 8-bit library not in UTF-8 mode, set the bit for 0xA0, unless
+ the code is EBCDIC. */
+ {
+#ifndef EBCDIC
+ SET_BIT(0xA0);
+#endif /* Not EBCDIC */
+ }
+#endif /* 8-bit support */
+ break;
+
+ case OP_ANYNL:
+ case OP_VSPACE:
+ SET_BIT(CHAR_LF);
+ SET_BIT(CHAR_VT);
+ SET_BIT(CHAR_FF);
+ SET_BIT(CHAR_CR);
+
+ /* For the 16-bit and 32-bit libraries (which can never be EBCDIC), set
+ the bits for NEL and for code units >= 255, independently of UTF. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ SET_BIT(CHAR_NEL);
+ SET_BIT(0xFF);
+#else
+ /* For the 8-bit library in UTF-8 mode, set the bits for the first code
+ units of vertical space characters. */
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ SET_BIT(0xC2); /* For U+0085 (NEL) */
+ SET_BIT(0xE2); /* For U+2028, U+2029 */
+ }
+ else
+#endif
+ /* For the 8-bit library not in UTF-8 mode, set the bit for NEL. */
+ {
+ SET_BIT(CHAR_NEL);
+ }
+#endif /* 8-bit support */
+ break;
+
+ case OP_NOT_DIGIT:
+ set_nottype_bits(re, cbit_digit, table_limit);
+ break;
+
+ case OP_DIGIT:
+ set_type_bits(re, cbit_digit, table_limit);
+ break;
+
+ case OP_NOT_WHITESPACE:
+ set_nottype_bits(re, cbit_space, table_limit);
+ break;
+
+ case OP_WHITESPACE:
+ set_type_bits(re, cbit_space, table_limit);
+ break;
+
+ case OP_NOT_WORDCHAR:
+ set_nottype_bits(re, cbit_word, table_limit);
+ break;
+
+ case OP_WORDCHAR:
+ set_type_bits(re, cbit_word, table_limit);
+ break;
+ }
+
+ tcode += 2;
+ break;
+
+ /* Extended class: if there are any property checks, or if this is a
+ negative XCLASS without a map, give up. If there are no property checks,
+ there must be wide characters on the XCLASS list, because otherwise an
+ XCLASS would not have been created. This means that code points >= 255
+ are potential starters. In the UTF-8 case we can scan them and set bits
+ for the relevant leading bytes. */
+
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+ xclassflags = tcode[1 + LINK_SIZE];
+ if ((xclassflags & XCL_HASPROP) != 0 ||
+ (xclassflags & (XCL_MAP|XCL_NOT)) == XCL_NOT)
+ return SSB_FAIL;
+
+ /* We have a positive XCLASS or a negative one without a map. Set up the
+ map pointer if there is one, and fall through. */
+
+ classmap = ((xclassflags & XCL_MAP) == 0)? NULL :
+ (uint8_t *)(tcode + 1 + LINK_SIZE + 1);
+
+ /* In UTF-8 mode, scan the character list and set bits for leading bytes,
+ then jump to handle the map. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (utf && (xclassflags & XCL_NOT) == 0)
+ {
+ PCRE2_UCHAR b, e;
+ PCRE2_SPTR p = tcode + 1 + LINK_SIZE + 1 + ((classmap == NULL)? 0:32);
+ tcode += GET(tcode, 1);
+
+ for (;;) switch (*p++)
+ {
+ case XCL_SINGLE:
+ b = *p++;
+ while ((*p & 0xc0) == 0x80) p++;
+ re->start_bitmap[b/8] |= (1u << (b&7));
+ break;
+
+ case XCL_RANGE:
+ b = *p++;
+ while ((*p & 0xc0) == 0x80) p++;
+ e = *p++;
+ while ((*p & 0xc0) == 0x80) p++;
+ for (; b <= e; b++)
+ re->start_bitmap[b/8] |= (1u << (b&7));
+ break;
+
+ case XCL_END:
+ goto HANDLE_CLASSMAP;
+
+ default:
+ return SSB_UNKNOWN; /* Internal error, should not occur */
+ }
+ }
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 */
+#endif /* SUPPORT_WIDE_CHARS */
+
+ /* It seems that the fall through comment must be outside the #ifdef if
+ it is to avoid the gcc compiler warning. */
+
+ /* Fall through */
+
+ /* Enter here for a negative non-XCLASS. In the 8-bit library, if we are
+ in UTF mode, any byte with a value >= 0xc4 is a potentially valid starter
+ because it starts a character with a value > 255. In 8-bit non-UTF mode,
+ there is no difference between CLASS and NCLASS. In all other wide
+ character modes, set the 0xFF bit to indicate code units >= 255. */
+
+ case OP_NCLASS:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (utf)
+ {
+ re->start_bitmap[24] |= 0xf0; /* Bits for 0xc4 - 0xc8 */
+ memset(re->start_bitmap+25, 0xff, 7); /* Bits for 0xc9 - 0xff */
+ }
+#elif PCRE2_CODE_UNIT_WIDTH != 8
+ SET_BIT(0xFF); /* For characters >= 255 */
+#endif
+ /* Fall through */
+
+ /* Enter here for a positive non-XCLASS. If we have fallen through from
+ an XCLASS, classmap will already be set; just advance the code pointer.
+ Otherwise, set up classmap for a a non-XCLASS and advance past it. */
+
+ case OP_CLASS:
+ if (*tcode == OP_XCLASS) tcode += GET(tcode, 1); else
+ {
+ classmap = (uint8_t *)(++tcode);
+ tcode += 32 / sizeof(PCRE2_UCHAR);
+ }
+
+ /* When wide characters are supported, classmap may be NULL. In UTF-8
+ (sic) mode, the bits in a class bit map correspond to character values,
+ not to byte values. However, the bit map we are constructing is for byte
+ values. So we have to do a conversion for characters whose code point is
+ greater than 127. In fact, there are only two possible starting bytes for
+ characters in the range 128 - 255. */
+
+#if defined SUPPORT_WIDE_CHARS && PCRE2_CODE_UNIT_WIDTH == 8
+ HANDLE_CLASSMAP:
+#endif
+ if (classmap != NULL)
+ {
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (utf)
+ {
+ for (c = 0; c < 16; c++) re->start_bitmap[c] |= classmap[c];
+ for (c = 128; c < 256; c++)
+ {
+ if ((classmap[c/8] & (1u << (c&7))) != 0)
+ {
+ int d = (c >> 6) | 0xc0; /* Set bit for this starter */
+ re->start_bitmap[d/8] |= (1u << (d&7)); /* and then skip on to the */
+ c = (c & 0xc0) + 0x40 - 1; /* next relevant character. */
+ }
+ }
+ }
+ else
+#endif
+ /* In all modes except UTF-8, the two bit maps are compatible. */
+
+ {
+ for (c = 0; c < 32; c++) re->start_bitmap[c] |= classmap[c];
+ }
+ }
+
+ /* Act on what follows the class. For a zero minimum repeat, continue;
+ otherwise stop processing. */
+
+ switch (*tcode)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSSTAR:
+ case OP_CRPOSQUERY:
+ tcode++;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ if (GET2(tcode, 1) == 0) tcode += 1 + 2 * IMM2_SIZE;
+ else try_next = FALSE;
+ break;
+
+ default:
+ try_next = FALSE;
+ break;
+ }
+ break; /* End of class handling case */
+ } /* End of switch for opcodes */
+ } /* End of try_next loop */
+
+ code += GET(code, 1); /* Advance to next branch */
+ }
+while (*code == OP_ALT);
+
+return yield;
+}
+
+
+
+/*************************************************
+* Study a compiled expression *
+*************************************************/
+
+/* This function is handed a compiled expression that it must study to produce
+information that will speed up the matching.
+
+Argument:
+ re points to the compiled expression
+
+Returns: 0 normally; non-zero should never normally occur
+ 1 unknown opcode in set_start_bits
+ 2 missing capturing bracket
+ 3 unknown opcode in find_minlength
+*/
+
+int
+PRIV(study)(pcre2_real_code *re)
+{
+int count = 0;
+PCRE2_UCHAR *code;
+BOOL utf = (re->overall_options & PCRE2_UTF) != 0;
+BOOL ucp = (re->overall_options & PCRE2_UCP) != 0;
+
+/* Find start of compiled code */
+
+code = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)) +
+ re->name_entry_size * re->name_count;
+
+/* For a pattern that has a first code unit, or a multiline pattern that
+matches only at "line start", there is no point in seeking a list of starting
+code units. */
+
+if ((re->flags & (PCRE2_FIRSTSET|PCRE2_STARTLINE)) == 0)
+ {
+ int depth = 0;
+ int rc = set_start_bits(re, code, utf, ucp, &depth);
+ if (rc == SSB_UNKNOWN) return 1;
+
+ /* If a list of starting code units was set up, scan the list to see if only
+ one or two were listed. Having only one listed is rare because usually a
+ single starting code unit will have been recognized and PCRE2_FIRSTSET set.
+ If two are listed, see if they are caseless versions of the same character;
+ if so we can replace the list with a caseless first code unit. This gives
+ better performance and is plausibly worth doing for patterns such as [Ww]ord
+ or (word|WORD). */
+
+ if (rc == SSB_DONE)
+ {
+ int i;
+ int a = -1;
+ int b = -1;
+ uint8_t *p = re->start_bitmap;
+ uint32_t flags = PCRE2_FIRSTMAPSET;
+
+ for (i = 0; i < 256; p++, i += 8)
+ {
+ uint8_t x = *p;
+ if (x != 0)
+ {
+ int c;
+ uint8_t y = x & (~x + 1); /* Least significant bit */
+ if (y != x) goto DONE; /* More than one bit set */
+
+ /* In the 16-bit and 32-bit libraries, the bit for 0xff means "0xff and
+ all wide characters", so we cannot use it here. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (i == 248 && x == 0x80) goto DONE;
+#endif
+
+ /* Compute the character value */
+
+ c = i;
+ switch (x)
+ {
+ case 1: break;
+ case 2: c += 1; break; case 4: c += 2; break;
+ case 8: c += 3; break; case 16: c += 4; break;
+ case 32: c += 5; break; case 64: c += 6; break;
+ case 128: c += 7; break;
+ }
+
+ /* c contains the code unit value, in the range 0-255. In 8-bit UTF
+ mode, only values < 128 can be used. In all the other cases, c is a
+ character value. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (utf && c > 127) goto DONE;
+#endif
+ if (a < 0) a = c; /* First one found, save in a */
+ else if (b < 0) /* Second one found */
+ {
+ int d = TABLE_GET((unsigned int)c, re->tables + fcc_offset, c);
+
+#ifdef SUPPORT_UNICODE
+ if (utf || ucp)
+ {
+ if (UCD_CASESET(c) != 0) goto DONE; /* Multiple case set */
+ if (c > 127) d = UCD_OTHERCASE(c);
+ }
+#endif /* SUPPORT_UNICODE */
+
+ if (d != a) goto DONE; /* Not the other case of a */
+ b = c; /* Save second in b */
+ }
+ else goto DONE; /* More than two characters found */
+ }
+ }
+
+ /* Replace the start code unit bits with a first code unit, but only if it
+ is not the same as a required later code unit. This is because a search for
+ a required code unit starts after an explicit first code unit, but at a
+ code unit found from the bitmap. Patterns such as /a*a/ don't work
+ if both the start unit and required unit are the same. */
+
+ if (a >= 0 &&
+ (
+ (re->flags & PCRE2_LASTSET) == 0 ||
+ (
+ re->last_codeunit != (uint32_t)a &&
+ (b < 0 || re->last_codeunit != (uint32_t)b)
+ )
+ ))
+ {
+ re->first_codeunit = a;
+ flags = PCRE2_FIRSTSET;
+ if (b >= 0) flags |= PCRE2_FIRSTCASELESS;
+ }
+
+ DONE:
+ re->flags |= flags;
+ }
+ }
+
+/* Find the minimum length of subject string. If the pattern can match an empty
+string, the minimum length is already known. If the pattern contains (*ACCEPT)
+all bets are off, and we don't even try to find a minimum length. If there are
+more back references than the size of the vector we are going to cache them in,
+do nothing. A pattern that complicated will probably take a long time to
+analyze and may in any case turn out to be too complicated. Note that back
+reference minima are held as 16-bit numbers. */
+
+if ((re->flags & (PCRE2_MATCH_EMPTY|PCRE2_HASACCEPT)) == 0 &&
+ re->top_backref <= MAX_CACHE_BACKREF)
+ {
+ int min;
+ int backref_cache[MAX_CACHE_BACKREF+1];
+ backref_cache[0] = 0; /* Highest one that is set */
+ min = find_minlength(re, code, code, utf, NULL, &count, backref_cache);
+ switch(min)
+ {
+ case -1: /* \C in UTF mode or over-complex regex */
+ break; /* Leave minlength unchanged (will be zero) */
+
+ case -2:
+ return 2; /* missing capturing bracket */
+
+ case -3:
+ return 3; /* unrecognized opcode */
+
+ default:
+ re->minlength = (min > UINT16_MAX)? UINT16_MAX : min;
+ break;
+ }
+ }
+
+return 0;
+}
+
+/* End of pcre2_study.c */
diff --git a/contrib/libs/pcre2/src/pcre2_substitute.c b/contrib/libs/pcre2/src/pcre2_substitute.c
new file mode 100644
index 00000000000..d667d02f112
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_substitute.c
@@ -0,0 +1,1009 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+#define PTR_STACK_SIZE 20
+
+#define SUBSTITUTE_OPTIONS \
+ (PCRE2_SUBSTITUTE_EXTENDED|PCRE2_SUBSTITUTE_GLOBAL| \
+ PCRE2_SUBSTITUTE_LITERAL|PCRE2_SUBSTITUTE_MATCHED| \
+ PCRE2_SUBSTITUTE_OVERFLOW_LENGTH|PCRE2_SUBSTITUTE_REPLACEMENT_ONLY| \
+ PCRE2_SUBSTITUTE_UNKNOWN_UNSET|PCRE2_SUBSTITUTE_UNSET_EMPTY)
+
+
+
+/*************************************************
+* Find end of substitute text *
+*************************************************/
+
+/* In extended mode, we recognize ${name:+set text:unset text} and similar
+constructions. This requires the identification of unescaped : and }
+characters. This function scans for such. It must deal with nested ${
+constructions. The pointer to the text is updated, either to the required end
+character, or to where an error was detected.
+
+Arguments:
+ code points to the compiled expression (for options)
+ ptrptr points to the pointer to the start of the text (updated)
+ ptrend end of the whole string
+ last TRUE if the last expected string (only } recognized)
+
+Returns: 0 on success
+ negative error code on failure
+*/
+
+static int
+find_text_end(const pcre2_code *code, PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend,
+ BOOL last)
+{
+int rc = 0;
+uint32_t nestlevel = 0;
+BOOL literal = FALSE;
+PCRE2_SPTR ptr = *ptrptr;
+
+for (; ptr < ptrend; ptr++)
+ {
+ if (literal)
+ {
+ if (ptr[0] == CHAR_BACKSLASH && ptr < ptrend - 1 && ptr[1] == CHAR_E)
+ {
+ literal = FALSE;
+ ptr += 1;
+ }
+ }
+
+ else if (*ptr == CHAR_RIGHT_CURLY_BRACKET)
+ {
+ if (nestlevel == 0) goto EXIT;
+ nestlevel--;
+ }
+
+ else if (*ptr == CHAR_COLON && !last && nestlevel == 0) goto EXIT;
+
+ else if (*ptr == CHAR_DOLLAR_SIGN)
+ {
+ if (ptr < ptrend - 1 && ptr[1] == CHAR_LEFT_CURLY_BRACKET)
+ {
+ nestlevel++;
+ ptr += 1;
+ }
+ }
+
+ else if (*ptr == CHAR_BACKSLASH)
+ {
+ int erc;
+ int errorcode;
+ uint32_t ch;
+
+ if (ptr < ptrend - 1) switch (ptr[1])
+ {
+ case CHAR_L:
+ case CHAR_l:
+ case CHAR_U:
+ case CHAR_u:
+ ptr += 1;
+ continue;
+ }
+
+ ptr += 1; /* Must point after \ */
+ erc = PRIV(check_escape)(&ptr, ptrend, &ch, &errorcode,
+ code->overall_options, code->extra_options, FALSE, NULL);
+ ptr -= 1; /* Back to last code unit of escape */
+ if (errorcode != 0)
+ {
+ rc = errorcode;
+ goto EXIT;
+ }
+
+ switch(erc)
+ {
+ case 0: /* Data character */
+ case ESC_E: /* Isolated \E is ignored */
+ break;
+
+ case ESC_Q:
+ literal = TRUE;
+ break;
+
+ default:
+ rc = PCRE2_ERROR_BADREPESCAPE;
+ goto EXIT;
+ }
+ }
+ }
+
+rc = PCRE2_ERROR_REPMISSINGBRACE; /* Terminator not found */
+
+EXIT:
+*ptrptr = ptr;
+return rc;
+}
+
+
+
+/*************************************************
+* Match and substitute *
+*************************************************/
+
+/* This function applies a compiled re to a subject string and creates a new
+string with substitutions. The first 7 arguments are the same as for
+pcre2_match(). Either string length may be PCRE2_ZERO_TERMINATED.
+
+Arguments:
+ code points to the compiled expression
+ subject points to the subject string
+ length length of subject string (may contain binary zeros)
+ start_offset where to start in the subject string
+ options option bits
+ match_data points to a match_data block, or is NULL
+ context points a PCRE2 context
+ replacement points to the replacement string
+ rlength length of replacement string
+ buffer where to put the substituted string
+ blength points to length of buffer; updated to length of string
+
+Returns: >= 0 number of substitutions made
+ < 0 an error code
+ PCRE2_ERROR_BADREPLACEMENT means invalid use of $
+*/
+
+/* This macro checks for space in the buffer before copying into it. On
+overflow, either give an error immediately, or keep on, accumulating the
+length. */
+
+#define CHECKMEMCPY(from,length) \
+ { \
+ if (!overflowed && lengthleft < length) \
+ { \
+ if ((suboptions & PCRE2_SUBSTITUTE_OVERFLOW_LENGTH) == 0) goto NOROOM; \
+ overflowed = TRUE; \
+ extra_needed = length - lengthleft; \
+ } \
+ else if (overflowed) \
+ { \
+ extra_needed += length; \
+ } \
+ else \
+ { \
+ memcpy(buffer + buff_offset, from, CU2BYTES(length)); \
+ buff_offset += length; \
+ lengthleft -= length; \
+ } \
+ }
+
+/* Here's the function */
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substitute(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length,
+ PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data,
+ pcre2_match_context *mcontext, PCRE2_SPTR replacement, PCRE2_SIZE rlength,
+ PCRE2_UCHAR *buffer, PCRE2_SIZE *blength)
+{
+int rc;
+int subs;
+int forcecase = 0;
+int forcecasereset = 0;
+uint32_t ovector_count;
+uint32_t goptions = 0;
+uint32_t suboptions;
+pcre2_match_data *internal_match_data = NULL;
+BOOL escaped_literal = FALSE;
+BOOL overflowed = FALSE;
+BOOL use_existing_match;
+BOOL replacement_only;
+#ifdef SUPPORT_UNICODE
+BOOL utf = (code->overall_options & PCRE2_UTF) != 0;
+BOOL ucp = (code->overall_options & PCRE2_UCP) != 0;
+#endif
+PCRE2_UCHAR temp[6];
+PCRE2_SPTR ptr;
+PCRE2_SPTR repend;
+PCRE2_SIZE extra_needed = 0;
+PCRE2_SIZE buff_offset, buff_length, lengthleft, fraglength;
+PCRE2_SIZE *ovector;
+PCRE2_SIZE ovecsave[3];
+pcre2_substitute_callout_block scb;
+
+/* General initialization */
+
+buff_offset = 0;
+lengthleft = buff_length = *blength;
+*blength = PCRE2_UNSET;
+ovecsave[0] = ovecsave[1] = ovecsave[2] = PCRE2_UNSET;
+
+/* Partial matching is not valid. This must come after setting *blength to
+PCRE2_UNSET, so as not to imply an offset in the replacement. */
+
+if ((options & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0)
+ return PCRE2_ERROR_BADOPTION;
+
+/* Validate length and find the end of the replacement. A NULL replacement of
+zero length is interpreted as an empty string. */
+
+if (replacement == NULL)
+ {
+ if (rlength != 0) return PCRE2_ERROR_NULL;
+ replacement = (PCRE2_SPTR)"";
+ }
+
+if (rlength == PCRE2_ZERO_TERMINATED) rlength = PRIV(strlen)(replacement);
+repend = replacement + rlength;
+
+/* Check for using a match that has already happened. Note that the subject
+pointer in the match data may be NULL after a no-match. */
+
+use_existing_match = ((options & PCRE2_SUBSTITUTE_MATCHED) != 0);
+replacement_only = ((options & PCRE2_SUBSTITUTE_REPLACEMENT_ONLY) != 0);
+
+/* If starting from an existing match, there must be an externally provided
+match data block. We create an internal match_data block in two cases: (a) an
+external one is not supplied (and we are not starting from an existing match);
+(b) an existing match is to be used for the first substitution. In the latter
+case, we copy the existing match into the internal block, except for any cached
+heap frame size and pointer. This ensures that no changes are made to the
+external match data block. */
+
+if (match_data == NULL)
+ {
+ pcre2_general_context *gcontext;
+ if (use_existing_match) return PCRE2_ERROR_NULL;
+ gcontext = (mcontext == NULL)?
+ (pcre2_general_context *)code :
+ (pcre2_general_context *)mcontext;
+ match_data = internal_match_data =
+ pcre2_match_data_create_from_pattern(code, gcontext);
+ if (internal_match_data == NULL) return PCRE2_ERROR_NOMEMORY;
+ }
+
+else if (use_existing_match)
+ {
+ pcre2_general_context *gcontext = (mcontext == NULL)?
+ (pcre2_general_context *)code :
+ (pcre2_general_context *)mcontext;
+ int pairs = (code->top_bracket + 1 < match_data->oveccount)?
+ code->top_bracket + 1 : match_data->oveccount;
+ internal_match_data = pcre2_match_data_create(match_data->oveccount,
+ gcontext);
+ if (internal_match_data == NULL) return PCRE2_ERROR_NOMEMORY;
+ memcpy(internal_match_data, match_data, offsetof(pcre2_match_data, ovector)
+ + 2*pairs*sizeof(PCRE2_SIZE));
+ internal_match_data->heapframes = NULL;
+ internal_match_data->heapframes_size = 0;
+ match_data = internal_match_data;
+ }
+
+/* Remember ovector details */
+
+ovector = pcre2_get_ovector_pointer(match_data);
+ovector_count = pcre2_get_ovector_count(match_data);
+
+/* Fixed things in the callout block */
+
+scb.version = 0;
+scb.input = subject;
+scb.output = (PCRE2_SPTR)buffer;
+scb.ovector = ovector;
+
+/* A NULL subject of zero length is treated as an empty string. */
+
+if (subject == NULL)
+ {
+ if (length != 0) return PCRE2_ERROR_NULL;
+ subject = (PCRE2_SPTR)"";
+ }
+
+/* Find length of zero-terminated subject */
+
+if (length == PCRE2_ZERO_TERMINATED)
+ length = subject? PRIV(strlen)(subject) : 0;
+
+/* Check UTF replacement string if necessary. */
+
+#ifdef SUPPORT_UNICODE
+if (utf && (options & PCRE2_NO_UTF_CHECK) == 0)
+ {
+ rc = PRIV(valid_utf)(replacement, rlength, &(match_data->startchar));
+ if (rc != 0)
+ {
+ match_data->leftchar = 0;
+ goto EXIT;
+ }
+ }
+#endif /* SUPPORT_UNICODE */
+
+/* Save the substitute options and remove them from the match options. */
+
+suboptions = options & SUBSTITUTE_OPTIONS;
+options &= ~SUBSTITUTE_OPTIONS;
+
+/* Error if the start match offset is greater than the length of the subject. */
+
+if (start_offset > length)
+ {
+ match_data->leftchar = 0;
+ rc = PCRE2_ERROR_BADOFFSET;
+ goto EXIT;
+ }
+
+/* Copy up to the start offset, unless only the replacement is required. */
+
+if (!replacement_only) CHECKMEMCPY(subject, start_offset);
+
+/* Loop for global substituting. If PCRE2_SUBSTITUTE_MATCHED is set, the first
+match is taken from the match_data that was passed in. */
+
+subs = 0;
+do
+ {
+ PCRE2_SPTR ptrstack[PTR_STACK_SIZE];
+ uint32_t ptrstackptr = 0;
+
+ if (use_existing_match)
+ {
+ rc = match_data->rc;
+ use_existing_match = FALSE;
+ }
+ else rc = pcre2_match(code, subject, length, start_offset, options|goptions,
+ match_data, mcontext);
+
+#ifdef SUPPORT_UNICODE
+ if (utf) options |= PCRE2_NO_UTF_CHECK; /* Only need to check once */
+#endif
+
+ /* Any error other than no match returns the error code. No match when not
+ doing the special after-empty-match global rematch, or when at the end of the
+ subject, breaks the global loop. Otherwise, advance the starting point by one
+ character, copying it to the output, and try again. */
+
+ if (rc < 0)
+ {
+ PCRE2_SIZE save_start;
+
+ if (rc != PCRE2_ERROR_NOMATCH) goto EXIT;
+ if (goptions == 0 || start_offset >= length) break;
+
+ /* Advance by one code point. Then, if CRLF is a valid newline sequence and
+ we have advanced into the middle of it, advance one more code point. In
+ other words, do not start in the middle of CRLF, even if CR and LF on their
+ own are valid newlines. */
+
+ save_start = start_offset++;
+ if (subject[start_offset-1] == CHAR_CR &&
+ code->newline_convention != PCRE2_NEWLINE_CR &&
+ code->newline_convention != PCRE2_NEWLINE_LF &&
+ start_offset < length &&
+ subject[start_offset] == CHAR_LF)
+ start_offset++;
+
+ /* Otherwise, in UTF mode, advance past any secondary code points. */
+
+ else if ((code->overall_options & PCRE2_UTF) != 0)
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ while (start_offset < length && (subject[start_offset] & 0xc0) == 0x80)
+ start_offset++;
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+ while (start_offset < length &&
+ (subject[start_offset] & 0xfc00) == 0xdc00)
+ start_offset++;
+#endif
+ }
+
+ /* Copy what we have advanced past (unless not required), reset the special
+ global options, and continue to the next match. */
+
+ fraglength = start_offset - save_start;
+ if (!replacement_only) CHECKMEMCPY(subject + save_start, fraglength);
+ goptions = 0;
+ continue;
+ }
+
+ /* Handle a successful match. Matches that use \K to end before they start
+ or start before the current point in the subject are not supported. */
+
+ if (ovector[1] < ovector[0] || ovector[0] < start_offset)
+ {
+ rc = PCRE2_ERROR_BADSUBSPATTERN;
+ goto EXIT;
+ }
+
+ /* Check for the same match as previous. This is legitimate after matching an
+ empty string that starts after the initial match offset. We have tried again
+ at the match point in case the pattern is one like /(?<=\G.)/ which can never
+ match at its starting point, so running the match achieves the bumpalong. If
+ we do get the same (null) match at the original match point, it isn't such a
+ pattern, so we now do the empty string magic. In all other cases, a repeat
+ match should never occur. */
+
+ if (ovecsave[0] == ovector[0] && ovecsave[1] == ovector[1])
+ {
+ if (ovector[0] == ovector[1] && ovecsave[2] != start_offset)
+ {
+ goptions = PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED;
+ ovecsave[2] = start_offset;
+ continue; /* Back to the top of the loop */
+ }
+ rc = PCRE2_ERROR_INTERNAL_DUPMATCH;
+ goto EXIT;
+ }
+
+ /* Count substitutions with a paranoid check for integer overflow; surely no
+ real call to this function would ever hit this! */
+
+ if (subs == INT_MAX)
+ {
+ rc = PCRE2_ERROR_TOOMANYREPLACE;
+ goto EXIT;
+ }
+ subs++;
+
+ /* Copy the text leading up to the match (unless not required), and remember
+ where the insert begins and how many ovector pairs are set. */
+
+ if (rc == 0) rc = ovector_count;
+ fraglength = ovector[0] - start_offset;
+ if (!replacement_only) CHECKMEMCPY(subject + start_offset, fraglength);
+ scb.output_offsets[0] = buff_offset;
+ scb.oveccount = rc;
+
+ /* Process the replacement string. If the entire replacement is literal, just
+ copy it with length check. */
+
+ ptr = replacement;
+ if ((suboptions & PCRE2_SUBSTITUTE_LITERAL) != 0)
+ {
+ CHECKMEMCPY(ptr, rlength);
+ }
+
+ /* Within a non-literal replacement, which must be scanned character by
+ character, local literal mode can be set by \Q, but only in extended mode
+ when backslashes are being interpreted. In extended mode we must handle
+ nested substrings that are to be reprocessed. */
+
+ else for (;;)
+ {
+ uint32_t ch;
+ unsigned int chlen;
+
+ /* If at the end of a nested substring, pop the stack. */
+
+ if (ptr >= repend)
+ {
+ if (ptrstackptr == 0) break; /* End of replacement string */
+ repend = ptrstack[--ptrstackptr];
+ ptr = ptrstack[--ptrstackptr];
+ continue;
+ }
+
+ /* Handle the next character */
+
+ if (escaped_literal)
+ {
+ if (ptr[0] == CHAR_BACKSLASH && ptr < repend - 1 && ptr[1] == CHAR_E)
+ {
+ escaped_literal = FALSE;
+ ptr += 2;
+ continue;
+ }
+ goto LOADLITERAL;
+ }
+
+ /* Not in literal mode. */
+
+ if (*ptr == CHAR_DOLLAR_SIGN)
+ {
+ int group, n;
+ uint32_t special = 0;
+ BOOL inparens;
+ BOOL star;
+ PCRE2_SIZE sublength;
+ PCRE2_SPTR text1_start = NULL;
+ PCRE2_SPTR text1_end = NULL;
+ PCRE2_SPTR text2_start = NULL;
+ PCRE2_SPTR text2_end = NULL;
+ PCRE2_UCHAR next;
+ PCRE2_UCHAR name[33];
+
+ if (++ptr >= repend) goto BAD;
+ if ((next = *ptr) == CHAR_DOLLAR_SIGN) goto LOADLITERAL;
+
+ group = -1;
+ n = 0;
+ inparens = FALSE;
+ star = FALSE;
+
+ if (next == CHAR_LEFT_CURLY_BRACKET)
+ {
+ if (++ptr >= repend) goto BAD;
+ next = *ptr;
+ inparens = TRUE;
+ }
+
+ if (next == CHAR_ASTERISK)
+ {
+ if (++ptr >= repend) goto BAD;
+ next = *ptr;
+ star = TRUE;
+ }
+
+ if (!star && next >= CHAR_0 && next <= CHAR_9)
+ {
+ group = next - CHAR_0;
+ while (++ptr < repend)
+ {
+ next = *ptr;
+ if (next < CHAR_0 || next > CHAR_9) break;
+ group = group * 10 + next - CHAR_0;
+
+ /* A check for a number greater than the hightest captured group
+ is sufficient here; no need for a separate overflow check. If unknown
+ groups are to be treated as unset, just skip over any remaining
+ digits and carry on. */
+
+ if (group > code->top_bracket)
+ {
+ if ((suboptions & PCRE2_SUBSTITUTE_UNKNOWN_UNSET) != 0)
+ {
+ while (++ptr < repend && *ptr >= CHAR_0 && *ptr <= CHAR_9);
+ break;
+ }
+ else
+ {
+ rc = PCRE2_ERROR_NOSUBSTRING;
+ goto PTREXIT;
+ }
+ }
+ }
+ }
+ else
+ {
+ const uint8_t *ctypes = code->tables + ctypes_offset;
+ while (MAX_255(next) && (ctypes[next] & ctype_word) != 0)
+ {
+ name[n++] = next;
+ if (n > 32) goto BAD;
+ if (++ptr >= repend) break;
+ next = *ptr;
+ }
+ if (n == 0) goto BAD;
+ name[n] = 0;
+ }
+
+ /* In extended mode we recognize ${name:+set text:unset text} and
+ ${name:-default text}. */
+
+ if (inparens)
+ {
+ if ((suboptions & PCRE2_SUBSTITUTE_EXTENDED) != 0 &&
+ !star && ptr < repend - 2 && next == CHAR_COLON)
+ {
+ special = *(++ptr);
+ if (special != CHAR_PLUS && special != CHAR_MINUS)
+ {
+ rc = PCRE2_ERROR_BADSUBSTITUTION;
+ goto PTREXIT;
+ }
+
+ text1_start = ++ptr;
+ rc = find_text_end(code, &ptr, repend, special == CHAR_MINUS);
+ if (rc != 0) goto PTREXIT;
+ text1_end = ptr;
+
+ if (special == CHAR_PLUS && *ptr == CHAR_COLON)
+ {
+ text2_start = ++ptr;
+ rc = find_text_end(code, &ptr, repend, TRUE);
+ if (rc != 0) goto PTREXIT;
+ text2_end = ptr;
+ }
+ }
+
+ else
+ {
+ if (ptr >= repend || *ptr != CHAR_RIGHT_CURLY_BRACKET)
+ {
+ rc = PCRE2_ERROR_REPMISSINGBRACE;
+ goto PTREXIT;
+ }
+ }
+
+ ptr++;
+ }
+
+ /* Have found a syntactically correct group number or name, or *name.
+ Only *MARK is currently recognized. */
+
+ if (star)
+ {
+ if (PRIV(strcmp_c8)(name, STRING_MARK) == 0)
+ {
+ PCRE2_SPTR mark = pcre2_get_mark(match_data);
+ if (mark != NULL)
+ {
+ PCRE2_SPTR mark_start = mark;
+ while (*mark != 0) mark++;
+ fraglength = mark - mark_start;
+ CHECKMEMCPY(mark_start, fraglength);
+ }
+ }
+ else goto BAD;
+ }
+
+ /* Substitute the contents of a group. We don't use substring_copy
+ functions any more, in order to support case forcing. */
+
+ else
+ {
+ PCRE2_SPTR subptr, subptrend;
+
+ /* Find a number for a named group. In case there are duplicate names,
+ search for the first one that is set. If the name is not found when
+ PCRE2_SUBSTITUTE_UNKNOWN_EMPTY is set, set the group number to a
+ non-existent group. */
+
+ if (group < 0)
+ {
+ PCRE2_SPTR first, last, entry;
+ rc = pcre2_substring_nametable_scan(code, name, &first, &last);
+ if (rc == PCRE2_ERROR_NOSUBSTRING &&
+ (suboptions & PCRE2_SUBSTITUTE_UNKNOWN_UNSET) != 0)
+ {
+ group = code->top_bracket + 1;
+ }
+ else
+ {
+ if (rc < 0) goto PTREXIT;
+ for (entry = first; entry <= last; entry += rc)
+ {
+ uint32_t ng = GET2(entry, 0);
+ if (ng < ovector_count)
+ {
+ if (group < 0) group = ng; /* First in ovector */
+ if (ovector[ng*2] != PCRE2_UNSET)
+ {
+ group = ng; /* First that is set */
+ break;
+ }
+ }
+ }
+
+ /* If group is still negative, it means we did not find a group
+ that is in the ovector. Just set the first group. */
+
+ if (group < 0) group = GET2(first, 0);
+ }
+ }
+
+ /* We now have a group that is identified by number. Find the length of
+ the captured string. If a group in a non-special substitution is unset
+ when PCRE2_SUBSTITUTE_UNSET_EMPTY is set, substitute nothing. */
+
+ rc = pcre2_substring_length_bynumber(match_data, group, &sublength);
+ if (rc < 0)
+ {
+ if (rc == PCRE2_ERROR_NOSUBSTRING &&
+ (suboptions & PCRE2_SUBSTITUTE_UNKNOWN_UNSET) != 0)
+ {
+ rc = PCRE2_ERROR_UNSET;
+ }
+ if (rc != PCRE2_ERROR_UNSET) goto PTREXIT; /* Non-unset errors */
+ if (special == 0) /* Plain substitution */
+ {
+ if ((suboptions & PCRE2_SUBSTITUTE_UNSET_EMPTY) != 0) continue;
+ goto PTREXIT; /* Else error */
+ }
+ }
+
+ /* If special is '+' we have a 'set' and possibly an 'unset' text,
+ both of which are reprocessed when used. If special is '-' we have a
+ default text for when the group is unset; it must be reprocessed. */
+
+ if (special != 0)
+ {
+ if (special == CHAR_MINUS)
+ {
+ if (rc == 0) goto LITERAL_SUBSTITUTE;
+ text2_start = text1_start;
+ text2_end = text1_end;
+ }
+
+ if (ptrstackptr >= PTR_STACK_SIZE) goto BAD;
+ ptrstack[ptrstackptr++] = ptr;
+ ptrstack[ptrstackptr++] = repend;
+
+ if (rc == 0)
+ {
+ ptr = text1_start;
+ repend = text1_end;
+ }
+ else
+ {
+ ptr = text2_start;
+ repend = text2_end;
+ }
+ continue;
+ }
+
+ /* Otherwise we have a literal substitution of a group's contents. */
+
+ LITERAL_SUBSTITUTE:
+ subptr = subject + ovector[group*2];
+ subptrend = subject + ovector[group*2 + 1];
+
+ /* Substitute a literal string, possibly forcing alphabetic case. */
+
+ while (subptr < subptrend)
+ {
+ GETCHARINCTEST(ch, subptr);
+ if (forcecase != 0)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf || ucp)
+ {
+ uint32_t type = UCD_CHARTYPE(ch);
+ if (PRIV(ucp_gentype)[type] == ucp_L &&
+ type != ((forcecase > 0)? ucp_Lu : ucp_Ll))
+ ch = UCD_OTHERCASE(ch);
+ }
+ else
+#endif
+ {
+ if (((code->tables + cbits_offset +
+ ((forcecase > 0)? cbit_upper:cbit_lower)
+ )[ch/8] & (1u << (ch%8))) == 0)
+ ch = (code->tables + fcc_offset)[ch];
+ }
+ forcecase = forcecasereset;
+ }
+
+#ifdef SUPPORT_UNICODE
+ if (utf) chlen = PRIV(ord2utf)(ch, temp); else
+#endif
+ {
+ temp[0] = ch;
+ chlen = 1;
+ }
+ CHECKMEMCPY(temp, chlen);
+ }
+ }
+ }
+
+ /* Handle an escape sequence in extended mode. We can use check_escape()
+ to process \Q, \E, \c, \o, \x and \ followed by non-alphanumerics, but
+ the case-forcing escapes are not supported in pcre2_compile() so must be
+ recognized here. */
+
+ else if ((suboptions & PCRE2_SUBSTITUTE_EXTENDED) != 0 &&
+ *ptr == CHAR_BACKSLASH)
+ {
+ int errorcode;
+
+ if (ptr < repend - 1) switch (ptr[1])
+ {
+ case CHAR_L:
+ forcecase = forcecasereset = -1;
+ ptr += 2;
+ continue;
+
+ case CHAR_l:
+ forcecase = -1;
+ forcecasereset = 0;
+ ptr += 2;
+ continue;
+
+ case CHAR_U:
+ forcecase = forcecasereset = 1;
+ ptr += 2;
+ continue;
+
+ case CHAR_u:
+ forcecase = 1;
+ forcecasereset = 0;
+ ptr += 2;
+ continue;
+
+ default:
+ break;
+ }
+
+ ptr++; /* Point after \ */
+ rc = PRIV(check_escape)(&ptr, repend, &ch, &errorcode,
+ code->overall_options, code->extra_options, FALSE, NULL);
+ if (errorcode != 0) goto BADESCAPE;
+
+ switch(rc)
+ {
+ case ESC_E:
+ forcecase = forcecasereset = 0;
+ continue;
+
+ case ESC_Q:
+ escaped_literal = TRUE;
+ continue;
+
+ case 0: /* Data character */
+ goto LITERAL;
+
+ default:
+ goto BADESCAPE;
+ }
+ }
+
+ /* Handle a literal code unit */
+
+ else
+ {
+ LOADLITERAL:
+ GETCHARINCTEST(ch, ptr); /* Get character value, increment pointer */
+
+ LITERAL:
+ if (forcecase != 0)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf || ucp)
+ {
+ uint32_t type = UCD_CHARTYPE(ch);
+ if (PRIV(ucp_gentype)[type] == ucp_L &&
+ type != ((forcecase > 0)? ucp_Lu : ucp_Ll))
+ ch = UCD_OTHERCASE(ch);
+ }
+ else
+#endif
+ {
+ if (((code->tables + cbits_offset +
+ ((forcecase > 0)? cbit_upper:cbit_lower)
+ )[ch/8] & (1u << (ch%8))) == 0)
+ ch = (code->tables + fcc_offset)[ch];
+ }
+ forcecase = forcecasereset;
+ }
+
+#ifdef SUPPORT_UNICODE
+ if (utf) chlen = PRIV(ord2utf)(ch, temp); else
+#endif
+ {
+ temp[0] = ch;
+ chlen = 1;
+ }
+ CHECKMEMCPY(temp, chlen);
+ } /* End handling a literal code unit */
+ } /* End of loop for scanning the replacement. */
+
+ /* The replacement has been copied to the output, or its size has been
+ remembered. Do the callout if there is one and we have done an actual
+ replacement. */
+
+ if (!overflowed && mcontext != NULL && mcontext->substitute_callout != NULL)
+ {
+ scb.subscount = subs;
+ scb.output_offsets[1] = buff_offset;
+ rc = mcontext->substitute_callout(&scb, mcontext->substitute_callout_data);
+
+ /* A non-zero return means cancel this substitution. Instead, copy the
+ matched string fragment. */
+
+ if (rc != 0)
+ {
+ PCRE2_SIZE newlength = scb.output_offsets[1] - scb.output_offsets[0];
+ PCRE2_SIZE oldlength = ovector[1] - ovector[0];
+
+ buff_offset -= newlength;
+ lengthleft += newlength;
+ if (!replacement_only) CHECKMEMCPY(subject + ovector[0], oldlength);
+
+ /* A negative return means do not do any more. */
+
+ if (rc < 0) suboptions &= (~PCRE2_SUBSTITUTE_GLOBAL);
+ }
+ }
+
+ /* Save the details of this match. See above for how this data is used. If we
+ matched an empty string, do the magic for global matches. Update the start
+ offset to point to the rest of the subject string. If we re-used an existing
+ match for the first match, switch to the internal match data block. */
+
+ ovecsave[0] = ovector[0];
+ ovecsave[1] = ovector[1];
+ ovecsave[2] = start_offset;
+
+ goptions = (ovector[0] != ovector[1] || ovector[0] > start_offset)? 0 :
+ PCRE2_ANCHORED|PCRE2_NOTEMPTY_ATSTART;
+ start_offset = ovector[1];
+ } while ((suboptions & PCRE2_SUBSTITUTE_GLOBAL) != 0); /* Repeat "do" loop */
+
+/* Copy the rest of the subject unless not required, and terminate the output
+with a binary zero. */
+
+if (!replacement_only)
+ {
+ fraglength = length - start_offset;
+ CHECKMEMCPY(subject + start_offset, fraglength);
+ }
+
+temp[0] = 0;
+CHECKMEMCPY(temp, 1);
+
+/* If overflowed is set it means the PCRE2_SUBSTITUTE_OVERFLOW_LENGTH is set,
+and matching has carried on after a full buffer, in order to compute the length
+needed. Otherwise, an overflow generates an immediate error return. */
+
+if (overflowed)
+ {
+ rc = PCRE2_ERROR_NOMEMORY;
+ *blength = buff_length + extra_needed;
+ }
+
+/* After a successful execution, return the number of substitutions and set the
+length of buffer used, excluding the trailing zero. */
+
+else
+ {
+ rc = subs;
+ *blength = buff_offset - 1;
+ }
+
+EXIT:
+if (internal_match_data != NULL) pcre2_match_data_free(internal_match_data);
+ else match_data->rc = rc;
+return rc;
+
+NOROOM:
+rc = PCRE2_ERROR_NOMEMORY;
+goto EXIT;
+
+BAD:
+rc = PCRE2_ERROR_BADREPLACEMENT;
+goto PTREXIT;
+
+BADESCAPE:
+rc = PCRE2_ERROR_BADREPESCAPE;
+
+PTREXIT:
+*blength = (PCRE2_SIZE)(ptr - replacement);
+goto EXIT;
+}
+
+/* End of pcre2_substitute.c */
diff --git a/contrib/libs/pcre2/src/pcre2_substring.c b/contrib/libs/pcre2/src/pcre2_substring.c
new file mode 100644
index 00000000000..62b509da7b1
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_substring.c
@@ -0,0 +1,547 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2018 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+
+/*************************************************
+* Copy named captured string to given buffer *
+*************************************************/
+
+/* This function copies a single captured substring into a given buffer,
+identifying it by name. If the regex permits duplicate names, the first
+substring that is set is chosen.
+
+Arguments:
+ match_data points to the match data
+ stringname the name of the required substring
+ buffer where to put the substring
+ sizeptr the size of the buffer, updated to the size of the substring
+
+Returns: if successful: zero
+ if not successful, a negative error code:
+ (1) an error from nametable_scan()
+ (2) an error from copy_bynumber()
+ (3) PCRE2_ERROR_UNAVAILABLE: no group is in ovector
+ (4) PCRE2_ERROR_UNSET: all named groups in ovector are unset
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_copy_byname(pcre2_match_data *match_data, PCRE2_SPTR stringname,
+ PCRE2_UCHAR *buffer, PCRE2_SIZE *sizeptr)
+{
+PCRE2_SPTR first, last, entry;
+int failrc, entrysize;
+if (match_data->matchedby == PCRE2_MATCHEDBY_DFA_INTERPRETER)
+ return PCRE2_ERROR_DFA_UFUNC;
+entrysize = pcre2_substring_nametable_scan(match_data->code, stringname,
+ &first, &last);
+if (entrysize < 0) return entrysize;
+failrc = PCRE2_ERROR_UNAVAILABLE;
+for (entry = first; entry <= last; entry += entrysize)
+ {
+ uint32_t n = GET2(entry, 0);
+ if (n < match_data->oveccount)
+ {
+ if (match_data->ovector[n*2] != PCRE2_UNSET)
+ return pcre2_substring_copy_bynumber(match_data, n, buffer, sizeptr);
+ failrc = PCRE2_ERROR_UNSET;
+ }
+ }
+return failrc;
+}
+
+
+
+/*************************************************
+* Copy numbered captured string to given buffer *
+*************************************************/
+
+/* This function copies a single captured substring into a given buffer,
+identifying it by number.
+
+Arguments:
+ match_data points to the match data
+ stringnumber the number of the required substring
+ buffer where to put the substring
+ sizeptr the size of the buffer, updated to the size of the substring
+
+Returns: if successful: 0
+ if not successful, a negative error code:
+ PCRE2_ERROR_NOMEMORY: buffer too small
+ PCRE2_ERROR_NOSUBSTRING: no such substring
+ PCRE2_ERROR_UNAVAILABLE: ovector too small
+ PCRE2_ERROR_UNSET: substring is not set
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_copy_bynumber(pcre2_match_data *match_data,
+ uint32_t stringnumber, PCRE2_UCHAR *buffer, PCRE2_SIZE *sizeptr)
+{
+int rc;
+PCRE2_SIZE size;
+rc = pcre2_substring_length_bynumber(match_data, stringnumber, &size);
+if (rc < 0) return rc;
+if (size + 1 > *sizeptr) return PCRE2_ERROR_NOMEMORY;
+memcpy(buffer, match_data->subject + match_data->ovector[stringnumber*2],
+ CU2BYTES(size));
+buffer[size] = 0;
+*sizeptr = size;
+return 0;
+}
+
+
+
+/*************************************************
+* Extract named captured string *
+*************************************************/
+
+/* This function copies a single captured substring, identified by name, into
+new memory. If the regex permits duplicate names, the first substring that is
+set is chosen.
+
+Arguments:
+ match_data pointer to match_data
+ stringname the name of the required substring
+ stringptr where to put the pointer to the new memory
+ sizeptr where to put the length of the substring
+
+Returns: if successful: zero
+ if not successful, a negative value:
+ (1) an error from nametable_scan()
+ (2) an error from get_bynumber()
+ (3) PCRE2_ERROR_UNAVAILABLE: no group is in ovector
+ (4) PCRE2_ERROR_UNSET: all named groups in ovector are unset
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_get_byname(pcre2_match_data *match_data,
+ PCRE2_SPTR stringname, PCRE2_UCHAR **stringptr, PCRE2_SIZE *sizeptr)
+{
+PCRE2_SPTR first, last, entry;
+int failrc, entrysize;
+if (match_data->matchedby == PCRE2_MATCHEDBY_DFA_INTERPRETER)
+ return PCRE2_ERROR_DFA_UFUNC;
+entrysize = pcre2_substring_nametable_scan(match_data->code, stringname,
+ &first, &last);
+if (entrysize < 0) return entrysize;
+failrc = PCRE2_ERROR_UNAVAILABLE;
+for (entry = first; entry <= last; entry += entrysize)
+ {
+ uint32_t n = GET2(entry, 0);
+ if (n < match_data->oveccount)
+ {
+ if (match_data->ovector[n*2] != PCRE2_UNSET)
+ return pcre2_substring_get_bynumber(match_data, n, stringptr, sizeptr);
+ failrc = PCRE2_ERROR_UNSET;
+ }
+ }
+return failrc;
+}
+
+
+
+/*************************************************
+* Extract captured string to new memory *
+*************************************************/
+
+/* This function copies a single captured substring into a piece of new
+memory.
+
+Arguments:
+ match_data points to match data
+ stringnumber the number of the required substring
+ stringptr where to put a pointer to the new memory
+ sizeptr where to put the size of the substring
+
+Returns: if successful: 0
+ if not successful, a negative error code:
+ PCRE2_ERROR_NOMEMORY: failed to get memory
+ PCRE2_ERROR_NOSUBSTRING: no such substring
+ PCRE2_ERROR_UNAVAILABLE: ovector too small
+ PCRE2_ERROR_UNSET: substring is not set
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_get_bynumber(pcre2_match_data *match_data,
+ uint32_t stringnumber, PCRE2_UCHAR **stringptr, PCRE2_SIZE *sizeptr)
+{
+int rc;
+PCRE2_SIZE size;
+PCRE2_UCHAR *yield;
+rc = pcre2_substring_length_bynumber(match_data, stringnumber, &size);
+if (rc < 0) return rc;
+yield = PRIV(memctl_malloc)(sizeof(pcre2_memctl) +
+ (size + 1)*PCRE2_CODE_UNIT_WIDTH, (pcre2_memctl *)match_data);
+if (yield == NULL) return PCRE2_ERROR_NOMEMORY;
+yield = (PCRE2_UCHAR *)(((char *)yield) + sizeof(pcre2_memctl));
+memcpy(yield, match_data->subject + match_data->ovector[stringnumber*2],
+ CU2BYTES(size));
+yield[size] = 0;
+*stringptr = yield;
+*sizeptr = size;
+return 0;
+}
+
+
+
+/*************************************************
+* Free memory obtained by get_substring *
+*************************************************/
+
+/*
+Argument: the result of a previous pcre2_substring_get_byxxx()
+Returns: nothing
+*/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_substring_free(PCRE2_UCHAR *string)
+{
+if (string != NULL)
+ {
+ pcre2_memctl *memctl = (pcre2_memctl *)((char *)string - sizeof(pcre2_memctl));
+ memctl->free(memctl, memctl->memory_data);
+ }
+}
+
+
+
+/*************************************************
+* Get length of a named substring *
+*************************************************/
+
+/* This function returns the length of a named captured substring. If the regex
+permits duplicate names, the first substring that is set is chosen.
+
+Arguments:
+ match_data pointer to match data
+ stringname the name of the required substring
+ sizeptr where to put the length
+
+Returns: 0 if successful, else a negative error number
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_length_byname(pcre2_match_data *match_data,
+ PCRE2_SPTR stringname, PCRE2_SIZE *sizeptr)
+{
+PCRE2_SPTR first, last, entry;
+int failrc, entrysize;
+if (match_data->matchedby == PCRE2_MATCHEDBY_DFA_INTERPRETER)
+ return PCRE2_ERROR_DFA_UFUNC;
+entrysize = pcre2_substring_nametable_scan(match_data->code, stringname,
+ &first, &last);
+if (entrysize < 0) return entrysize;
+failrc = PCRE2_ERROR_UNAVAILABLE;
+for (entry = first; entry <= last; entry += entrysize)
+ {
+ uint32_t n = GET2(entry, 0);
+ if (n < match_data->oveccount)
+ {
+ if (match_data->ovector[n*2] != PCRE2_UNSET)
+ return pcre2_substring_length_bynumber(match_data, n, sizeptr);
+ failrc = PCRE2_ERROR_UNSET;
+ }
+ }
+return failrc;
+}
+
+
+
+/*************************************************
+* Get length of a numbered substring *
+*************************************************/
+
+/* This function returns the length of a captured substring. If the start is
+beyond the end (which can happen when \K is used in an assertion), it sets the
+length to zero.
+
+Arguments:
+ match_data pointer to match data
+ stringnumber the number of the required substring
+ sizeptr where to put the length, if not NULL
+
+Returns: if successful: 0
+ if not successful, a negative error code:
+ PCRE2_ERROR_NOSUBSTRING: no such substring
+ PCRE2_ERROR_UNAVAILABLE: ovector is too small
+ PCRE2_ERROR_UNSET: substring is not set
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_length_bynumber(pcre2_match_data *match_data,
+ uint32_t stringnumber, PCRE2_SIZE *sizeptr)
+{
+PCRE2_SIZE left, right;
+int count = match_data->rc;
+if (count == PCRE2_ERROR_PARTIAL)
+ {
+ if (stringnumber > 0) return PCRE2_ERROR_PARTIAL;
+ count = 0;
+ }
+else if (count < 0) return count; /* Match failed */
+
+if (match_data->matchedby != PCRE2_MATCHEDBY_DFA_INTERPRETER)
+ {
+ if (stringnumber > match_data->code->top_bracket)
+ return PCRE2_ERROR_NOSUBSTRING;
+ if (stringnumber >= match_data->oveccount)
+ return PCRE2_ERROR_UNAVAILABLE;
+ if (match_data->ovector[stringnumber*2] == PCRE2_UNSET)
+ return PCRE2_ERROR_UNSET;
+ }
+else /* Matched using pcre2_dfa_match() */
+ {
+ if (stringnumber >= match_data->oveccount) return PCRE2_ERROR_UNAVAILABLE;
+ if (count != 0 && stringnumber >= (uint32_t)count) return PCRE2_ERROR_UNSET;
+ }
+
+left = match_data->ovector[stringnumber*2];
+right = match_data->ovector[stringnumber*2+1];
+if (sizeptr != NULL) *sizeptr = (left > right)? 0 : right - left;
+return 0;
+}
+
+
+
+/*************************************************
+* Extract all captured strings to new memory *
+*************************************************/
+
+/* This function gets one chunk of memory and builds a list of pointers and all
+the captured substrings in it. A NULL pointer is put on the end of the list.
+The substrings are zero-terminated, but also, if the final argument is
+non-NULL, a list of lengths is also returned. This allows binary data to be
+handled.
+
+Arguments:
+ match_data points to the match data
+ listptr set to point to the list of pointers
+ lengthsptr set to point to the list of lengths (may be NULL)
+
+Returns: if successful: 0
+ if not successful, a negative error code:
+ PCRE2_ERROR_NOMEMORY: failed to get memory,
+ or a match failure code
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_list_get(pcre2_match_data *match_data, PCRE2_UCHAR ***listptr,
+ PCRE2_SIZE **lengthsptr)
+{
+int i, count, count2;
+PCRE2_SIZE size;
+PCRE2_SIZE *lensp;
+pcre2_memctl *memp;
+PCRE2_UCHAR **listp;
+PCRE2_UCHAR *sp;
+PCRE2_SIZE *ovector;
+
+if ((count = match_data->rc) < 0) return count; /* Match failed */
+if (count == 0) count = match_data->oveccount; /* Ovector too small */
+
+count2 = 2*count;
+ovector = match_data->ovector;
+size = sizeof(pcre2_memctl) + sizeof(PCRE2_UCHAR *); /* For final NULL */
+if (lengthsptr != NULL) size += sizeof(PCRE2_SIZE) * count; /* For lengths */
+
+for (i = 0; i < count2; i += 2)
+ {
+ size += sizeof(PCRE2_UCHAR *) + CU2BYTES(1);
+ if (ovector[i+1] > ovector[i]) size += CU2BYTES(ovector[i+1] - ovector[i]);
+ }
+
+memp = PRIV(memctl_malloc)(size, (pcre2_memctl *)match_data);
+if (memp == NULL) return PCRE2_ERROR_NOMEMORY;
+
+*listptr = listp = (PCRE2_UCHAR **)((char *)memp + sizeof(pcre2_memctl));
+lensp = (PCRE2_SIZE *)((char *)listp + sizeof(PCRE2_UCHAR *) * (count + 1));
+
+if (lengthsptr == NULL)
+ {
+ sp = (PCRE2_UCHAR *)lensp;
+ lensp = NULL;
+ }
+else
+ {
+ *lengthsptr = lensp;
+ sp = (PCRE2_UCHAR *)((char *)lensp + sizeof(PCRE2_SIZE) * count);
+ }
+
+for (i = 0; i < count2; i += 2)
+ {
+ size = (ovector[i+1] > ovector[i])? (ovector[i+1] - ovector[i]) : 0;
+
+ /* Size == 0 includes the case when the capture is unset. Avoid adding
+ PCRE2_UNSET to match_data->subject because it overflows, even though with
+ zero size calling memcpy() is harmless. */
+
+ if (size != 0) memcpy(sp, match_data->subject + ovector[i], CU2BYTES(size));
+ *listp++ = sp;
+ if (lensp != NULL) *lensp++ = size;
+ sp += size;
+ *sp++ = 0;
+ }
+
+*listp = NULL;
+return 0;
+}
+
+
+
+/*************************************************
+* Free memory obtained by substring_list_get *
+*************************************************/
+
+/*
+Argument: the result of a previous pcre2_substring_list_get()
+Returns: nothing
+*/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_substring_list_free(PCRE2_SPTR *list)
+{
+if (list != NULL)
+ {
+ pcre2_memctl *memctl = (pcre2_memctl *)((char *)list - sizeof(pcre2_memctl));
+ memctl->free(memctl, memctl->memory_data);
+ }
+}
+
+
+
+/*************************************************
+* Find (multiple) entries for named string *
+*************************************************/
+
+/* This function scans the nametable for a given name, using binary chop. It
+returns either two pointers to the entries in the table, or, if no pointers are
+given, the number of a unique group with the given name. If duplicate names are
+permitted, and the name is not unique, an error is generated.
+
+Arguments:
+ code the compiled regex
+ stringname the name whose entries required
+ firstptr where to put the pointer to the first entry
+ lastptr where to put the pointer to the last entry
+
+Returns: PCRE2_ERROR_NOSUBSTRING if the name is not found
+ otherwise, if firstptr and lastptr are NULL:
+ a group number for a unique substring
+ else PCRE2_ERROR_NOUNIQUESUBSTRING
+ otherwise:
+ the length of each entry, having set firstptr and lastptr
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_nametable_scan(const pcre2_code *code, PCRE2_SPTR stringname,
+ PCRE2_SPTR *firstptr, PCRE2_SPTR *lastptr)
+{
+uint16_t bot = 0;
+uint16_t top = code->name_count;
+uint16_t entrysize = code->name_entry_size;
+PCRE2_SPTR nametable = (PCRE2_SPTR)((char *)code + sizeof(pcre2_real_code));
+
+while (top > bot)
+ {
+ uint16_t mid = (top + bot) / 2;
+ PCRE2_SPTR entry = nametable + entrysize*mid;
+ int c = PRIV(strcmp)(stringname, entry + IMM2_SIZE);
+ if (c == 0)
+ {
+ PCRE2_SPTR first;
+ PCRE2_SPTR last;
+ PCRE2_SPTR lastentry;
+ lastentry = nametable + entrysize * (code->name_count - 1);
+ first = last = entry;
+ while (first > nametable)
+ {
+ if (PRIV(strcmp)(stringname, (first - entrysize + IMM2_SIZE)) != 0) break;
+ first -= entrysize;
+ }
+ while (last < lastentry)
+ {
+ if (PRIV(strcmp)(stringname, (last + entrysize + IMM2_SIZE)) != 0) break;
+ last += entrysize;
+ }
+ if (firstptr == NULL) return (first == last)?
+ (int)GET2(entry, 0) : PCRE2_ERROR_NOUNIQUESUBSTRING;
+ *firstptr = first;
+ *lastptr = last;
+ return entrysize;
+ }
+ if (c > 0) bot = mid + 1; else top = mid;
+ }
+
+return PCRE2_ERROR_NOSUBSTRING;
+}
+
+
+/*************************************************
+* Find number for named string *
+*************************************************/
+
+/* This function is a convenience wrapper for pcre2_substring_nametable_scan()
+when it is known that names are unique. If there are duplicate names, it is not
+defined which number is returned.
+
+Arguments:
+ code the compiled regex
+ stringname the name whose number is required
+
+Returns: the number of the named parenthesis, or a negative number
+ PCRE2_ERROR_NOSUBSTRING if not found
+ PCRE2_ERROR_NOUNIQUESUBSTRING if not unique
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_number_from_name(const pcre2_code *code,
+ PCRE2_SPTR stringname)
+{
+return pcre2_substring_nametable_scan(code, stringname, NULL, NULL);
+}
+
+/* End of pcre2_substring.c */
diff --git a/contrib/libs/pcre2/src/pcre2_tables.c b/contrib/libs/pcre2/src/pcre2_tables.c
new file mode 100644
index 00000000000..701a4f7c017
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_tables.c
@@ -0,0 +1,234 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2021 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains some fixed tables that are used by more than one of the
+PCRE2 code modules. The tables are also #included by the pcre2test program,
+which uses macros to change their names from _pcre2_xxx to xxxx, thereby
+avoiding name clashes with the library. In this case, PCRE2_PCRE2TEST is
+defined. */
+
+#ifndef PCRE2_PCRE2TEST /* We're compiling the library */
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+#include "pcre2_internal.h"
+#endif /* PCRE2_PCRE2TEST */
+
+/* Table of sizes for the fixed-length opcodes. It's defined in a macro so that
+the definition is next to the definition of the opcodes in pcre2_internal.h.
+This is mode-dependent, so it is skipped when this file is included by
+pcre2test. */
+
+#ifndef PCRE2_PCRE2TEST
+const uint8_t PRIV(OP_lengths)[] = { OP_LENGTHS };
+#endif
+
+/* Tables of horizontal and vertical whitespace characters, suitable for
+adding to classes. */
+
+const uint32_t PRIV(hspace_list)[] = { HSPACE_LIST };
+const uint32_t PRIV(vspace_list)[] = { VSPACE_LIST };
+
+/* These tables are the pairs of delimiters that are valid for callout string
+arguments. For each starting delimiter there must be a matching ending
+delimiter, which in fact is different only for bracket-like delimiters. */
+
+const uint32_t PRIV(callout_start_delims)[] = {
+ CHAR_GRAVE_ACCENT, CHAR_APOSTROPHE, CHAR_QUOTATION_MARK,
+ CHAR_CIRCUMFLEX_ACCENT, CHAR_PERCENT_SIGN, CHAR_NUMBER_SIGN,
+ CHAR_DOLLAR_SIGN, CHAR_LEFT_CURLY_BRACKET, 0 };
+
+const uint32_t PRIV(callout_end_delims[]) = {
+ CHAR_GRAVE_ACCENT, CHAR_APOSTROPHE, CHAR_QUOTATION_MARK,
+ CHAR_CIRCUMFLEX_ACCENT, CHAR_PERCENT_SIGN, CHAR_NUMBER_SIGN,
+ CHAR_DOLLAR_SIGN, CHAR_RIGHT_CURLY_BRACKET, 0 };
+
+
+/*************************************************
+* Tables for UTF-8 support *
+*************************************************/
+
+/* These tables are required by pcre2test in 16- or 32-bit mode, as well
+as for the library in 8-bit mode, because pcre2test uses UTF-8 internally for
+handling wide characters. */
+
+#if defined PCRE2_PCRE2TEST || \
+ (defined SUPPORT_UNICODE && \
+ defined PCRE2_CODE_UNIT_WIDTH && \
+ PCRE2_CODE_UNIT_WIDTH == 8)
+
+/* These are the breakpoints for different numbers of bytes in a UTF-8
+character. */
+
+const int PRIV(utf8_table1)[] =
+ { 0x7f, 0x7ff, 0xffff, 0x1fffff, 0x3ffffff, 0x7fffffff};
+
+const int PRIV(utf8_table1_size) = sizeof(PRIV(utf8_table1)) / sizeof(int);
+
+/* These are the indicator bits and the mask for the data bits to set in the
+first byte of a character, indexed by the number of additional bytes. */
+
+const int PRIV(utf8_table2)[] = { 0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc};
+const int PRIV(utf8_table3)[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01};
+
+/* Table of the number of extra bytes, indexed by the first byte masked with
+0x3f. The highest number for a valid UTF-8 first byte is in fact 0x3d. */
+
+const uint8_t PRIV(utf8_table4)[] = {
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 };
+
+#endif /* UTF-8 support needed */
+
+/* Tables concerned with Unicode properties are relevant only when Unicode
+support is enabled. See also the pcre2_ucptables.c file, which is generated by
+a Python script from Unicode data files. */
+
+#ifdef SUPPORT_UNICODE
+
+/* Table to translate from particular type value to the general value. */
+
+const uint32_t PRIV(ucp_gentype)[] = {
+ ucp_C, ucp_C, ucp_C, ucp_C, ucp_C, /* Cc, Cf, Cn, Co, Cs */
+ ucp_L, ucp_L, ucp_L, ucp_L, ucp_L, /* Ll, Lu, Lm, Lo, Lt */
+ ucp_M, ucp_M, ucp_M, /* Mc, Me, Mn */
+ ucp_N, ucp_N, ucp_N, /* Nd, Nl, No */
+ ucp_P, ucp_P, ucp_P, ucp_P, ucp_P, /* Pc, Pd, Pe, Pf, Pi */
+ ucp_P, ucp_P, /* Ps, Po */
+ ucp_S, ucp_S, ucp_S, ucp_S, /* Sc, Sk, Sm, So */
+ ucp_Z, ucp_Z, ucp_Z /* Zl, Zp, Zs */
+};
+
+/* This table encodes the rules for finding the end of an extended grapheme
+cluster. Every code point has a grapheme break property which is one of the
+ucp_gbXX values defined in pcre2_ucp.h. These changed between Unicode versions
+10 and 11. The 2-dimensional table is indexed by the properties of two adjacent
+code points. The left property selects a word from the table, and the right
+property selects a bit from that word like this:
+
+ PRIV(ucp_gbtable)[left-property] & (1u << right-property)
+
+The value is non-zero if a grapheme break is NOT permitted between the relevant
+two code points. The breaking rules are as follows:
+
+1. Break at the start and end of text (pretty obviously).
+
+2. Do not break between a CR and LF; otherwise, break before and after
+ controls.
+
+3. Do not break Hangul syllable sequences, the rules for which are:
+
+ L may be followed by L, V, LV or LVT
+ LV or V may be followed by V or T
+ LVT or T may be followed by T
+
+4. Do not break before extending characters or zero-width-joiner (ZWJ).
+
+The following rules are only for extended grapheme clusters (but that's what we
+are implementing).
+
+5. Do not break before SpacingMarks.
+
+6. Do not break after Prepend characters.
+
+7. Do not break within emoji modifier sequences or emoji zwj sequences. That
+ is, do not break between characters with the Extended_Pictographic property.
+ Extend and ZWJ characters are allowed between the characters; this cannot be
+ represented in this table, the code has to deal with it.
+
+8. Do not break within emoji flag sequences. That is, do not break between
+ regional indicator (RI) symbols if there are an odd number of RI characters
+ before the break point. This table encodes "join RI characters"; the code
+ has to deal with checking for previous adjoining RIs.
+
+9. Otherwise, break everywhere.
+*/
+
+#define ESZ (1<<ucp_gbExtend)|(1<<ucp_gbSpacingMark)|(1<<ucp_gbZWJ)
+
+const uint32_t PRIV(ucp_gbtable)[] = {
+ (1u<<ucp_gbLF), /* 0 CR */
+ 0, /* 1 LF */
+ 0, /* 2 Control */
+ ESZ, /* 3 Extend */
+ ESZ|(1u<<ucp_gbPrepend)| /* 4 Prepend */
+ (1u<<ucp_gbL)|(1u<<ucp_gbV)|(1u<<ucp_gbT)|
+ (1u<<ucp_gbLV)|(1u<<ucp_gbLVT)|(1u<<ucp_gbOther)|
+ (1u<<ucp_gbRegional_Indicator),
+ ESZ, /* 5 SpacingMark */
+ ESZ|(1u<<ucp_gbL)|(1u<<ucp_gbV)|(1u<<ucp_gbLV)| /* 6 L */
+ (1u<<ucp_gbLVT),
+ ESZ|(1u<<ucp_gbV)|(1u<<ucp_gbT), /* 7 V */
+ ESZ|(1u<<ucp_gbT), /* 8 T */
+ ESZ|(1u<<ucp_gbV)|(1u<<ucp_gbT), /* 9 LV */
+ ESZ|(1u<<ucp_gbT), /* 10 LVT */
+ (1u<<ucp_gbRegional_Indicator), /* 11 Regional Indicator */
+ ESZ, /* 12 Other */
+ ESZ, /* 13 ZWJ */
+ ESZ|(1u<<ucp_gbExtended_Pictographic) /* 14 Extended Pictographic */
+};
+
+#undef ESZ
+
+#ifdef SUPPORT_JIT
+/* This table reverses PRIV(ucp_gentype). We can save the cost
+of a memory load. */
+
+const int PRIV(ucp_typerange)[] = {
+ ucp_Cc, ucp_Cs,
+ ucp_Ll, ucp_Lu,
+ ucp_Mc, ucp_Mn,
+ ucp_Nd, ucp_No,
+ ucp_Pc, ucp_Ps,
+ ucp_Sc, ucp_So,
+ ucp_Zl, ucp_Zs,
+};
+#endif /* SUPPORT_JIT */
+
+/* Finally, include the tables that are auto-generated from the Unicode data
+files. */
+
+#include "pcre2_ucptables.c"
+
+#endif /* SUPPORT_UNICODE */
+
+/* End of pcre2_tables.c */
diff --git a/contrib/libs/pcre2/src/pcre2_ucd.c b/contrib/libs/pcre2/src/pcre2_ucd.c
new file mode 100644
index 00000000000..496c2319153
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_ucd.c
@@ -0,0 +1,5396 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+This module is auto-generated from Unicode data files. DO NOT EDIT MANUALLY!
+Instead, modify the maint/GenerateUcd.py script and run it to generate
+a new version of this code.
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+/* This file contains tables of Unicode properties that are extracted from
+Unicode data files. See the comments at the start of maint/GenerateUcd.py for
+details.
+
+As well as being part of the PCRE2 library, this file is #included by the
+pcre2test program, which redefines the PRIV macro to change table names from
+_pcre2_xxx to xxxx, thereby avoiding name clashes with the library. At present,
+just one of these tables is actually needed. When compiling the library, some
+headers are needed. */
+
+#ifndef PCRE2_PCRE2TEST
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+#include "pcre2_internal.h"
+#endif /* PCRE2_PCRE2TEST */
+
+/* The tables herein are needed only when UCP support is built, and in PCRE2
+that happens automatically with UTF support. This module should not be
+referenced otherwise, so it should not matter whether it is compiled or not.
+However a comment was received about space saving - maybe the guy linked all
+the modules rather than using a library - so we include a condition to cut out
+the tables when not needed. But don't leave a totally empty module because some
+compilers barf at that. Instead, just supply some small dummy tables. */
+
+#ifndef SUPPORT_UNICODE
+const ucd_record PRIV(ucd_records)[] = {{0,0,0,0,0,0,0 }};
+const uint16_t PRIV(ucd_stage1)[] = {0};
+const uint16_t PRIV(ucd_stage2)[] = {0};
+const uint32_t PRIV(ucd_caseless_sets)[] = {0};
+#else
+
+/* Total size: 111116 bytes, block size: 128. */
+
+const char *PRIV(unicode_version) = "14.0.0";
+
+/* When recompiling tables with a new Unicode version, please check the types
+in this structure definition with those in pcre2_internal.h (the actual field
+names will be different).
+
+typedef struct {
+uint8_t property_0;
+uint8_t property_1;
+uint8_t property_2;
+uint8_t property_3;
+int32_t property_4;
+uint16_t property_5;
+uint16_t property_6;
+} ucd_record;
+*/
+
+/* If the 32-bit library is run in non-32-bit mode, character values greater
+than 0x10ffff may be encountered. For these we set up a special record. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+const ucd_record PRIV(dummy_ucd_record)[] = {{
+ ucp_Unknown, /* script */
+ ucp_Cn, /* type unassigned */
+ ucp_gbOther, /* grapheme break property */
+ 0, /* case set */
+ 0, /* other case */
+ 0 | (ucp_bidiL << UCD_BIDICLASS_SHIFT), /* script extension and bidi class */
+ 0, /* bool properties offset */
+ }};
+#endif
+
+/* This table contains lists of characters that are caseless sets of
+more than one character. Each list is terminated by NOTACHAR. */
+
+const uint32_t PRIV(ucd_caseless_sets)[] = {
+ NOTACHAR,
+ 0x0053, 0x0073, 0x017f, NOTACHAR,
+ 0x01c4, 0x01c5, 0x01c6, NOTACHAR,
+ 0x01c7, 0x01c8, 0x01c9, NOTACHAR,
+ 0x01ca, 0x01cb, 0x01cc, NOTACHAR,
+ 0x01f1, 0x01f2, 0x01f3, NOTACHAR,
+ 0x0345, 0x0399, 0x03b9, 0x1fbe, NOTACHAR,
+ 0x00b5, 0x039c, 0x03bc, NOTACHAR,
+ 0x03a3, 0x03c2, 0x03c3, NOTACHAR,
+ 0x0392, 0x03b2, 0x03d0, NOTACHAR,
+ 0x0398, 0x03b8, 0x03d1, 0x03f4, NOTACHAR,
+ 0x03a6, 0x03c6, 0x03d5, NOTACHAR,
+ 0x03a0, 0x03c0, 0x03d6, NOTACHAR,
+ 0x039a, 0x03ba, 0x03f0, NOTACHAR,
+ 0x03a1, 0x03c1, 0x03f1, NOTACHAR,
+ 0x0395, 0x03b5, 0x03f5, NOTACHAR,
+ 0x0412, 0x0432, 0x1c80, NOTACHAR,
+ 0x0414, 0x0434, 0x1c81, NOTACHAR,
+ 0x041e, 0x043e, 0x1c82, NOTACHAR,
+ 0x0421, 0x0441, 0x1c83, NOTACHAR,
+ 0x0422, 0x0442, 0x1c84, 0x1c85, NOTACHAR,
+ 0x042a, 0x044a, 0x1c86, NOTACHAR,
+ 0x0462, 0x0463, 0x1c87, NOTACHAR,
+ 0x1e60, 0x1e61, 0x1e9b, NOTACHAR,
+ 0x03a9, 0x03c9, 0x2126, NOTACHAR,
+ 0x004b, 0x006b, 0x212a, NOTACHAR,
+ 0x00c5, 0x00e5, 0x212b, NOTACHAR,
+ 0x1c88, 0xa64a, 0xa64b, NOTACHAR,
+};
+
+/* When #included in pcre2test, we don't need the table of digit sets, nor the
+the large main UCD tables. */
+
+#ifndef PCRE2_PCRE2TEST
+
+/* This table lists the code points for the '9' characters in each set of
+decimal digits. It is used to ensure that all the digits in a script run come
+from the same set. */
+
+const uint32_t PRIV(ucd_digit_sets)[] = {
+ 66, /* Number of subsequent values */
+ 0x00039, 0x00669, 0x006f9, 0x007c9, 0x0096f, 0x009ef, 0x00a6f, 0x00aef,
+ 0x00b6f, 0x00bef, 0x00c6f, 0x00cef, 0x00d6f, 0x00def, 0x00e59, 0x00ed9,
+ 0x00f29, 0x01049, 0x01099, 0x017e9, 0x01819, 0x0194f, 0x019d9, 0x01a89,
+ 0x01a99, 0x01b59, 0x01bb9, 0x01c49, 0x01c59, 0x0a629, 0x0a8d9, 0x0a909,
+ 0x0a9d9, 0x0a9f9, 0x0aa59, 0x0abf9, 0x0ff19, 0x104a9, 0x10d39, 0x1106f,
+ 0x110f9, 0x1113f, 0x111d9, 0x112f9, 0x11459, 0x114d9, 0x11659, 0x116c9,
+ 0x11739, 0x118e9, 0x11959, 0x11c59, 0x11d59, 0x11da9, 0x16a69, 0x16ac9,
+ 0x16b59, 0x1d7d7, 0x1d7e1, 0x1d7eb, 0x1d7f5, 0x1d7ff, 0x1e149, 0x1e2f9,
+ 0x1e959, 0x1fbf9,
+};
+
+/* This vector is a list of script bitsets for the Script Extension property.
+The number of 32-bit words in each bitset is #defined in pcre2_ucp.h as
+ucd_script_sets_item_size. */
+
+const uint32_t PRIV(ucd_script_sets)[] = {
+ 0x00000000u, 0x00000000u, 0x00000000u,
+ 0x00000080u, 0x00000000u, 0x00000000u,
+ 0x00000040u, 0x00000000u, 0x00000000u,
+ 0x00000000u, 0x00004000u, 0x00000000u,
+ 0x00000002u, 0x00000000u, 0x00000000u,
+ 0x00800000u, 0x00000000u, 0x00000000u,
+ 0x00000001u, 0x00000000u, 0x00000000u,
+ 0x00000000u, 0x00000000u, 0x00000001u,
+ 0x00000010u, 0x00000000u, 0x00000000u,
+ 0x00000008u, 0x00000004u, 0x00000000u,
+ 0x00000008u, 0x40000000u, 0x00000000u,
+ 0x00000008u, 0x00000040u, 0x00000000u,
+ 0x00000018u, 0x00000000u, 0x00000000u,
+ 0x00000028u, 0x00000000u, 0x00000000u,
+ 0x000000c0u, 0x00000000u, 0x00000000u,
+ 0x00c00000u, 0x00000000u, 0x00000000u,
+ 0x00000000u, 0x00000102u, 0x00000000u,
+ 0x80000000u, 0x00000001u, 0x00000000u,
+ 0x00000004u, 0x00000008u, 0x00000000u,
+ 0x00000005u, 0x00000000u, 0x00000000u,
+ 0x00000004u, 0x00200000u, 0x00000000u,
+ 0x00000014u, 0x00000000u, 0x00000000u,
+ 0x00000040u, 0x00008000u, 0x00000000u,
+ 0x00000040u, 0x00000000u, 0x00000001u,
+ 0x00000040u, 0x00001000u, 0x00000000u,
+ 0x00000840u, 0x00000000u, 0x00000000u,
+ 0x00020001u, 0x00000000u, 0x00000000u,
+ 0x00000800u, 0x00008000u, 0x00000000u,
+ 0x00000200u, 0x00010000u, 0x00000000u,
+ 0x00000100u, 0x02000000u, 0x00000000u,
+ 0x00800001u, 0x00000000u, 0x00000000u,
+ 0x00300000u, 0x00000000u, 0x00000000u,
+ 0x00002000u, 0x00000000u, 0x00000001u,
+ 0x00080001u, 0x00000000u, 0x00000000u,
+ 0x00000000u, 0x00080000u, 0x00000008u,
+ 0x00080000u, 0x00000020u, 0x00000000u,
+ 0x00000038u, 0x00000000u, 0x00000000u,
+ 0x00000028u, 0x00000000u, 0x00000002u,
+ 0x00000080u, 0x00000810u, 0x00000000u,
+ 0x40010000u, 0x00000800u, 0x00000000u,
+ 0x80000000u, 0x00000001u, 0x00000004u,
+ 0x80000000u, 0x00020001u, 0x00000000u,
+ 0x00002040u, 0x00008000u, 0x00000000u,
+ 0x00000041u, 0x00008000u, 0x00000000u,
+ 0x00b00000u, 0x00000000u, 0x00000000u,
+ 0x00010001u, 0x00000080u, 0x00000000u,
+ 0x000020c0u, 0x00008000u, 0x00000000u,
+ 0x1e000000u, 0x00000000u, 0x00000000u,
+ 0x00000040u, 0x10040200u, 0x00000000u,
+ 0x00f40000u, 0x00000000u, 0x00000000u,
+ 0x00000038u, 0x40000040u, 0x00000002u,
+ 0x01f40000u, 0x00000000u, 0x00000000u,
+ 0x00007c40u, 0x00000000u, 0x00000000u,
+ 0x00000038u, 0x44000040u, 0x00000002u,
+ 0x000034c0u, 0x01008000u, 0x00000001u,
+ 0x00000018u, 0xc4480400u, 0x00000008u,
+ 0x00000340u, 0x11952200u, 0x00000000u,
+ 0x00007fc1u, 0x01008000u, 0x00000000u,
+ 0x00007fc1u, 0x01009000u, 0x00000000u,
+ 0x00002340u, 0x11952200u, 0x00000001u,
+ 0x00006340u, 0x11952200u, 0x00000001u,
+ 0x0000ffc0u, 0x3984a010u, 0x00000001u,
+ 0x2000ffc0u, 0x3984a010u, 0x00000001u,
+};
+
+/* This vector is a list of bitsets for Boolean properties. The number of
+32_bit words in each bitset is #defined as ucd_boolprop_sets_item_size in
+pcre2_ucp.h. */
+
+const uint32_t PRIV(ucd_boolprop_sets)[] = {
+ 0x00000000u, 0x00000000u,
+ 0x00000001u, 0x00000000u,
+ 0x00000001u, 0x00020040u,
+ 0x00800001u, 0x00020040u,
+ 0x00800001u, 0x00002820u,
+ 0x00800001u, 0x00000120u,
+ 0x00830001u, 0x00000020u,
+ 0x00800001u, 0x00000020u,
+ 0x00800021u, 0x00000120u,
+ 0x00800011u, 0x00000020u,
+ 0x00800001u, 0x00000028u,
+ 0x00800001u, 0x00002020u,
+ 0x00801001u, 0x00000020u,
+ 0x00800021u, 0x00002820u,
+ 0x24830003u, 0x00040000u,
+ 0x00800021u, 0x00002020u,
+ 0x00800011u, 0x00000028u,
+ 0x648003c7u, 0x000c8000u,
+ 0x608003c5u, 0x000c8000u,
+ 0x00808021u, 0x00000028u,
+ 0x20800001u, 0x00040000u,
+ 0x00808021u, 0x00000020u,
+ 0x64800d47u, 0x000c0004u,
+ 0x60800d45u, 0x000c0004u,
+ 0x60800d45u, 0x000c1004u,
+ 0x00000000u, 0x00020040u,
+ 0x00800000u, 0x00020000u,
+ 0x00800000u, 0x00000020u,
+ 0x00808020u, 0x00000000u,
+ 0x00a10000u, 0x00000020u,
+ 0x60800044u, 0x000c0004u,
+ 0x00800010u, 0x00000120u,
+ 0x00800000u, 0x00000028u,
+ 0x00002020u, 0x00000000u,
+ 0x00800000u, 0x00000000u,
+ 0x60800dc4u, 0x000c0004u,
+ 0x20c08020u, 0x00040000u,
+ 0x608003c4u, 0x000c8000u,
+ 0x60800d44u, 0x000c0004u,
+ 0x60800d44u, 0x000c1004u,
+ 0x60804dc4u, 0x000c0004u,
+ 0x60800004u, 0x000c0000u,
+ 0x608007c4u, 0x000c8000u,
+ 0x60800bc4u, 0x000c0000u,
+ 0x60808064u, 0x000c0004u,
+ 0x60808064u, 0x000c1004u,
+ 0x60808024u, 0x000c0000u,
+ 0x60c08024u, 0x000c0000u,
+ 0x21008020u, 0x00040000u,
+ 0x21008de4u, 0x00040004u,
+ 0x21002020u, 0x00040000u,
+ 0x21000020u, 0x00040000u,
+ 0x60808064u, 0x00000004u,
+ 0x00800000u, 0x00002000u,
+ 0x20800020u, 0x00042000u,
+ 0x60800dc4u, 0x000c000cu,
+ 0x60800044u, 0x000c8008u,
+ 0x60800044u, 0x000c8000u,
+ 0x608003c4u, 0x000c8008u,
+ 0x00800000u, 0x00000008u,
+ 0x01000020u, 0x00000000u,
+ 0x00800020u, 0x00000000u,
+ 0x00800000u, 0x00002800u,
+ 0x00801000u, 0x00000000u,
+ 0x21008024u, 0x00040000u,
+ 0x21000024u, 0x00040000u,
+ 0x00000020u, 0x00000080u,
+ 0x00002028u, 0x00000000u,
+ 0x60c00024u, 0x000c0000u,
+ 0x20800000u, 0x00040000u,
+ 0x60804004u, 0x000c0000u,
+ 0x60800024u, 0x000c0000u,
+ 0x20800004u, 0x00040000u,
+ 0x23008020u, 0x00040000u,
+ 0x21000004u, 0x00040000u,
+ 0x21408020u, 0x00040000u,
+ 0x60800004u, 0x00040000u,
+ 0x23000024u, 0x00040000u,
+ 0x60800004u, 0x000c0002u,
+ 0x00800010u, 0x00000000u,
+ 0x20808000u, 0x00040000u,
+ 0x21004024u, 0x00040000u,
+ 0x20808004u, 0x00040000u,
+ 0x60800944u, 0x000c0004u,
+ 0x60802004u, 0x000c0000u,
+ 0x60800344u, 0x000c8000u,
+ 0x22808000u, 0x00040000u,
+ 0x22800000u, 0x00040000u,
+ 0x00c00000u, 0x00000000u,
+ 0x21002020u, 0x00050000u,
+ 0x61000024u, 0x000c0000u,
+ 0x23000020u, 0x00040000u,
+ 0x01008020u, 0x00000000u,
+ 0x21408024u, 0x00040000u,
+ 0x00808000u, 0x00000000u,
+ 0x60800064u, 0x000c0004u,
+ 0x60800044u, 0x000c1004u,
+ 0x60800064u, 0x000c1004u,
+ 0x01002020u, 0x00000001u,
+ 0x00022020u, 0x00000001u,
+ 0x00002028u, 0x00000040u,
+ 0x00801000u, 0x00000020u,
+ 0x00800020u, 0x00000120u,
+ 0x00800000u, 0x00000120u,
+ 0x00800020u, 0x00000020u,
+ 0x00a10000u, 0x00002820u,
+ 0x00800000u, 0x00002820u,
+ 0x20800000u, 0x00040008u,
+ 0x00800010u, 0x00000020u,
+ 0x00002020u, 0x00000008u,
+ 0x00002000u, 0x00000000u,
+ 0x00006020u, 0x00000000u,
+ 0x00801000u, 0x00000008u,
+ 0x00800010u, 0x00000008u,
+ 0x21000020u, 0x00040008u,
+ 0x01020020u, 0x00000000u,
+ 0x60800044u, 0x000c000cu,
+ 0x60800000u, 0x000c0008u,
+ 0x00a10000u, 0x00000000u,
+ 0x60800000u, 0x000c0000u,
+ 0x60800004u, 0x000c0008u,
+ 0x60a10044u, 0x000c0004u,
+ 0x60800044u, 0x000c100cu,
+ 0x00a10000u, 0x00000028u,
+ 0x00800010u, 0x00000028u,
+ 0x00801000u, 0x00000028u,
+ 0x00b10000u, 0x00000020u,
+ 0x00804010u, 0x00000020u,
+ 0x00a00000u, 0x00000020u,
+ 0x00000000u, 0x00000020u,
+ 0x008003c4u, 0x00008000u,
+ 0x00a103c4u, 0x00008000u,
+ 0x00800d44u, 0x00000004u,
+ 0x00b10000u, 0x00000028u,
+ 0x00a00000u, 0x00000028u,
+ 0x00a90000u, 0x00000020u,
+ 0x00b90000u, 0x00000020u,
+ 0x00808024u, 0x00000020u,
+ 0x00800000u, 0x00002020u,
+ 0x00800000u, 0x00000200u,
+ 0x08800000u, 0x00000000u,
+ 0x10800000u, 0x00000000u,
+ 0xe0800004u, 0x000c0000u,
+ 0x21008000u, 0x00040000u,
+ 0x00a11000u, 0x00000020u,
+ 0x60808020u, 0x00000000u,
+ 0xe0800004u, 0x000c4000u,
+ 0x60808004u, 0x000c0000u,
+ 0x60800004u, 0x00000000u,
+ 0x00000000u, 0x00000010u,
+ 0x21022020u, 0x00050000u,
+ 0x00800000u, 0x00000100u,
+ 0x00800020u, 0x00002800u,
+ 0x00800020u, 0x00002000u,
+ 0x00800020u, 0x00000100u,
+ 0x24800000u, 0x00040000u,
+ 0x648003c4u, 0x000c8000u,
+ 0x00808020u, 0x00000008u,
+ 0x64800d44u, 0x000c0004u,
+ 0x00800010u, 0x00000100u,
+ 0x61008024u, 0x00040000u,
+ 0x00000020u, 0x00000000u,
+ 0x60c00004u, 0x000c0000u,
+ 0x21400020u, 0x00040000u,
+ 0xa1000020u, 0x00040000u,
+ 0x21000000u, 0x00040000u,
+ 0x00a00000u, 0x00000000u,
+ 0x00b10000u, 0x00000000u,
+ 0x00200000u, 0x00000000u,
+ 0x00800044u, 0x00008000u,
+ 0x00a10044u, 0x00008000u,
+ 0x00930000u, 0x00000400u,
+ 0x00b90000u, 0x00000000u,
+ 0x00a90000u, 0x00000000u,
+ 0x00970020u, 0x00000000u,
+ 0x00b30000u, 0x00000000u,
+ 0x01022020u, 0x00000000u,
+};
+
+/* These are the main two-stage UCD tables. The fields in each record are:
+script (8 bits), character type (8 bits), grapheme break property (8 bits),
+offset to multichar other cases or zero (8 bits), offset to other case or zero
+(32 bits, signed), bidi class (5 bits) and script extension (11 bits) packed
+into a 16-bit field, and offset in binary properties table (16 bits). */
+
+const ucd_record PRIV(ucd_records)[] = { /* 16908 bytes, record size 12 */
+ { 69, 0, 2, 0, 0, 6144, 2, }, /* 0 */
+ { 69, 0, 2, 0, 0, 43008, 4, }, /* 1 */
+ { 69, 0, 1, 0, 0, 4096, 4, }, /* 2 */
+ { 69, 0, 2, 0, 0, 45056, 4, }, /* 3 */
+ { 69, 0, 0, 0, 0, 4096, 4, }, /* 4 */
+ { 69, 0, 2, 0, 0, 4096, 2, }, /* 5 */
+ { 69, 0, 2, 0, 0, 43008, 2, }, /* 6 */
+ { 69, 29, 12, 0, 0, 45056, 6, }, /* 7 */
+ { 69, 21, 12, 0, 0, 28672, 8, }, /* 8 */
+ { 69, 21, 12, 0, 0, 28672, 10, }, /* 9 */
+ { 69, 21, 12, 0, 0, 14336, 12, }, /* 10 */
+ { 69, 23, 12, 0, 0, 14336, 14, }, /* 11 */
+ { 69, 21, 12, 0, 0, 14336, 14, }, /* 12 */
+ { 69, 21, 12, 0, 0, 28672, 14, }, /* 13 */
+ { 69, 21, 12, 0, 0, 28672, 16, }, /* 14 */
+ { 69, 22, 12, 0, 0, 28672, 18, }, /* 15 */
+ { 69, 18, 12, 0, 0, 28672, 18, }, /* 16 */
+ { 69, 21, 12, 0, 0, 28672, 12, }, /* 17 */
+ { 69, 25, 12, 0, 0, 12288, 20, }, /* 18 */
+ { 69, 21, 12, 0, 0, 8192, 22, }, /* 19 */
+ { 69, 17, 12, 0, 0, 12288, 24, }, /* 20 */
+ { 69, 21, 12, 0, 0, 8192, 26, }, /* 21 */
+ { 69, 21, 12, 0, 0, 8192, 14, }, /* 22 */
+ { 69, 13, 12, 0, 0, 10240, 28, }, /* 23 */
+ { 69, 21, 12, 0, 0, 8192, 30, }, /* 24 */
+ { 69, 21, 12, 0, 0, 28672, 22, }, /* 25 */
+ { 69, 25, 12, 0, 0, 28672, 32, }, /* 26 */
+ { 69, 25, 12, 0, 0, 28672, 20, }, /* 27 */
+ { 0, 9, 12, 0, 32, 18432, 34, }, /* 28 */
+ { 0, 9, 12, 0, 32, 18432, 36, }, /* 29 */
+ { 0, 9, 12, 100, 32, 18432, 36, }, /* 30 */
+ { 0, 9, 12, 1, 32, 18432, 36, }, /* 31 */
+ { 69, 24, 12, 0, 0, 28672, 38, }, /* 32 */
+ { 69, 16, 12, 0, 0, 28672, 40, }, /* 33 */
+ { 69, 24, 12, 0, 0, 28672, 42, }, /* 34 */
+ { 0, 5, 12, 0, -32, 18432, 44, }, /* 35 */
+ { 0, 5, 12, 0, -32, 18432, 46, }, /* 36 */
+ { 0, 5, 12, 0, -32, 18432, 48, }, /* 37 */
+ { 0, 5, 12, 100, -32, 18432, 46, }, /* 38 */
+ { 0, 5, 12, 1, -32, 18432, 46, }, /* 39 */
+ { 69, 0, 2, 0, 0, 6144, 0, }, /* 40 */
+ { 69, 0, 2, 0, 0, 4096, 50, }, /* 41 */
+ { 69, 29, 12, 0, 0, 8192, 52, }, /* 42 */
+ { 69, 21, 12, 0, 0, 28672, 54, }, /* 43 */
+ { 69, 23, 12, 0, 0, 14336, 54, }, /* 44 */
+ { 69, 26, 12, 0, 0, 28672, 54, }, /* 45 */
+ { 69, 24, 12, 0, 0, 28672, 56, }, /* 46 */
+ { 69, 26, 14, 0, 0, 28672, 58, }, /* 47 */
+ { 0, 7, 12, 0, 0, 18432, 60, }, /* 48 */
+ { 69, 20, 12, 0, 0, 28672, 62, }, /* 49 */
+ { 69, 25, 12, 0, 0, 28672, 64, }, /* 50 */
+ { 69, 1, 2, 0, 0, 6144, 66, }, /* 51 */
+ { 69, 26, 12, 0, 0, 14336, 54, }, /* 52 */
+ { 69, 25, 12, 0, 0, 14336, 64, }, /* 53 */
+ { 69, 15, 12, 0, 0, 10240, 68, }, /* 54 */
+ { 69, 5, 12, 26, 775, 18432, 70, }, /* 55 */
+ { 69, 21, 12, 0, 0, 28672, 72, }, /* 56 */
+ { 69, 19, 12, 0, 0, 28672, 62, }, /* 57 */
+ { 69, 15, 12, 0, 0, 28672, 68, }, /* 58 */
+ { 0, 9, 12, 0, 32, 18432, 74, }, /* 59 */
+ { 0, 9, 12, 104, 32, 18432, 74, }, /* 60 */
+ { 0, 5, 12, 0, 7615, 18432, 70, }, /* 61 */
+ { 0, 5, 12, 0, -32, 18432, 76, }, /* 62 */
+ { 0, 5, 12, 104, -32, 18432, 76, }, /* 63 */
+ { 0, 5, 12, 0, 121, 18432, 76, }, /* 64 */
+ { 0, 9, 12, 0, 1, 18432, 74, }, /* 65 */
+ { 0, 5, 12, 0, -1, 18432, 76, }, /* 66 */
+ { 0, 5, 12, 0, -1, 18432, 78, }, /* 67 */
+ { 0, 9, 12, 0, 0, 18432, 74, }, /* 68 */
+ { 0, 5, 12, 0, 0, 18432, 76, }, /* 69 */
+ { 0, 5, 12, 0, 0, 18432, 60, }, /* 70 */
+ { 0, 5, 12, 0, 0, 18432, 80, }, /* 71 */
+ { 0, 9, 12, 0, -121, 18432, 74, }, /* 72 */
+ { 0, 5, 12, 1, -268, 18432, 70, }, /* 73 */
+ { 0, 5, 12, 0, 195, 18432, 76, }, /* 74 */
+ { 0, 9, 12, 0, 210, 18432, 74, }, /* 75 */
+ { 0, 9, 12, 0, 206, 18432, 74, }, /* 76 */
+ { 0, 9, 12, 0, 205, 18432, 74, }, /* 77 */
+ { 0, 9, 12, 0, 79, 18432, 74, }, /* 78 */
+ { 0, 9, 12, 0, 202, 18432, 74, }, /* 79 */
+ { 0, 9, 12, 0, 203, 18432, 74, }, /* 80 */
+ { 0, 9, 12, 0, 207, 18432, 74, }, /* 81 */
+ { 0, 5, 12, 0, 97, 18432, 76, }, /* 82 */
+ { 0, 9, 12, 0, 211, 18432, 74, }, /* 83 */
+ { 0, 9, 12, 0, 209, 18432, 74, }, /* 84 */
+ { 0, 5, 12, 0, 163, 18432, 76, }, /* 85 */
+ { 0, 9, 12, 0, 213, 18432, 74, }, /* 86 */
+ { 0, 5, 12, 0, 130, 18432, 76, }, /* 87 */
+ { 0, 9, 12, 0, 214, 18432, 74, }, /* 88 */
+ { 0, 9, 12, 0, 218, 18432, 74, }, /* 89 */
+ { 0, 9, 12, 0, 217, 18432, 74, }, /* 90 */
+ { 0, 9, 12, 0, 219, 18432, 74, }, /* 91 */
+ { 0, 7, 12, 0, 0, 18432, 82, }, /* 92 */
+ { 0, 5, 12, 0, 56, 18432, 76, }, /* 93 */
+ { 0, 9, 12, 5, 2, 18432, 84, }, /* 94 */
+ { 0, 8, 12, 5, 1, 18432, 86, }, /* 95 */
+ { 0, 5, 12, 5, -2, 18432, 76, }, /* 96 */
+ { 0, 9, 12, 9, 2, 18432, 84, }, /* 97 */
+ { 0, 8, 12, 9, 1, 18432, 86, }, /* 98 */
+ { 0, 5, 12, 9, -2, 18432, 76, }, /* 99 */
+ { 0, 9, 12, 13, 2, 18432, 84, }, /* 100 */
+ { 0, 8, 12, 13, 1, 18432, 86, }, /* 101 */
+ { 0, 5, 12, 13, -2, 18432, 76, }, /* 102 */
+ { 0, 5, 12, 0, -79, 18432, 76, }, /* 103 */
+ { 0, 9, 12, 17, 2, 18432, 84, }, /* 104 */
+ { 0, 8, 12, 17, 1, 18432, 86, }, /* 105 */
+ { 0, 5, 12, 17, -2, 18432, 76, }, /* 106 */
+ { 0, 9, 12, 0, -97, 18432, 74, }, /* 107 */
+ { 0, 9, 12, 0, -56, 18432, 74, }, /* 108 */
+ { 0, 9, 12, 0, -130, 18432, 74, }, /* 109 */
+ { 0, 9, 12, 0, 10795, 18432, 74, }, /* 110 */
+ { 0, 9, 12, 0, -163, 18432, 74, }, /* 111 */
+ { 0, 9, 12, 0, 10792, 18432, 74, }, /* 112 */
+ { 0, 5, 12, 0, 10815, 18432, 76, }, /* 113 */
+ { 0, 9, 12, 0, -195, 18432, 74, }, /* 114 */
+ { 0, 9, 12, 0, 69, 18432, 74, }, /* 115 */
+ { 0, 9, 12, 0, 71, 18432, 74, }, /* 116 */
+ { 0, 5, 12, 0, 10783, 18432, 76, }, /* 117 */
+ { 0, 5, 12, 0, 10780, 18432, 76, }, /* 118 */
+ { 0, 5, 12, 0, 10782, 18432, 76, }, /* 119 */
+ { 0, 5, 12, 0, -210, 18432, 76, }, /* 120 */
+ { 0, 5, 12, 0, -206, 18432, 76, }, /* 121 */
+ { 0, 5, 12, 0, -205, 18432, 76, }, /* 122 */
+ { 0, 5, 12, 0, -202, 18432, 76, }, /* 123 */
+ { 0, 5, 12, 0, -203, 18432, 76, }, /* 124 */
+ { 0, 5, 12, 0, 42319, 18432, 76, }, /* 125 */
+ { 0, 5, 12, 0, 42315, 18432, 76, }, /* 126 */
+ { 0, 5, 12, 0, -207, 18432, 76, }, /* 127 */
+ { 0, 5, 12, 0, 42280, 18432, 76, }, /* 128 */
+ { 0, 5, 12, 0, 42308, 18432, 76, }, /* 129 */
+ { 0, 5, 12, 0, -209, 18432, 78, }, /* 130 */
+ { 0, 5, 12, 0, -211, 18432, 76, }, /* 131 */
+ { 0, 5, 12, 0, 10743, 18432, 76, }, /* 132 */
+ { 0, 5, 12, 0, 42305, 18432, 76, }, /* 133 */
+ { 0, 5, 12, 0, 10749, 18432, 76, }, /* 134 */
+ { 0, 5, 12, 0, -213, 18432, 76, }, /* 135 */
+ { 0, 5, 12, 0, -214, 18432, 76, }, /* 136 */
+ { 0, 5, 12, 0, 10727, 18432, 76, }, /* 137 */
+ { 0, 5, 12, 0, -218, 18432, 76, }, /* 138 */
+ { 0, 5, 12, 0, 42307, 18432, 76, }, /* 139 */
+ { 0, 5, 12, 0, 42282, 18432, 76, }, /* 140 */
+ { 0, 5, 12, 0, -69, 18432, 76, }, /* 141 */
+ { 0, 5, 12, 0, -217, 18432, 76, }, /* 142 */
+ { 0, 5, 12, 0, -71, 18432, 76, }, /* 143 */
+ { 0, 5, 12, 0, -219, 18432, 76, }, /* 144 */
+ { 0, 5, 12, 0, 42261, 18432, 78, }, /* 145 */
+ { 0, 5, 12, 0, 42258, 18432, 76, }, /* 146 */
+ { 0, 6, 12, 0, 0, 18432, 88, }, /* 147 */
+ { 0, 6, 12, 0, 0, 18432, 90, }, /* 148 */
+ { 69, 6, 12, 0, 0, 28672, 92, }, /* 149 */
+ { 69, 6, 12, 0, 0, 18432, 92, }, /* 150 */
+ { 69, 6, 12, 0, 0, 18432, 88, }, /* 151 */
+ { 69, 6, 12, 0, 0, 18432, 94, }, /* 152 */
+ { 22, 24, 12, 0, 0, 28672, 56, }, /* 153 */
+ { 84, 12, 3, 0, 0, 26624, 96, }, /* 154 */
+ { 84, 12, 3, 0, 0, 26636, 96, }, /* 155 */
+ { 84, 12, 3, 21, 116, 26636, 98, }, /* 156 */
+ { 84, 12, 3, 0, 0, 26624, 100, }, /* 157 */
+ { 84, 12, 3, 0, 0, 26624, 102, }, /* 158 */
+ { 84, 12, 3, 0, 0, 26642, 102, }, /* 159 */
+ { 1, 9, 12, 0, 1, 18432, 74, }, /* 160 */
+ { 1, 5, 12, 0, -1, 18432, 76, }, /* 161 */
+ { 1, 24, 12, 0, 0, 28672, 56, }, /* 162 */
+ { 68, 2, 12, 0, 0, 18432, 0, }, /* 163 */
+ { 1, 6, 12, 0, 0, 18432, 104, }, /* 164 */
+ { 1, 5, 12, 0, 130, 18432, 76, }, /* 165 */
+ { 69, 21, 12, 0, 0, 28672, 106, }, /* 166 */
+ { 1, 9, 12, 0, 116, 18432, 74, }, /* 167 */
+ { 1, 9, 12, 0, 38, 18432, 74, }, /* 168 */
+ { 69, 21, 12, 0, 0, 28672, 108, }, /* 169 */
+ { 1, 9, 12, 0, 37, 18432, 74, }, /* 170 */
+ { 1, 9, 12, 0, 64, 18432, 74, }, /* 171 */
+ { 1, 9, 12, 0, 63, 18432, 74, }, /* 172 */
+ { 1, 5, 12, 0, 0, 18432, 76, }, /* 173 */
+ { 1, 9, 12, 0, 32, 18432, 74, }, /* 174 */
+ { 1, 9, 12, 34, 32, 18432, 74, }, /* 175 */
+ { 1, 9, 12, 59, 32, 18432, 74, }, /* 176 */
+ { 1, 9, 12, 38, 32, 18432, 74, }, /* 177 */
+ { 1, 9, 12, 21, 32, 18432, 74, }, /* 178 */
+ { 1, 9, 12, 51, 32, 18432, 74, }, /* 179 */
+ { 1, 9, 12, 26, 32, 18432, 74, }, /* 180 */
+ { 1, 9, 12, 47, 32, 18432, 74, }, /* 181 */
+ { 1, 9, 12, 55, 32, 18432, 74, }, /* 182 */
+ { 1, 9, 12, 30, 32, 18432, 74, }, /* 183 */
+ { 1, 9, 12, 43, 32, 18432, 74, }, /* 184 */
+ { 1, 9, 12, 96, 32, 18432, 74, }, /* 185 */
+ { 1, 5, 12, 0, -38, 18432, 76, }, /* 186 */
+ { 1, 5, 12, 0, -37, 18432, 76, }, /* 187 */
+ { 1, 5, 12, 0, -32, 18432, 76, }, /* 188 */
+ { 1, 5, 12, 34, -32, 18432, 76, }, /* 189 */
+ { 1, 5, 12, 59, -32, 18432, 76, }, /* 190 */
+ { 1, 5, 12, 38, -32, 18432, 76, }, /* 191 */
+ { 1, 5, 12, 21, -116, 18432, 76, }, /* 192 */
+ { 1, 5, 12, 51, -32, 18432, 76, }, /* 193 */
+ { 1, 5, 12, 26, -775, 18432, 76, }, /* 194 */
+ { 1, 5, 12, 47, -32, 18432, 76, }, /* 195 */
+ { 1, 5, 12, 55, -32, 18432, 76, }, /* 196 */
+ { 1, 5, 12, 30, 1, 18432, 70, }, /* 197 */
+ { 1, 5, 12, 30, -32, 18432, 76, }, /* 198 */
+ { 1, 5, 12, 43, -32, 18432, 76, }, /* 199 */
+ { 1, 5, 12, 96, -32, 18432, 76, }, /* 200 */
+ { 1, 5, 12, 0, -64, 18432, 76, }, /* 201 */
+ { 1, 5, 12, 0, -63, 18432, 76, }, /* 202 */
+ { 1, 9, 12, 0, 8, 18432, 74, }, /* 203 */
+ { 1, 5, 12, 34, -30, 18432, 110, }, /* 204 */
+ { 1, 5, 12, 38, -25, 18432, 110, }, /* 205 */
+ { 1, 9, 12, 0, 0, 18432, 112, }, /* 206 */
+ { 1, 9, 12, 0, 0, 18432, 114, }, /* 207 */
+ { 1, 5, 12, 43, -15, 18432, 110, }, /* 208 */
+ { 1, 5, 12, 47, -22, 18432, 70, }, /* 209 */
+ { 1, 5, 12, 0, -8, 18432, 76, }, /* 210 */
+ { 34, 9, 12, 0, 1, 18432, 74, }, /* 211 */
+ { 34, 5, 12, 0, -1, 18432, 76, }, /* 212 */
+ { 1, 5, 12, 51, -54, 18432, 110, }, /* 213 */
+ { 1, 5, 12, 55, -48, 18432, 110, }, /* 214 */
+ { 1, 5, 12, 0, 7, 18432, 76, }, /* 215 */
+ { 1, 5, 12, 0, -116, 18432, 78, }, /* 216 */
+ { 1, 9, 12, 38, -60, 18432, 116, }, /* 217 */
+ { 1, 5, 12, 59, -64, 18432, 110, }, /* 218 */
+ { 1, 25, 12, 0, 0, 28672, 118, }, /* 219 */
+ { 1, 9, 12, 0, -7, 18432, 74, }, /* 220 */
+ { 1, 5, 12, 0, 0, 18432, 60, }, /* 221 */
+ { 1, 9, 12, 0, -130, 18432, 74, }, /* 222 */
+ { 2, 9, 12, 0, 80, 18432, 74, }, /* 223 */
+ { 2, 9, 12, 0, 32, 18432, 74, }, /* 224 */
+ { 2, 9, 12, 63, 32, 18432, 74, }, /* 225 */
+ { 2, 9, 12, 67, 32, 18432, 74, }, /* 226 */
+ { 2, 9, 12, 71, 32, 18432, 74, }, /* 227 */
+ { 2, 9, 12, 75, 32, 18432, 74, }, /* 228 */
+ { 2, 9, 12, 79, 32, 18432, 74, }, /* 229 */
+ { 2, 9, 12, 84, 32, 18432, 74, }, /* 230 */
+ { 2, 5, 12, 0, -32, 18432, 76, }, /* 231 */
+ { 2, 5, 12, 63, -32, 18432, 76, }, /* 232 */
+ { 2, 5, 12, 67, -32, 18432, 76, }, /* 233 */
+ { 2, 5, 12, 71, -32, 18432, 76, }, /* 234 */
+ { 2, 5, 12, 75, -32, 18432, 76, }, /* 235 */
+ { 2, 5, 12, 79, -32, 18432, 76, }, /* 236 */
+ { 2, 5, 12, 84, -32, 18432, 76, }, /* 237 */
+ { 2, 5, 12, 0, -80, 18432, 76, }, /* 238 */
+ { 2, 5, 12, 0, -80, 18432, 78, }, /* 239 */
+ { 2, 9, 12, 0, 1, 18432, 74, }, /* 240 */
+ { 2, 5, 12, 0, -1, 18432, 76, }, /* 241 */
+ { 2, 9, 12, 88, 1, 18432, 74, }, /* 242 */
+ { 2, 5, 12, 88, -1, 18432, 76, }, /* 243 */
+ { 2, 26, 12, 0, 0, 18432, 68, }, /* 244 */
+ { 2, 12, 3, 0, 0, 26684, 96, }, /* 245 */
+ { 2, 12, 3, 0, 0, 26678, 96, }, /* 246 */
+ { 84, 12, 3, 0, 0, 26681, 96, }, /* 247 */
+ { 2, 11, 3, 0, 0, 26624, 120, }, /* 248 */
+ { 2, 9, 12, 0, 15, 18432, 74, }, /* 249 */
+ { 2, 5, 12, 0, -15, 18432, 76, }, /* 250 */
+ { 70, 9, 12, 0, 48, 18432, 74, }, /* 251 */
+ { 70, 6, 12, 0, 0, 18432, 92, }, /* 252 */
+ { 70, 21, 12, 0, 0, 18432, 68, }, /* 253 */
+ { 70, 21, 12, 0, 0, 18432, 122, }, /* 254 */
+ { 70, 5, 12, 0, 0, 18432, 60, }, /* 255 */
+ { 70, 5, 12, 0, -48, 18432, 76, }, /* 256 */
+ { 70, 5, 12, 0, 0, 18432, 70, }, /* 257 */
+ { 70, 21, 12, 0, 0, 18432, 124, }, /* 258 */
+ { 70, 17, 12, 0, 0, 28672, 126, }, /* 259 */
+ { 70, 26, 12, 0, 0, 28672, 68, }, /* 260 */
+ { 70, 23, 12, 0, 0, 14336, 68, }, /* 261 */
+ { 68, 2, 12, 0, 0, 34816, 0, }, /* 262 */
+ { 71, 12, 3, 0, 0, 26624, 96, }, /* 263 */
+ { 71, 12, 3, 0, 0, 26624, 102, }, /* 264 */
+ { 71, 12, 3, 0, 0, 26624, 128, }, /* 265 */
+ { 71, 17, 12, 0, 0, 34816, 126, }, /* 266 */
+ { 71, 21, 12, 0, 0, 34816, 68, }, /* 267 */
+ { 71, 21, 12, 0, 0, 34816, 106, }, /* 268 */
+ { 71, 12, 3, 0, 0, 26624, 130, }, /* 269 */
+ { 71, 7, 12, 0, 0, 34816, 82, }, /* 270 */
+ { 71, 21, 12, 0, 0, 34816, 122, }, /* 271 */
+ { 3, 1, 4, 0, 0, 2048, 132, }, /* 272 */
+ { 69, 1, 4, 0, 0, 2048, 132, }, /* 273 */
+ { 3, 25, 12, 0, 0, 28672, 118, }, /* 274 */
+ { 3, 25, 12, 0, 0, 0, 118, }, /* 275 */
+ { 3, 21, 12, 0, 0, 14336, 68, }, /* 276 */
+ { 3, 23, 12, 0, 0, 0, 68, }, /* 277 */
+ { 69, 21, 12, 0, 0, 8342, 106, }, /* 278 */
+ { 3, 21, 12, 0, 0, 0, 68, }, /* 279 */
+ { 3, 26, 12, 0, 0, 28672, 68, }, /* 280 */
+ { 3, 12, 3, 0, 0, 26624, 130, }, /* 281 */
+ { 69, 21, 12, 0, 0, 150, 106, }, /* 282 */
+ { 3, 1, 2, 0, 0, 108, 134, }, /* 283 */
+ { 3, 21, 12, 0, 0, 0, 124, }, /* 284 */
+ { 69, 21, 12, 0, 0, 159, 124, }, /* 285 */
+ { 3, 7, 12, 0, 0, 0, 82, }, /* 286 */
+ { 69, 6, 12, 0, 0, 165, 136, }, /* 287 */
+ { 84, 12, 3, 0, 0, 26660, 128, }, /* 288 */
+ { 84, 12, 3, 0, 0, 26660, 130, }, /* 289 */
+ { 3, 12, 3, 0, 0, 26624, 128, }, /* 290 */
+ { 3, 12, 3, 0, 0, 26624, 96, }, /* 291 */
+ { 3, 13, 12, 0, 0, 2159, 138, }, /* 292 */
+ { 3, 21, 12, 0, 0, 2048, 68, }, /* 293 */
+ { 3, 7, 12, 0, 0, 0, 140, }, /* 294 */
+ { 3, 21, 12, 0, 0, 30, 124, }, /* 295 */
+ { 3, 6, 12, 0, 0, 0, 92, }, /* 296 */
+ { 3, 13, 12, 0, 0, 10240, 138, }, /* 297 */
+ { 3, 26, 12, 0, 0, 0, 68, }, /* 298 */
+ { 4, 21, 12, 0, 0, 0, 124, }, /* 299 */
+ { 4, 21, 12, 0, 0, 0, 106, }, /* 300 */
+ { 4, 21, 12, 0, 0, 0, 68, }, /* 301 */
+ { 68, 2, 12, 0, 0, 0, 0, }, /* 302 */
+ { 4, 1, 4, 0, 0, 0, 132, }, /* 303 */
+ { 4, 7, 12, 0, 0, 0, 82, }, /* 304 */
+ { 4, 12, 3, 0, 0, 26624, 130, }, /* 305 */
+ { 4, 12, 3, 0, 0, 26624, 128, }, /* 306 */
+ { 4, 12, 3, 0, 0, 26624, 96, }, /* 307 */
+ { 5, 7, 12, 0, 0, 0, 82, }, /* 308 */
+ { 5, 12, 3, 0, 0, 26624, 128, }, /* 309 */
+ { 38, 13, 12, 0, 0, 34816, 138, }, /* 310 */
+ { 38, 7, 12, 0, 0, 34816, 82, }, /* 311 */
+ { 38, 12, 3, 0, 0, 26624, 96, }, /* 312 */
+ { 38, 6, 12, 0, 0, 34816, 92, }, /* 313 */
+ { 38, 26, 12, 0, 0, 28672, 68, }, /* 314 */
+ { 38, 21, 12, 0, 0, 28672, 68, }, /* 315 */
+ { 38, 21, 12, 0, 0, 28672, 106, }, /* 316 */
+ { 38, 21, 12, 0, 0, 28672, 124, }, /* 317 */
+ { 38, 6, 12, 0, 0, 34816, 136, }, /* 318 */
+ { 38, 12, 3, 0, 0, 26624, 102, }, /* 319 */
+ { 38, 23, 12, 0, 0, 34816, 68, }, /* 320 */
+ { 110, 7, 12, 0, 0, 34816, 82, }, /* 321 */
+ { 110, 12, 3, 0, 0, 26624, 130, }, /* 322 */
+ { 110, 12, 3, 0, 0, 26624, 96, }, /* 323 */
+ { 110, 6, 12, 0, 0, 34816, 142, }, /* 324 */
+ { 110, 12, 3, 0, 0, 26624, 102, }, /* 325 */
+ { 110, 21, 12, 0, 0, 34816, 106, }, /* 326 */
+ { 110, 21, 12, 0, 0, 34816, 124, }, /* 327 */
+ { 42, 7, 12, 0, 0, 34816, 82, }, /* 328 */
+ { 42, 12, 3, 0, 0, 26624, 102, }, /* 329 */
+ { 42, 21, 12, 0, 0, 34816, 106, }, /* 330 */
+ { 3, 24, 12, 0, 0, 0, 122, }, /* 331 */
+ { 3, 12, 3, 0, 0, 26624, 102, }, /* 332 */
+ { 6, 12, 3, 0, 0, 26624, 130, }, /* 333 */
+ { 6, 10, 5, 0, 0, 18432, 144, }, /* 334 */
+ { 6, 7, 12, 0, 0, 18432, 82, }, /* 335 */
+ { 6, 12, 3, 0, 0, 26624, 96, }, /* 336 */
+ { 6, 12, 3, 0, 0, 26624, 146, }, /* 337 */
+ { 84, 12, 3, 0, 0, 26798, 96, }, /* 338 */
+ { 84, 12, 3, 0, 0, 26795, 96, }, /* 339 */
+ { 69, 21, 12, 0, 0, 18615, 124, }, /* 340 */
+ { 69, 21, 12, 0, 0, 18618, 124, }, /* 341 */
+ { 6, 13, 12, 0, 0, 18576, 138, }, /* 342 */
+ { 6, 21, 12, 0, 0, 18432, 68, }, /* 343 */
+ { 6, 6, 12, 0, 0, 18432, 92, }, /* 344 */
+ { 7, 7, 12, 0, 0, 18432, 82, }, /* 345 */
+ { 7, 12, 3, 0, 0, 26624, 130, }, /* 346 */
+ { 7, 10, 5, 0, 0, 18432, 144, }, /* 347 */
+ { 7, 12, 3, 0, 0, 26624, 96, }, /* 348 */
+ { 7, 10, 3, 0, 0, 18432, 148, }, /* 349 */
+ { 7, 12, 3, 0, 0, 26624, 146, }, /* 350 */
+ { 7, 13, 12, 0, 0, 18546, 138, }, /* 351 */
+ { 7, 23, 12, 0, 0, 14336, 68, }, /* 352 */
+ { 7, 15, 12, 0, 0, 18432, 68, }, /* 353 */
+ { 7, 26, 12, 0, 0, 18432, 68, }, /* 354 */
+ { 7, 21, 12, 0, 0, 18432, 68, }, /* 355 */
+ { 7, 12, 3, 0, 0, 26624, 102, }, /* 356 */
+ { 8, 12, 3, 0, 0, 26624, 130, }, /* 357 */
+ { 8, 10, 5, 0, 0, 18432, 144, }, /* 358 */
+ { 8, 7, 12, 0, 0, 18432, 82, }, /* 359 */
+ { 8, 12, 3, 0, 0, 26624, 96, }, /* 360 */
+ { 8, 12, 3, 0, 0, 26624, 146, }, /* 361 */
+ { 8, 13, 12, 0, 0, 18519, 138, }, /* 362 */
+ { 8, 21, 12, 0, 0, 18432, 68, }, /* 363 */
+ { 9, 12, 3, 0, 0, 26624, 130, }, /* 364 */
+ { 9, 10, 5, 0, 0, 18432, 144, }, /* 365 */
+ { 9, 7, 12, 0, 0, 18432, 82, }, /* 366 */
+ { 9, 12, 3, 0, 0, 26624, 96, }, /* 367 */
+ { 9, 12, 3, 0, 0, 26624, 146, }, /* 368 */
+ { 9, 13, 12, 0, 0, 18516, 138, }, /* 369 */
+ { 9, 21, 12, 0, 0, 18432, 68, }, /* 370 */
+ { 9, 23, 12, 0, 0, 14336, 68, }, /* 371 */
+ { 10, 12, 3, 0, 0, 26624, 130, }, /* 372 */
+ { 10, 10, 5, 0, 0, 18432, 144, }, /* 373 */
+ { 10, 7, 12, 0, 0, 18432, 82, }, /* 374 */
+ { 10, 12, 3, 0, 0, 26624, 96, }, /* 375 */
+ { 10, 10, 3, 0, 0, 18432, 148, }, /* 376 */
+ { 10, 12, 3, 0, 0, 26624, 146, }, /* 377 */
+ { 10, 12, 3, 0, 0, 26624, 150, }, /* 378 */
+ { 10, 13, 12, 0, 0, 18432, 138, }, /* 379 */
+ { 10, 26, 12, 0, 0, 18432, 68, }, /* 380 */
+ { 10, 15, 12, 0, 0, 18432, 68, }, /* 381 */
+ { 11, 12, 3, 0, 0, 26624, 130, }, /* 382 */
+ { 11, 7, 12, 0, 0, 18432, 82, }, /* 383 */
+ { 11, 10, 3, 0, 0, 18432, 148, }, /* 384 */
+ { 11, 10, 5, 0, 0, 18432, 144, }, /* 385 */
+ { 11, 12, 3, 0, 0, 26624, 146, }, /* 386 */
+ { 11, 13, 12, 0, 0, 18513, 138, }, /* 387 */
+ { 11, 15, 12, 0, 0, 18513, 68, }, /* 388 */
+ { 11, 26, 12, 0, 0, 28753, 68, }, /* 389 */
+ { 11, 26, 12, 0, 0, 28672, 68, }, /* 390 */
+ { 11, 23, 12, 0, 0, 14336, 68, }, /* 391 */
+ { 12, 12, 3, 0, 0, 26624, 130, }, /* 392 */
+ { 12, 10, 5, 0, 0, 18432, 144, }, /* 393 */
+ { 12, 12, 3, 0, 0, 26624, 102, }, /* 394 */
+ { 12, 7, 12, 0, 0, 18432, 82, }, /* 395 */
+ { 12, 12, 3, 0, 0, 26624, 96, }, /* 396 */
+ { 12, 12, 3, 0, 0, 26624, 146, }, /* 397 */
+ { 12, 13, 12, 0, 0, 18432, 138, }, /* 398 */
+ { 12, 21, 12, 0, 0, 18432, 68, }, /* 399 */
+ { 12, 15, 12, 0, 0, 28672, 68, }, /* 400 */
+ { 12, 26, 12, 0, 0, 18432, 68, }, /* 401 */
+ { 13, 7, 12, 0, 0, 18432, 82, }, /* 402 */
+ { 13, 12, 3, 0, 0, 26624, 130, }, /* 403 */
+ { 13, 10, 5, 0, 0, 18432, 144, }, /* 404 */
+ { 13, 21, 12, 0, 0, 18432, 68, }, /* 405 */
+ { 13, 12, 3, 0, 0, 26624, 96, }, /* 406 */
+ { 13, 12, 3, 0, 0, 18432, 130, }, /* 407 */
+ { 13, 10, 3, 0, 0, 18432, 148, }, /* 408 */
+ { 13, 12, 3, 0, 0, 26624, 146, }, /* 409 */
+ { 13, 13, 12, 0, 0, 18528, 138, }, /* 410 */
+ { 14, 12, 3, 0, 0, 26624, 130, }, /* 411 */
+ { 14, 10, 5, 0, 0, 18432, 144, }, /* 412 */
+ { 14, 7, 12, 0, 0, 18432, 82, }, /* 413 */
+ { 14, 12, 3, 0, 0, 26624, 146, }, /* 414 */
+ { 14, 10, 3, 0, 0, 18432, 148, }, /* 415 */
+ { 14, 7, 4, 0, 0, 18432, 82, }, /* 416 */
+ { 14, 26, 12, 0, 0, 18432, 68, }, /* 417 */
+ { 14, 15, 12, 0, 0, 18432, 68, }, /* 418 */
+ { 14, 13, 12, 0, 0, 18432, 138, }, /* 419 */
+ { 15, 12, 3, 0, 0, 26624, 130, }, /* 420 */
+ { 15, 10, 5, 0, 0, 18432, 144, }, /* 421 */
+ { 15, 7, 12, 0, 0, 18432, 82, }, /* 422 */
+ { 15, 12, 3, 0, 0, 26624, 146, }, /* 423 */
+ { 15, 10, 3, 0, 0, 18432, 148, }, /* 424 */
+ { 15, 13, 12, 0, 0, 18432, 138, }, /* 425 */
+ { 15, 21, 12, 0, 0, 18432, 68, }, /* 426 */
+ { 72, 7, 12, 0, 0, 18432, 82, }, /* 427 */
+ { 72, 12, 3, 0, 0, 26624, 130, }, /* 428 */
+ { 72, 7, 5, 0, 0, 18432, 152, }, /* 429 */
+ { 72, 12, 3, 0, 0, 26624, 154, }, /* 430 */
+ { 69, 23, 12, 0, 0, 14336, 68, }, /* 431 */
+ { 72, 7, 12, 0, 0, 18432, 156, }, /* 432 */
+ { 72, 6, 12, 0, 0, 18432, 136, }, /* 433 */
+ { 72, 12, 3, 0, 0, 26624, 96, }, /* 434 */
+ { 72, 21, 12, 0, 0, 18432, 68, }, /* 435 */
+ { 72, 13, 12, 0, 0, 18432, 138, }, /* 436 */
+ { 72, 21, 12, 0, 0, 18432, 106, }, /* 437 */
+ { 73, 7, 12, 0, 0, 18432, 82, }, /* 438 */
+ { 73, 12, 3, 0, 0, 26624, 130, }, /* 439 */
+ { 73, 7, 5, 0, 0, 18432, 152, }, /* 440 */
+ { 73, 12, 3, 0, 0, 26624, 146, }, /* 441 */
+ { 73, 7, 12, 0, 0, 18432, 156, }, /* 442 */
+ { 73, 6, 12, 0, 0, 18432, 136, }, /* 443 */
+ { 73, 12, 3, 0, 0, 26624, 96, }, /* 444 */
+ { 73, 13, 12, 0, 0, 18432, 138, }, /* 445 */
+ { 74, 7, 12, 0, 0, 18432, 82, }, /* 446 */
+ { 74, 26, 12, 0, 0, 18432, 68, }, /* 447 */
+ { 74, 21, 12, 0, 0, 18432, 68, }, /* 448 */
+ { 74, 21, 12, 0, 0, 18432, 106, }, /* 449 */
+ { 74, 12, 3, 0, 0, 26624, 96, }, /* 450 */
+ { 74, 13, 12, 0, 0, 18432, 138, }, /* 451 */
+ { 74, 15, 12, 0, 0, 18432, 68, }, /* 452 */
+ { 74, 22, 12, 0, 0, 28672, 158, }, /* 453 */
+ { 74, 18, 12, 0, 0, 28672, 158, }, /* 454 */
+ { 74, 10, 5, 0, 0, 18432, 160, }, /* 455 */
+ { 74, 12, 3, 0, 0, 26624, 130, }, /* 456 */
+ { 74, 12, 3, 0, 0, 26624, 162, }, /* 457 */
+ { 74, 10, 5, 0, 0, 18432, 144, }, /* 458 */
+ { 74, 12, 3, 0, 0, 26624, 146, }, /* 459 */
+ { 69, 26, 12, 0, 0, 18432, 68, }, /* 460 */
+ { 16, 7, 12, 0, 0, 18432, 82, }, /* 461 */
+ { 16, 10, 12, 0, 0, 18432, 144, }, /* 462 */
+ { 16, 12, 3, 0, 0, 26624, 130, }, /* 463 */
+ { 16, 10, 5, 0, 0, 18432, 144, }, /* 464 */
+ { 16, 12, 3, 0, 0, 26624, 96, }, /* 465 */
+ { 16, 12, 3, 0, 0, 26624, 146, }, /* 466 */
+ { 16, 13, 12, 0, 0, 18549, 138, }, /* 467 */
+ { 16, 21, 12, 0, 0, 18432, 124, }, /* 468 */
+ { 16, 21, 12, 0, 0, 18432, 68, }, /* 469 */
+ { 16, 10, 12, 0, 0, 18432, 164, }, /* 470 */
+ { 16, 12, 3, 0, 0, 26624, 128, }, /* 471 */
+ { 16, 13, 12, 0, 0, 18432, 138, }, /* 472 */
+ { 16, 26, 12, 0, 0, 18432, 68, }, /* 473 */
+ { 17, 9, 12, 0, 7264, 18432, 74, }, /* 474 */
+ { 17, 5, 12, 0, 3008, 18432, 166, }, /* 475 */
+ { 69, 21, 12, 0, 0, 18510, 68, }, /* 476 */
+ { 17, 6, 12, 0, 0, 18432, 142, }, /* 477 */
+ { 18, 7, 6, 0, 0, 18432, 82, }, /* 478 */
+ { 18, 7, 6, 0, 0, 18432, 168, }, /* 479 */
+ { 18, 7, 7, 0, 0, 18432, 168, }, /* 480 */
+ { 18, 7, 7, 0, 0, 18432, 82, }, /* 481 */
+ { 18, 7, 8, 0, 0, 18432, 82, }, /* 482 */
+ { 75, 7, 12, 0, 0, 18432, 82, }, /* 483 */
+ { 75, 12, 3, 0, 0, 26624, 96, }, /* 484 */
+ { 75, 21, 12, 0, 0, 18432, 68, }, /* 485 */
+ { 75, 21, 12, 0, 0, 18432, 106, }, /* 486 */
+ { 75, 21, 12, 0, 0, 18432, 124, }, /* 487 */
+ { 75, 15, 12, 0, 0, 18432, 138, }, /* 488 */
+ { 75, 15, 12, 0, 0, 18432, 68, }, /* 489 */
+ { 75, 26, 12, 0, 0, 28672, 68, }, /* 490 */
+ { 76, 9, 12, 0, 38864, 18432, 170, }, /* 491 */
+ { 76, 9, 12, 0, 8, 18432, 170, }, /* 492 */
+ { 76, 5, 12, 0, -8, 18432, 70, }, /* 493 */
+ { 77, 17, 12, 0, 0, 28672, 126, }, /* 494 */
+ { 77, 7, 12, 0, 0, 18432, 82, }, /* 495 */
+ { 77, 26, 12, 0, 0, 18432, 68, }, /* 496 */
+ { 77, 21, 12, 0, 0, 18432, 124, }, /* 497 */
+ { 78, 29, 12, 0, 0, 45056, 52, }, /* 498 */
+ { 78, 7, 12, 0, 0, 18432, 82, }, /* 499 */
+ { 78, 22, 12, 0, 0, 28672, 158, }, /* 500 */
+ { 78, 18, 12, 0, 0, 28672, 158, }, /* 501 */
+ { 79, 7, 12, 0, 0, 18432, 82, }, /* 502 */
+ { 69, 21, 12, 0, 0, 18432, 106, }, /* 503 */
+ { 79, 14, 12, 0, 0, 18432, 82, }, /* 504 */
+ { 25, 7, 12, 0, 0, 18432, 82, }, /* 505 */
+ { 25, 12, 3, 0, 0, 26624, 130, }, /* 506 */
+ { 25, 12, 3, 0, 0, 26624, 146, }, /* 507 */
+ { 25, 10, 5, 0, 0, 18432, 172, }, /* 508 */
+ { 26, 7, 12, 0, 0, 18432, 82, }, /* 509 */
+ { 26, 12, 3, 0, 0, 26624, 130, }, /* 510 */
+ { 26, 10, 5, 0, 0, 18432, 174, }, /* 511 */
+ { 69, 21, 12, 0, 0, 18573, 124, }, /* 512 */
+ { 27, 7, 12, 0, 0, 18432, 82, }, /* 513 */
+ { 27, 12, 3, 0, 0, 26624, 130, }, /* 514 */
+ { 28, 7, 12, 0, 0, 18432, 82, }, /* 515 */
+ { 28, 12, 3, 0, 0, 26624, 130, }, /* 516 */
+ { 80, 7, 12, 0, 0, 18432, 82, }, /* 517 */
+ { 80, 7, 12, 0, 0, 18432, 140, }, /* 518 */
+ { 80, 12, 3, 0, 0, 26624, 100, }, /* 519 */
+ { 80, 10, 5, 0, 0, 18432, 144, }, /* 520 */
+ { 80, 12, 3, 0, 0, 26624, 130, }, /* 521 */
+ { 80, 12, 3, 0, 0, 26624, 96, }, /* 522 */
+ { 80, 12, 3, 0, 0, 26624, 146, }, /* 523 */
+ { 80, 21, 12, 0, 0, 18432, 106, }, /* 524 */
+ { 80, 6, 12, 0, 0, 18432, 142, }, /* 525 */
+ { 80, 21, 12, 0, 0, 18432, 68, }, /* 526 */
+ { 80, 23, 12, 0, 0, 14336, 68, }, /* 527 */
+ { 80, 13, 12, 0, 0, 18432, 138, }, /* 528 */
+ { 80, 15, 12, 0, 0, 28672, 68, }, /* 529 */
+ { 19, 21, 12, 0, 0, 28672, 68, }, /* 530 */
+ { 69, 21, 12, 0, 0, 28777, 106, }, /* 531 */
+ { 69, 21, 12, 0, 0, 28777, 124, }, /* 532 */
+ { 19, 21, 12, 0, 0, 28672, 106, }, /* 533 */
+ { 19, 17, 12, 0, 0, 28672, 126, }, /* 534 */
+ { 19, 21, 12, 0, 0, 28672, 124, }, /* 535 */
+ { 19, 21, 12, 0, 0, 28672, 176, }, /* 536 */
+ { 19, 12, 3, 0, 0, 26624, 178, }, /* 537 */
+ { 19, 1, 2, 0, 0, 6144, 66, }, /* 538 */
+ { 19, 13, 12, 0, 0, 18432, 138, }, /* 539 */
+ { 19, 7, 12, 0, 0, 18432, 82, }, /* 540 */
+ { 19, 6, 12, 0, 0, 18432, 136, }, /* 541 */
+ { 19, 12, 3, 0, 0, 26624, 180, }, /* 542 */
+ { 19, 12, 3, 0, 0, 26624, 130, }, /* 543 */
+ { 29, 7, 12, 0, 0, 18432, 82, }, /* 544 */
+ { 29, 12, 3, 0, 0, 26624, 130, }, /* 545 */
+ { 29, 10, 5, 0, 0, 18432, 144, }, /* 546 */
+ { 29, 12, 3, 0, 0, 26624, 96, }, /* 547 */
+ { 29, 26, 12, 0, 0, 28672, 68, }, /* 548 */
+ { 29, 21, 12, 0, 0, 28672, 124, }, /* 549 */
+ { 29, 13, 12, 0, 0, 18432, 138, }, /* 550 */
+ { 30, 7, 12, 0, 0, 18432, 82, }, /* 551 */
+ { 89, 7, 12, 0, 0, 18432, 82, }, /* 552 */
+ { 89, 7, 12, 0, 0, 18432, 156, }, /* 553 */
+ { 89, 13, 12, 0, 0, 18432, 138, }, /* 554 */
+ { 89, 15, 12, 0, 0, 18432, 138, }, /* 555 */
+ { 89, 26, 12, 0, 0, 28672, 68, }, /* 556 */
+ { 80, 26, 12, 0, 0, 28672, 68, }, /* 557 */
+ { 33, 7, 12, 0, 0, 18432, 82, }, /* 558 */
+ { 33, 12, 3, 0, 0, 26624, 130, }, /* 559 */
+ { 33, 10, 5, 0, 0, 18432, 144, }, /* 560 */
+ { 33, 21, 12, 0, 0, 18432, 68, }, /* 561 */
+ { 106, 7, 12, 0, 0, 18432, 82, }, /* 562 */
+ { 106, 10, 5, 0, 0, 18432, 144, }, /* 563 */
+ { 106, 12, 3, 0, 0, 26624, 130, }, /* 564 */
+ { 106, 12, 3, 0, 0, 26624, 182, }, /* 565 */
+ { 106, 10, 12, 0, 0, 18432, 144, }, /* 566 */
+ { 106, 12, 3, 0, 0, 26624, 96, }, /* 567 */
+ { 106, 13, 12, 0, 0, 18432, 138, }, /* 568 */
+ { 106, 21, 12, 0, 0, 18432, 68, }, /* 569 */
+ { 106, 6, 12, 0, 0, 18432, 136, }, /* 570 */
+ { 106, 21, 12, 0, 0, 18432, 124, }, /* 571 */
+ { 84, 11, 3, 0, 0, 26624, 184, }, /* 572 */
+ { 84, 12, 3, 0, 0, 26624, 130, }, /* 573 */
+ { 93, 12, 3, 0, 0, 26624, 130, }, /* 574 */
+ { 93, 10, 5, 0, 0, 18432, 144, }, /* 575 */
+ { 93, 7, 12, 0, 0, 18432, 82, }, /* 576 */
+ { 93, 12, 3, 0, 0, 26624, 96, }, /* 577 */
+ { 93, 10, 3, 0, 0, 18432, 148, }, /* 578 */
+ { 93, 10, 5, 0, 0, 18432, 172, }, /* 579 */
+ { 93, 13, 12, 0, 0, 18432, 138, }, /* 580 */
+ { 93, 21, 12, 0, 0, 18432, 124, }, /* 581 */
+ { 93, 21, 12, 0, 0, 18432, 68, }, /* 582 */
+ { 93, 21, 12, 0, 0, 18432, 106, }, /* 583 */
+ { 93, 26, 12, 0, 0, 18432, 68, }, /* 584 */
+ { 96, 12, 3, 0, 0, 26624, 130, }, /* 585 */
+ { 96, 10, 5, 0, 0, 18432, 144, }, /* 586 */
+ { 96, 7, 12, 0, 0, 18432, 82, }, /* 587 */
+ { 96, 10, 5, 0, 0, 18432, 172, }, /* 588 */
+ { 96, 12, 3, 0, 0, 26624, 146, }, /* 589 */
+ { 96, 13, 12, 0, 0, 18432, 138, }, /* 590 */
+ { 119, 7, 12, 0, 0, 18432, 82, }, /* 591 */
+ { 119, 12, 3, 0, 0, 26624, 102, }, /* 592 */
+ { 119, 10, 5, 0, 0, 18432, 144, }, /* 593 */
+ { 119, 12, 3, 0, 0, 26624, 130, }, /* 594 */
+ { 119, 10, 5, 0, 0, 18432, 174, }, /* 595 */
+ { 119, 21, 12, 0, 0, 18432, 68, }, /* 596 */
+ { 97, 7, 12, 0, 0, 18432, 82, }, /* 597 */
+ { 97, 10, 5, 0, 0, 18432, 144, }, /* 598 */
+ { 97, 12, 3, 0, 0, 26624, 130, }, /* 599 */
+ { 97, 12, 3, 0, 0, 26624, 186, }, /* 600 */
+ { 97, 12, 3, 0, 0, 26624, 96, }, /* 601 */
+ { 97, 21, 12, 0, 0, 18432, 124, }, /* 602 */
+ { 97, 21, 12, 0, 0, 18432, 106, }, /* 603 */
+ { 97, 13, 12, 0, 0, 18432, 138, }, /* 604 */
+ { 98, 13, 12, 0, 0, 18432, 138, }, /* 605 */
+ { 98, 7, 12, 0, 0, 18432, 82, }, /* 606 */
+ { 98, 6, 12, 0, 0, 18432, 92, }, /* 607 */
+ { 98, 6, 12, 0, 0, 18432, 94, }, /* 608 */
+ { 98, 21, 12, 0, 0, 18432, 124, }, /* 609 */
+ { 2, 5, 12, 63, -6222, 18432, 70, }, /* 610 */
+ { 2, 5, 12, 67, -6221, 18432, 70, }, /* 611 */
+ { 2, 5, 12, 71, -6212, 18432, 70, }, /* 612 */
+ { 2, 5, 12, 75, -6210, 18432, 70, }, /* 613 */
+ { 2, 5, 12, 79, -6210, 18432, 70, }, /* 614 */
+ { 2, 5, 12, 79, -6211, 18432, 70, }, /* 615 */
+ { 2, 5, 12, 84, -6204, 18432, 70, }, /* 616 */
+ { 2, 5, 12, 88, -6180, 18432, 70, }, /* 617 */
+ { 2, 5, 12, 108, 35267, 18432, 70, }, /* 618 */
+ { 17, 9, 12, 0, -3008, 18432, 74, }, /* 619 */
+ { 96, 21, 12, 0, 0, 18432, 68, }, /* 620 */
+ { 84, 12, 3, 0, 0, 26762, 96, }, /* 621 */
+ { 84, 12, 3, 0, 0, 26630, 96, }, /* 622 */
+ { 69, 21, 12, 0, 0, 18498, 188, }, /* 623 */
+ { 84, 12, 3, 0, 0, 26666, 96, }, /* 624 */
+ { 84, 12, 3, 0, 0, 26696, 96, }, /* 625 */
+ { 84, 12, 3, 0, 0, 26780, 96, }, /* 626 */
+ { 69, 10, 5, 0, 0, 18474, 160, }, /* 627 */
+ { 69, 7, 12, 0, 0, 18501, 82, }, /* 628 */
+ { 69, 7, 12, 0, 0, 18474, 82, }, /* 629 */
+ { 69, 7, 12, 0, 0, 18438, 82, }, /* 630 */
+ { 69, 7, 12, 0, 0, 18594, 82, }, /* 631 */
+ { 69, 7, 12, 0, 0, 18498, 82, }, /* 632 */
+ { 84, 12, 3, 0, 0, 26750, 96, }, /* 633 */
+ { 69, 10, 5, 0, 0, 18435, 160, }, /* 634 */
+ { 84, 12, 3, 0, 0, 26690, 96, }, /* 635 */
+ { 69, 7, 12, 0, 0, 18453, 82, }, /* 636 */
+ { 2, 5, 12, 0, 0, 18432, 60, }, /* 637 */
+ { 1, 6, 12, 0, 0, 18432, 88, }, /* 638 */
+ { 2, 6, 12, 0, 0, 18432, 190, }, /* 639 */
+ { 0, 5, 12, 0, 35332, 18432, 76, }, /* 640 */
+ { 0, 5, 12, 0, 3814, 18432, 76, }, /* 641 */
+ { 0, 5, 12, 0, 35384, 18432, 76, }, /* 642 */
+ { 0, 5, 12, 0, 0, 18432, 192, }, /* 643 */
+ { 0, 6, 12, 0, 0, 18432, 190, }, /* 644 */
+ { 0, 6, 12, 0, 0, 18432, 194, }, /* 645 */
+ { 1, 6, 12, 0, 0, 18432, 190, }, /* 646 */
+ { 84, 12, 3, 0, 0, 26636, 102, }, /* 647 */
+ { 84, 12, 3, 0, 0, 26687, 96, }, /* 648 */
+ { 84, 12, 3, 0, 0, 26648, 96, }, /* 649 */
+ { 0, 9, 12, 92, 1, 18432, 74, }, /* 650 */
+ { 0, 5, 12, 92, -1, 18432, 76, }, /* 651 */
+ { 0, 5, 12, 0, 0, 18432, 70, }, /* 652 */
+ { 0, 5, 12, 92, -58, 18432, 70, }, /* 653 */
+ { 0, 9, 12, 0, -7615, 18432, 74, }, /* 654 */
+ { 1, 5, 12, 0, 8, 18432, 76, }, /* 655 */
+ { 1, 9, 12, 0, -8, 18432, 74, }, /* 656 */
+ { 1, 5, 12, 0, 74, 18432, 76, }, /* 657 */
+ { 1, 5, 12, 0, 86, 18432, 76, }, /* 658 */
+ { 1, 5, 12, 0, 100, 18432, 76, }, /* 659 */
+ { 1, 5, 12, 0, 128, 18432, 76, }, /* 660 */
+ { 1, 5, 12, 0, 112, 18432, 76, }, /* 661 */
+ { 1, 5, 12, 0, 126, 18432, 76, }, /* 662 */
+ { 1, 5, 12, 0, 8, 18432, 70, }, /* 663 */
+ { 1, 8, 12, 0, -8, 18432, 86, }, /* 664 */
+ { 1, 5, 12, 0, 0, 18432, 70, }, /* 665 */
+ { 1, 5, 12, 0, 9, 18432, 70, }, /* 666 */
+ { 1, 9, 12, 0, -74, 18432, 74, }, /* 667 */
+ { 1, 8, 12, 0, -9, 18432, 86, }, /* 668 */
+ { 1, 5, 12, 21, -7173, 18432, 76, }, /* 669 */
+ { 1, 9, 12, 0, -86, 18432, 74, }, /* 670 */
+ { 1, 9, 12, 0, -100, 18432, 74, }, /* 671 */
+ { 1, 9, 12, 0, -112, 18432, 74, }, /* 672 */
+ { 1, 9, 12, 0, -128, 18432, 74, }, /* 673 */
+ { 1, 9, 12, 0, -126, 18432, 74, }, /* 674 */
+ { 69, 29, 12, 0, 0, 45056, 52, }, /* 675 */
+ { 84, 1, 3, 0, 0, 6144, 196, }, /* 676 */
+ { 84, 1, 13, 0, 0, 6144, 198, }, /* 677 */
+ { 69, 1, 2, 0, 0, 18432, 200, }, /* 678 */
+ { 69, 1, 2, 0, 0, 34816, 200, }, /* 679 */
+ { 69, 17, 12, 0, 0, 28672, 202, }, /* 680 */
+ { 69, 21, 12, 0, 0, 28672, 64, }, /* 681 */
+ { 69, 20, 12, 0, 0, 28672, 204, }, /* 682 */
+ { 69, 19, 12, 0, 0, 28672, 204, }, /* 683 */
+ { 69, 22, 12, 0, 0, 28672, 206, }, /* 684 */
+ { 69, 20, 12, 0, 0, 28672, 206, }, /* 685 */
+ { 69, 19, 12, 0, 0, 28672, 206, }, /* 686 */
+ { 69, 21, 12, 0, 0, 28672, 208, }, /* 687 */
+ { 69, 27, 2, 0, 0, 45056, 50, }, /* 688 */
+ { 69, 28, 2, 0, 0, 4096, 50, }, /* 689 */
+ { 69, 1, 2, 0, 0, 20480, 134, }, /* 690 */
+ { 69, 1, 2, 0, 0, 36864, 134, }, /* 691 */
+ { 69, 1, 2, 0, 0, 30720, 134, }, /* 692 */
+ { 69, 1, 2, 0, 0, 24576, 134, }, /* 693 */
+ { 69, 1, 2, 0, 0, 40960, 134, }, /* 694 */
+ { 69, 29, 12, 0, 0, 8291, 52, }, /* 695 */
+ { 69, 21, 12, 0, 0, 14336, 54, }, /* 696 */
+ { 69, 21, 12, 0, 0, 14336, 64, }, /* 697 */
+ { 69, 21, 14, 0, 0, 28672, 210, }, /* 698 */
+ { 69, 21, 12, 0, 0, 28672, 212, }, /* 699 */
+ { 69, 16, 12, 0, 0, 28672, 138, }, /* 700 */
+ { 69, 16, 12, 0, 0, 28672, 214, }, /* 701 */
+ { 69, 25, 12, 0, 0, 8192, 64, }, /* 702 */
+ { 69, 22, 12, 0, 0, 28672, 216, }, /* 703 */
+ { 69, 18, 12, 0, 0, 28672, 216, }, /* 704 */
+ { 69, 21, 12, 0, 0, 28672, 202, }, /* 705 */
+ { 69, 1, 2, 0, 0, 6144, 218, }, /* 706 */
+ { 68, 2, 2, 0, 0, 6144, 220, }, /* 707 */
+ { 69, 1, 2, 0, 0, 22528, 134, }, /* 708 */
+ { 69, 1, 2, 0, 0, 38912, 134, }, /* 709 */
+ { 69, 1, 2, 0, 0, 16384, 134, }, /* 710 */
+ { 69, 1, 2, 0, 0, 32768, 134, }, /* 711 */
+ { 69, 1, 2, 0, 0, 6144, 222, }, /* 712 */
+ { 69, 25, 12, 0, 0, 12288, 118, }, /* 713 */
+ { 69, 25, 12, 0, 0, 12288, 224, }, /* 714 */
+ { 69, 25, 12, 0, 0, 28672, 118, }, /* 715 */
+ { 69, 22, 12, 0, 0, 28672, 226, }, /* 716 */
+ { 69, 18, 12, 0, 0, 28672, 226, }, /* 717 */
+ { 68, 2, 12, 0, 0, 14336, 0, }, /* 718 */
+ { 84, 12, 3, 0, 0, 26624, 228, }, /* 719 */
+ { 84, 11, 3, 0, 0, 26624, 120, }, /* 720 */
+ { 84, 11, 3, 0, 0, 26624, 230, }, /* 721 */
+ { 84, 12, 3, 0, 0, 26753, 102, }, /* 722 */
+ { 69, 26, 12, 0, 0, 28672, 68, }, /* 723 */
+ { 69, 9, 12, 0, 0, 18432, 112, }, /* 724 */
+ { 69, 5, 12, 0, 0, 18432, 232, }, /* 725 */
+ { 69, 25, 12, 0, 0, 28672, 234, }, /* 726 */
+ { 69, 26, 14, 0, 0, 28672, 236, }, /* 727 */
+ { 1, 9, 12, 96, -7517, 18432, 74, }, /* 728 */
+ { 69, 26, 12, 0, 0, 28672, 118, }, /* 729 */
+ { 0, 9, 12, 100, -8383, 18432, 74, }, /* 730 */
+ { 0, 9, 12, 104, -8262, 18432, 74, }, /* 731 */
+ { 69, 26, 12, 0, 0, 14336, 238, }, /* 732 */
+ { 0, 9, 12, 0, 28, 18432, 74, }, /* 733 */
+ { 69, 7, 12, 0, 0, 18432, 240, }, /* 734 */
+ { 69, 5, 14, 0, 0, 18432, 242, }, /* 735 */
+ { 69, 5, 12, 0, 0, 18432, 244, }, /* 736 */
+ { 0, 5, 12, 0, -28, 18432, 76, }, /* 737 */
+ { 0, 14, 12, 0, 16, 18432, 74, }, /* 738 */
+ { 0, 14, 12, 0, -16, 18432, 76, }, /* 739 */
+ { 0, 14, 12, 0, 0, 18432, 82, }, /* 740 */
+ { 69, 25, 14, 0, 0, 28672, 246, }, /* 741 */
+ { 69, 26, 14, 0, 0, 28672, 246, }, /* 742 */
+ { 69, 26, 12, 0, 0, 28672, 64, }, /* 743 */
+ { 69, 25, 12, 0, 0, 28672, 248, }, /* 744 */
+ { 69, 25, 12, 0, 0, 12288, 250, }, /* 745 */
+ { 69, 22, 12, 0, 0, 28672, 248, }, /* 746 */
+ { 69, 18, 12, 0, 0, 28672, 248, }, /* 747 */
+ { 69, 26, 14, 0, 0, 28672, 252, }, /* 748 */
+ { 69, 22, 12, 0, 0, 28672, 254, }, /* 749 */
+ { 69, 18, 12, 0, 0, 28672, 254, }, /* 750 */
+ { 69, 26, 12, 0, 0, 18432, 54, }, /* 751 */
+ { 69, 26, 14, 0, 0, 28672, 256, }, /* 752 */
+ { 68, 2, 12, 0, 0, 18432, 258, }, /* 753 */
+ { 69, 26, 12, 0, 26, 18432, 260, }, /* 754 */
+ { 69, 26, 14, 0, 26, 18432, 262, }, /* 755 */
+ { 69, 26, 12, 0, -26, 18432, 264, }, /* 756 */
+ { 69, 25, 14, 0, 0, 28672, 266, }, /* 757 */
+ { 69, 26, 14, 0, 0, 28672, 268, }, /* 758 */
+ { 69, 26, 14, 0, 0, 28672, 270, }, /* 759 */
+ { 69, 25, 14, 0, 0, 28672, 268, }, /* 760 */
+ { 69, 26, 14, 0, 0, 18432, 256, }, /* 761 */
+ { 69, 26, 14, 0, 0, 28672, 272, }, /* 762 */
+ { 88, 26, 12, 0, 0, 18432, 54, }, /* 763 */
+ { 69, 26, 12, 0, 0, 28672, 216, }, /* 764 */
+ { 35, 9, 12, 0, 48, 18432, 74, }, /* 765 */
+ { 35, 5, 12, 0, -48, 18432, 76, }, /* 766 */
+ { 0, 9, 12, 0, -10743, 18432, 74, }, /* 767 */
+ { 0, 9, 12, 0, -3814, 18432, 74, }, /* 768 */
+ { 0, 9, 12, 0, -10727, 18432, 74, }, /* 769 */
+ { 0, 5, 12, 0, -10795, 18432, 76, }, /* 770 */
+ { 0, 5, 12, 0, -10792, 18432, 76, }, /* 771 */
+ { 0, 9, 12, 0, -10780, 18432, 74, }, /* 772 */
+ { 0, 9, 12, 0, -10749, 18432, 74, }, /* 773 */
+ { 0, 9, 12, 0, -10783, 18432, 74, }, /* 774 */
+ { 0, 9, 12, 0, -10782, 18432, 74, }, /* 775 */
+ { 0, 9, 12, 0, -10815, 18432, 74, }, /* 776 */
+ { 34, 5, 12, 0, 0, 18432, 60, }, /* 777 */
+ { 34, 26, 12, 0, 0, 28672, 68, }, /* 778 */
+ { 34, 12, 3, 0, 0, 26624, 96, }, /* 779 */
+ { 34, 21, 12, 0, 0, 28672, 68, }, /* 780 */
+ { 34, 15, 12, 0, 0, 28672, 68, }, /* 781 */
+ { 17, 5, 12, 0, -7264, 18432, 76, }, /* 782 */
+ { 90, 7, 12, 0, 0, 18432, 82, }, /* 783 */
+ { 90, 6, 12, 0, 0, 18432, 142, }, /* 784 */
+ { 90, 21, 12, 0, 0, 18432, 68, }, /* 785 */
+ { 90, 12, 3, 0, 0, 26624, 182, }, /* 786 */
+ { 2, 12, 3, 0, 0, 26624, 130, }, /* 787 */
+ { 69, 20, 12, 0, 0, 28672, 216, }, /* 788 */
+ { 69, 19, 12, 0, 0, 28672, 216, }, /* 789 */
+ { 69, 6, 12, 0, 0, 28672, 274, }, /* 790 */
+ { 69, 21, 12, 0, 0, 28672, 276, }, /* 791 */
+ { 69, 21, 12, 0, 0, 28726, 54, }, /* 792 */
+ { 23, 26, 12, 0, 0, 28672, 278, }, /* 793 */
+ { 69, 26, 12, 0, 0, 28672, 280, }, /* 794 */
+ { 69, 26, 12, 0, 0, 28672, 282, }, /* 795 */
+ { 69, 21, 12, 0, 0, 28825, 276, }, /* 796 */
+ { 69, 21, 12, 0, 0, 28825, 212, }, /* 797 */
+ { 69, 21, 12, 0, 0, 28819, 54, }, /* 798 */
+ { 23, 6, 12, 0, 0, 18432, 136, }, /* 799 */
+ { 69, 7, 12, 0, 0, 18447, 284, }, /* 800 */
+ { 23, 14, 12, 0, 0, 18432, 284, }, /* 801 */
+ { 69, 22, 12, 0, 0, 28825, 216, }, /* 802 */
+ { 69, 18, 12, 0, 0, 28825, 216, }, /* 803 */
+ { 69, 22, 12, 0, 0, 28825, 62, }, /* 804 */
+ { 69, 18, 12, 0, 0, 28825, 62, }, /* 805 */
+ { 69, 26, 12, 0, 0, 28819, 54, }, /* 806 */
+ { 69, 17, 12, 0, 0, 28819, 202, }, /* 807 */
+ { 69, 22, 12, 0, 0, 28819, 206, }, /* 808 */
+ { 69, 18, 12, 0, 0, 28819, 206, }, /* 809 */
+ { 84, 12, 3, 0, 0, 26669, 96, }, /* 810 */
+ { 18, 10, 3, 0, 0, 18432, 286, }, /* 811 */
+ { 69, 17, 14, 0, 0, 28819, 288, }, /* 812 */
+ { 69, 6, 12, 0, 0, 18525, 136, }, /* 813 */
+ { 69, 26, 12, 0, 0, 28819, 68, }, /* 814 */
+ { 23, 6, 12, 0, 0, 18432, 142, }, /* 815 */
+ { 69, 7, 12, 0, 0, 18564, 82, }, /* 816 */
+ { 69, 21, 14, 0, 0, 28804, 236, }, /* 817 */
+ { 69, 26, 12, 0, 0, 28687, 68, }, /* 818 */
+ { 20, 7, 12, 0, 0, 18432, 82, }, /* 819 */
+ { 84, 12, 3, 0, 0, 26717, 96, }, /* 820 */
+ { 69, 24, 12, 0, 0, 28765, 290, }, /* 821 */
+ { 20, 6, 12, 0, 0, 18432, 136, }, /* 822 */
+ { 69, 17, 12, 0, 0, 28765, 126, }, /* 823 */
+ { 21, 7, 12, 0, 0, 18432, 82, }, /* 824 */
+ { 69, 21, 12, 0, 0, 28825, 68, }, /* 825 */
+ { 69, 6, 12, 0, 0, 18525, 94, }, /* 826 */
+ { 21, 6, 12, 0, 0, 18432, 136, }, /* 827 */
+ { 22, 7, 12, 0, 0, 18432, 82, }, /* 828 */
+ { 18, 7, 12, 0, 0, 18432, 82, }, /* 829 */
+ { 18, 7, 12, 0, 0, 18432, 168, }, /* 830 */
+ { 69, 26, 12, 0, 0, 18447, 68, }, /* 831 */
+ { 69, 15, 12, 0, 0, 18447, 68, }, /* 832 */
+ { 18, 26, 12, 0, 0, 18432, 68, }, /* 833 */
+ { 18, 26, 12, 0, 0, 28672, 68, }, /* 834 */
+ { 69, 15, 12, 0, 0, 18432, 68, }, /* 835 */
+ { 69, 26, 14, 0, 0, 18447, 236, }, /* 836 */
+ { 21, 26, 12, 0, 0, 18432, 68, }, /* 837 */
+ { 23, 7, 12, 0, 0, 18432, 292, }, /* 838 */
+ { 24, 7, 12, 0, 0, 18432, 82, }, /* 839 */
+ { 24, 6, 12, 0, 0, 18432, 136, }, /* 840 */
+ { 24, 26, 12, 0, 0, 28672, 68, }, /* 841 */
+ { 111, 7, 12, 0, 0, 18432, 82, }, /* 842 */
+ { 111, 6, 12, 0, 0, 18432, 142, }, /* 843 */
+ { 111, 21, 12, 0, 0, 18432, 106, }, /* 844 */
+ { 111, 21, 12, 0, 0, 18432, 124, }, /* 845 */
+ { 99, 7, 12, 0, 0, 18432, 82, }, /* 846 */
+ { 99, 6, 12, 0, 0, 18432, 136, }, /* 847 */
+ { 99, 21, 12, 0, 0, 28672, 106, }, /* 848 */
+ { 99, 21, 12, 0, 0, 28672, 124, }, /* 849 */
+ { 99, 13, 12, 0, 0, 18432, 138, }, /* 850 */
+ { 2, 9, 12, 108, 1, 18432, 74, }, /* 851 */
+ { 2, 5, 12, 108, -35267, 18432, 76, }, /* 852 */
+ { 2, 7, 12, 0, 0, 18432, 82, }, /* 853 */
+ { 2, 21, 12, 0, 0, 28672, 68, }, /* 854 */
+ { 2, 12, 3, 0, 0, 26624, 96, }, /* 855 */
+ { 2, 6, 12, 0, 0, 28672, 92, }, /* 856 */
+ { 2, 6, 12, 0, 0, 18432, 88, }, /* 857 */
+ { 112, 7, 12, 0, 0, 18432, 82, }, /* 858 */
+ { 112, 14, 12, 0, 0, 18432, 82, }, /* 859 */
+ { 112, 12, 3, 0, 0, 26624, 96, }, /* 860 */
+ { 112, 21, 12, 0, 0, 18432, 68, }, /* 861 */
+ { 112, 21, 12, 0, 0, 18432, 124, }, /* 862 */
+ { 112, 21, 12, 0, 0, 18432, 106, }, /* 863 */
+ { 69, 24, 12, 0, 0, 28762, 56, }, /* 864 */
+ { 0, 9, 12, 0, -35332, 18432, 74, }, /* 865 */
+ { 69, 24, 12, 0, 0, 18432, 56, }, /* 866 */
+ { 0, 9, 12, 0, -42280, 18432, 74, }, /* 867 */
+ { 0, 5, 12, 0, 48, 18432, 76, }, /* 868 */
+ { 0, 9, 12, 0, -42308, 18432, 74, }, /* 869 */
+ { 0, 9, 12, 0, -42319, 18432, 74, }, /* 870 */
+ { 0, 9, 12, 0, -42315, 18432, 74, }, /* 871 */
+ { 0, 9, 12, 0, -42305, 18432, 74, }, /* 872 */
+ { 0, 9, 12, 0, -42258, 18432, 74, }, /* 873 */
+ { 0, 9, 12, 0, -42282, 18432, 74, }, /* 874 */
+ { 0, 9, 12, 0, -42261, 18432, 74, }, /* 875 */
+ { 0, 9, 12, 0, 928, 18432, 74, }, /* 876 */
+ { 0, 9, 12, 0, -48, 18432, 74, }, /* 877 */
+ { 0, 9, 12, 0, -42307, 18432, 74, }, /* 878 */
+ { 0, 9, 12, 0, -35384, 18432, 74, }, /* 879 */
+ { 0, 6, 12, 0, 0, 18432, 142, }, /* 880 */
+ { 36, 7, 12, 0, 0, 18432, 82, }, /* 881 */
+ { 36, 12, 3, 0, 0, 26624, 130, }, /* 882 */
+ { 36, 12, 3, 0, 0, 26624, 182, }, /* 883 */
+ { 36, 10, 5, 0, 0, 18432, 144, }, /* 884 */
+ { 36, 26, 12, 0, 0, 28672, 68, }, /* 885 */
+ { 69, 15, 12, 0, 0, 18612, 68, }, /* 886 */
+ { 69, 15, 12, 0, 0, 18609, 68, }, /* 887 */
+ { 69, 26, 12, 0, 0, 18600, 68, }, /* 888 */
+ { 69, 23, 12, 0, 0, 14504, 68, }, /* 889 */
+ { 69, 26, 12, 0, 0, 14504, 68, }, /* 890 */
+ { 37, 7, 12, 0, 0, 18432, 82, }, /* 891 */
+ { 37, 21, 12, 0, 0, 28672, 68, }, /* 892 */
+ { 37, 21, 12, 0, 0, 28672, 124, }, /* 893 */
+ { 100, 10, 5, 0, 0, 18432, 144, }, /* 894 */
+ { 100, 7, 12, 0, 0, 18432, 82, }, /* 895 */
+ { 100, 12, 3, 0, 0, 26624, 146, }, /* 896 */
+ { 100, 12, 3, 0, 0, 26624, 130, }, /* 897 */
+ { 100, 21, 12, 0, 0, 18432, 124, }, /* 898 */
+ { 100, 13, 12, 0, 0, 18432, 138, }, /* 899 */
+ { 6, 12, 3, 0, 0, 26666, 96, }, /* 900 */
+ { 6, 7, 12, 0, 0, 18507, 82, }, /* 901 */
+ { 39, 13, 12, 0, 0, 18432, 138, }, /* 902 */
+ { 39, 7, 12, 0, 0, 18432, 82, }, /* 903 */
+ { 39, 12, 3, 0, 0, 26624, 130, }, /* 904 */
+ { 39, 12, 3, 0, 0, 26624, 96, }, /* 905 */
+ { 69, 21, 12, 0, 0, 18567, 188, }, /* 906 */
+ { 39, 21, 12, 0, 0, 18432, 124, }, /* 907 */
+ { 101, 7, 12, 0, 0, 18432, 82, }, /* 908 */
+ { 101, 12, 3, 0, 0, 26624, 130, }, /* 909 */
+ { 101, 10, 5, 0, 0, 18432, 144, }, /* 910 */
+ { 101, 10, 5, 0, 0, 18432, 172, }, /* 911 */
+ { 101, 21, 12, 0, 0, 18432, 68, }, /* 912 */
+ { 40, 12, 3, 0, 0, 26624, 130, }, /* 913 */
+ { 40, 10, 5, 0, 0, 18432, 144, }, /* 914 */
+ { 40, 7, 12, 0, 0, 18432, 82, }, /* 915 */
+ { 40, 12, 3, 0, 0, 26624, 96, }, /* 916 */
+ { 40, 10, 5, 0, 0, 18432, 172, }, /* 917 */
+ { 40, 21, 12, 0, 0, 18432, 68, }, /* 918 */
+ { 40, 21, 12, 0, 0, 18432, 106, }, /* 919 */
+ { 40, 21, 12, 0, 0, 18432, 124, }, /* 920 */
+ { 69, 6, 12, 0, 0, 18480, 136, }, /* 921 */
+ { 40, 13, 12, 0, 0, 18432, 138, }, /* 922 */
+ { 16, 6, 12, 0, 0, 18432, 136, }, /* 923 */
+ { 105, 7, 12, 0, 0, 18432, 82, }, /* 924 */
+ { 105, 12, 3, 0, 0, 26624, 130, }, /* 925 */
+ { 105, 10, 5, 0, 0, 18432, 144, }, /* 926 */
+ { 105, 13, 12, 0, 0, 18432, 138, }, /* 927 */
+ { 105, 21, 12, 0, 0, 18432, 68, }, /* 928 */
+ { 105, 21, 12, 0, 0, 18432, 124, }, /* 929 */
+ { 107, 7, 12, 0, 0, 18432, 82, }, /* 930 */
+ { 107, 12, 3, 0, 0, 26624, 130, }, /* 931 */
+ { 107, 7, 12, 0, 0, 18432, 156, }, /* 932 */
+ { 107, 12, 3, 0, 0, 26624, 96, }, /* 933 */
+ { 107, 7, 12, 0, 0, 18432, 294, }, /* 934 */
+ { 107, 6, 12, 0, 0, 18432, 136, }, /* 935 */
+ { 107, 21, 12, 0, 0, 18432, 68, }, /* 936 */
+ { 107, 21, 12, 0, 0, 18432, 106, }, /* 937 */
+ { 113, 7, 12, 0, 0, 18432, 82, }, /* 938 */
+ { 113, 10, 5, 0, 0, 18432, 144, }, /* 939 */
+ { 113, 12, 3, 0, 0, 26624, 130, }, /* 940 */
+ { 113, 21, 12, 0, 0, 18432, 124, }, /* 941 */
+ { 113, 6, 12, 0, 0, 18432, 136, }, /* 942 */
+ { 113, 12, 3, 0, 0, 26624, 146, }, /* 943 */
+ { 0, 5, 12, 0, -928, 18432, 76, }, /* 944 */
+ { 0, 6, 12, 0, 0, 18432, 92, }, /* 945 */
+ { 76, 5, 12, 0, -38864, 18432, 70, }, /* 946 */
+ { 113, 10, 5, 0, 0, 18432, 160, }, /* 947 */
+ { 113, 13, 12, 0, 0, 18432, 138, }, /* 948 */
+ { 18, 7, 9, 0, 0, 18432, 82, }, /* 949 */
+ { 18, 7, 10, 0, 0, 18432, 82, }, /* 950 */
+ { 68, 4, 12, 0, 0, 18432, 0, }, /* 951 */
+ { 68, 3, 12, 0, 0, 18432, 0, }, /* 952 */
+ { 23, 7, 12, 0, 0, 18432, 284, }, /* 953 */
+ { 71, 25, 12, 0, 0, 12288, 118, }, /* 954 */
+ { 3, 7, 12, 0, 0, 0, 296, }, /* 955 */
+ { 69, 18, 12, 0, 0, 28705, 54, }, /* 956 */
+ { 69, 22, 12, 0, 0, 28705, 54, }, /* 957 */
+ { 68, 2, 12, 0, 0, 6144, 298, }, /* 958 */
+ { 3, 7, 12, 0, 0, 39, 82, }, /* 959 */
+ { 3, 26, 12, 0, 0, 28711, 68, }, /* 960 */
+ { 84, 12, 3, 0, 0, 26624, 178, }, /* 961 */
+ { 84, 12, 3, 0, 0, 26624, 300, }, /* 962 */
+ { 69, 21, 12, 0, 0, 28672, 68, }, /* 963 */
+ { 69, 21, 12, 0, 0, 28672, 122, }, /* 964 */
+ { 69, 22, 12, 0, 0, 28672, 68, }, /* 965 */
+ { 69, 18, 12, 0, 0, 28672, 68, }, /* 966 */
+ { 69, 17, 12, 0, 0, 28672, 126, }, /* 967 */
+ { 69, 22, 12, 0, 0, 28672, 302, }, /* 968 */
+ { 69, 18, 12, 0, 0, 28672, 302, }, /* 969 */
+ { 69, 21, 12, 0, 0, 8192, 106, }, /* 970 */
+ { 69, 21, 12, 0, 0, 8192, 304, }, /* 971 */
+ { 69, 21, 12, 0, 0, 8192, 306, }, /* 972 */
+ { 69, 21, 12, 0, 0, 28672, 124, }, /* 973 */
+ { 69, 22, 12, 0, 0, 28672, 158, }, /* 974 */
+ { 69, 18, 12, 0, 0, 28672, 158, }, /* 975 */
+ { 69, 21, 12, 0, 0, 14336, 68, }, /* 976 */
+ { 69, 21, 12, 0, 0, 28672, 118, }, /* 977 */
+ { 69, 17, 12, 0, 0, 12288, 224, }, /* 978 */
+ { 69, 25, 12, 0, 0, 28672, 226, }, /* 979 */
+ { 69, 21, 12, 0, 0, 28672, 302, }, /* 980 */
+ { 69, 21, 12, 0, 0, 28672, 308, }, /* 981 */
+ { 69, 17, 12, 0, 0, 12288, 126, }, /* 982 */
+ { 69, 21, 12, 0, 0, 8192, 68, }, /* 983 */
+ { 69, 13, 12, 0, 0, 10240, 310, }, /* 984 */
+ { 0, 9, 12, 0, 32, 18432, 312, }, /* 985 */
+ { 69, 24, 12, 0, 0, 28672, 314, }, /* 986 */
+ { 0, 5, 12, 0, -32, 18432, 316, }, /* 987 */
+ { 69, 21, 12, 0, 0, 28825, 124, }, /* 988 */
+ { 69, 22, 12, 0, 0, 28825, 318, }, /* 989 */
+ { 69, 18, 12, 0, 0, 28825, 318, }, /* 990 */
+ { 69, 21, 12, 0, 0, 28825, 106, }, /* 991 */
+ { 69, 6, 3, 0, 0, 18525, 320, }, /* 992 */
+ { 69, 1, 2, 0, 0, 28672, 322, }, /* 993 */
+ { 31, 7, 12, 0, 0, 18432, 82, }, /* 994 */
+ { 69, 21, 12, 0, 0, 18552, 68, }, /* 995 */
+ { 69, 21, 12, 0, 0, 28792, 68, }, /* 996 */
+ { 69, 21, 12, 0, 0, 18483, 68, }, /* 997 */
+ { 69, 15, 12, 0, 0, 18555, 68, }, /* 998 */
+ { 69, 26, 12, 0, 0, 18483, 68, }, /* 999 */
+ { 1, 14, 12, 0, 0, 28672, 82, }, /* 1000 */
+ { 1, 15, 12, 0, 0, 28672, 68, }, /* 1001 */
+ { 1, 26, 12, 0, 0, 28672, 68, }, /* 1002 */
+ { 1, 26, 12, 0, 0, 18432, 68, }, /* 1003 */
+ { 102, 7, 12, 0, 0, 18432, 82, }, /* 1004 */
+ { 103, 7, 12, 0, 0, 18432, 82, }, /* 1005 */
+ { 84, 12, 3, 0, 0, 26651, 96, }, /* 1006 */
+ { 69, 15, 12, 0, 0, 10267, 68, }, /* 1007 */
+ { 81, 7, 12, 0, 0, 18432, 82, }, /* 1008 */
+ { 81, 15, 12, 0, 0, 18432, 68, }, /* 1009 */
+ { 82, 7, 12, 0, 0, 18432, 82, }, /* 1010 */
+ { 82, 14, 12, 0, 0, 18432, 82, }, /* 1011 */
+ { 53, 7, 12, 0, 0, 18432, 82, }, /* 1012 */
+ { 53, 12, 3, 0, 0, 26624, 130, }, /* 1013 */
+ { 85, 7, 12, 0, 0, 18432, 82, }, /* 1014 */
+ { 85, 21, 12, 0, 0, 18432, 106, }, /* 1015 */
+ { 91, 7, 12, 0, 0, 18432, 82, }, /* 1016 */
+ { 91, 21, 12, 0, 0, 18432, 106, }, /* 1017 */
+ { 91, 14, 12, 0, 0, 18432, 82, }, /* 1018 */
+ { 83, 9, 12, 0, 40, 18432, 74, }, /* 1019 */
+ { 83, 5, 12, 0, -40, 18432, 76, }, /* 1020 */
+ { 86, 7, 12, 0, 0, 18432, 82, }, /* 1021 */
+ { 87, 7, 12, 0, 0, 18432, 82, }, /* 1022 */
+ { 87, 13, 12, 0, 0, 18432, 138, }, /* 1023 */
+ { 145, 9, 12, 0, 40, 18432, 74, }, /* 1024 */
+ { 145, 5, 12, 0, -40, 18432, 76, }, /* 1025 */
+ { 127, 7, 12, 0, 0, 18432, 82, }, /* 1026 */
+ { 125, 7, 12, 0, 0, 18432, 82, }, /* 1027 */
+ { 125, 21, 12, 0, 0, 18432, 68, }, /* 1028 */
+ { 161, 9, 12, 0, 39, 18432, 74, }, /* 1029 */
+ { 161, 5, 12, 0, -39, 18432, 76, }, /* 1030 */
+ { 49, 7, 12, 0, 0, 18432, 82, }, /* 1031 */
+ { 0, 6, 12, 0, 0, 18432, 94, }, /* 1032 */
+ { 32, 7, 12, 0, 0, 34816, 82, }, /* 1033 */
+ { 114, 7, 12, 0, 0, 34816, 82, }, /* 1034 */
+ { 114, 21, 12, 0, 0, 34816, 106, }, /* 1035 */
+ { 114, 15, 12, 0, 0, 34816, 68, }, /* 1036 */
+ { 133, 7, 12, 0, 0, 34816, 82, }, /* 1037 */
+ { 133, 26, 12, 0, 0, 34816, 68, }, /* 1038 */
+ { 133, 15, 12, 0, 0, 34816, 68, }, /* 1039 */
+ { 132, 7, 12, 0, 0, 34816, 82, }, /* 1040 */
+ { 132, 15, 12, 0, 0, 34816, 68, }, /* 1041 */
+ { 139, 7, 12, 0, 0, 34816, 82, }, /* 1042 */
+ { 139, 15, 12, 0, 0, 34816, 68, }, /* 1043 */
+ { 95, 7, 12, 0, 0, 34816, 82, }, /* 1044 */
+ { 95, 15, 12, 0, 0, 34816, 68, }, /* 1045 */
+ { 95, 21, 12, 0, 0, 28672, 106, }, /* 1046 */
+ { 104, 7, 12, 0, 0, 34816, 82, }, /* 1047 */
+ { 104, 21, 12, 0, 0, 34816, 68, }, /* 1048 */
+ { 122, 7, 12, 0, 0, 34816, 82, }, /* 1049 */
+ { 121, 7, 12, 0, 0, 34816, 82, }, /* 1050 */
+ { 121, 15, 12, 0, 0, 34816, 68, }, /* 1051 */
+ { 92, 7, 12, 0, 0, 34816, 82, }, /* 1052 */
+ { 92, 12, 3, 0, 0, 26624, 130, }, /* 1053 */
+ { 92, 12, 3, 0, 0, 26624, 102, }, /* 1054 */
+ { 92, 12, 3, 0, 0, 26624, 182, }, /* 1055 */
+ { 92, 15, 12, 0, 0, 34816, 68, }, /* 1056 */
+ { 92, 21, 12, 0, 0, 34816, 68, }, /* 1057 */
+ { 92, 21, 12, 0, 0, 34816, 124, }, /* 1058 */
+ { 115, 7, 12, 0, 0, 34816, 82, }, /* 1059 */
+ { 115, 15, 12, 0, 0, 34816, 68, }, /* 1060 */
+ { 115, 21, 12, 0, 0, 34816, 68, }, /* 1061 */
+ { 131, 7, 12, 0, 0, 34816, 82, }, /* 1062 */
+ { 131, 15, 12, 0, 0, 34816, 68, }, /* 1063 */
+ { 51, 7, 12, 0, 0, 34816, 82, }, /* 1064 */
+ { 51, 26, 12, 0, 0, 34816, 68, }, /* 1065 */
+ { 51, 12, 3, 0, 0, 26624, 96, }, /* 1066 */
+ { 51, 15, 12, 0, 0, 34816, 68, }, /* 1067 */
+ { 51, 21, 12, 0, 0, 34816, 106, }, /* 1068 */
+ { 51, 21, 12, 0, 0, 34918, 106, }, /* 1069 */
+ { 51, 21, 12, 0, 0, 34816, 68, }, /* 1070 */
+ { 108, 7, 12, 0, 0, 34816, 82, }, /* 1071 */
+ { 108, 21, 12, 0, 0, 28672, 68, }, /* 1072 */
+ { 108, 21, 12, 0, 0, 28672, 106, }, /* 1073 */
+ { 116, 7, 12, 0, 0, 34816, 82, }, /* 1074 */
+ { 116, 15, 12, 0, 0, 34816, 68, }, /* 1075 */
+ { 117, 7, 12, 0, 0, 34816, 82, }, /* 1076 */
+ { 117, 15, 12, 0, 0, 34816, 68, }, /* 1077 */
+ { 54, 7, 12, 0, 0, 34816, 82, }, /* 1078 */
+ { 54, 21, 12, 0, 0, 34816, 106, }, /* 1079 */
+ { 54, 15, 12, 0, 0, 34816, 68, }, /* 1080 */
+ { 118, 7, 12, 0, 0, 34816, 82, }, /* 1081 */
+ { 140, 9, 12, 0, 64, 34816, 74, }, /* 1082 */
+ { 140, 5, 12, 0, -64, 34816, 76, }, /* 1083 */
+ { 140, 15, 12, 0, 0, 34816, 68, }, /* 1084 */
+ { 62, 7, 12, 0, 0, 0, 82, }, /* 1085 */
+ { 62, 7, 12, 0, 0, 0, 294, }, /* 1086 */
+ { 62, 12, 3, 0, 0, 26624, 128, }, /* 1087 */
+ { 62, 13, 12, 0, 0, 2048, 138, }, /* 1088 */
+ { 3, 15, 12, 0, 0, 2048, 68, }, /* 1089 */
+ { 65, 7, 12, 0, 0, 34816, 82, }, /* 1090 */
+ { 65, 12, 3, 0, 0, 26624, 130, }, /* 1091 */
+ { 65, 17, 12, 0, 0, 34816, 126, }, /* 1092 */
+ { 152, 7, 12, 0, 0, 34816, 82, }, /* 1093 */
+ { 152, 15, 12, 0, 0, 34816, 68, }, /* 1094 */
+ { 63, 7, 12, 0, 0, 0, 82, }, /* 1095 */
+ { 63, 12, 3, 0, 0, 26624, 96, }, /* 1096 */
+ { 63, 15, 12, 0, 0, 0, 68, }, /* 1097 */
+ { 63, 21, 12, 0, 0, 0, 124, }, /* 1098 */
+ { 67, 7, 12, 0, 0, 34816, 82, }, /* 1099 */
+ { 67, 12, 3, 0, 0, 26624, 96, }, /* 1100 */
+ { 67, 21, 12, 0, 0, 34816, 124, }, /* 1101 */
+ { 156, 7, 12, 0, 0, 34816, 82, }, /* 1102 */
+ { 156, 15, 12, 0, 0, 34816, 68, }, /* 1103 */
+ { 153, 7, 12, 0, 0, 34816, 82, }, /* 1104 */
+ { 120, 10, 5, 0, 0, 18432, 144, }, /* 1105 */
+ { 120, 12, 3, 0, 0, 26624, 130, }, /* 1106 */
+ { 120, 7, 12, 0, 0, 18432, 82, }, /* 1107 */
+ { 120, 12, 3, 0, 0, 26624, 146, }, /* 1108 */
+ { 120, 21, 12, 0, 0, 18432, 124, }, /* 1109 */
+ { 120, 21, 12, 0, 0, 18432, 106, }, /* 1110 */
+ { 120, 15, 12, 0, 0, 28672, 68, }, /* 1111 */
+ { 120, 13, 12, 0, 0, 18432, 138, }, /* 1112 */
+ { 120, 12, 3, 0, 0, 26624, 182, }, /* 1113 */
+ { 41, 12, 3, 0, 0, 26624, 102, }, /* 1114 */
+ { 41, 10, 5, 0, 0, 18432, 144, }, /* 1115 */
+ { 41, 7, 12, 0, 0, 18432, 82, }, /* 1116 */
+ { 41, 12, 3, 0, 0, 26624, 130, }, /* 1117 */
+ { 41, 12, 3, 0, 0, 26624, 146, }, /* 1118 */
+ { 41, 12, 3, 0, 0, 26624, 96, }, /* 1119 */
+ { 41, 21, 12, 0, 0, 18432, 68, }, /* 1120 */
+ { 41, 1, 4, 0, 0, 18432, 132, }, /* 1121 */
+ { 41, 21, 12, 0, 0, 18432, 124, }, /* 1122 */
+ { 124, 7, 12, 0, 0, 18432, 82, }, /* 1123 */
+ { 124, 13, 12, 0, 0, 18432, 138, }, /* 1124 */
+ { 43, 12, 3, 0, 0, 26624, 130, }, /* 1125 */
+ { 43, 7, 12, 0, 0, 18432, 82, }, /* 1126 */
+ { 43, 10, 5, 0, 0, 18432, 144, }, /* 1127 */
+ { 43, 12, 3, 0, 0, 26624, 146, }, /* 1128 */
+ { 43, 13, 12, 0, 0, 18432, 138, }, /* 1129 */
+ { 43, 21, 12, 0, 0, 18432, 68, }, /* 1130 */
+ { 43, 21, 12, 0, 0, 18432, 124, }, /* 1131 */
+ { 50, 7, 12, 0, 0, 18432, 82, }, /* 1132 */
+ { 50, 12, 3, 0, 0, 26624, 96, }, /* 1133 */
+ { 50, 21, 12, 0, 0, 18432, 68, }, /* 1134 */
+ { 44, 12, 3, 0, 0, 26624, 130, }, /* 1135 */
+ { 44, 10, 5, 0, 0, 18432, 144, }, /* 1136 */
+ { 44, 7, 12, 0, 0, 18432, 82, }, /* 1137 */
+ { 44, 10, 5, 0, 0, 18432, 172, }, /* 1138 */
+ { 44, 7, 4, 0, 0, 18432, 82, }, /* 1139 */
+ { 44, 21, 12, 0, 0, 18432, 124, }, /* 1140 */
+ { 44, 21, 12, 0, 0, 18432, 68, }, /* 1141 */
+ { 44, 12, 3, 0, 0, 26624, 102, }, /* 1142 */
+ { 44, 12, 3, 0, 0, 26624, 96, }, /* 1143 */
+ { 44, 13, 12, 0, 0, 18432, 138, }, /* 1144 */
+ { 15, 15, 12, 0, 0, 18432, 68, }, /* 1145 */
+ { 48, 7, 12, 0, 0, 18432, 82, }, /* 1146 */
+ { 48, 10, 5, 0, 0, 18432, 144, }, /* 1147 */
+ { 48, 12, 3, 0, 0, 26624, 130, }, /* 1148 */
+ { 48, 10, 5, 0, 0, 18432, 172, }, /* 1149 */
+ { 48, 12, 3, 0, 0, 26624, 96, }, /* 1150 */
+ { 48, 21, 12, 0, 0, 18432, 124, }, /* 1151 */
+ { 48, 21, 12, 0, 0, 18432, 106, }, /* 1152 */
+ { 48, 21, 12, 0, 0, 18432, 68, }, /* 1153 */
+ { 57, 7, 12, 0, 0, 18432, 82, }, /* 1154 */
+ { 57, 21, 12, 0, 0, 18432, 124, }, /* 1155 */
+ { 55, 7, 12, 0, 0, 18432, 82, }, /* 1156 */
+ { 55, 12, 3, 0, 0, 26624, 130, }, /* 1157 */
+ { 55, 10, 5, 0, 0, 18432, 144, }, /* 1158 */
+ { 55, 12, 3, 0, 0, 26624, 96, }, /* 1159 */
+ { 55, 12, 3, 0, 0, 26624, 146, }, /* 1160 */
+ { 55, 13, 12, 0, 0, 18432, 138, }, /* 1161 */
+ { 47, 12, 3, 0, 0, 26624, 130, }, /* 1162 */
+ { 47, 12, 3, 0, 0, 26705, 130, }, /* 1163 */
+ { 47, 10, 5, 0, 0, 18432, 144, }, /* 1164 */
+ { 47, 10, 5, 0, 0, 18513, 144, }, /* 1165 */
+ { 47, 7, 12, 0, 0, 18432, 82, }, /* 1166 */
+ { 84, 12, 3, 0, 0, 26705, 102, }, /* 1167 */
+ { 47, 12, 3, 0, 0, 26705, 96, }, /* 1168 */
+ { 47, 10, 3, 0, 0, 18432, 148, }, /* 1169 */
+ { 47, 10, 5, 0, 0, 18432, 172, }, /* 1170 */
+ { 47, 7, 12, 0, 0, 18432, 324, }, /* 1171 */
+ { 47, 12, 3, 0, 0, 26624, 96, }, /* 1172 */
+ { 144, 7, 12, 0, 0, 18432, 82, }, /* 1173 */
+ { 144, 10, 5, 0, 0, 18432, 144, }, /* 1174 */
+ { 144, 12, 3, 0, 0, 26624, 130, }, /* 1175 */
+ { 144, 12, 3, 0, 0, 26624, 146, }, /* 1176 */
+ { 144, 12, 3, 0, 0, 26624, 96, }, /* 1177 */
+ { 144, 21, 12, 0, 0, 18432, 124, }, /* 1178 */
+ { 144, 21, 12, 0, 0, 18432, 106, }, /* 1179 */
+ { 144, 21, 12, 0, 0, 18432, 68, }, /* 1180 */
+ { 144, 13, 12, 0, 0, 18432, 138, }, /* 1181 */
+ { 144, 12, 3, 0, 0, 26624, 102, }, /* 1182 */
+ { 56, 7, 12, 0, 0, 18432, 82, }, /* 1183 */
+ { 56, 10, 3, 0, 0, 18432, 148, }, /* 1184 */
+ { 56, 10, 5, 0, 0, 18432, 144, }, /* 1185 */
+ { 56, 12, 3, 0, 0, 26624, 130, }, /* 1186 */
+ { 56, 12, 3, 0, 0, 26624, 146, }, /* 1187 */
+ { 56, 12, 3, 0, 0, 26624, 96, }, /* 1188 */
+ { 56, 21, 12, 0, 0, 18432, 68, }, /* 1189 */
+ { 56, 13, 12, 0, 0, 18432, 138, }, /* 1190 */
+ { 135, 7, 12, 0, 0, 18432, 82, }, /* 1191 */
+ { 135, 10, 3, 0, 0, 18432, 148, }, /* 1192 */
+ { 135, 10, 5, 0, 0, 18432, 144, }, /* 1193 */
+ { 135, 12, 3, 0, 0, 26624, 130, }, /* 1194 */
+ { 135, 12, 3, 0, 0, 26624, 146, }, /* 1195 */
+ { 135, 12, 3, 0, 0, 26624, 96, }, /* 1196 */
+ { 135, 21, 12, 0, 0, 18432, 68, }, /* 1197 */
+ { 135, 21, 12, 0, 0, 18432, 124, }, /* 1198 */
+ { 135, 21, 12, 0, 0, 18432, 106, }, /* 1199 */
+ { 135, 21, 12, 0, 0, 18432, 176, }, /* 1200 */
+ { 52, 7, 12, 0, 0, 18432, 82, }, /* 1201 */
+ { 52, 10, 5, 0, 0, 18432, 144, }, /* 1202 */
+ { 52, 12, 3, 0, 0, 26624, 130, }, /* 1203 */
+ { 52, 12, 3, 0, 0, 26624, 146, }, /* 1204 */
+ { 52, 21, 12, 0, 0, 18432, 124, }, /* 1205 */
+ { 52, 21, 12, 0, 0, 18432, 68, }, /* 1206 */
+ { 52, 13, 12, 0, 0, 18432, 138, }, /* 1207 */
+ { 45, 7, 12, 0, 0, 18432, 82, }, /* 1208 */
+ { 45, 12, 3, 0, 0, 26624, 130, }, /* 1209 */
+ { 45, 10, 5, 0, 0, 18432, 144, }, /* 1210 */
+ { 45, 10, 5, 0, 0, 18432, 172, }, /* 1211 */
+ { 45, 12, 3, 0, 0, 26624, 96, }, /* 1212 */
+ { 45, 21, 12, 0, 0, 18432, 68, }, /* 1213 */
+ { 45, 13, 12, 0, 0, 18432, 138, }, /* 1214 */
+ { 137, 7, 12, 0, 0, 18432, 82, }, /* 1215 */
+ { 137, 12, 3, 0, 0, 26624, 130, }, /* 1216 */
+ { 137, 10, 12, 0, 0, 18432, 144, }, /* 1217 */
+ { 137, 10, 5, 0, 0, 18432, 144, }, /* 1218 */
+ { 137, 12, 3, 0, 0, 26624, 146, }, /* 1219 */
+ { 137, 13, 12, 0, 0, 18432, 138, }, /* 1220 */
+ { 137, 15, 12, 0, 0, 18432, 68, }, /* 1221 */
+ { 137, 21, 12, 0, 0, 18432, 124, }, /* 1222 */
+ { 137, 26, 12, 0, 0, 18432, 68, }, /* 1223 */
+ { 60, 7, 12, 0, 0, 18432, 82, }, /* 1224 */
+ { 60, 10, 5, 0, 0, 18432, 144, }, /* 1225 */
+ { 60, 12, 3, 0, 0, 26624, 130, }, /* 1226 */
+ { 60, 12, 3, 0, 0, 26624, 146, }, /* 1227 */
+ { 60, 12, 3, 0, 0, 26624, 96, }, /* 1228 */
+ { 60, 21, 12, 0, 0, 18432, 68, }, /* 1229 */
+ { 136, 9, 12, 0, 32, 18432, 74, }, /* 1230 */
+ { 136, 5, 12, 0, -32, 18432, 76, }, /* 1231 */
+ { 136, 13, 12, 0, 0, 18432, 138, }, /* 1232 */
+ { 136, 15, 12, 0, 0, 18432, 68, }, /* 1233 */
+ { 136, 7, 12, 0, 0, 18432, 82, }, /* 1234 */
+ { 157, 7, 12, 0, 0, 18432, 82, }, /* 1235 */
+ { 157, 10, 3, 0, 0, 18432, 148, }, /* 1236 */
+ { 157, 10, 5, 0, 0, 18432, 144, }, /* 1237 */
+ { 157, 12, 3, 0, 0, 26624, 130, }, /* 1238 */
+ { 157, 10, 5, 0, 0, 18432, 172, }, /* 1239 */
+ { 157, 12, 3, 0, 0, 26624, 146, }, /* 1240 */
+ { 157, 7, 4, 0, 0, 18432, 82, }, /* 1241 */
+ { 157, 12, 3, 0, 0, 26624, 96, }, /* 1242 */
+ { 157, 21, 12, 0, 0, 18432, 124, }, /* 1243 */
+ { 157, 21, 12, 0, 0, 18432, 68, }, /* 1244 */
+ { 157, 13, 12, 0, 0, 18432, 138, }, /* 1245 */
+ { 64, 7, 12, 0, 0, 18432, 82, }, /* 1246 */
+ { 64, 10, 5, 0, 0, 18432, 144, }, /* 1247 */
+ { 64, 12, 3, 0, 0, 26624, 130, }, /* 1248 */
+ { 64, 12, 3, 0, 0, 26624, 146, }, /* 1249 */
+ { 64, 21, 12, 0, 0, 18432, 68, }, /* 1250 */
+ { 149, 7, 12, 0, 0, 18432, 82, }, /* 1251 */
+ { 149, 12, 3, 0, 0, 26624, 130, }, /* 1252 */
+ { 149, 12, 3, 0, 0, 18432, 130, }, /* 1253 */
+ { 149, 12, 3, 0, 0, 26624, 102, }, /* 1254 */
+ { 149, 12, 3, 0, 0, 26624, 146, }, /* 1255 */
+ { 149, 10, 5, 0, 0, 18432, 144, }, /* 1256 */
+ { 149, 7, 4, 0, 0, 18432, 82, }, /* 1257 */
+ { 149, 21, 12, 0, 0, 18432, 68, }, /* 1258 */
+ { 149, 21, 12, 0, 0, 18432, 124, }, /* 1259 */
+ { 148, 7, 12, 0, 0, 18432, 82, }, /* 1260 */
+ { 148, 12, 3, 0, 0, 26624, 130, }, /* 1261 */
+ { 148, 10, 5, 0, 0, 18432, 144, }, /* 1262 */
+ { 148, 7, 4, 0, 0, 18432, 82, }, /* 1263 */
+ { 148, 12, 3, 0, 0, 26624, 326, }, /* 1264 */
+ { 148, 12, 3, 0, 0, 26624, 146, }, /* 1265 */
+ { 148, 21, 12, 0, 0, 18432, 68, }, /* 1266 */
+ { 148, 21, 12, 0, 0, 18432, 124, }, /* 1267 */
+ { 148, 21, 12, 0, 0, 18432, 106, }, /* 1268 */
+ { 134, 7, 12, 0, 0, 18432, 82, }, /* 1269 */
+ { 142, 7, 12, 0, 0, 18432, 82, }, /* 1270 */
+ { 142, 10, 5, 0, 0, 18432, 144, }, /* 1271 */
+ { 142, 12, 3, 0, 0, 26624, 130, }, /* 1272 */
+ { 142, 12, 3, 0, 0, 18432, 146, }, /* 1273 */
+ { 142, 21, 12, 0, 0, 18432, 124, }, /* 1274 */
+ { 142, 21, 12, 0, 0, 18432, 106, }, /* 1275 */
+ { 142, 21, 12, 0, 0, 18432, 68, }, /* 1276 */
+ { 142, 13, 12, 0, 0, 18432, 138, }, /* 1277 */
+ { 142, 15, 12, 0, 0, 18432, 68, }, /* 1278 */
+ { 143, 21, 12, 0, 0, 18432, 68, }, /* 1279 */
+ { 143, 21, 12, 0, 0, 18432, 106, }, /* 1280 */
+ { 143, 7, 12, 0, 0, 18432, 82, }, /* 1281 */
+ { 143, 12, 3, 0, 0, 26624, 130, }, /* 1282 */
+ { 143, 10, 5, 0, 0, 18432, 144, }, /* 1283 */
+ { 59, 7, 12, 0, 0, 18432, 82, }, /* 1284 */
+ { 59, 12, 3, 0, 0, 26624, 130, }, /* 1285 */
+ { 59, 12, 3, 0, 0, 26624, 96, }, /* 1286 */
+ { 59, 12, 3, 0, 0, 26624, 146, }, /* 1287 */
+ { 59, 7, 4, 0, 0, 18432, 82, }, /* 1288 */
+ { 59, 13, 12, 0, 0, 18432, 138, }, /* 1289 */
+ { 61, 7, 12, 0, 0, 18432, 82, }, /* 1290 */
+ { 61, 10, 5, 0, 0, 18432, 144, }, /* 1291 */
+ { 61, 12, 3, 0, 0, 26624, 130, }, /* 1292 */
+ { 61, 12, 3, 0, 0, 26624, 146, }, /* 1293 */
+ { 61, 13, 12, 0, 0, 18432, 138, }, /* 1294 */
+ { 150, 7, 12, 0, 0, 18432, 82, }, /* 1295 */
+ { 150, 12, 3, 0, 0, 26624, 130, }, /* 1296 */
+ { 150, 10, 5, 0, 0, 18432, 144, }, /* 1297 */
+ { 150, 21, 12, 0, 0, 18432, 124, }, /* 1298 */
+ { 11, 15, 12, 0, 0, 18432, 68, }, /* 1299 */
+ { 11, 21, 12, 0, 0, 18432, 68, }, /* 1300 */
+ { 94, 7, 12, 0, 0, 18432, 82, }, /* 1301 */
+ { 94, 14, 12, 0, 0, 18432, 82, }, /* 1302 */
+ { 94, 21, 12, 0, 0, 18432, 106, }, /* 1303 */
+ { 66, 7, 12, 0, 0, 18432, 82, }, /* 1304 */
+ { 66, 21, 12, 0, 0, 18432, 68, }, /* 1305 */
+ { 109, 7, 12, 0, 0, 18432, 82, }, /* 1306 */
+ { 109, 1, 2, 0, 0, 18432, 322, }, /* 1307 */
+ { 138, 7, 12, 0, 0, 18432, 82, }, /* 1308 */
+ { 130, 7, 12, 0, 0, 18432, 82, }, /* 1309 */
+ { 130, 13, 12, 0, 0, 18432, 138, }, /* 1310 */
+ { 130, 21, 12, 0, 0, 18432, 124, }, /* 1311 */
+ { 159, 7, 12, 0, 0, 18432, 82, }, /* 1312 */
+ { 159, 13, 12, 0, 0, 18432, 138, }, /* 1313 */
+ { 126, 7, 12, 0, 0, 18432, 82, }, /* 1314 */
+ { 126, 12, 3, 0, 0, 26624, 96, }, /* 1315 */
+ { 126, 21, 12, 0, 0, 18432, 124, }, /* 1316 */
+ { 128, 7, 12, 0, 0, 18432, 82, }, /* 1317 */
+ { 128, 12, 3, 0, 0, 26624, 96, }, /* 1318 */
+ { 128, 21, 12, 0, 0, 18432, 124, }, /* 1319 */
+ { 128, 21, 12, 0, 0, 18432, 106, }, /* 1320 */
+ { 128, 21, 12, 0, 0, 18432, 68, }, /* 1321 */
+ { 128, 26, 12, 0, 0, 18432, 68, }, /* 1322 */
+ { 128, 6, 12, 0, 0, 18432, 142, }, /* 1323 */
+ { 128, 6, 12, 0, 0, 18432, 136, }, /* 1324 */
+ { 128, 13, 12, 0, 0, 18432, 138, }, /* 1325 */
+ { 128, 15, 12, 0, 0, 18432, 68, }, /* 1326 */
+ { 151, 9, 12, 0, 32, 18432, 74, }, /* 1327 */
+ { 151, 5, 12, 0, -32, 18432, 76, }, /* 1328 */
+ { 151, 15, 12, 0, 0, 18432, 68, }, /* 1329 */
+ { 151, 21, 12, 0, 0, 18432, 106, }, /* 1330 */
+ { 151, 21, 12, 0, 0, 18432, 124, }, /* 1331 */
+ { 151, 21, 12, 0, 0, 18432, 68, }, /* 1332 */
+ { 123, 7, 12, 0, 0, 18432, 82, }, /* 1333 */
+ { 123, 12, 3, 0, 0, 26624, 130, }, /* 1334 */
+ { 123, 10, 5, 0, 0, 18432, 144, }, /* 1335 */
+ { 123, 12, 3, 0, 0, 26624, 128, }, /* 1336 */
+ { 123, 6, 12, 0, 0, 18432, 92, }, /* 1337 */
+ { 146, 6, 12, 0, 0, 18432, 136, }, /* 1338 */
+ { 147, 6, 12, 0, 0, 18432, 136, }, /* 1339 */
+ { 23, 21, 12, 0, 0, 28672, 68, }, /* 1340 */
+ { 158, 12, 3, 0, 0, 26624, 328, }, /* 1341 */
+ { 23, 10, 5, 0, 0, 18432, 164, }, /* 1342 */
+ { 146, 7, 12, 0, 0, 18432, 284, }, /* 1343 */
+ { 158, 7, 12, 0, 0, 18432, 284, }, /* 1344 */
+ { 21, 6, 12, 0, 0, 18432, 92, }, /* 1345 */
+ { 147, 7, 12, 0, 0, 18432, 284, }, /* 1346 */
+ { 46, 7, 12, 0, 0, 18432, 82, }, /* 1347 */
+ { 46, 26, 12, 0, 0, 18432, 68, }, /* 1348 */
+ { 46, 12, 3, 0, 0, 26624, 102, }, /* 1349 */
+ { 46, 12, 3, 0, 0, 26624, 130, }, /* 1350 */
+ { 46, 21, 12, 0, 0, 18432, 124, }, /* 1351 */
+ { 69, 1, 2, 0, 0, 6153, 66, }, /* 1352 */
+ { 69, 10, 3, 0, 0, 18432, 330, }, /* 1353 */
+ { 69, 10, 5, 0, 0, 18432, 138, }, /* 1354 */
+ { 69, 10, 5, 0, 0, 18432, 160, }, /* 1355 */
+ { 69, 10, 3, 0, 0, 18432, 286, }, /* 1356 */
+ { 1, 12, 3, 0, 0, 26624, 102, }, /* 1357 */
+ { 69, 25, 12, 0, 0, 18432, 118, }, /* 1358 */
+ { 69, 13, 12, 0, 0, 10240, 214, }, /* 1359 */
+ { 141, 26, 12, 0, 0, 18432, 68, }, /* 1360 */
+ { 141, 12, 3, 0, 0, 26624, 102, }, /* 1361 */
+ { 141, 21, 12, 0, 0, 18432, 106, }, /* 1362 */
+ { 141, 21, 12, 0, 0, 18432, 124, }, /* 1363 */
+ { 141, 21, 12, 0, 0, 18432, 68, }, /* 1364 */
+ { 35, 12, 3, 0, 0, 26624, 130, }, /* 1365 */
+ { 154, 7, 12, 0, 0, 18432, 82, }, /* 1366 */
+ { 154, 12, 3, 0, 0, 26624, 96, }, /* 1367 */
+ { 154, 6, 12, 0, 0, 18432, 142, }, /* 1368 */
+ { 154, 6, 12, 0, 0, 18432, 136, }, /* 1369 */
+ { 154, 13, 12, 0, 0, 18432, 138, }, /* 1370 */
+ { 154, 26, 12, 0, 0, 18432, 68, }, /* 1371 */
+ { 160, 7, 12, 0, 0, 18432, 82, }, /* 1372 */
+ { 160, 12, 3, 0, 0, 26624, 96, }, /* 1373 */
+ { 155, 7, 12, 0, 0, 18432, 82, }, /* 1374 */
+ { 155, 12, 3, 0, 0, 26624, 96, }, /* 1375 */
+ { 155, 13, 12, 0, 0, 18432, 138, }, /* 1376 */
+ { 155, 23, 12, 0, 0, 14336, 68, }, /* 1377 */
+ { 129, 7, 12, 0, 0, 34816, 82, }, /* 1378 */
+ { 129, 15, 12, 0, 0, 34816, 68, }, /* 1379 */
+ { 129, 12, 3, 0, 0, 26624, 96, }, /* 1380 */
+ { 58, 9, 12, 0, 34, 34816, 74, }, /* 1381 */
+ { 58, 5, 12, 0, -34, 34816, 76, }, /* 1382 */
+ { 58, 12, 3, 0, 0, 26624, 150, }, /* 1383 */
+ { 58, 12, 3, 0, 0, 26624, 130, }, /* 1384 */
+ { 58, 12, 3, 0, 0, 26624, 96, }, /* 1385 */
+ { 58, 6, 12, 0, 0, 34816, 142, }, /* 1386 */
+ { 58, 13, 12, 0, 0, 34816, 138, }, /* 1387 */
+ { 58, 21, 12, 0, 0, 34816, 68, }, /* 1388 */
+ { 69, 15, 12, 0, 0, 0, 68, }, /* 1389 */
+ { 69, 26, 12, 0, 0, 0, 68, }, /* 1390 */
+ { 69, 23, 12, 0, 0, 0, 68, }, /* 1391 */
+ { 3, 7, 12, 0, 0, 0, 240, }, /* 1392 */
+ { 69, 26, 14, 0, 0, 28672, 332, }, /* 1393 */
+ { 69, 26, 14, 0, 0, 28672, 334, }, /* 1394 */
+ { 68, 2, 14, 0, 0, 18432, 336, }, /* 1395 */
+ { 69, 26, 12, 0, 0, 18432, 338, }, /* 1396 */
+ { 69, 26, 14, 0, 0, 18432, 340, }, /* 1397 */
+ { 69, 26, 14, 0, 0, 18432, 334, }, /* 1398 */
+ { 69, 26, 11, 0, 0, 18432, 342, }, /* 1399 */
+ { 20, 26, 12, 0, 0, 18432, 68, }, /* 1400 */
+ { 69, 26, 14, 0, 0, 18432, 236, }, /* 1401 */
+ { 69, 26, 14, 0, 0, 18447, 334, }, /* 1402 */
+ { 69, 26, 14, 0, 0, 28672, 344, }, /* 1403 */
+ { 69, 26, 14, 0, 0, 28672, 346, }, /* 1404 */
+ { 69, 24, 3, 0, 0, 28672, 348, }, /* 1405 */
+ { 69, 26, 14, 0, 0, 28672, 350, }, /* 1406 */
+ { 69, 13, 12, 0, 0, 10240, 138, }, /* 1407 */
+ { 69, 1, 3, 0, 0, 6144, 352, }, /* 1408 */
+};
+
+const uint16_t PRIV(ucd_stage1)[] = { /* 17408 bytes */
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* U+0000 */
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* U+0800 */
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 41, 41, 42, 43, 44, 45, /* U+1000 */
+ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* U+1800 */
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, /* U+2000 */
+ 78, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, /* U+2800 */
+ 93, 94, 95, 96, 97, 98, 99,100,101,101,101,101,101,101,101,101, /* U+3000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+3800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+4000 */
+101,101,101,101,101,101,101,101,101,101,101,102,101,101,101,101, /* U+4800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+5000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+5800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+6000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+6800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+7000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+7800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+8000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+8800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+9000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+9800 */
+103,104,104,104,104,104,104,104,104,105,106,106,107,108,109,110, /* U+A000 */
+111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,119, /* U+A800 */
+120,121,122,123,124,125,119,120,121,122,123,124,125,119,120,121, /* U+B000 */
+122,123,124,125,119,120,121,122,123,124,125,119,120,121,122,123, /* U+B800 */
+124,125,119,120,121,122,123,124,125,119,120,121,122,123,124,125, /* U+C000 */
+119,120,121,122,123,124,125,119,120,121,122,123,124,125,119,120, /* U+C800 */
+121,122,123,124,125,119,120,121,122,123,124,125,119,120,121,126, /* U+D000 */
+127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127, /* U+D800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+E000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+E800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F000 */
+128,128,129,129,130,131,132,133,134,135,136,137,138,139,140,141, /* U+F800 */
+142,143,144,145,146,147,148,149,150,151,152,153,154,154,155,156, /* U+10000 */
+157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172, /* U+10800 */
+173,174,175,176,177,178,179,146,180,181,146,182,183,184,185,146, /* U+11000 */
+186,187,188,189,190,191,146,146,192,193,194,195,146,196,146,197, /* U+11800 */
+198,198,198,198,198,198,198,199,200,198,201,146,146,146,146,146, /* U+12000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,202, /* U+12800 */
+203,203,203,203,203,203,203,203,204,146,146,146,146,146,146,146, /* U+13000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+13800 */
+146,146,146,146,146,146,146,146,205,205,205,205,206,146,146,146, /* U+14000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+14800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+15000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+15800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+16000 */
+207,207,207,207,208,209,210,211,146,146,146,146,212,213,214,215, /* U+16800 */
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216, /* U+17000 */
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216, /* U+17800 */
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,217, /* U+18000 */
+216,216,216,216,216,216,218,218,218,219,220,146,146,146,146,146, /* U+18800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+19000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+19800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+1A000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,221, /* U+1A800 */
+222,223,224,225,225,226,146,146,146,146,146,146,146,146,146,146, /* U+1B000 */
+146,146,146,146,146,146,146,146,227,228,146,146,146,146,146,146, /* U+1B800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+1C000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,229,230, /* U+1C800 */
+231,232,233,234,235,236,237,146,238,239,240,241,242,243,244,245, /* U+1D000 */
+246,246,246,246,247,248,146,146,146,146,146,146,146,146,249,146, /* U+1D800 */
+250,146,251,146,146,252,146,146,146,146,146,146,146,146,146,253, /* U+1E000 */
+254,255,256,168,168,168,168,168,257,258,259,168,260,261,168,168, /* U+1E800 */
+262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277, /* U+1F000 */
+278,279,280,281,282,283,284,285,267,267,267,267,267,267,267,286, /* U+1F800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+20000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+20800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+21000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+21800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+22000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+22800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+23000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+23800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+24000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+24800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+25000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+25800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+26000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+26800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+27000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+27800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+28000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+28800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+29000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+29800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,287,101,101, /* U+2A000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2A800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,288,101, /* U+2B000 */
+289,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2B800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2C000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,290,101,101, /* U+2C800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2D000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2D800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2E000 */
+101,101,101,101,101,101,101,291,146,146,146,146,146,146,146,146, /* U+2E800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+2F000 */
+129,129,129,129,292,146,146,146,146,146,146,146,146,146,146,293, /* U+2F800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+30000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+30800 */
+101,101,101,101,101,101,294,146,146,146,146,146,146,146,146,146, /* U+31000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+31800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+32000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+32800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+33000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+33800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+34000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+34800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+35000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+35800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+36000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+36800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+37000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+37800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+38000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+38800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+39000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+39800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3A000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3A800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3B000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3B800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3C000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3C800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3D000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3D800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3E000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3E800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3F000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+3F800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+40000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+40800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+41000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+41800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+42000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+42800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+43000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+43800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+44000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+44800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+45000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+45800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+46000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+46800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+47000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+47800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+48000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+48800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+49000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+49800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4A000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4A800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4B000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4B800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4C000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4C800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4D000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4D800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4E000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4E800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4F000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+4F800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+50000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+50800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+51000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+51800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+52000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+52800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+53000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+53800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+54000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+54800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+55000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+55800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+56000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+56800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+57000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+57800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+58000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+58800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+59000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+59800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5A000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5A800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5B000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5B800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5C000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5C800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5D000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5D800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5E000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5E800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5F000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+5F800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+60000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+60800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+61000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+61800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+62000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+62800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+63000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+63800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+64000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+64800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+65000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+65800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+66000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+66800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+67000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+67800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+68000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+68800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+69000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+69800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6A000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6A800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6B000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6B800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6C000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6C800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6D000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6D800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6E000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6E800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6F000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+6F800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+70000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+70800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+71000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+71800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+72000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+72800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+73000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+73800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+74000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+74800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+75000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+75800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+76000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+76800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+77000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+77800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+78000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+78800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+79000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+79800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7A000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7A800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7B000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7B800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7C000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7C800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7D000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7D800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7E000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7E800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7F000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+7F800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+80000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+80800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+81000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+81800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+82000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+82800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+83000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+83800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+84000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+84800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+85000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+85800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+86000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+86800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+87000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+87800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+88000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+88800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+89000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+89800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8A000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8A800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8B000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8B800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8C000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8C800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8D000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8D800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8E000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8E800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8F000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+8F800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+90000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+90800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+91000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+91800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+92000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+92800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+93000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+93800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+94000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+94800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+95000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+95800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+96000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+96800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+97000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+97800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+98000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+98800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+99000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+99800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9A000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9A800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9B000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9B800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9C000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9C800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9D000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9D800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9E000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9E800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9F000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+9F800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A0000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A0800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A1000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A1800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A2000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A2800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A3000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A3800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A4000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A4800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A5000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A5800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A6000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A6800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A7000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A7800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A8000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A8800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A9000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A9800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AA000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AA800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AB000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AB800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AC000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AC800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AD000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AD800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AE000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AE800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AF000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+AF800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B0000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B0800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B1000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B1800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B2000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B2800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B3000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B3800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B4000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B4800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B5000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B5800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B6000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B6800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B7000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B7800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B8000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B8800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B9000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B9800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BA000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BA800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BB000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BB800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BC000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BC800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BD000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BD800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BE000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BE800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BF000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+BF800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C0000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C0800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C1000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C1800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C2000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C2800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C3000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C3800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C4000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C4800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C5000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C5800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C6000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C6800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C7000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C7800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C8000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C8800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C9000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C9800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CA000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CA800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CB000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CB800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CC000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CC800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CD000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CD800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CE000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CE800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CF000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+CF800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D0000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D0800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D1000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D1800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D2000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D2800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D3000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D3800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D4000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D4800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D5000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D5800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D6000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D6800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D7000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D7800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D8000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D8800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D9000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D9800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DA000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DA800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DB000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DB800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DC000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DC800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DD000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DD800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DE000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DE800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DF000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+DF800 */
+295,296,297,298,296,296,296,296,296,296,296,296,296,296,296,296, /* U+E0000 */
+296,296,296,296,296,296,296,296,296,296,296,296,296,296,296,296, /* U+E0800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E1000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E1800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E2000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E2800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E3000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E3800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E4000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E4800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E5000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E5800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E6000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E6800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E7000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E7800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E8000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E8800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E9000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E9800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EA000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EA800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EB000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EB800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EC000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EC800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+ED000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+ED800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EE000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EE800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EF000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+EF800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F0000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F0800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F1000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F1800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F2000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F2800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F3000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F3800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F4000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F4800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F5000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F5800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F6000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F6800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F7000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F7800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F8000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F8800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F9000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F9800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FA000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FA800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FB000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FB800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FC000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FC800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FD000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FD800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FE000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FE800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FF000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,299, /* U+FF800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+100000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+100800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+101000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+101800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+102000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+102800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+103000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+103800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+104000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+104800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+105000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+105800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+106000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+106800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+107000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+107800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+108000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+108800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+109000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+109800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10A000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10A800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10B000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10B800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10C000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10C800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10D000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10D800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10E000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10E800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10F000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,299, /* U+10F800 */
+};
+
+const uint16_t PRIV(ucd_stage2)[] = { /* 76800 bytes, block = 128 */
+
+/* block 0 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 3, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 25, 26, 27, 26, 8,
+ 13, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 30, 29, 29, 29, 29,
+ 29, 29, 29, 31, 29, 29, 29, 29, 29, 29, 29, 15, 13, 16, 32, 33,
+ 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 38, 36, 36, 36, 36,
+ 36, 36, 36, 39, 36, 36, 36, 36, 36, 36, 36, 15, 27, 16, 27, 0,
+
+/* block 1 */
+ 40, 40, 40, 40, 40, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
+ 42, 43, 44, 44, 44, 44, 45, 43, 46, 47, 48, 49, 50, 51, 47, 46,
+ 52, 53, 54, 54, 46, 55, 43, 56, 46, 54, 48, 57, 58, 58, 58, 43,
+ 59, 59, 59, 59, 59, 60, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 50, 59, 59, 59, 59, 59, 59, 59, 61,
+ 62, 62, 62, 62, 62, 63, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 50, 62, 62, 62, 62, 62, 62, 62, 64,
+
+/* block 2 */
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 67,
+ 68, 69, 65, 66, 65, 66, 65, 66, 70, 65, 66, 65, 66, 65, 66, 65,
+ 66, 65, 66, 65, 66, 65, 66, 65, 66, 71, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 72, 65, 66, 65, 66, 65, 66, 73,
+
+/* block 3 */
+ 74, 75, 65, 66, 65, 66, 76, 65, 66, 77, 77, 65, 66, 70, 78, 79,
+ 80, 65, 66, 77, 81, 82, 83, 84, 65, 66, 85, 70, 83, 86, 87, 88,
+ 65, 66, 65, 66, 65, 66, 89, 65, 66, 89, 70, 70, 65, 66, 89, 65,
+ 66, 90, 90, 65, 66, 65, 66, 91, 65, 66, 70, 92, 65, 66, 70, 93,
+ 92, 92, 92, 92, 94, 95, 96, 97, 98, 99,100,101,102, 65, 66, 65,
+ 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,103, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 69,104,105,106, 65, 66,107,108, 65, 66, 65, 66, 65, 66, 65, 66,
+
+/* block 4 */
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+109, 70, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 70, 70, 70, 70, 70, 70,110, 65, 66,111,112,113,
+113, 65, 66,114,115,116, 65, 66, 65, 67, 65, 66, 65, 66, 65, 66,
+117,118,119,120,121, 70,122,122, 70,123, 70,124,125, 70, 70, 70,
+122,126, 70,127, 70,128,129, 70,130,131,129,132,133, 70, 70,131,
+ 70,134,135, 70, 70,136, 70, 70, 70, 70, 70, 70, 70,137, 70, 70,
+
+/* block 5 */
+138, 70,139,138, 70, 70, 70,140,138,141,142,142,143, 70, 70, 70,
+ 70, 70,144, 70, 92, 70, 70, 70, 70, 70, 70, 70, 70,145,146, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,
+147,147,148,147,147,147,147,147,147,149,149,150,150,150,150,150,
+151,151, 46, 46, 46, 46,149,149,149,149,149,149,149,149,149,149,
+152,152, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
+147,147,147,147,147, 46, 46, 46, 46, 46,153,153,149, 46,150, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
+
+/* block 6 */
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,
+154,154,155,154,154,156,154,154,154,154,154,154,154,154,154,157,
+154,154,154,154,154,154,154,154,158,158,158,158,158,154,154,154,
+154,154,154,159,159,159,159,159,159,159,159,159,159,159,159,159,
+160,161,160,161,149,162,160,161,163,163,164,165,165,165,166,167,
+
+/* block 7 */
+163,163,163,163,162, 46,168,169,170,170,170,163,171,163,172,172,
+173,174,175,174,174,176,174,174,177,178,179,174,180,174,174,174,
+181,182,163,183,174,174,184,174,174,185,174,174,186,187,187,187,
+173,188,189,188,188,190,188,188,191,192,193,188,194,188,188,188,
+195,196,197,198,188,188,199,188,188,200,188,188,201,202,202,203,
+204,205,206,207,207,208,209,210,160,161,160,161,160,161,160,161,
+160,161,211,212,211,212,211,212,211,212,211,212,211,212,211,212,
+213,214,215,216,217,218,219,160,161,220,160,161,221,222,222,222,
+
+/* block 8 */
+223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,
+224,224,225,224,226,224,224,224,224,224,224,224,224,224,227,224,
+224,228,229,224,224,224,224,224,224,224,230,224,224,224,224,224,
+231,231,232,231,233,231,231,231,231,231,231,231,231,231,234,231,
+231,235,236,231,231,231,231,231,231,231,237,231,231,231,231,231,
+238,238,238,238,238,238,239,238,239,238,238,238,238,238,238,238,
+240,241,242,243,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+
+/* block 9 */
+240,241,244,245,246,247,247,246,248,248,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+249,240,241,240,241,240,241,240,241,240,241,240,241,240,241,250,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+
+/* block 10 */
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+163,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,
+251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,
+251,251,251,251,251,251,251,163,163,252,253,253,253,253,253,254,
+255,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,
+256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,
+
+/* block 11 */
+256,256,256,256,256,256,256,257,255,258,259,163,163,260,260,261,
+262,263,263,263,263,263,263,263,263,263,263,263,263,263,263,263,
+263,263,264,263,263,263,263,263,263,263,263,263,263,263,263,263,
+265,265,265,265,265,265,265,265,265,265,265,265,265,265,266,265,
+267,265,265,268,265,269,267,269,262,262,262,262,262,262,262,262,
+270,270,270,270,270,270,270,270,270,270,270,270,270,270,270,270,
+270,270,270,270,270,270,270,270,270,270,270,262,262,262,262,270,
+270,270,270,267,271,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 12 */
+272,272,272,272,272,273,274,274,275,276,276,277,278,279,280,280,
+281,281,281,281,281,281,281,281,281,281,281,282,283,284,284,285,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+287,286,286,286,286,286,286,286,286,286,286,288,288,288,288,288,
+288,288,288,289,289,289,281,290,291,281,281,281,281,281,281,281,
+292,292,292,292,292,292,292,292,292,292,276,293,293,279,286,286,
+289,286,286,294,286,286,286,286,286,286,286,286,286,286,286,286,
+
+/* block 13 */
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,295,286,281,281,281,281,281,281,281,273,280,291,
+291,281,281,281,281,296,296,281,281,280,291,291,291,281,286,286,
+297,297,297,297,297,297,297,297,297,297,286,286,286,298,298,286,
+
+/* block 14 */
+299,299,299,300,300,300,300,300,300,300,300,301,300,301,302,303,
+304,305,304,304,304,304,304,304,304,304,304,304,304,304,304,304,
+304,304,304,304,304,304,304,304,304,304,304,304,304,304,304,304,
+306,306,306,306,306,306,306,306,306,306,306,306,306,306,306,306,
+307,307,307,307,307,307,307,307,307,307,307,302,302,304,304,304,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+
+/* block 15 */
+308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,
+308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,
+308,308,308,308,308,308,309,309,309,309,309,309,309,309,309,309,
+309,308,302,302,302,302,302,302,302,302,302,302,302,302,302,302,
+310,310,310,310,310,310,310,310,310,310,311,311,311,311,311,311,
+311,311,311,311,311,311,311,311,311,311,311,311,311,311,311,311,
+311,311,311,311,311,311,311,311,311,311,311,312,312,312,312,312,
+312,312,312,312,313,313,314,315,316,317,318,262,262,319,320,320,
+
+/* block 16 */
+321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,
+321,321,321,321,321,321,322,322,323,323,324,322,322,322,322,322,
+322,322,322,322,324,322,322,322,324,322,322,322,322,325,262,262,
+326,326,326,326,326,326,326,327,326,327,326,326,326,327,327,262,
+328,328,328,328,328,328,328,328,328,328,328,328,328,328,328,328,
+328,328,328,328,328,328,328,328,328,329,329,329,262,262,330,262,
+304,304,304,304,304,304,304,304,304,304,304,302,302,302,302,302,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+
+/* block 17 */
+286,286,286,286,286,286,286,286,331,286,286,286,286,286,286,302,
+272,272,302,302,302,302,302,302,291,291,291,291,291,291,291,291,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,296,291,291,291,291,291,291,
+291,291,291,332,281,281,281,281,281,281,281,281,281,281,281,281,
+332,332,273,290,290,290,290,290,290,290,291,291,291,291,291,291,
+290,290,290,290,290,290,290,290,290,290,290,290,290,290,290,281,
+
+/* block 18 */
+333,333,333,334,335,335,335,335,335,335,335,335,335,335,335,335,
+335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,
+335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,
+335,335,335,335,335,335,335,335,335,335,333,334,336,335,334,334,
+334,333,333,333,333,333,333,333,333,334,334,334,334,337,334,334,
+335,338,339,154,154,333,333,333,335,335,335,335,335,335,335,335,
+335,335,333,333,340,341,342,342,342,342,342,342,342,342,342,342,
+343,344,335,335,335,335,335,335,335,335,335,335,335,335,335,335,
+
+/* block 19 */
+345,346,347,347,163,345,345,345,345,345,345,345,345,163,163,345,
+345,163,163,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,163,345,345,345,345,345,345,
+345,163,345,163,163,163,345,345,345,345,163,163,348,345,349,347,
+347,346,346,346,346,163,163,347,347,163,163,347,347,350,345,163,
+163,163,163,163,163,163,163,349,163,163,163,163,345,345,163,345,
+345,345,346,346,163,163,351,351,351,351,351,351,351,351,351,351,
+345,345,352,352,353,353,353,353,353,353,354,352,345,355,356,163,
+
+/* block 20 */
+163,357,357,358,163,359,359,359,359,359,359,163,163,163,163,359,
+359,163,163,359,359,359,359,359,359,359,359,359,359,359,359,359,
+359,359,359,359,359,359,359,359,359,163,359,359,359,359,359,359,
+359,163,359,359,163,359,359,163,359,359,163,163,360,163,358,358,
+358,357,357,163,163,163,163,357,357,163,163,357,357,361,163,163,
+163,357,163,163,163,163,163,163,163,359,359,359,359,163,359,163,
+163,163,163,163,163,163,362,362,362,362,362,362,362,362,362,362,
+357,357,359,359,359,357,363,163,163,163,163,163,163,163,163,163,
+
+/* block 21 */
+163,364,364,365,163,366,366,366,366,366,366,366,366,366,163,366,
+366,366,163,366,366,366,366,366,366,366,366,366,366,366,366,366,
+366,366,366,366,366,366,366,366,366,163,366,366,366,366,366,366,
+366,163,366,366,163,366,366,366,366,366,163,163,367,366,365,365,
+365,364,364,364,364,364,163,364,364,365,163,365,365,368,163,163,
+366,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+366,366,364,364,163,163,369,369,369,369,369,369,369,369,369,369,
+370,371,163,163,163,163,163,163,163,366,364,364,364,367,367,367,
+
+/* block 22 */
+163,372,373,373,163,374,374,374,374,374,374,374,374,163,163,374,
+374,163,163,374,374,374,374,374,374,374,374,374,374,374,374,374,
+374,374,374,374,374,374,374,374,374,163,374,374,374,374,374,374,
+374,163,374,374,163,374,374,374,374,374,163,163,375,374,376,372,
+373,372,372,372,372,163,163,373,373,163,163,373,373,377,163,163,
+163,163,163,163,163,378,372,376,163,163,163,163,374,374,163,374,
+374,374,372,372,163,163,379,379,379,379,379,379,379,379,379,379,
+380,374,381,381,381,381,381,381,163,163,163,163,163,163,163,163,
+
+/* block 23 */
+163,163,382,383,163,383,383,383,383,383,383,163,163,163,383,383,
+383,163,383,383,383,383,163,163,163,383,383,163,383,163,383,383,
+163,163,163,383,383,163,163,163,383,383,383,163,163,163,383,383,
+383,383,383,383,383,383,383,383,383,383,163,163,163,163,384,385,
+382,385,385,163,163,163,385,385,385,163,385,385,385,386,163,163,
+383,163,163,163,163,163,163,384,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,387,387,387,387,387,387,387,387,387,387,
+388,388,388,389,390,390,390,390,390,391,390,163,163,163,163,163,
+
+/* block 24 */
+392,393,393,393,394,395,395,395,395,395,395,395,395,163,395,395,
+395,163,395,395,395,395,395,395,395,395,395,395,395,395,395,395,
+395,395,395,395,395,395,395,395,395,163,395,395,395,395,395,395,
+395,395,395,395,395,395,395,395,395,395,163,163,396,395,392,392,
+392,393,393,393,393,163,392,392,392,163,392,392,392,397,163,163,
+163,163,163,163,163,392,392,163,395,395,395,163,163,395,163,163,
+395,395,392,392,163,163,398,398,398,398,398,398,398,398,398,398,
+163,163,163,163,163,163,163,399,400,400,400,400,400,400,400,401,
+
+/* block 25 */
+402,403,404,404,405,402,402,402,402,402,402,402,402,163,402,402,
+402,163,402,402,402,402,402,402,402,402,402,402,402,402,402,402,
+402,402,402,402,402,402,402,402,402,163,402,402,402,402,402,402,
+402,402,402,402,163,402,402,402,402,402,163,163,406,402,404,407,
+404,404,408,404,404,163,407,404,404,163,404,404,403,409,163,163,
+163,163,163,163,163,408,408,163,163,163,163,163,163,402,402,163,
+402,402,403,403,163,163,410,410,410,410,410,410,410,410,410,410,
+163,402,402,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 26 */
+411,411,412,412,413,413,413,413,413,413,413,413,413,163,413,413,
+413,163,413,413,413,413,413,413,413,413,413,413,413,413,413,413,
+413,413,413,413,413,413,413,413,413,413,413,413,413,413,413,413,
+413,413,413,413,413,413,413,413,413,413,413,414,414,413,415,412,
+412,411,411,411,411,163,412,412,412,163,412,412,412,414,416,417,
+163,163,163,163,413,413,413,415,418,418,418,418,418,418,418,413,
+413,413,411,411,163,163,419,419,419,419,419,419,419,419,419,419,
+418,418,418,418,418,418,418,418,418,417,413,413,413,413,413,413,
+
+/* block 27 */
+163,420,421,421,163,422,422,422,422,422,422,422,422,422,422,422,
+422,422,422,422,422,422,422,163,163,163,422,422,422,422,422,422,
+422,422,422,422,422,422,422,422,422,422,422,422,422,422,422,422,
+422,422,163,422,422,422,422,422,422,422,422,422,163,422,163,163,
+422,422,422,422,422,422,422,163,163,163,423,163,163,163,163,424,
+421,421,420,420,420,163,420,163,421,421,421,421,421,421,421,424,
+163,163,163,163,163,163,425,425,425,425,425,425,425,425,425,425,
+163,163,421,421,426,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 28 */
+163,427,427,427,427,427,427,427,427,427,427,427,427,427,427,427,
+427,427,427,427,427,427,427,427,427,427,427,427,427,427,427,427,
+427,427,427,427,427,427,427,427,427,427,427,427,427,427,427,427,
+427,428,427,429,428,428,428,428,428,428,430,163,163,163,163,431,
+432,432,432,432,432,427,433,434,434,434,434,434,434,428,434,435,
+436,436,436,436,436,436,436,436,436,436,437,437,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 29 */
+163,438,438,163,438,163,438,438,438,438,438,163,438,438,438,438,
+438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,
+438,438,438,438,163,438,163,438,438,438,438,438,438,438,438,438,
+438,439,438,440,439,439,439,439,439,439,441,439,439,438,163,163,
+442,442,442,442,442,163,443,163,444,444,444,444,444,439,163,163,
+445,445,445,445,445,445,445,445,445,445,163,163,438,438,438,438,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 30 */
+446,447,447,447,448,448,448,448,449,448,448,448,448,449,449,449,
+449,449,449,447,448,447,447,447,450,450,447,447,447,447,447,447,
+451,451,451,451,451,451,451,451,451,451,452,452,452,452,452,452,
+452,452,452,452,447,450,447,450,447,450,453,454,453,454,455,455,
+446,446,446,446,446,446,446,446,163,446,446,446,446,446,446,446,
+446,446,446,446,446,446,446,446,446,446,446,446,446,446,446,446,
+446,446,446,446,446,446,446,446,446,446,446,446,446,163,163,163,
+163,456,456,456,456,456,456,457,456,457,456,456,456,456,456,458,
+
+/* block 31 */
+456,456,450,450,459,448,450,450,446,446,446,446,446,456,456,456,
+456,456,456,456,456,456,456,456,163,456,456,456,456,456,456,456,
+456,456,456,456,456,456,456,456,456,456,456,456,456,456,456,456,
+456,456,456,456,456,456,456,456,456,456,456,456,456,163,447,447,
+447,447,447,447,447,447,450,447,447,447,447,447,447,163,447,447,
+448,448,448,448,448,460,460,460,460,448,448,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 32 */
+461,461,461,461,461,461,461,461,461,461,461,461,461,461,461,461,
+461,461,461,461,461,461,461,461,461,461,461,461,461,461,461,461,
+461,461,461,461,461,461,461,461,461,461,461,462,462,463,463,463,
+463,464,463,463,463,463,463,465,462,466,466,464,464,463,463,461,
+467,467,467,467,467,467,467,467,467,467,468,468,469,469,469,469,
+461,461,461,461,461,461,464,464,463,463,461,461,461,461,463,463,
+463,461,462,470,470,461,461,462,462,470,470,470,470,470,461,461,
+461,463,463,463,463,461,461,461,461,461,461,461,461,461,461,461,
+
+/* block 33 */
+461,461,463,462,464,463,463,470,470,470,470,470,470,471,461,470,
+472,472,472,472,472,472,472,472,472,472,470,470,462,463,473,473,
+474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,
+474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,
+474,474,474,474,474,474,163,474,163,163,163,163,163,474,163,163,
+475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,
+475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,
+475,475,475,475,475,475,475,475,475,475,475,476,477,475,475,475,
+
+/* block 34 */
+478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,
+478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,
+478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,
+478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,
+478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,
+478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,479,
+480,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,
+481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,
+
+/* block 35 */
+481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,
+481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,
+481,481,481,481,481,481,481,481,482,482,482,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
+
+/* block 36 */
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,163,483,483,483,483,163,163,
+483,483,483,483,483,483,483,163,483,163,483,483,483,483,163,163,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+
+/* block 37 */
+483,483,483,483,483,483,483,483,483,163,483,483,483,483,163,163,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,163,483,483,483,483,163,163,483,483,483,483,483,483,483,163,
+483,163,483,483,483,483,163,163,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,163,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+
+/* block 38 */
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,163,483,483,483,483,163,163,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,163,163,484,484,484,
+485,486,487,486,486,486,486,487,487,488,488,488,488,488,488,488,
+488,488,489,489,489,489,489,489,489,489,489,489,489,163,163,163,
+
+/* block 39 */
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+490,490,490,490,490,490,490,490,490,490,163,163,163,163,163,163,
+491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,
+491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,
+491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,
+491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,
+491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,
+492,492,492,492,492,492,163,163,493,493,493,493,493,493,163,163,
+
+/* block 40 */
+494,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+
+/* block 41 */
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+
+/* block 42 */
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,496,497,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+
+/* block 43 */
+498,499,499,499,499,499,499,499,499,499,499,499,499,499,499,499,
+499,499,499,499,499,499,499,499,499,499,499,500,501,163,163,163,
+502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,
+502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,
+502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,
+502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,
+502,502,502,502,502,502,502,502,502,502,502,503,503,503,504,504,
+504,502,502,502,502,502,502,502,502,163,163,163,163,163,163,163,
+
+/* block 44 */
+505,505,505,505,505,505,505,505,505,505,505,505,505,505,505,505,
+505,505,506,506,507,508,163,163,163,163,163,163,163,163,163,505,
+509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,
+509,509,510,510,511,512,512,163,163,163,163,163,163,163,163,163,
+513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,
+513,513,514,514,163,163,163,163,163,163,163,163,163,163,163,163,
+515,515,515,515,515,515,515,515,515,515,515,515,515,163,515,515,
+515,163,516,516,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 45 */
+517,517,517,517,517,517,517,517,517,517,517,517,517,517,517,517,
+517,517,517,517,517,517,517,517,517,517,517,517,517,517,517,517,
+517,517,517,518,518,517,517,517,517,517,517,517,517,517,517,517,
+517,517,517,517,519,519,520,521,521,521,521,521,521,521,520,520,
+520,520,520,520,520,520,521,520,520,522,522,522,522,522,522,522,
+522,522,523,522,524,524,524,525,526,526,524,527,517,522,163,163,
+528,528,528,528,528,528,528,528,528,528,163,163,163,163,163,163,
+529,529,529,529,529,529,529,529,529,529,163,163,163,163,163,163,
+
+/* block 46 */
+530,530,531,532,533,531,534,530,533,535,536,537,537,537,538,537,
+539,539,539,539,539,539,539,539,539,539,163,163,163,163,163,163,
+540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,
+540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,
+540,540,540,541,540,540,540,540,540,540,540,540,540,540,540,540,
+540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,
+540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,
+540,540,540,540,540,540,540,540,540,163,163,163,163,163,163,163,
+
+/* block 47 */
+540,540,540,540,540,542,542,540,540,540,540,540,540,540,540,540,
+540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,
+540,540,540,540,540,540,540,540,540,543,540,163,163,163,163,163,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,163,163,163,163,163,163,163,163,163,163,
+
+/* block 48 */
+544,544,544,544,544,544,544,544,544,544,544,544,544,544,544,544,
+544,544,544,544,544,544,544,544,544,544,544,544,544,544,544,163,
+545,545,545,546,546,546,546,545,545,546,546,546,163,163,163,163,
+546,546,545,546,546,546,546,546,546,547,547,547,163,163,163,163,
+548,163,163,163,549,549,550,550,550,550,550,550,550,550,550,550,
+551,551,551,551,551,551,551,551,551,551,551,551,551,551,551,551,
+551,551,551,551,551,551,551,551,551,551,551,551,551,551,163,163,
+551,551,551,551,551,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 49 */
+552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,
+552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,
+552,552,552,552,552,552,552,552,552,552,552,552,163,163,163,163,
+552,552,552,552,552,553,553,553,552,552,553,552,552,552,552,552,
+552,552,552,552,552,552,552,552,552,552,163,163,163,163,163,163,
+554,554,554,554,554,554,554,554,554,554,555,163,163,163,556,556,
+557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
+557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
+
+/* block 50 */
+558,558,558,558,558,558,558,558,558,558,558,558,558,558,558,558,
+558,558,558,558,558,558,558,559,559,560,560,559,163,163,561,561,
+562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,
+562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,
+562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,
+562,562,562,562,562,563,564,563,564,564,564,564,564,564,564,163,
+565,566,564,566,566,564,564,564,564,564,564,564,564,563,563,563,
+563,563,563,564,564,567,567,567,567,567,567,567,567,163,163,567,
+
+/* block 51 */
+568,568,568,568,568,568,568,568,568,568,163,163,163,163,163,163,
+568,568,568,568,568,568,568,568,568,568,163,163,163,163,163,163,
+569,569,569,569,569,569,569,570,571,571,571,571,569,569,163,163,
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,572,573,
+573,154,154,154,154,154,154,154,154,154,154,154,573,573,573,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 52 */
+574,574,574,574,575,576,576,576,576,576,576,576,576,576,576,576,
+576,576,576,576,576,576,576,576,576,576,576,576,576,576,576,576,
+576,576,576,576,576,576,576,576,576,576,576,576,576,576,576,576,
+576,576,576,576,577,578,574,574,574,574,574,575,574,575,575,575,
+575,575,574,575,579,576,576,576,576,576,576,576,576,163,163,163,
+580,580,580,580,580,580,580,580,580,580,581,581,582,583,581,581,
+582,584,584,584,584,584,584,584,584,584,584,577,577,577,577,577,
+577,577,577,577,584,584,584,584,584,584,584,584,584,581,581,163,
+
+/* block 53 */
+585,585,586,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,586,585,585,585,585,586,586,585,585,588,589,585,585,587,587,
+590,590,590,590,590,590,590,590,590,590,587,587,587,587,587,587,
+591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,
+591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,
+591,591,591,591,591,591,592,593,594,594,593,593,593,594,593,594,
+594,594,595,595,163,163,163,163,163,163,163,163,596,596,596,596,
+
+/* block 54 */
+597,597,597,597,597,597,597,597,597,597,597,597,597,597,597,597,
+597,597,597,597,597,597,597,597,597,597,597,597,597,597,597,597,
+597,597,597,597,598,598,598,598,598,598,598,598,599,599,599,599,
+599,599,599,599,598,598,600,601,163,163,163,602,602,603,603,603,
+604,604,604,604,604,604,604,604,604,604,163,163,163,597,597,597,
+605,605,605,605,605,605,605,605,605,605,606,606,606,606,606,606,
+606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,
+606,606,606,606,606,606,606,606,607,607,607,608,607,607,609,609,
+
+/* block 55 */
+610,611,612,613,614,615,616,617,618,163,163,163,163,163,163,163,
+619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,
+619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,
+619,619,619,619,619,619,619,619,619,619,619,163,163,619,619,619,
+620,620,620,620,620,620,620,620,163,163,163,163,163,163,163,163,
+621,622,621,623,622,624,624,625,624,625,626,622,625,625,622,622,
+625,627,622,622,622,622,622,622,622,628,629,630,630,624,630,630,
+630,630,631,632,633,629,629,634,635,635,636,163,163,163,163,163,
+
+/* block 56 */
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70,221,221,221,221,221,637,147,147,147,147,
+147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,
+147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,
+147,147,147,147,147,147,147,147,147,147,147,147,147,638,638,638,
+638,638,148,147,147,147,638,638,638,638,638, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,639,640, 70, 70, 70,641, 70, 70,
+
+/* block 57 */
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,642, 70,
+ 70, 70, 70, 70, 70, 70,643, 70, 70, 70, 70,644,644,644,644,644,
+644,644,644,644,645,644,644,644,645,644,644,644,644,644,644,644,
+644,644,644,644,644,644,644,644,644,644,644,644,644,644,644,646,
+647,647,158,158,154,154,154,154,154,154,154,154,154,154,154,154,
+158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,
+158,158,158,158,158,158,158,573,573,573,573,573,573,573,573,573,
+573,573,573,573,573,154,154,154,648,154,649,154,154,154,154,154,
+
+/* block 58 */
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 67, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+650,651, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+
+/* block 59 */
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 69, 69, 69, 69,652,653, 70, 70,654, 70,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 67, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+
+/* block 60 */
+655,655,655,655,655,655,655,655,656,656,656,656,656,656,656,656,
+655,655,655,655,655,655,163,163,656,656,656,656,656,656,163,163,
+655,655,655,655,655,655,655,655,656,656,656,656,656,656,656,656,
+655,655,655,655,655,655,655,655,656,656,656,656,656,656,656,656,
+655,655,655,655,655,655,163,163,656,656,656,656,656,656,163,163,
+173,655,173,655,173,655,173,655,163,656,163,656,163,656,163,656,
+655,655,655,655,655,655,655,655,656,656,656,656,656,656,656,656,
+657,657,658,658,658,658,659,659,660,660,661,661,662,662,163,163,
+
+/* block 61 */
+663,663,663,663,663,663,663,663,664,664,664,664,664,664,664,664,
+663,663,663,663,663,663,663,663,664,664,664,664,664,664,664,664,
+663,663,663,663,663,663,663,663,664,664,664,664,664,664,664,664,
+655,655,665,666,665,163,173,665,656,656,667,667,668,162,669,162,
+162,162,665,666,665,163,173,665,670,670,670,670,668,162,162,162,
+655,655,173,173,163,163,173,173,656,656,671,671,163,162,162,162,
+655,655,173,173,173,215,173,173,656,656,672,672,220,162,162,162,
+163,163,665,666,665,163,173,665,673,673,674,674,668,162,162,163,
+
+/* block 62 */
+675,675,675,675,675,675,675,675,675,675,675, 51,676,677,678,679,
+680,680,680,680,680,680,681, 43,682,683,684,685,685,686,684,685,
+ 43, 43, 43, 43,687, 43, 43,687,688,689,690,691,692,693,694,695,
+696,696,697,697,697, 43, 43, 43, 43, 49, 57, 43,698,699, 43,700,
+701, 43, 43, 43,702,703,704,699,699,698, 43, 43, 43, 43, 43, 43,
+ 43, 43, 50,705,700, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,675,
+ 51,706,706,706,706,707,708,709,710,711,712,712,712,712,712,712,
+ 54,645,163,163, 54, 54, 54, 54, 54, 54,713,714,715,716,717,644,
+
+/* block 63 */
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,713,714,715,716,717,163,
+644,644,644,644,644,644,644,644,644,644,644,644,644,163,163,163,
+431,431,431,431,431,431,431,431,431,431,431,431,431,431,431,431,
+431,431,431,431,431,431,431,431,431,431,431,431,431,431,431,431,
+431,718,718,718,718,718,718,718,718,718,718,718,718,718,718,718,
+719,719,719,719,719,719,719,719,719,719,719,719,719,720,720,720,
+720,719,720,721,720,719,719,158,158,158,158,719,719,719,719,719,
+722,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 64 */
+723,723,724,723,723,723,723,724,723,723,725,724,724,724,725,725,
+724,724,724,725,723,724,723,723,726,724,724,724,724,724,723,723,
+723,723,727,723,724,723,728,723,724,729,730,731,724,724,732,725,
+724,724,733,724,725,734,734,734,734,735,723,723,725,725,724,724,
+715,715,715,715,715,724,725,725,736,736,723,715,723,723,737,460,
+ 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+738,738,738,738,738,738,738,738,738,738,738,738,738,738,738,738,
+739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,
+
+/* block 65 */
+740,740,740, 65, 66,740,740,740,740, 58,723,723,163,163,163,163,
+ 50, 50, 50, 50,741,742,742,742,742,742, 50, 50,743,743,743,743,
+ 50,743,743, 50,743,743, 50,743, 45,742,742,743,743,743, 50, 45,
+743,743, 45, 45, 45, 45,743,743, 45, 45, 45, 45,743,743,743,743,
+743,743,743,743,743,743,743,743,743,743,743,743,743,743, 50, 50,
+743,743, 50,743, 50,743,743,743,743,743,743,743, 45,743, 45, 45,
+ 45, 45, 45, 45,743,743, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+
+/* block 66 */
+ 50, 50, 50, 50, 50, 50, 50, 50,744,744,744,744,744,744, 50, 50,
+ 50, 50,745, 53, 50,744, 50, 50, 50, 50, 50, 50, 50, 50, 50,744,
+744,744,744, 50,744, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,744,744, 50, 50,
+ 50, 50, 50,744, 50,744, 50, 50, 50, 50, 50, 50,744, 50, 50, 50,
+ 50, 50,744,744,744,744, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50,744,744,744,744,744,744,744,744, 50, 50,744,744,
+744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,
+
+/* block 67 */
+744,744,744,744,744,744,744,744,744,744,744,744, 50, 50, 50,744,
+744,744,744, 50, 50, 50, 50, 50,744, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50,744,744, 50, 50,744, 50,744,744, 50,744, 50, 50, 50, 50,
+744,744,744,744,744,744,744,744,744, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50,744,744,744,744,744, 50, 50,
+744,744, 50, 50, 50, 50,744,744,744,744,744,744,744,744,744,744,
+744,744,744,744,744,744,744,744,744,744,744,744,744,744, 50, 50,
+744,744,744,744,744, 50,744,744, 50, 50,744,744,744,744,744, 50,
+
+/* block 68 */
+ 45, 45, 45, 45, 45, 45, 45, 45,746,747,746,747, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,748,748, 45, 45, 45, 45,
+ 50, 50, 45, 45, 45, 45, 45, 45, 47,749,750, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45,751,751,751,751,751,751,751,751,751,751,
+751,751,751,751,751,751,751,751,751,751,751,751,751,751,751,751,
+751,751,751,751,751,751,751,751,751,751,751,751,751,751,751,751,
+751,751,751,751,751,751,751,751,751,751,751,751,751,751,751,751,
+751,751,751,751,751,751,751,751,751,751,751, 45, 50, 45, 45, 45,
+
+/* block 69 */
+ 45, 45, 45, 45, 45, 45, 45, 45,752, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45,751, 45, 45, 45, 45, 45, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50,743,743, 45,743, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 47,
+743, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 50, 50, 50, 50,
+ 50, 50,743, 45, 45, 45, 45, 45, 45,748,748,748,748, 47, 47, 47,
+748, 47, 47,748, 45, 45, 45, 45, 47, 47, 47, 45, 45, 45, 45, 45,
+
+/* block 70 */
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45,753,753,753,753,753,753,753,753,753,
+753,753,753,753,753,753,753,753,753,753,753,753,753,753,753,753,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,753,753,753,753,753,
+753,753,753,753,753,753,753,753,753,753,753,753,753,753,753,753,
+ 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+ 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+
+/* block 71 */
+ 58, 58, 58, 58, 58, 58, 58, 58, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,754,754,754,754,754,754,754,754,754,754,
+754,754,755,754,754,754,754,754,754,754,754,754,754,754,754,754,
+756,756,756,756,756,756,756,756,756,756,756,756,756,756,756,756,
+756,756,756,756,756,756,756,756,756,756, 58, 58, 58, 58, 58, 58,
+ 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+
+/* block 72 */
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+
+/* block 73 */
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+743,743, 45, 45, 45, 45, 45, 45, 45, 45, 47, 47, 45, 45,743,743,
+743,743,743,743,743,743,742, 50, 45, 45, 45, 45,743,743,743,743,
+742, 50, 45, 45, 45, 45,743,743, 45, 45,743,743, 45, 45, 45,743,
+743,743,743,743, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45,743, 45,743, 45, 45,743,743,743,743,743,743, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 50, 50, 50,741,741,757,757, 50,
+
+/* block 74 */
+ 47, 47, 47, 47, 47,758,743,752,752,752,752,752,752,752, 47,752,
+752, 47,752, 45,748,748,752,752, 47,752,752,752,752,759,752,752,
+ 47,752, 47, 47,752,752, 47,752,752,752, 47,752,752,752, 47, 47,
+752,752,752,752,752,752,752,752, 47, 47, 47,752,752,752,752,752,
+742,752,742,752,752,752,752,752,748,748,748,748,748,748,748,748,
+748,748,748,748,752,752,752,752,752,752,752,752,752,752,752, 47,
+742,758,758,742,752, 47, 47,752, 47,752,752,752,752,758,758,760,
+752,752,752,752,752,752,752,752,752,752,752, 47,752,752, 47,748,
+
+/* block 75 */
+752,752,752,752,752,752, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+752,752, 47,748, 47, 47, 47, 47,752, 47,752, 47, 47,752,752,752,
+ 47,748,752,752,752,752,752, 47,752,752,748,748,761,752,752,752,
+ 47, 47,752,752,752,752,752,752,752,752,752,752,752,748,748,752,
+752,752,752,752,748,748,752,752, 47,752,752,752,752,752,748, 47,
+752, 47,752, 47,748,752,752,752,752,752,752,752,752,752,752,752,
+752,752,752,752,752,752,752,752,752, 47,748,752,752,752,752,752,
+ 47, 47,748,748, 47,748,752, 47, 47,759,748,752,752,748,752,752,
+
+/* block 76 */
+752,752, 47,752,752,748, 45, 45, 47, 47,762,762,759,759,752, 47,
+752,752, 47, 45, 47, 45, 47, 45, 45, 45, 45, 45, 45, 47, 45, 45,
+ 45, 47, 45, 45, 45, 45, 45, 45,748, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 47, 47, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 47, 45, 45, 47, 45, 45, 45, 45,748, 45,748, 45,
+ 45, 45, 45,748,748,748, 45,748, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 47, 47,752,752,752,703,704,703,704,703,704,703,704,
+703,704,703,704,703,704, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+
+/* block 77 */
+ 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+ 58, 58, 58, 58, 45,748,748,748, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 47, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+748, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,748,
+ 50, 50, 50,744,744,746,747, 50,744,744, 50,744, 50,744, 50, 50,
+ 50, 50, 50, 50, 50,744,744, 50, 50, 50, 50, 50,744,744,744, 50,
+ 50, 50,744,744,744,744,746,747,746,747,746,747,746,747,746,747,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+
+/* block 78 */
+763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,
+763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,
+763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,
+763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,
+763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,
+763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,
+763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,
+763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,
+
+/* block 79 */
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50,741,741, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+
+/* block 80 */
+ 50, 50, 50,746,747,746,747,746,747,746,747,746,747,746,747,746,
+747,746,747,746,747,746,747,746,747, 50, 50,744, 50, 50, 50, 50,
+744, 50, 50,744,744,744, 50, 50,744,744,744,744,744,744,744,744,
+ 50, 50, 50, 50, 50, 50, 50, 50,744, 50, 50, 50, 50, 50, 50, 50,
+744,744, 50, 50,744,744, 50, 50, 50, 50, 50, 50, 50, 50, 50,744,
+744,744,744, 50,744,744, 50, 50,746,747,746,747, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,744,744, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50,744, 50, 50,744,744, 50, 50,746,747, 50, 50,
+
+/* block 81 */
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,744,744,744,744, 50,
+ 50, 50, 50, 50,744,744, 50, 50, 50, 50, 50, 50,744,744, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50,744,744, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50,744,744,744,744,744,744,744,
+
+/* block 82 */
+744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,
+744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,
+744,744,744, 50, 50, 50,744,744,744,744,744,744,744,744, 50,744,
+744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,
+744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,
+744,744,744,744,744,744,744, 50, 50, 50, 50, 50, 50, 50,744, 50,
+ 50, 50, 50,744,744,744, 50, 50, 50, 50, 50, 50,744,744,744, 50,
+ 50, 50, 50, 50, 50, 50, 50,744,744,744,744, 50, 50, 50, 50, 50,
+
+/* block 83 */
+ 45, 45, 45, 45, 45, 47, 47, 47, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,748,748, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 45, 45, 50, 50, 50, 50, 50, 50, 45, 45, 45,
+748, 45, 45, 45, 45,748, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45,753,753, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+
+/* block 84 */
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45,753, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,764, 45,
+
+/* block 85 */
+765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,
+765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,
+765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,
+766,766,766,766,766,766,766,766,766,766,766,766,766,766,766,766,
+766,766,766,766,766,766,766,766,766,766,766,766,766,766,766,766,
+766,766,766,766,766,766,766,766,766,766,766,766,766,766,766,766,
+ 65, 66,767,768,769,770,771, 65, 66, 65, 66, 65, 66,772,773,774,
+775, 70, 65, 66, 70, 65, 66, 70, 70, 70, 70, 70,645,644,776,776,
+
+/* block 86 */
+211,212,211,212,211,212,211,212,211,212,211,212,211,212,211,212,
+211,212,211,212,211,212,211,212,211,212,211,212,211,212,211,212,
+211,212,211,212,211,212,211,212,211,212,211,212,211,212,211,212,
+211,212,211,212,211,212,211,212,211,212,211,212,211,212,211,212,
+211,212,211,212,211,212,211,212,211,212,211,212,211,212,211,212,
+211,212,211,212,211,212,211,212,211,212,211,212,211,212,211,212,
+211,212,211,212,777,778,778,778,778,778,778,211,212,211,212,779,
+779,779,211,212,163,163,163,163,163,780,780,780,780,781,780,780,
+
+/* block 87 */
+782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,
+782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,
+782,782,782,782,782,782,163,782,163,163,163,163,163,782,163,163,
+783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,
+783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,
+783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,
+783,783,783,783,783,783,783,783,163,163,163,163,163,163,163,784,
+785,163,163,163,163,163,163,163,163,163,163,163,163,163,163,786,
+
+/* block 88 */
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,163,163,163,163,163,163,163,163,163,
+483,483,483,483,483,483,483,163,483,483,483,483,483,483,483,163,
+483,483,483,483,483,483,483,163,483,483,483,483,483,483,483,163,
+483,483,483,483,483,483,483,163,483,483,483,483,483,483,483,163,
+483,483,483,483,483,483,483,163,483,483,483,483,483,483,483,163,
+787,787,787,787,787,787,787,787,787,787,787,787,787,787,787,787,
+787,787,787,787,787,787,787,787,787,787,787,787,787,787,787,787,
+
+/* block 89 */
+ 43, 43,788,789,788,789, 43, 43, 43,788,789, 43,788,789, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43,680, 43, 43,680, 43,788,789, 43, 43,
+788,789,703,704,703,704,703,704,703,704, 43, 43, 43, 43,699,790,
+ 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,680,680,699, 43, 43, 43,
+680,791,684,792, 43, 43, 43, 43, 43, 43, 43, 43,791, 43,791,791,
+ 45, 45, 43,699,699,703,704,703,704,703,704,703,704,680,753,753,
+753,753,753,753,753,753,753,753,753,753,753,753,753,753,753,753,
+753,753,753,753,753,753,753,753,753,753,753,753,753,753,753,753,
+
+/* block 90 */
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,163,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 91 */
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+
+/* block 92 */
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+794,794,795,795,794,794,794,794,794,794,794,794,163,163,163,163,
+
+/* block 93 */
+675,796,797,798,723,799,800,801,802,803,802,803,804,805,804,805,
+802,803, 45,806,802,803,802,803,802,803,802,803,807,808,809,809,
+ 45,801,801,801,801,801,801,801,801,801,810,810,810,810,811,811,
+812,813,813,813,813,813,723,814,801,801,801,815,816,817,818,818,
+163,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+
+/* block 94 */
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,163,163,820,820,821,821,822,822,819,
+823,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,
+824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,
+824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,
+824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,
+824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,
+824,824,824,824,824,824,824,824,824,824,824,825,826,827,827,824,
+
+/* block 95 */
+163,163,163,163,163,828,828,828,828,828,828,828,828,828,828,828,
+828,828,828,828,828,828,828,828,828,828,828,828,828,828,828,828,
+828,828,828,828,828,828,828,828,828,828,828,828,828,828,828,828,
+163,829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,
+829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,
+829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,
+829,829,829,829,830,829,829,829,829,829,829,829,829,829,829,829,
+829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,
+
+/* block 96 */
+829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,163,
+831,831,832,832,832,832,831,831,831,831,831,831,831,831,831,831,
+828,828,828,828,828,828,828,828,828,828,828,828,828,828,828,828,
+828,828,828,828,828,828,828,828,828,828,828,828,828,828,828,828,
+818,818,818,818,818,818,818,818,818,818,818,818,818,818,818,818,
+818,818,818,818,818,818,818,818,818,818,818,818,818,818,818,818,
+818,818,818,818,163,163,163,163,163,163,163,163,163,163,163,163,
+824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,
+
+/* block 97 */
+833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,
+833,833,833,833,833,833,833,833,833,833,833,833,833,834,834,163,
+832,832,832,832,832,832,832,832,832,832,831,831,831,831,831,831,
+831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,
+831,831,831,831,831,831,831,831,835,835,835,835,835,835,835,835,
+723, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,
+833,833,833,833,833,833,833,833,833,833,833,833,834,834,834,460,
+
+/* block 98 */
+832,832,832,832,832,832,832,832,832,832,831,831,831,831,831,831,
+831,831,831,831,831,831,831,836,831,836,831,831,831,831,831,831,
+831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,
+831, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+831,831,831,831,831,831,831,831,831,831,831,831,723,723,723,723,
+837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,
+837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,
+837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,831,
+
+/* block 99 */
+837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,
+837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,
+837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,
+837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,
+837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,
+837,837,837,837,837,837,837,837,831,831,831,831,831,831,831,831,
+831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,
+831,460,460,460,460,460,460,723,723,723,723,831,831,831,831,831,
+
+/* block 100 */
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,723,723,
+831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,
+831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,723,
+
+/* block 101 */
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+
+/* block 102 */
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+
+/* block 103 */
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,840,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+
+/* block 104 */
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+
+/* block 105 */
+839,839,839,839,839,839,839,839,839,839,839,839,839,163,163,163,
+841,841,841,841,841,841,841,841,841,841,841,841,841,841,841,841,
+841,841,841,841,841,841,841,841,841,841,841,841,841,841,841,841,
+841,841,841,841,841,841,841,841,841,841,841,841,841,841,841,841,
+841,841,841,841,841,841,841,163,163,163,163,163,163,163,163,163,
+842,842,842,842,842,842,842,842,842,842,842,842,842,842,842,842,
+842,842,842,842,842,842,842,842,842,842,842,842,842,842,842,842,
+842,842,842,842,842,842,842,842,843,843,843,843,843,843,844,845,
+
+/* block 106 */
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+
+/* block 107 */
+846,846,846,846,846,846,846,846,846,846,846,846,847,848,849,849,
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+850,850,850,850,850,850,850,850,850,850,846,846,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+240,241,240,241,240,241,240,241,240,241,851,852,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,853,246,
+248,248,248,854,787,787,787,787,787,787,787,787,855,855,854,856,
+
+/* block 108 */
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,857,857,787,787,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,859,859,859,859,859,859,859,859,859,859,
+860,860,861,862,863,863,863,862,163,163,163,163,163,163,163,163,
+
+/* block 109 */
+864,864,864,864,864,864,864,864, 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46,149,149,149,149,149,149,149,149,149,
+ 46, 46, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 70, 70, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+644, 70, 70, 70, 70, 70, 70, 70, 70, 65, 66, 65, 66,865, 65, 66,
+
+/* block 110 */
+ 65, 66, 65, 66, 65, 66, 65, 66,149,866,866, 65, 66,867, 70, 92,
+ 65, 66, 65, 66,868, 70, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,869,870,871,872,869, 70,
+873,874,875,876, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66,877,878,879, 65, 66, 65, 66,163,163,163,163,163,
+ 65, 66,163, 70,163, 70, 65, 66, 65, 66,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,880,880,880, 65, 66, 92,147,147, 70, 92, 92, 92, 92, 92,
+
+/* block 111 */
+881,881,882,881,881,881,883,881,881,881,881,882,881,881,881,881,
+881,881,881,881,881,881,881,881,881,881,881,881,881,881,881,881,
+881,881,881,884,884,882,882,884,885,885,885,885,883,163,163,163,
+886,886,886,887,887,887,888,888,889,890,163,163,163,163,163,163,
+891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,
+891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,
+891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,
+891,891,891,891,892,892,893,893,163,163,163,163,163,163,163,163,
+
+/* block 112 */
+894,894,895,895,895,895,895,895,895,895,895,895,895,895,895,895,
+895,895,895,895,895,895,895,895,895,895,895,895,895,895,895,895,
+895,895,895,895,895,895,895,895,895,895,895,895,895,895,895,895,
+895,895,895,895,894,894,894,894,894,894,894,894,894,894,894,894,
+894,894,894,894,896,897,163,163,163,163,163,163,163,163,898,898,
+899,899,899,899,899,899,899,899,899,899,163,163,163,163,163,163,
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
+336,900,335,901,335,335,335,335,343,343,343,335,343,335,335,333,
+
+/* block 113 */
+902,902,902,902,902,902,902,902,902,902,903,903,903,903,903,903,
+903,903,903,903,903,903,903,903,903,903,903,903,903,903,903,903,
+903,903,903,903,903,903,904,904,904,904,904,905,905,905,906,907,
+908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,
+908,908,908,908,908,908,908,909,909,909,909,909,909,909,909,909,
+909,909,910,911,163,163,163,163,163,163,163,163,163,163,163,912,
+478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,
+478,478,478,478,478,478,478,478,478,478,478,478,478,163,163,163,
+
+/* block 114 */
+913,913,913,914,915,915,915,915,915,915,915,915,915,915,915,915,
+915,915,915,915,915,915,915,915,915,915,915,915,915,915,915,915,
+915,915,915,915,915,915,915,915,915,915,915,915,915,915,915,915,
+915,915,915,916,914,914,913,913,913,913,914,914,913,913,914,914,
+917,918,918,918,918,918,918,919,920,920,918,918,918,918,163,921,
+922,922,922,922,922,922,922,922,922,922,163,163,163,163,918,918,
+461,461,461,461,461,471,923,461,461,461,461,461,461,461,461,461,
+472,472,472,472,472,472,472,472,472,472,461,461,461,461,461,163,
+
+/* block 115 */
+924,924,924,924,924,924,924,924,924,924,924,924,924,924,924,924,
+924,924,924,924,924,924,924,924,924,924,924,924,924,924,924,924,
+924,924,924,924,924,924,924,924,924,925,925,925,925,925,925,926,
+926,925,925,926,926,925,925,163,163,163,163,163,163,163,163,163,
+924,924,924,925,924,924,924,924,924,924,924,924,925,926,163,163,
+927,927,927,927,927,927,927,927,927,927,163,163,928,929,929,929,
+461,461,461,461,461,461,461,461,461,461,461,461,461,461,461,461,
+923,461,461,461,461,461,461,473,473,473,461,470,471,470,461,461,
+
+/* block 116 */
+930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,
+930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,
+930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,
+931,930,931,931,931,932,932,931,931,932,930,932,932,930,931,933,
+934,933,934,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,930,930,935,936,937,
+938,938,938,938,938,938,938,938,938,938,938,939,940,940,939,939,
+941,941,938,942,942,939,943,163,163,163,163,163,163,163,163,163,
+
+/* block 117 */
+163,483,483,483,483,483,483,163,163,483,483,483,483,483,483,163,
+163,483,483,483,483,483,483,163,163,163,163,163,163,163,163,163,
+483,483,483,483,483,483,483,163,483,483,483,483,483,483,483,163,
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70,944, 70, 70, 70, 70, 70, 70, 70,866,147,147,147,147,
+ 70, 70, 70, 70, 70,221, 70, 70, 70,945, 46, 46,163,163,163,163,
+946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,
+
+/* block 118 */
+946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,
+946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,
+946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,
+946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,
+938,938,938,938,938,938,938,938,938,938,938,938,938,938,938,938,
+938,938,938,938,938,938,938,938,938,938,938,938,938,938,938,938,
+938,938,938,939,939,940,939,939,940,939,939,941,947,943,163,163,
+948,948,948,948,948,948,948,948,948,948,163,163,163,163,163,163,
+
+/* block 119 */
+949,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,949,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,949,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+949,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+
+/* block 120 */
+950,950,950,950,950,950,950,950,950,950,950,950,949,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,949,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+949,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,949,950,950,950,
+
+/* block 121 */
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,949,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+949,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,949,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+
+/* block 122 */
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,949,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+949,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,949,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+
+/* block 123 */
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,949,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+949,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,949,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+
+/* block 124 */
+950,950,950,950,949,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+949,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,949,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,949,950,950,950,950,950,950,950,950,950,950,950,
+
+/* block 125 */
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+949,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,949,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,949,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+
+/* block 126 */
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,163,163,163,163,163,163,163,163,163,163,163,163,
+481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,
+481,481,481,481,481,481,481,163,163,163,163,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,163,163,163,163,
+
+/* block 127 */
+951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,
+951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,
+951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,
+951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,
+951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,
+951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,
+951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,
+951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,
+
+/* block 128 */
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+
+/* block 129 */
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+
+/* block 130 */
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,838,838,
+953,838,953,838,838,953,953,953,953,953,953,953,953,953,953,838,
+953,838,953,838,838,953,953,838,838,838,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,163,163,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+
+/* block 131 */
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 132 */
+652,652,652,652,652,652,652,163,163,163,163,163,163,163,163,163,
+163,163,163,257,257,257,257,257,163,163,163,163,163,270,265,270,
+270,270,270,270,270,270,270,270,270,954,270,270,270,270,270,270,
+270,270,270,270,270,270,270,262,270,270,270,270,270,262,270,262,
+270,270,262,270,270,262,270,270,270,270,270,270,270,270,270,270,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+
+/* block 133 */
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,331,331,331,331,331,331,331,331,331,331,331,331,331,331,
+331,331,331,302,302,302,302,302,302,302,302,302,302,302,302,302,
+302,302,302,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+
+/* block 134 */
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,955,955,
+955,955,955,955,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+
+/* block 135 */
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+
+/* block 136 */
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,956,957,
+280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+
+/* block 137 */
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+302,302,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,302,302,302,302,302,302,302,280,
+958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,
+958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,
+286,286,959,286,286,286,286,286,286,286,955,955,277,960,280,280,
+
+/* block 138 */
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,962,
+963,963,963,964,963,963,963,965,966,963,163,163,163,163,163,163,
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,855,855,
+963,967,967,700,700,965,966,965,966,965,966,965,966,965,966,965,
+966,968,969,968,969,798,798,965,966,963,963,963,963,700,700,700,
+970,166,971,163,166,972,973,973,967,974,975,974,975,974,975,976,
+963,977,713,978,979,979,715,163,977,431,976,963,163,163,163,163,
+955,286,955,286,955,302,955,286,955,286,955,286,955,286,955,286,
+
+/* block 139 */
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,302,302, 51,
+
+/* block 140 */
+163,973,980,976,431,976,963,981,974,975,963,713,970,982,971,983,
+984,984,984,984,984,984,984,984,984,984,972,166,979,715,979,973,
+963,985,985,985,985,985,985, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,974,977,975,986,700,
+ 46,987,987,987,987,987,987, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,974,715,975,715,974,
+975,988,989,990,991,825,824,824,824,824,824,824,824,824,824,824,
+826,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,
+
+/* block 141 */
+824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,
+824,824,824,824,824,824,824,824,824,824,824,824,824,824,992,992,
+830,829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,
+829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,163,
+163,163,829,829,829,829,829,829,163,163,829,829,829,829,829,829,
+163,163,829,829,829,829,829,829,163,163,829,829,829,163,163,163,
+431,431,715, 46,723,431,431,163,723,715,715,715,715,723,723,163,
+707,707,707,707,707,707,707,707,707,993,993,993,723,723,958,958,
+
+/* block 142 */
+994,994,994,994,994,994,994,994,994,994,994,994,163,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,163,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,163,994,994,163,994,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,163,163,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 143 */
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,163,163,163,163,163,
+
+/* block 144 */
+995,996,997,163,163,163,163,998,998,998,998,998,998,998,998,998,
+998,998,998,998,998,998,998,998,998,998,998,998,998,998,998,998,
+998,998,998,998,998,998,998,998,998,998,998,998,998,998,998,998,
+998,998,998,998,163,163,163,999,999,999,999,999,999,999,999,999,
+1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,
+1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,
+1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,
+1000,1000,1000,1000,1000,1001,1001,1001,1001,1002,1002,1002,1002,1002,1002,1002,
+
+/* block 145 */
+1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1001,1001,1002,1003,1003,163,
+723,723,723,723,723,723,723,723,723,723,723,723,723,163,163,163,
+1002,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,158,163,163,
+
+/* block 146 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 147 */
+1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,
+1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,163,163,163,
+1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,
+1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,
+1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,
+1005,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1006,1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,
+1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,163,163,163,163,
+
+/* block 148 */
+1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,
+1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,
+1009,1009,1009,1009,163,163,163,163,163,163,163,163,163,1008,1008,1008,
+1010,1010,1010,1010,1010,1010,1010,1010,1010,1010,1010,1010,1010,1010,1010,1010,
+1010,1011,1010,1010,1010,1010,1010,1010,1010,1010,1011,163,163,163,163,163,
+1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,
+1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,
+1012,1012,1012,1012,1012,1012,1013,1013,1013,1013,1013,163,163,163,163,163,
+
+/* block 149 */
+1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,
+1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,163,1015,
+1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,
+1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,
+1016,1016,1016,1016,163,163,163,163,1016,1016,1016,1016,1016,1016,1016,1016,
+1017,1018,1018,1018,1018,1018,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 150 */
+1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,
+1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,
+1019,1019,1019,1019,1019,1019,1019,1019,1020,1020,1020,1020,1020,1020,1020,1020,
+1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,
+1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,
+1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,
+1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,
+1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,
+
+/* block 151 */
+1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,
+1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,163,163,
+1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,163,163,163,163,163,163,
+1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,
+1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,
+1024,1024,1024,1024,163,163,163,163,1025,1025,1025,1025,1025,1025,1025,1025,
+1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,
+1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,163,163,163,163,
+
+/* block 152 */
+1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,
+1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,
+1026,1026,1026,1026,1026,1026,1026,1026,163,163,163,163,163,163,163,163,
+1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,
+1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,
+1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,
+1027,1027,1027,1027,163,163,163,163,163,163,163,163,163,163,163,1028,
+1029,1029,1029,1029,1029,1029,1029,1029,1029,1029,1029,163,1029,1029,1029,1029,
+
+/* block 153 */
+1029,1029,1029,1029,1029,1029,1029,1029,1029,1029,1029,163,1029,1029,1029,1029,
+1029,1029,1029,163,1029,1029,163,1030,1030,1030,1030,1030,1030,1030,1030,1030,
+1030,1030,163,1030,1030,1030,1030,1030,1030,1030,1030,1030,1030,1030,1030,1030,
+1030,1030,163,1030,1030,1030,1030,1030,1030,1030,163,1030,1030,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 154 */
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+
+/* block 155 */
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,163,163,163,163,163,163,163,163,163,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,163,163,163,163,163,163,163,163,163,163,
+1031,1031,1031,1031,1031,1031,1031,1031,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 156 */
+147,1032,1032,147,147,147,163,147,147,147,147,147,147,147,147,147,
+147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,
+147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,
+147,163,147,147,147,147,147,147,147,147,147,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 157 */
+1033,1033,1033,1033,1033,1033,262,262,1033,262,1033,1033,1033,1033,1033,1033,
+1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,
+1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,
+1033,1033,1033,1033,1033,1033,262,1033,1033,262,262,262,1033,262,262,1033,
+1034,1034,1034,1034,1034,1034,1034,1034,1034,1034,1034,1034,1034,1034,1034,1034,
+1034,1034,1034,1034,1034,1034,262,1035,1036,1036,1036,1036,1036,1036,1036,1036,
+1037,1037,1037,1037,1037,1037,1037,1037,1037,1037,1037,1037,1037,1037,1037,1037,
+1037,1037,1037,1037,1037,1037,1037,1038,1038,1039,1039,1039,1039,1039,1039,1039,
+
+/* block 158 */
+1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,
+1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,262,
+262,262,262,262,262,262,262,1041,1041,1041,1041,1041,1041,1041,1041,1041,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+1042,1042,1042,1042,1042,1042,1042,1042,1042,1042,1042,1042,1042,1042,1042,1042,
+1042,1042,1042,262,1042,1042,262,262,262,262,262,1043,1043,1043,1043,1043,
+
+/* block 159 */
+1044,1044,1044,1044,1044,1044,1044,1044,1044,1044,1044,1044,1044,1044,1044,1044,
+1044,1044,1044,1044,1044,1044,1045,1045,1045,1045,1045,1045,262,262,262,1046,
+1047,1047,1047,1047,1047,1047,1047,1047,1047,1047,1047,1047,1047,1047,1047,1047,
+1047,1047,1047,1047,1047,1047,1047,1047,1047,1047,262,262,262,262,262,1048,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 160 */
+1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,
+1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,
+1050,1050,1050,1050,1050,1050,1050,1050,1050,1050,1050,1050,1050,1050,1050,1050,
+1050,1050,1050,1050,1050,1050,1050,1050,262,262,262,262,1051,1051,1050,1050,
+1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,
+262,262,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,
+1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,
+1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,
+
+/* block 161 */
+1052,1053,1053,1053,262,1053,1053,262,262,262,262,262,1053,1053,1053,1053,
+1052,1052,1052,1052,262,1052,1052,1052,262,1052,1052,1052,1052,1052,1052,1052,
+1052,1052,1052,1052,1052,1052,1052,1052,1052,1052,1052,1052,1052,1052,1052,1052,
+1052,1052,1052,1052,1052,1052,262,262,1054,1054,1054,262,262,262,262,1055,
+1056,1056,1056,1056,1056,1056,1056,1056,1056,262,262,262,262,262,262,262,
+1057,1057,1057,1057,1057,1057,1058,1058,1057,262,262,262,262,262,262,262,
+1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,
+1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1060,1060,1061,
+
+/* block 162 */
+1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,
+1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1063,1063,1063,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+1064,1064,1064,1064,1064,1064,1064,1064,1065,1064,1064,1064,1064,1064,1064,1064,
+1064,1064,1064,1064,1064,1064,1064,1064,1064,1064,1064,1064,1064,1064,1064,1064,
+1064,1064,1064,1064,1064,1066,1066,262,262,262,262,1067,1067,1067,1067,1067,
+1068,1068,1069,1068,1068,1068,1070,262,262,262,262,262,262,262,262,262,
+
+/* block 163 */
+1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,
+1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,
+1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,
+1071,1071,1071,1071,1071,1071,262,262,262,1072,1073,1073,1073,1073,1073,1073,
+1074,1074,1074,1074,1074,1074,1074,1074,1074,1074,1074,1074,1074,1074,1074,1074,
+1074,1074,1074,1074,1074,1074,262,262,1075,1075,1075,1075,1075,1075,1075,1075,
+1076,1076,1076,1076,1076,1076,1076,1076,1076,1076,1076,1076,1076,1076,1076,1076,
+1076,1076,1076,262,262,262,262,262,1077,1077,1077,1077,1077,1077,1077,1077,
+
+/* block 164 */
+1078,1078,1078,1078,1078,1078,1078,1078,1078,1078,1078,1078,1078,1078,1078,1078,
+1078,1078,262,262,262,262,262,262,262,1079,1079,1079,1079,262,262,262,
+262,262,262,262,262,262,262,262,262,1080,1080,1080,1080,1080,1080,1080,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 165 */
+1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,
+1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,
+1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,
+1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,
+1081,1081,1081,1081,1081,1081,1081,1081,1081,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 166 */
+1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,
+1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,
+1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,
+1082,1082,1082,262,262,262,262,262,262,262,262,262,262,262,262,262,
+1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,
+1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,
+1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,
+1083,1083,1083,262,262,262,262,262,262,262,1084,1084,1084,1084,1084,1084,
+
+/* block 167 */
+1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,
+1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,
+1085,1085,1086,1086,1087,1087,1087,1087,302,302,302,302,302,302,302,302,
+1088,1088,1088,1088,1088,1088,1088,1088,1088,1088,302,302,302,302,302,302,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 168 */
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 169 */
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,
+1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,262,
+
+/* block 170 */
+1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,
+1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,
+1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,262,1091,1091,1092,262,262,
+1090,1090,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 171 */
+1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,
+1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1094,1094,1094,
+1094,1094,1094,1094,1094,1094,1094,1093,262,262,262,262,262,262,262,262,
+1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,
+1095,1095,1095,1095,1095,1095,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,
+1096,1097,1097,1097,1097,1098,1098,1098,1098,1098,302,302,302,302,302,302,
+302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,
+1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,
+
+/* block 172 */
+1099,1099,1100,1100,1100,1100,1101,1101,1101,1101,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,
+1102,1102,1102,1102,1102,1103,1103,1103,1103,1103,1103,1103,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,
+1104,1104,1104,1104,1104,1104,1104,262,262,262,262,262,262,262,262,262,
+
+/* block 173 */
+1105,1106,1105,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,
+1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,
+1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,
+1107,1107,1107,1107,1107,1107,1107,1107,1106,1106,1106,1106,1106,1106,1106,1106,
+1106,1106,1106,1106,1106,1106,1108,1109,1109,1110,1110,1110,1110,1110,163,163,
+163,163,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,
+1111,1111,1111,1111,1111,1111,1112,1112,1112,1112,1112,1112,1112,1112,1112,1112,
+1108,1107,1107,1106,1106,1107,163,163,163,163,163,163,163,163,163,1113,
+
+/* block 174 */
+1114,1114,1115,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,
+1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,
+1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,
+1115,1115,1115,1117,1117,1117,1117,1115,1115,1118,1119,1120,1120,1121,1122,1122,
+1122,1122,1117,163,163,163,163,163,163,163,163,163,163,1121,163,163,
+1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,
+1123,1123,1123,1123,1123,1123,1123,1123,1123,163,163,163,163,163,163,163,
+1124,1124,1124,1124,1124,1124,1124,1124,1124,1124,163,163,163,163,163,163,
+
+/* block 175 */
+1125,1125,1125,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,
+1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,
+1126,1126,1126,1126,1126,1126,1126,1125,1125,1125,1125,1125,1127,1125,1125,1125,
+1125,1125,1125,1128,1128,163,1129,1129,1129,1129,1129,1129,1129,1129,1129,1129,
+1130,1131,1131,1131,1126,1127,1127,1126,163,163,163,163,163,163,163,163,
+1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,
+1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,
+1132,1132,1132,1133,1134,1134,1132,163,163,163,163,163,163,163,163,163,
+
+/* block 176 */
+1135,1135,1136,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,
+1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,
+1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,
+1137,1137,1137,1136,1136,1136,1135,1135,1135,1135,1135,1135,1135,1135,1135,1136,
+1138,1137,1139,1139,1137,1140,1140,1141,1141,1142,1143,1143,1143,1140,1136,1135,
+1144,1144,1144,1144,1144,1144,1144,1144,1144,1144,1137,1141,1137,1141,1140,1140,
+163,1145,1145,1145,1145,1145,1145,1145,1145,1145,1145,1145,1145,1145,1145,1145,
+1145,1145,1145,1145,1145,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 177 */
+1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,
+1146,1146,163,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,
+1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1147,1147,1147,1148,
+1148,1148,1147,1147,1148,1149,1150,1148,1151,1151,1152,1151,1151,1153,1148,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 178 */
+1154,1154,1154,1154,1154,1154,1154,163,1154,163,1154,1154,1154,1154,163,1154,
+1154,1154,1154,1154,1154,1154,1154,1154,1154,1154,1154,1154,1154,1154,163,1154,
+1154,1154,1154,1154,1154,1154,1154,1154,1154,1155,163,163,163,163,163,163,
+1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,
+1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,
+1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1157,
+1158,1158,1158,1157,1157,1157,1157,1157,1157,1159,1160,163,163,163,163,163,
+1161,1161,1161,1161,1161,1161,1161,1161,1161,1161,163,163,163,163,163,163,
+
+/* block 179 */
+1162,1163,1164,1165,163,1166,1166,1166,1166,1166,1166,1166,1166,163,163,1166,
+1166,163,163,1166,1166,1166,1166,1166,1166,1166,1166,1166,1166,1166,1166,1166,
+1166,1166,1166,1166,1166,1166,1166,1166,1166,163,1166,1166,1166,1166,1166,1166,
+1166,163,1166,1166,163,1166,1166,1166,1166,1166,163,1167,1168,1166,1169,1164,
+1162,1164,1164,1164,1164,163,163,1164,1164,163,163,1164,1164,1170,163,163,
+1166,163,163,163,163,163,163,1169,163,163,163,163,163,1171,1166,1166,
+1166,1166,1164,1164,163,163,1172,1172,1172,1172,1172,1172,1172,163,163,163,
+1172,1172,1172,1172,1172,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 180 */
+1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,
+1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,
+1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,
+1173,1173,1173,1173,1173,1174,1174,1174,1175,1175,1175,1175,1175,1175,1175,1175,
+1174,1174,1176,1175,1175,1174,1177,1173,1173,1173,1173,1178,1178,1179,1180,1180,
+1181,1181,1181,1181,1181,1181,1181,1181,1181,1181,1179,1179,163,1180,1182,1173,
+1173,1173,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 181 */
+1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,
+1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,
+1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,
+1184,1185,1185,1186,1186,1186,1186,1186,1186,1185,1186,1185,1185,1184,1185,1186,
+1186,1185,1187,1188,1183,1183,1189,1183,163,163,163,163,163,163,163,163,
+1190,1190,1190,1190,1190,1190,1190,1190,1190,1190,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 182 */
+1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,
+1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,
+1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1192,
+1193,1193,1194,1194,1194,1194,163,163,1193,1193,1193,1193,1194,1194,1193,1195,
+1196,1197,1198,1198,1199,1199,1200,1200,1200,1198,1198,1198,1198,1198,1198,1198,
+1198,1198,1198,1198,1198,1198,1198,1198,1191,1191,1191,1191,1194,1194,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 183 */
+1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,
+1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,
+1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,
+1202,1202,1202,1203,1203,1203,1203,1203,1203,1203,1203,1202,1202,1203,1202,1204,
+1203,1205,1205,1206,1201,163,163,163,163,163,163,163,163,163,163,163,
+1207,1207,1207,1207,1207,1207,1207,1207,1207,1207,163,163,163,163,163,163,
+530,530,530,530,530,530,530,530,530,530,530,530,530,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 184 */
+1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,
+1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,
+1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1209,1210,1209,1210,1210,
+1209,1209,1209,1209,1209,1209,1211,1212,1208,1213,163,163,163,163,163,163,
+1214,1214,1214,1214,1214,1214,1214,1214,1214,1214,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 185 */
+1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,
+1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,163,163,1216,1216,1216,
+1217,1217,1216,1216,1216,1216,1218,1216,1216,1216,1216,1219,163,163,163,163,
+1220,1220,1220,1220,1220,1220,1220,1220,1220,1220,1221,1221,1222,1222,1222,1223,
+1215,1215,1215,1215,1215,1215,1215,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 186 */
+1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,
+1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,
+1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1225,1225,1225,1226,
+1226,1226,1226,1226,1226,1226,1226,1226,1225,1227,1228,1229,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 187 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,
+1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,
+1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,
+1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,
+1232,1232,1232,1232,1232,1232,1232,1232,1232,1232,1233,1233,1233,1233,1233,1233,
+1233,1233,1233,163,163,163,163,163,163,163,163,163,163,163,163,1234,
+
+/* block 188 */
+1235,1235,1235,1235,1235,1235,1235,163,163,1235,163,163,1235,1235,1235,1235,
+1235,1235,1235,1235,163,1235,1235,163,1235,1235,1235,1235,1235,1235,1235,1235,
+1235,1235,1235,1235,1235,1235,1235,1235,1235,1235,1235,1235,1235,1235,1235,1235,
+1236,1237,1237,1237,1237,1237,163,1237,1237,163,163,1238,1238,1239,1240,1241,
+1237,1241,1237,1242,1243,1244,1243,163,163,163,163,163,163,163,163,163,
+1245,1245,1245,1245,1245,1245,1245,1245,1245,1245,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 189 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1246,1246,1246,1246,1246,1246,1246,1246,163,163,1246,1246,1246,1246,1246,1246,
+1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,
+1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,
+1246,1247,1247,1247,1248,1248,1248,1248,163,163,1248,1248,1247,1247,1247,1247,
+1249,1246,1250,1246,1247,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 190 */
+1251,1252,1252,1252,1252,1252,1252,1253,1253,1252,1252,1251,1251,1251,1251,1251,
+1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,
+1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,
+1251,1251,1251,1254,1255,1252,1252,1252,1252,1256,1257,1252,1252,1252,1252,1258,
+1258,1258,1259,1259,1258,1258,1258,1255,163,163,163,163,163,163,163,163,
+1260,1261,1261,1261,1261,1261,1261,1262,1262,1261,1261,1261,1260,1260,1260,1260,
+1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,
+1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,
+
+/* block 191 */
+1260,1260,1260,1260,1263,1263,1263,1263,1263,1263,1261,1261,1261,1261,1261,1261,
+1261,1261,1261,1261,1261,1261,1261,1262,1264,1265,1266,1267,1267,1260,1266,1266,
+1266,1268,1268,163,163,163,163,163,163,163,163,163,163,163,163,163,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,
+1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,
+1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,
+1269,1269,1269,1269,1269,1269,1269,1269,1269,163,163,163,163,163,163,163,
+
+/* block 192 */
+1270,1270,1270,1270,1270,1270,1270,1270,1270,163,1270,1270,1270,1270,1270,1270,
+1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,
+1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1271,
+1272,1272,1272,1272,1272,1272,1272,163,1272,1272,1272,1272,1272,1272,1271,1273,
+1270,1274,1274,1275,1276,1276,163,163,163,163,163,163,163,163,163,163,
+1277,1277,1277,1277,1277,1277,1277,1277,1277,1277,1278,1278,1278,1278,1278,1278,
+1278,1278,1278,1278,1278,1278,1278,1278,1278,1278,1278,1278,1278,163,163,163,
+1279,1280,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,
+
+/* block 193 */
+1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,
+163,163,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,
+1282,1282,1282,1282,1282,1282,1282,1282,163,1283,1282,1282,1282,1282,1282,1282,
+1282,1283,1282,1282,1283,1282,1282,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 194 */
+1284,1284,1284,1284,1284,1284,1284,163,1284,1284,163,1284,1284,1284,1284,1284,
+1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,
+1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,
+1284,1285,1285,1285,1285,1285,1285,163,163,163,1285,163,1285,1285,163,1285,
+1285,1285,1286,1285,1287,1287,1288,1285,163,163,163,163,163,163,163,163,
+1289,1289,1289,1289,1289,1289,1289,1289,1289,1289,163,163,163,163,163,163,
+1290,1290,1290,1290,1290,1290,163,1290,1290,163,1290,1290,1290,1290,1290,1290,
+1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,
+
+/* block 195 */
+1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1291,1291,1291,1291,1291,163,
+1292,1292,163,1291,1291,1292,1291,1293,1290,163,163,163,163,163,163,163,
+1294,1294,1294,1294,1294,1294,1294,1294,1294,1294,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 196 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1295,1295,1295,1295,1295,1295,1295,1295,1295,1295,1295,1295,1295,1295,1295,1295,
+1295,1295,1295,1296,1296,1297,1297,1298,1298,163,163,163,163,163,163,163,
+
+/* block 197 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+842,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1299,1299,1299,1299,1299,1299,1299,1299,1299,1299,1299,1299,1299,1299,1299,1299,
+388,388,1299,388,1299,390,390,390,390,390,390,390,390,391,391,391,
+391,390,390,390,390,390,390,390,390,390,390,390,390,390,390,390,
+390,390,163,163,163,163,163,163,163,163,163,163,163,163,163,1300,
+
+/* block 198 */
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+
+/* block 199 */
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 200 */
+1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,
+1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,
+1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,
+1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,
+1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,
+1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,
+1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,163,
+1303,1303,1303,1303,1303,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 201 */
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 202 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,
+1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,
+1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,
+1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,
+1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,
+1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,
+1304,1305,1305,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 203 */
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+
+/* block 204 */
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,163,
+1307,1307,1307,1307,1307,1307,1307,1307,1307,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 205 */
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+
+/* block 206 */
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 207 */
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+
+/* block 208 */
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,163,163,163,163,163,163,163,
+1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,
+1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,163,
+1310,1310,1310,1310,1310,1310,1310,1310,1310,1310,163,163,163,163,1311,1311,
+1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,
+
+/* block 209 */
+1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,
+1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,
+1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,
+1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,163,
+1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,163,163,163,163,163,163,
+1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,
+1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,163,163,
+1315,1315,1315,1315,1315,1316,163,163,163,163,163,163,163,163,163,163,
+
+/* block 210 */
+1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,
+1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,
+1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,
+1318,1318,1318,1318,1318,1318,1318,1319,1319,1320,1321,1321,1322,1322,1322,1322,
+1323,1323,1324,1324,1319,1322,163,163,163,163,163,163,163,163,163,163,
+1325,1325,1325,1325,1325,1325,1325,1325,1325,1325,163,1326,1326,1326,1326,1326,
+1326,1326,163,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,
+1317,1317,1317,1317,1317,1317,1317,1317,163,163,163,163,163,1317,1317,1317,
+
+/* block 211 */
+1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 212 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,
+1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,
+1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,
+1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,
+
+/* block 213 */
+1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,
+1329,1329,1329,1329,1329,1329,1329,1330,1331,1332,1332,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 214 */
+1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,
+1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,
+1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,
+1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,
+1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,163,163,163,163,1334,
+1333,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,
+1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,
+1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,
+
+/* block 215 */
+1335,1335,1335,1335,1335,1335,1335,1335,163,163,163,163,163,163,163,1336,
+1336,1336,1336,1337,1337,1337,1337,1337,1337,1337,1337,1337,1337,1337,1337,1337,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1338,1339,1340,799,1341,163,163,163,163,163,163,163,163,163,163,163,
+1342,1342,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 216 */
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+
+/* block 217 */
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,163,163,163,163,163,163,163,163,
+
+/* block 218 */
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+
+/* block 219 */
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 220 */
+1343,1343,1343,1343,1343,1343,1343,1343,1343,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 221 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1345,1345,1345,1345,163,1345,1345,1345,1345,1345,1345,1345,163,1345,1345,163,
+
+/* block 222 */
+824,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+
+/* block 223 */
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+
+/* block 224 */
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+824,824,824,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+819,819,819,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,824,824,824,824,163,163,163,163,163,163,163,163,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+
+/* block 225 */
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+
+/* block 226 */
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,163,163,163,163,
+
+/* block 227 */
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,163,163,163,163,163,
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,163,163,163,
+
+/* block 228 */
+1347,1347,1347,1347,1347,1347,1347,1347,1347,163,163,163,163,163,163,163,
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,163,163,1348,1349,1350,1351,
+1352,1352,1352,1352,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 229 */
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,163,163,
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,
+154,154,154,154,154,154,154,163,163,163,163,163,163,163,163,163,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+
+/* block 230 */
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 231 */
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+
+/* block 232 */
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,163,163,163,163,163,163,163,163,163,163,
+
+/* block 233 */
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,163,163,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,1353,1354,154,154,154,460,460,460,1355,1356,1356,
+1356,1356,1356, 51, 51, 51, 51, 51, 51, 51, 51,154,154,154,154,154,
+
+/* block 234 */
+154,154,154,460,460,154,154,154,154,154,154,154,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,154,154,154,154,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,723,723,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 235 */
+1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,
+1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,
+1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,
+1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,
+1002,1002,1357,1357,1357,1002,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 236 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+835,835,835,835,835,835,835,835,835,835,835,835,835,835,835,835,
+835,835,835,835,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 237 */
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,163,163,163,163,163,163,163,163,163,
+832,832,832,832,832,832,832,832,832,832,832,832,832,832,832,832,
+832,832,835,835,835,835,835,835,835,163,163,163,163,163,163,163,
+
+/* block 238 */
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,725,725,725,725,725,725,
+725,725,736,736,725,725,725,725,725,725,725,725,725,725,725,725,
+725,725,725,725,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,725,725,
+725,725,725,725,725,163,736,736,725,725,725,725,725,725,725,725,
+725,725,725,725,725,725,725,725,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+
+/* block 239 */
+724,724,725,725,725,725,725,725,725,725,736,736,725,725,725,725,
+725,725,725,725,725,725,725,725,725,725,725,725,724,163,724,724,
+163,163,724,163,163,724,724,163,163,724,724,724,724,163,724,724,
+724,724,724,724,724,724,725,725,725,725,163,725,163,725,736,736,
+725,725,725,725,163,725,725,725,725,725,725,725,725,725,725,725,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,725,725,725,725,725,725,
+725,725,736,736,725,725,725,725,725,725,725,725,725,725,725,725,
+
+/* block 240 */
+725,725,725,725,724,724,163,724,724,724,724,163,163,724,724,724,
+724,724,724,724,724,163,724,724,724,724,724,724,724,163,725,725,
+725,725,725,725,725,725,736,736,725,725,725,725,725,725,725,725,
+725,725,725,725,725,725,725,725,724,724,163,724,724,724,724,163,
+724,724,724,724,724,163,724,163,163,163,724,724,724,724,724,724,
+724,163,725,725,725,725,725,725,725,725,736,736,725,725,725,725,
+725,725,725,725,725,725,725,725,725,725,725,725,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+
+/* block 241 */
+724,724,724,724,724,724,725,725,725,725,725,725,725,725,736,736,
+725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,725,725,725,725,725,725,
+725,725,736,736,725,725,725,725,725,725,725,725,725,725,725,725,
+725,725,725,725,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,725,725,
+725,725,725,725,725,725,736,736,725,725,725,725,725,725,725,725,
+
+/* block 242 */
+725,725,725,725,725,725,725,725,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,725,725,725,725,725,725,725,725,736,736,725,725,725,725,
+725,725,725,725,725,725,725,725,725,725,725,725,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,725,725,725,725,725,725,725,725,736,736,
+725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+
+/* block 243 */
+724,724,724,724,724,724,724,724,724,724,725,725,725,725,725,725,
+725,725,736,736,725,725,725,725,725,725,725,725,725,725,725,725,
+725,725,725,725,725,725,163,163,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,1358,725,725,725,725,725,725,725,725,725,725,725,725,725,725,
+725,725,725,725,725,725,725,725,725,725,725,715,725,725,725,725,
+725,725,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,1358,725,725,725,725,
+
+/* block 244 */
+725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,
+725,725,725,725,725,715,725,725,725,725,725,725,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,1358,725,725,725,725,725,725,725,725,725,725,
+725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,715,
+725,725,725,725,725,725,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,1358,
+725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,
+
+/* block 245 */
+725,725,725,725,725,725,725,725,725,715,725,725,725,725,725,725,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,1358,725,725,725,725,725,725,
+725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,
+725,725,725,715,725,725,725,725,725,725,724,725,163,163,1359,1359,
+1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,
+1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,
+1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,
+
+/* block 246 */
+1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+
+/* block 247 */
+1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,
+1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,
+1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,
+1361,1361,1361,1361,1361,1361,1361,1360,1360,1360,1360,1361,1361,1361,1361,1361,
+1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,
+1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,
+1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1360,1360,1360,
+1360,1360,1360,1360,1360,1361,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+
+/* block 248 */
+1360,1360,1360,1360,1361,1360,1360,1362,1363,1362,1362,1364,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,1361,1361,1361,1361,1361,
+163,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 249 */
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 92, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,643, 70, 70, 70, 70,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 250 */
+1365,1365,1365,1365,1365,1365,1365,163,1365,1365,1365,1365,1365,1365,1365,1365,
+1365,1365,1365,1365,1365,1365,1365,1365,1365,163,163,1365,1365,1365,1365,1365,
+1365,1365,163,1365,1365,163,1365,1365,1365,1365,1365,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 251 */
+1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,
+1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,
+1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,163,163,163,
+1367,1367,1367,1367,1367,1367,1367,1368,1368,1368,1368,1368,1369,1369,163,163,
+1370,1370,1370,1370,1370,1370,1370,1370,1370,1370,163,163,163,163,1366,1371,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 252 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,
+1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1373,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,
+1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,
+1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1375,1375,1375,1375,
+1376,1376,1376,1376,1376,1376,1376,1376,1376,1376,163,163,163,163,163,1377,
+
+/* block 253 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+483,483,483,483,483,483,483,163,483,483,483,483,163,483,483,163,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,163,
+
+/* block 254 */
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+
+/* block 255 */
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,262,262,1379,1379,1379,1379,1379,1379,1379,1379,1379,
+1380,1380,1380,1380,1380,1380,1380,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 256 */
+1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,
+1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,
+1381,1381,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,
+1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,
+1382,1382,1382,1382,1383,1383,1383,1384,1385,1385,1385,1386,262,262,262,262,
+1387,1387,1387,1387,1387,1387,1387,1387,1387,1387,262,262,262,262,1388,1388,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 257 */
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+302,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,
+
+/* block 258 */
+1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,
+1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,
+1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1390,1389,1389,1389,
+1391,1389,1389,1389,1389,302,302,302,302,302,302,302,302,302,302,302,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 259 */
+302,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,
+1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,
+1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1390,1389,
+1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,302,302,
+302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 260 */
+1392,1392,1392,1392,302,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,
+1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,
+302,1392,1392,302,1392,302,302,1392,302,1392,1392,1392,1392,1392,1392,1392,
+1392,1392,1392,302,1392,1392,1392,1392,302,1392,302,1392,302,302,302,302,
+302,302,1392,302,302,302,302,1392,302,1392,302,1392,302,1392,1392,1392,
+302,1392,1392,302,1392,302,302,1392,302,1392,302,1392,302,1392,302,1392,
+302,1392,1392,302,1392,302,302,1392,1392,1392,1392,302,1392,1392,1392,1392,
+1392,1392,1392,302,1392,1392,1392,1392,302,1392,1392,1392,1392,302,1392,302,
+
+/* block 261 */
+1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,302,1392,1392,1392,1392,1392,
+1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,302,302,302,302,
+302,1392,1392,1392,302,1392,1392,1392,1392,1392,302,1392,1392,1392,1392,1392,
+1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,302,302,302,302,
+302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,
+302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,
+302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,
+274,274,302,302,302,302,302,302,302,302,302,302,302,302,302,302,
+
+/* block 262 */
+1393,1393,1393,1393,1394,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1395,1395,1395,1395,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+
+/* block 263 */
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1395,
+1395,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1395,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1394,
+1395,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+
+/* block 264 */
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 58, 58,1393,1393,1393,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,1393,
+1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,
+1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,460,460,460,460,460,460,
+1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,
+1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,723,723,1393,1393,1393,1393,
+1397,1397,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1397,1397,
+
+/* block 265 */
+1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,460,460,460,460,1398,460,
+460,1398,1398,1398,1398,1398,1398,1398,1398,1398,1398,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,1393,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1399,1399,1399,1399,1399,1399,1399,1399,1399,1399,
+1399,1399,1399,1399,1399,1399,1399,1399,1399,1399,1399,1399,1399,1399,1399,1399,
+
+/* block 266 */
+1400,1398,1401,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+460,460,460,460,460,460,460,460,460,460,1398,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,1398,
+460,460,1398,1398,1398,1398,1398,1401,1398,1398,1398,460,1395,1395,1395,1395,
+460,460,460,460,460,460,460,460,460,1395,1395,1395,1395,1395,1395,1395,
+1402,1402,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1393,1393,1393,1393,1393,1393,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+
+/* block 267 */
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+
+/* block 268 */
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,727,1393,1393,727,727,727,727,727,727,727,727,727,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,727,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,727,1394,1394,
+
+/* block 269 */
+1394,1394,1394,1394,1394,1403,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1393,1393,727,727,1393,727,727,727,1393,1393,727,727,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1403,1403,1403,1394,1394,1403,1394,1394,1403,1404,1404,727,727,1394,
+1394,1394,1394,1394,727,727,727,727,727,727,727,727,727,727,727,727,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1393,1393,727,1394,727,1393,727,1394,1394,1394,1405,1405,1405,1405,1405,
+
+/* block 270 */
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,727,
+1394,727,1403,1403,1394,1394,1403,1403,1403,1403,1403,1403,1403,1403,1403,1403,
+1403,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1403,1403,1403,1403,1403,1403,1403,1403,1403,1403,
+1403,1403,1403,1403,1403,1403,1403,1403,1403,1394,1394,1394,1403,1394,1394,1394,
+
+/* block 271 */
+1394,1403,1403,1403,1394,1403,1403,1403,1394,1394,1394,1394,1394,1394,1394,1403,
+1394,1403,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1403,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,727,1393,1394,
+
+/* block 272 */
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,723,723,
+723,723,723,723,723,723,1393,1393,1393,727,727,1394,1394,1394,1394,1393,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1393,1393,1393,1393,1393,1393,1393,727,
+727,1393,1393,727,1404,1404,727,727,727,727,1403,1393,1393,1393,1393,1393,
+
+/* block 273 */
+1393,1393,1393,1393,1393,1393,1393,727,1393,1393,727,727,727,727,1393,1393,
+1404,1393,1393,1393,1393,1403,1403,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1394,727,1393,1393,727,1393,1393,1393,1393,1393,1393,1393,
+1393,727,727,1393,1393,1393,1393,1393,1393,1393,1393,1393,727,1393,1393,1393,
+1393,1393,727,727,727,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,727,727,727,1393,1393,1393,1393,1393,1393,1393,1393,727,727,727,1393,
+1393,727,1393,727,1393,1393,1393,1393,727,1393,1393,1393,1393,1393,1393,727,
+1393,1393,1393,727,1393,1393,1393,1393,1393,1393,727,1394,1394,1394,1394,1394,
+
+/* block 274 */
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1403,1403,1403,1394,1394,1394,1403,1403,1403,1403,1403,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+
+/* block 275 */
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1403,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1403,1403,1403,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1403,1394,1394,1394,1394,1394,1393,1393,1393,1393,1393,727,1403,727,727,727,
+1394,1394,1394,1393,1393,1394,1394,1394,1395,1395,1395,1395,1395,1394,1394,1394,
+727,727,727,727,727,727,1393,1393,1393,727,1393,1394,1394,1395,1395,1395,
+727,1393,1393,727,1394,1394,1394,1394,1394,1394,1394,1394,1394,1395,1395,1395,
+
+/* block 276 */
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+
+/* block 277 */
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,1393,1393,1393,1393,1395,1395,1395,1395,1395,1395,1395,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1395,1395,1395,1395,
+1394,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+
+/* block 278 */
+723,723,723,723,723,723,723,723,723,723,723,723,1395,1395,1395,1395,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,1395,1395,1395,1395,1395,1395,1395,1395,
+723,723,723,723,723,723,723,723,723,723,1395,1395,1395,1395,1395,1395,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+
+/* block 279 */
+723,723,723,723,723,723,723,723,1395,1395,1395,1395,1395,1395,1395,1395,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,1395,1395,
+1393,1393,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+
+/* block 280 */
+723,723,723,723,723,723,723,723,723,723,723,723,1403,1394,1394,1403,
+1394,1394,1394,1394,1394,1394,1394,1394,1403,1403,1403,1403,1403,1403,1403,1403,
+1394,1394,1394,1394,1394,1394,1403,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1403,1403,1403,1403,1403,1403,1403,1403,1403,1403,1394,723,1403,1403,1403,1394,
+1394,1394,1394,1394,1394,1394,723,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1403,1394,1394,1394,1394,1394,1394,1394,1394,
+
+/* block 281 */
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1406,1406,1406,1406,1394,1403,1403,1394,1403,1403,1394,1403,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1403,1403,1403,
+1394,1403,1403,1403,1403,1403,1403,1403,1403,1403,1403,1403,1403,1403,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+
+/* block 282 */
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1395,1395,
+1394,1394,1394,1394,1394,1395,1395,1395,1394,1394,1394,1394,1394,1395,1395,1395,
+
+/* block 283 */
+1394,1394,1394,1394,1394,1394,1394,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1395,1395,1395,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1395,1395,1395,1395,1395,
+1394,1394,1394,1403,1403,1403,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1395,1395,1395,1395,1395,1395,
+1394,1394,1394,1394,1394,1394,1394,1394,1395,1395,1395,1395,1395,1395,1395,1395,
+1403,1403,1403,1403,1403,1403,1403,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+
+/* block 284 */
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+
+/* block 285 */
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,163,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1407,1407,1407,1407,1407,1407,1407,1407,1407,1407,163,163,163,163,163,163,
+
+/* block 286 */
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,958,958,
+
+/* block 287 */
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 288 */
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,163,163,163,163,163,163,163,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+
+/* block 289 */
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,163,163,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+
+/* block 290 */
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+
+/* block 291 */
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 292 */
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 293 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,958,958,
+
+/* block 294 */
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 295 */
+707,712,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,
+1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,
+1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,
+1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,
+1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,
+1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,
+
+/* block 296 */
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+
+/* block 297 */
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+
+/* block 298 */
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+
+/* block 299 */
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,958,958,
+};
+
+#if UCD_BLOCK_SIZE != 128
+#error Please correct UCD_BLOCK_SIZE in pcre2_internal.h
+#endif
+#endif /* SUPPORT_UNICODE */
+
+#endif /* PCRE2_PCRE2TEST */
+
+/* End of pcre2_ucd.c */
diff --git a/contrib/libs/pcre2/src/pcre2_ucp.h b/contrib/libs/pcre2/src/pcre2_ucp.h
new file mode 100644
index 00000000000..282238982dc
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_ucp.h
@@ -0,0 +1,394 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+This module is auto-generated from Unicode data files. DO NOT EDIT MANUALLY!
+Instead, modify the maint/GenerateUcpHeader.py script and run it to generate
+a new version of this code.
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#ifndef PCRE2_UCP_H_IDEMPOTENT_GUARD
+#define PCRE2_UCP_H_IDEMPOTENT_GUARD
+
+/* This file contains definitions of the Unicode property values that are
+returned by the UCD access macros and used throughout PCRE2.
+
+IMPORTANT: The specific values of the first two enums (general and particular
+character categories) are assumed by the table called catposstab in the file
+pcre2_auto_possess.c. They are unlikely to change, but should be checked after
+an update. */
+
+/* These are the general character categories. */
+
+enum {
+ ucp_C,
+ ucp_L,
+ ucp_M,
+ ucp_N,
+ ucp_P,
+ ucp_S,
+ ucp_Z,
+};
+
+/* These are the particular character categories. */
+
+enum {
+ ucp_Cc, /* Control */
+ ucp_Cf, /* Format */
+ ucp_Cn, /* Unassigned */
+ ucp_Co, /* Private use */
+ ucp_Cs, /* Surrogate */
+ ucp_Ll, /* Lower case letter */
+ ucp_Lm, /* Modifier letter */
+ ucp_Lo, /* Other letter */
+ ucp_Lt, /* Title case letter */
+ ucp_Lu, /* Upper case letter */
+ ucp_Mc, /* Spacing mark */
+ ucp_Me, /* Enclosing mark */
+ ucp_Mn, /* Non-spacing mark */
+ ucp_Nd, /* Decimal number */
+ ucp_Nl, /* Letter number */
+ ucp_No, /* Other number */
+ ucp_Pc, /* Connector punctuation */
+ ucp_Pd, /* Dash punctuation */
+ ucp_Pe, /* Close punctuation */
+ ucp_Pf, /* Final punctuation */
+ ucp_Pi, /* Initial punctuation */
+ ucp_Po, /* Other punctuation */
+ ucp_Ps, /* Open punctuation */
+ ucp_Sc, /* Currency symbol */
+ ucp_Sk, /* Modifier symbol */
+ ucp_Sm, /* Mathematical symbol */
+ ucp_So, /* Other symbol */
+ ucp_Zl, /* Line separator */
+ ucp_Zp, /* Paragraph separator */
+ ucp_Zs, /* Space separator */
+};
+
+/* These are Boolean properties. */
+
+enum {
+ ucp_ASCII,
+ ucp_ASCII_Hex_Digit,
+ ucp_Alphabetic,
+ ucp_Bidi_Control,
+ ucp_Bidi_Mirrored,
+ ucp_Case_Ignorable,
+ ucp_Cased,
+ ucp_Changes_When_Casefolded,
+ ucp_Changes_When_Casemapped,
+ ucp_Changes_When_Lowercased,
+ ucp_Changes_When_Titlecased,
+ ucp_Changes_When_Uppercased,
+ ucp_Dash,
+ ucp_Default_Ignorable_Code_Point,
+ ucp_Deprecated,
+ ucp_Diacritic,
+ ucp_Emoji,
+ ucp_Emoji_Component,
+ ucp_Emoji_Modifier,
+ ucp_Emoji_Modifier_Base,
+ ucp_Emoji_Presentation,
+ ucp_Extended_Pictographic,
+ ucp_Extender,
+ ucp_Grapheme_Base,
+ ucp_Grapheme_Extend,
+ ucp_Grapheme_Link,
+ ucp_Hex_Digit,
+ ucp_IDS_Binary_Operator,
+ ucp_IDS_Trinary_Operator,
+ ucp_ID_Continue,
+ ucp_ID_Start,
+ ucp_Ideographic,
+ ucp_Join_Control,
+ ucp_Logical_Order_Exception,
+ ucp_Lowercase,
+ ucp_Math,
+ ucp_Noncharacter_Code_Point,
+ ucp_Pattern_Syntax,
+ ucp_Pattern_White_Space,
+ ucp_Prepended_Concatenation_Mark,
+ ucp_Quotation_Mark,
+ ucp_Radical,
+ ucp_Regional_Indicator,
+ ucp_Sentence_Terminal,
+ ucp_Soft_Dotted,
+ ucp_Terminal_Punctuation,
+ ucp_Unified_Ideograph,
+ ucp_Uppercase,
+ ucp_Variation_Selector,
+ ucp_White_Space,
+ ucp_XID_Continue,
+ ucp_XID_Start,
+ /* This must be last */
+ ucp_Bprop_Count
+};
+
+/* Size of entries in ucd_boolprop_sets[] */
+
+#define ucd_boolprop_sets_item_size 2
+
+/* These are the bidi class values. */
+
+enum {
+ ucp_bidiAL, /* Arabic letter */
+ ucp_bidiAN, /* Arabic number */
+ ucp_bidiB, /* Paragraph separator */
+ ucp_bidiBN, /* Boundary neutral */
+ ucp_bidiCS, /* Common separator */
+ ucp_bidiEN, /* European number */
+ ucp_bidiES, /* European separator */
+ ucp_bidiET, /* European terminator */
+ ucp_bidiFSI, /* First strong isolate */
+ ucp_bidiL, /* Left to right */
+ ucp_bidiLRE, /* Left to right embedding */
+ ucp_bidiLRI, /* Left to right isolate */
+ ucp_bidiLRO, /* Left to right override */
+ ucp_bidiNSM, /* Non-spacing mark */
+ ucp_bidiON, /* Other neutral */
+ ucp_bidiPDF, /* Pop directional format */
+ ucp_bidiPDI, /* Pop directional isolate */
+ ucp_bidiR, /* Right to left */
+ ucp_bidiRLE, /* Right to left embedding */
+ ucp_bidiRLI, /* Right to left isolate */
+ ucp_bidiRLO, /* Right to left override */
+ ucp_bidiS, /* Segment separator */
+ ucp_bidiWS, /* White space */
+};
+
+/* These are grapheme break properties. The Extended Pictographic property
+comes from the emoji-data.txt file. */
+
+enum {
+ ucp_gbCR, /* 0 */
+ ucp_gbLF, /* 1 */
+ ucp_gbControl, /* 2 */
+ ucp_gbExtend, /* 3 */
+ ucp_gbPrepend, /* 4 */
+ ucp_gbSpacingMark, /* 5 */
+ ucp_gbL, /* 6 Hangul syllable type L */
+ ucp_gbV, /* 7 Hangul syllable type V */
+ ucp_gbT, /* 8 Hangul syllable type T */
+ ucp_gbLV, /* 9 Hangul syllable type LV */
+ ucp_gbLVT, /* 10 Hangul syllable type LVT */
+ ucp_gbRegional_Indicator, /* 11 */
+ ucp_gbOther, /* 12 */
+ ucp_gbZWJ, /* 13 */
+ ucp_gbExtended_Pictographic, /* 14 */
+};
+
+/* These are the script identifications. */
+
+enum {
+ /* Scripts which has characters in other scripts. */
+ ucp_Latin,
+ ucp_Greek,
+ ucp_Cyrillic,
+ ucp_Arabic,
+ ucp_Syriac,
+ ucp_Thaana,
+ ucp_Devanagari,
+ ucp_Bengali,
+ ucp_Gurmukhi,
+ ucp_Gujarati,
+ ucp_Oriya,
+ ucp_Tamil,
+ ucp_Telugu,
+ ucp_Kannada,
+ ucp_Malayalam,
+ ucp_Sinhala,
+ ucp_Myanmar,
+ ucp_Georgian,
+ ucp_Hangul,
+ ucp_Mongolian,
+ ucp_Hiragana,
+ ucp_Katakana,
+ ucp_Bopomofo,
+ ucp_Han,
+ ucp_Yi,
+ ucp_Tagalog,
+ ucp_Hanunoo,
+ ucp_Buhid,
+ ucp_Tagbanwa,
+ ucp_Limbu,
+ ucp_Tai_Le,
+ ucp_Linear_B,
+ ucp_Cypriot,
+ ucp_Buginese,
+ ucp_Coptic,
+ ucp_Glagolitic,
+ ucp_Syloti_Nagri,
+ ucp_Phags_Pa,
+ ucp_Nko,
+ ucp_Kayah_Li,
+ ucp_Javanese,
+ ucp_Kaithi,
+ ucp_Mandaic,
+ ucp_Chakma,
+ ucp_Sharada,
+ ucp_Takri,
+ ucp_Duployan,
+ ucp_Grantha,
+ ucp_Khojki,
+ ucp_Linear_A,
+ ucp_Mahajani,
+ ucp_Manichaean,
+ ucp_Modi,
+ ucp_Old_Permic,
+ ucp_Psalter_Pahlavi,
+ ucp_Khudawadi,
+ ucp_Tirhuta,
+ ucp_Multani,
+ ucp_Adlam,
+ ucp_Masaram_Gondi,
+ ucp_Dogra,
+ ucp_Gunjala_Gondi,
+ ucp_Hanifi_Rohingya,
+ ucp_Sogdian,
+ ucp_Nandinagari,
+ ucp_Yezidi,
+ ucp_Cypro_Minoan,
+ ucp_Old_Uyghur,
+
+ /* Scripts which has no characters in other scripts. */
+ ucp_Unknown,
+ ucp_Common,
+ ucp_Armenian,
+ ucp_Hebrew,
+ ucp_Thai,
+ ucp_Lao,
+ ucp_Tibetan,
+ ucp_Ethiopic,
+ ucp_Cherokee,
+ ucp_Canadian_Aboriginal,
+ ucp_Ogham,
+ ucp_Runic,
+ ucp_Khmer,
+ ucp_Old_Italic,
+ ucp_Gothic,
+ ucp_Deseret,
+ ucp_Inherited,
+ ucp_Ugaritic,
+ ucp_Shavian,
+ ucp_Osmanya,
+ ucp_Braille,
+ ucp_New_Tai_Lue,
+ ucp_Tifinagh,
+ ucp_Old_Persian,
+ ucp_Kharoshthi,
+ ucp_Balinese,
+ ucp_Cuneiform,
+ ucp_Phoenician,
+ ucp_Sundanese,
+ ucp_Lepcha,
+ ucp_Ol_Chiki,
+ ucp_Vai,
+ ucp_Saurashtra,
+ ucp_Rejang,
+ ucp_Lycian,
+ ucp_Carian,
+ ucp_Lydian,
+ ucp_Cham,
+ ucp_Tai_Tham,
+ ucp_Tai_Viet,
+ ucp_Avestan,
+ ucp_Egyptian_Hieroglyphs,
+ ucp_Samaritan,
+ ucp_Lisu,
+ ucp_Bamum,
+ ucp_Meetei_Mayek,
+ ucp_Imperial_Aramaic,
+ ucp_Old_South_Arabian,
+ ucp_Inscriptional_Parthian,
+ ucp_Inscriptional_Pahlavi,
+ ucp_Old_Turkic,
+ ucp_Batak,
+ ucp_Brahmi,
+ ucp_Meroitic_Cursive,
+ ucp_Meroitic_Hieroglyphs,
+ ucp_Miao,
+ ucp_Sora_Sompeng,
+ ucp_Caucasian_Albanian,
+ ucp_Bassa_Vah,
+ ucp_Elbasan,
+ ucp_Pahawh_Hmong,
+ ucp_Mende_Kikakui,
+ ucp_Mro,
+ ucp_Old_North_Arabian,
+ ucp_Nabataean,
+ ucp_Palmyrene,
+ ucp_Pau_Cin_Hau,
+ ucp_Siddham,
+ ucp_Warang_Citi,
+ ucp_Ahom,
+ ucp_Anatolian_Hieroglyphs,
+ ucp_Hatran,
+ ucp_Old_Hungarian,
+ ucp_SignWriting,
+ ucp_Bhaiksuki,
+ ucp_Marchen,
+ ucp_Newa,
+ ucp_Osage,
+ ucp_Tangut,
+ ucp_Nushu,
+ ucp_Soyombo,
+ ucp_Zanabazar_Square,
+ ucp_Makasar,
+ ucp_Medefaidrin,
+ ucp_Old_Sogdian,
+ ucp_Elymaic,
+ ucp_Nyiakeng_Puachue_Hmong,
+ ucp_Wancho,
+ ucp_Chorasmian,
+ ucp_Dives_Akuru,
+ ucp_Khitan_Small_Script,
+ ucp_Tangsa,
+ ucp_Toto,
+ ucp_Vithkuqi,
+
+ /* This must be last */
+ ucp_Script_Count
+};
+
+/* Size of entries in ucd_script_sets[] */
+
+#define ucd_script_sets_item_size 3
+
+#endif /* PCRE2_UCP_H_IDEMPOTENT_GUARD */
+
+/* End of pcre2_ucp.h */
diff --git a/contrib/libs/pcre2/src/pcre2_ucptables.c b/contrib/libs/pcre2/src/pcre2_ucptables.c
new file mode 100644
index 00000000000..bd1b67a9f1b
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_ucptables.c
@@ -0,0 +1,1524 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+This module is auto-generated from Unicode data files. DO NOT EDIT MANUALLY!
+Instead, modify the maint/GenerateUcpTables.py script and run it to generate
+a new version of this code.
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#ifdef SUPPORT_UNICODE
+
+/* The PRIV(utt)[] table below translates Unicode property names into type and
+code values. It is searched by binary chop, so must be in collating sequence of
+name. Originally, the table contained pointers to the name strings in the first
+field of each entry. However, that leads to a large number of relocations when
+a shared library is dynamically loaded. A significant reduction is made by
+putting all the names into a single, large string and using offsets instead.
+All letters are lower cased, and underscores are removed, in accordance with
+the "loose matching" rules that Unicode advises and Perl uses. */
+
+#define STRING_adlam0 STR_a STR_d STR_l STR_a STR_m "\0"
+#define STRING_adlm0 STR_a STR_d STR_l STR_m "\0"
+#define STRING_aghb0 STR_a STR_g STR_h STR_b "\0"
+#define STRING_ahex0 STR_a STR_h STR_e STR_x "\0"
+#define STRING_ahom0 STR_a STR_h STR_o STR_m "\0"
+#define STRING_alpha0 STR_a STR_l STR_p STR_h STR_a "\0"
+#define STRING_alphabetic0 STR_a STR_l STR_p STR_h STR_a STR_b STR_e STR_t STR_i STR_c "\0"
+#define STRING_anatolianhieroglyphs0 STR_a STR_n STR_a STR_t STR_o STR_l STR_i STR_a STR_n STR_h STR_i STR_e STR_r STR_o STR_g STR_l STR_y STR_p STR_h STR_s "\0"
+#define STRING_any0 STR_a STR_n STR_y "\0"
+#define STRING_arab0 STR_a STR_r STR_a STR_b "\0"
+#define STRING_arabic0 STR_a STR_r STR_a STR_b STR_i STR_c "\0"
+#define STRING_armenian0 STR_a STR_r STR_m STR_e STR_n STR_i STR_a STR_n "\0"
+#define STRING_armi0 STR_a STR_r STR_m STR_i "\0"
+#define STRING_armn0 STR_a STR_r STR_m STR_n "\0"
+#define STRING_ascii0 STR_a STR_s STR_c STR_i STR_i "\0"
+#define STRING_asciihexdigit0 STR_a STR_s STR_c STR_i STR_i STR_h STR_e STR_x STR_d STR_i STR_g STR_i STR_t "\0"
+#define STRING_avestan0 STR_a STR_v STR_e STR_s STR_t STR_a STR_n "\0"
+#define STRING_avst0 STR_a STR_v STR_s STR_t "\0"
+#define STRING_bali0 STR_b STR_a STR_l STR_i "\0"
+#define STRING_balinese0 STR_b STR_a STR_l STR_i STR_n STR_e STR_s STR_e "\0"
+#define STRING_bamu0 STR_b STR_a STR_m STR_u "\0"
+#define STRING_bamum0 STR_b STR_a STR_m STR_u STR_m "\0"
+#define STRING_bass0 STR_b STR_a STR_s STR_s "\0"
+#define STRING_bassavah0 STR_b STR_a STR_s STR_s STR_a STR_v STR_a STR_h "\0"
+#define STRING_batak0 STR_b STR_a STR_t STR_a STR_k "\0"
+#define STRING_batk0 STR_b STR_a STR_t STR_k "\0"
+#define STRING_beng0 STR_b STR_e STR_n STR_g "\0"
+#define STRING_bengali0 STR_b STR_e STR_n STR_g STR_a STR_l STR_i "\0"
+#define STRING_bhaiksuki0 STR_b STR_h STR_a STR_i STR_k STR_s STR_u STR_k STR_i "\0"
+#define STRING_bhks0 STR_b STR_h STR_k STR_s "\0"
+#define STRING_bidial0 STR_b STR_i STR_d STR_i STR_a STR_l "\0"
+#define STRING_bidian0 STR_b STR_i STR_d STR_i STR_a STR_n "\0"
+#define STRING_bidib0 STR_b STR_i STR_d STR_i STR_b "\0"
+#define STRING_bidibn0 STR_b STR_i STR_d STR_i STR_b STR_n "\0"
+#define STRING_bidic0 STR_b STR_i STR_d STR_i STR_c "\0"
+#define STRING_bidicontrol0 STR_b STR_i STR_d STR_i STR_c STR_o STR_n STR_t STR_r STR_o STR_l "\0"
+#define STRING_bidics0 STR_b STR_i STR_d STR_i STR_c STR_s "\0"
+#define STRING_bidien0 STR_b STR_i STR_d STR_i STR_e STR_n "\0"
+#define STRING_bidies0 STR_b STR_i STR_d STR_i STR_e STR_s "\0"
+#define STRING_bidiet0 STR_b STR_i STR_d STR_i STR_e STR_t "\0"
+#define STRING_bidifsi0 STR_b STR_i STR_d STR_i STR_f STR_s STR_i "\0"
+#define STRING_bidil0 STR_b STR_i STR_d STR_i STR_l "\0"
+#define STRING_bidilre0 STR_b STR_i STR_d STR_i STR_l STR_r STR_e "\0"
+#define STRING_bidilri0 STR_b STR_i STR_d STR_i STR_l STR_r STR_i "\0"
+#define STRING_bidilro0 STR_b STR_i STR_d STR_i STR_l STR_r STR_o "\0"
+#define STRING_bidim0 STR_b STR_i STR_d STR_i STR_m "\0"
+#define STRING_bidimirrored0 STR_b STR_i STR_d STR_i STR_m STR_i STR_r STR_r STR_o STR_r STR_e STR_d "\0"
+#define STRING_bidinsm0 STR_b STR_i STR_d STR_i STR_n STR_s STR_m "\0"
+#define STRING_bidion0 STR_b STR_i STR_d STR_i STR_o STR_n "\0"
+#define STRING_bidipdf0 STR_b STR_i STR_d STR_i STR_p STR_d STR_f "\0"
+#define STRING_bidipdi0 STR_b STR_i STR_d STR_i STR_p STR_d STR_i "\0"
+#define STRING_bidir0 STR_b STR_i STR_d STR_i STR_r "\0"
+#define STRING_bidirle0 STR_b STR_i STR_d STR_i STR_r STR_l STR_e "\0"
+#define STRING_bidirli0 STR_b STR_i STR_d STR_i STR_r STR_l STR_i "\0"
+#define STRING_bidirlo0 STR_b STR_i STR_d STR_i STR_r STR_l STR_o "\0"
+#define STRING_bidis0 STR_b STR_i STR_d STR_i STR_s "\0"
+#define STRING_bidiws0 STR_b STR_i STR_d STR_i STR_w STR_s "\0"
+#define STRING_bopo0 STR_b STR_o STR_p STR_o "\0"
+#define STRING_bopomofo0 STR_b STR_o STR_p STR_o STR_m STR_o STR_f STR_o "\0"
+#define STRING_brah0 STR_b STR_r STR_a STR_h "\0"
+#define STRING_brahmi0 STR_b STR_r STR_a STR_h STR_m STR_i "\0"
+#define STRING_brai0 STR_b STR_r STR_a STR_i "\0"
+#define STRING_braille0 STR_b STR_r STR_a STR_i STR_l STR_l STR_e "\0"
+#define STRING_bugi0 STR_b STR_u STR_g STR_i "\0"
+#define STRING_buginese0 STR_b STR_u STR_g STR_i STR_n STR_e STR_s STR_e "\0"
+#define STRING_buhd0 STR_b STR_u STR_h STR_d "\0"
+#define STRING_buhid0 STR_b STR_u STR_h STR_i STR_d "\0"
+#define STRING_c0 STR_c "\0"
+#define STRING_cakm0 STR_c STR_a STR_k STR_m "\0"
+#define STRING_canadianaboriginal0 STR_c STR_a STR_n STR_a STR_d STR_i STR_a STR_n STR_a STR_b STR_o STR_r STR_i STR_g STR_i STR_n STR_a STR_l "\0"
+#define STRING_cans0 STR_c STR_a STR_n STR_s "\0"
+#define STRING_cari0 STR_c STR_a STR_r STR_i "\0"
+#define STRING_carian0 STR_c STR_a STR_r STR_i STR_a STR_n "\0"
+#define STRING_cased0 STR_c STR_a STR_s STR_e STR_d "\0"
+#define STRING_caseignorable0 STR_c STR_a STR_s STR_e STR_i STR_g STR_n STR_o STR_r STR_a STR_b STR_l STR_e "\0"
+#define STRING_caucasianalbanian0 STR_c STR_a STR_u STR_c STR_a STR_s STR_i STR_a STR_n STR_a STR_l STR_b STR_a STR_n STR_i STR_a STR_n "\0"
+#define STRING_cc0 STR_c STR_c "\0"
+#define STRING_cf0 STR_c STR_f "\0"
+#define STRING_chakma0 STR_c STR_h STR_a STR_k STR_m STR_a "\0"
+#define STRING_cham0 STR_c STR_h STR_a STR_m "\0"
+#define STRING_changeswhencasefolded0 STR_c STR_h STR_a STR_n STR_g STR_e STR_s STR_w STR_h STR_e STR_n STR_c STR_a STR_s STR_e STR_f STR_o STR_l STR_d STR_e STR_d "\0"
+#define STRING_changeswhencasemapped0 STR_c STR_h STR_a STR_n STR_g STR_e STR_s STR_w STR_h STR_e STR_n STR_c STR_a STR_s STR_e STR_m STR_a STR_p STR_p STR_e STR_d "\0"
+#define STRING_changeswhenlowercased0 STR_c STR_h STR_a STR_n STR_g STR_e STR_s STR_w STR_h STR_e STR_n STR_l STR_o STR_w STR_e STR_r STR_c STR_a STR_s STR_e STR_d "\0"
+#define STRING_changeswhentitlecased0 STR_c STR_h STR_a STR_n STR_g STR_e STR_s STR_w STR_h STR_e STR_n STR_t STR_i STR_t STR_l STR_e STR_c STR_a STR_s STR_e STR_d "\0"
+#define STRING_changeswhenuppercased0 STR_c STR_h STR_a STR_n STR_g STR_e STR_s STR_w STR_h STR_e STR_n STR_u STR_p STR_p STR_e STR_r STR_c STR_a STR_s STR_e STR_d "\0"
+#define STRING_cher0 STR_c STR_h STR_e STR_r "\0"
+#define STRING_cherokee0 STR_c STR_h STR_e STR_r STR_o STR_k STR_e STR_e "\0"
+#define STRING_chorasmian0 STR_c STR_h STR_o STR_r STR_a STR_s STR_m STR_i STR_a STR_n "\0"
+#define STRING_chrs0 STR_c STR_h STR_r STR_s "\0"
+#define STRING_ci0 STR_c STR_i "\0"
+#define STRING_cn0 STR_c STR_n "\0"
+#define STRING_co0 STR_c STR_o "\0"
+#define STRING_common0 STR_c STR_o STR_m STR_m STR_o STR_n "\0"
+#define STRING_copt0 STR_c STR_o STR_p STR_t "\0"
+#define STRING_coptic0 STR_c STR_o STR_p STR_t STR_i STR_c "\0"
+#define STRING_cpmn0 STR_c STR_p STR_m STR_n "\0"
+#define STRING_cprt0 STR_c STR_p STR_r STR_t "\0"
+#define STRING_cs0 STR_c STR_s "\0"
+#define STRING_cuneiform0 STR_c STR_u STR_n STR_e STR_i STR_f STR_o STR_r STR_m "\0"
+#define STRING_cwcf0 STR_c STR_w STR_c STR_f "\0"
+#define STRING_cwcm0 STR_c STR_w STR_c STR_m "\0"
+#define STRING_cwl0 STR_c STR_w STR_l "\0"
+#define STRING_cwt0 STR_c STR_w STR_t "\0"
+#define STRING_cwu0 STR_c STR_w STR_u "\0"
+#define STRING_cypriot0 STR_c STR_y STR_p STR_r STR_i STR_o STR_t "\0"
+#define STRING_cyprominoan0 STR_c STR_y STR_p STR_r STR_o STR_m STR_i STR_n STR_o STR_a STR_n "\0"
+#define STRING_cyrillic0 STR_c STR_y STR_r STR_i STR_l STR_l STR_i STR_c "\0"
+#define STRING_cyrl0 STR_c STR_y STR_r STR_l "\0"
+#define STRING_dash0 STR_d STR_a STR_s STR_h "\0"
+#define STRING_defaultignorablecodepoint0 STR_d STR_e STR_f STR_a STR_u STR_l STR_t STR_i STR_g STR_n STR_o STR_r STR_a STR_b STR_l STR_e STR_c STR_o STR_d STR_e STR_p STR_o STR_i STR_n STR_t "\0"
+#define STRING_dep0 STR_d STR_e STR_p "\0"
+#define STRING_deprecated0 STR_d STR_e STR_p STR_r STR_e STR_c STR_a STR_t STR_e STR_d "\0"
+#define STRING_deseret0 STR_d STR_e STR_s STR_e STR_r STR_e STR_t "\0"
+#define STRING_deva0 STR_d STR_e STR_v STR_a "\0"
+#define STRING_devanagari0 STR_d STR_e STR_v STR_a STR_n STR_a STR_g STR_a STR_r STR_i "\0"
+#define STRING_di0 STR_d STR_i "\0"
+#define STRING_dia0 STR_d STR_i STR_a "\0"
+#define STRING_diacritic0 STR_d STR_i STR_a STR_c STR_r STR_i STR_t STR_i STR_c "\0"
+#define STRING_diak0 STR_d STR_i STR_a STR_k "\0"
+#define STRING_divesakuru0 STR_d STR_i STR_v STR_e STR_s STR_a STR_k STR_u STR_r STR_u "\0"
+#define STRING_dogr0 STR_d STR_o STR_g STR_r "\0"
+#define STRING_dogra0 STR_d STR_o STR_g STR_r STR_a "\0"
+#define STRING_dsrt0 STR_d STR_s STR_r STR_t "\0"
+#define STRING_dupl0 STR_d STR_u STR_p STR_l "\0"
+#define STRING_duployan0 STR_d STR_u STR_p STR_l STR_o STR_y STR_a STR_n "\0"
+#define STRING_ebase0 STR_e STR_b STR_a STR_s STR_e "\0"
+#define STRING_ecomp0 STR_e STR_c STR_o STR_m STR_p "\0"
+#define STRING_egyp0 STR_e STR_g STR_y STR_p "\0"
+#define STRING_egyptianhieroglyphs0 STR_e STR_g STR_y STR_p STR_t STR_i STR_a STR_n STR_h STR_i STR_e STR_r STR_o STR_g STR_l STR_y STR_p STR_h STR_s "\0"
+#define STRING_elba0 STR_e STR_l STR_b STR_a "\0"
+#define STRING_elbasan0 STR_e STR_l STR_b STR_a STR_s STR_a STR_n "\0"
+#define STRING_elym0 STR_e STR_l STR_y STR_m "\0"
+#define STRING_elymaic0 STR_e STR_l STR_y STR_m STR_a STR_i STR_c "\0"
+#define STRING_emod0 STR_e STR_m STR_o STR_d "\0"
+#define STRING_emoji0 STR_e STR_m STR_o STR_j STR_i "\0"
+#define STRING_emojicomponent0 STR_e STR_m STR_o STR_j STR_i STR_c STR_o STR_m STR_p STR_o STR_n STR_e STR_n STR_t "\0"
+#define STRING_emojimodifier0 STR_e STR_m STR_o STR_j STR_i STR_m STR_o STR_d STR_i STR_f STR_i STR_e STR_r "\0"
+#define STRING_emojimodifierbase0 STR_e STR_m STR_o STR_j STR_i STR_m STR_o STR_d STR_i STR_f STR_i STR_e STR_r STR_b STR_a STR_s STR_e "\0"
+#define STRING_emojipresentation0 STR_e STR_m STR_o STR_j STR_i STR_p STR_r STR_e STR_s STR_e STR_n STR_t STR_a STR_t STR_i STR_o STR_n "\0"
+#define STRING_epres0 STR_e STR_p STR_r STR_e STR_s "\0"
+#define STRING_ethi0 STR_e STR_t STR_h STR_i "\0"
+#define STRING_ethiopic0 STR_e STR_t STR_h STR_i STR_o STR_p STR_i STR_c "\0"
+#define STRING_ext0 STR_e STR_x STR_t "\0"
+#define STRING_extendedpictographic0 STR_e STR_x STR_t STR_e STR_n STR_d STR_e STR_d STR_p STR_i STR_c STR_t STR_o STR_g STR_r STR_a STR_p STR_h STR_i STR_c "\0"
+#define STRING_extender0 STR_e STR_x STR_t STR_e STR_n STR_d STR_e STR_r "\0"
+#define STRING_extpict0 STR_e STR_x STR_t STR_p STR_i STR_c STR_t "\0"
+#define STRING_geor0 STR_g STR_e STR_o STR_r "\0"
+#define STRING_georgian0 STR_g STR_e STR_o STR_r STR_g STR_i STR_a STR_n "\0"
+#define STRING_glag0 STR_g STR_l STR_a STR_g "\0"
+#define STRING_glagolitic0 STR_g STR_l STR_a STR_g STR_o STR_l STR_i STR_t STR_i STR_c "\0"
+#define STRING_gong0 STR_g STR_o STR_n STR_g "\0"
+#define STRING_gonm0 STR_g STR_o STR_n STR_m "\0"
+#define STRING_goth0 STR_g STR_o STR_t STR_h "\0"
+#define STRING_gothic0 STR_g STR_o STR_t STR_h STR_i STR_c "\0"
+#define STRING_gran0 STR_g STR_r STR_a STR_n "\0"
+#define STRING_grantha0 STR_g STR_r STR_a STR_n STR_t STR_h STR_a "\0"
+#define STRING_graphemebase0 STR_g STR_r STR_a STR_p STR_h STR_e STR_m STR_e STR_b STR_a STR_s STR_e "\0"
+#define STRING_graphemeextend0 STR_g STR_r STR_a STR_p STR_h STR_e STR_m STR_e STR_e STR_x STR_t STR_e STR_n STR_d "\0"
+#define STRING_graphemelink0 STR_g STR_r STR_a STR_p STR_h STR_e STR_m STR_e STR_l STR_i STR_n STR_k "\0"
+#define STRING_grbase0 STR_g STR_r STR_b STR_a STR_s STR_e "\0"
+#define STRING_greek0 STR_g STR_r STR_e STR_e STR_k "\0"
+#define STRING_grek0 STR_g STR_r STR_e STR_k "\0"
+#define STRING_grext0 STR_g STR_r STR_e STR_x STR_t "\0"
+#define STRING_grlink0 STR_g STR_r STR_l STR_i STR_n STR_k "\0"
+#define STRING_gujarati0 STR_g STR_u STR_j STR_a STR_r STR_a STR_t STR_i "\0"
+#define STRING_gujr0 STR_g STR_u STR_j STR_r "\0"
+#define STRING_gunjalagondi0 STR_g STR_u STR_n STR_j STR_a STR_l STR_a STR_g STR_o STR_n STR_d STR_i "\0"
+#define STRING_gurmukhi0 STR_g STR_u STR_r STR_m STR_u STR_k STR_h STR_i "\0"
+#define STRING_guru0 STR_g STR_u STR_r STR_u "\0"
+#define STRING_han0 STR_h STR_a STR_n "\0"
+#define STRING_hang0 STR_h STR_a STR_n STR_g "\0"
+#define STRING_hangul0 STR_h STR_a STR_n STR_g STR_u STR_l "\0"
+#define STRING_hani0 STR_h STR_a STR_n STR_i "\0"
+#define STRING_hanifirohingya0 STR_h STR_a STR_n STR_i STR_f STR_i STR_r STR_o STR_h STR_i STR_n STR_g STR_y STR_a "\0"
+#define STRING_hano0 STR_h STR_a STR_n STR_o "\0"
+#define STRING_hanunoo0 STR_h STR_a STR_n STR_u STR_n STR_o STR_o "\0"
+#define STRING_hatr0 STR_h STR_a STR_t STR_r "\0"
+#define STRING_hatran0 STR_h STR_a STR_t STR_r STR_a STR_n "\0"
+#define STRING_hebr0 STR_h STR_e STR_b STR_r "\0"
+#define STRING_hebrew0 STR_h STR_e STR_b STR_r STR_e STR_w "\0"
+#define STRING_hex0 STR_h STR_e STR_x "\0"
+#define STRING_hexdigit0 STR_h STR_e STR_x STR_d STR_i STR_g STR_i STR_t "\0"
+#define STRING_hira0 STR_h STR_i STR_r STR_a "\0"
+#define STRING_hiragana0 STR_h STR_i STR_r STR_a STR_g STR_a STR_n STR_a "\0"
+#define STRING_hluw0 STR_h STR_l STR_u STR_w "\0"
+#define STRING_hmng0 STR_h STR_m STR_n STR_g "\0"
+#define STRING_hmnp0 STR_h STR_m STR_n STR_p "\0"
+#define STRING_hung0 STR_h STR_u STR_n STR_g "\0"
+#define STRING_idc0 STR_i STR_d STR_c "\0"
+#define STRING_idcontinue0 STR_i STR_d STR_c STR_o STR_n STR_t STR_i STR_n STR_u STR_e "\0"
+#define STRING_ideo0 STR_i STR_d STR_e STR_o "\0"
+#define STRING_ideographic0 STR_i STR_d STR_e STR_o STR_g STR_r STR_a STR_p STR_h STR_i STR_c "\0"
+#define STRING_ids0 STR_i STR_d STR_s "\0"
+#define STRING_idsb0 STR_i STR_d STR_s STR_b "\0"
+#define STRING_idsbinaryoperator0 STR_i STR_d STR_s STR_b STR_i STR_n STR_a STR_r STR_y STR_o STR_p STR_e STR_r STR_a STR_t STR_o STR_r "\0"
+#define STRING_idst0 STR_i STR_d STR_s STR_t "\0"
+#define STRING_idstart0 STR_i STR_d STR_s STR_t STR_a STR_r STR_t "\0"
+#define STRING_idstrinaryoperator0 STR_i STR_d STR_s STR_t STR_r STR_i STR_n STR_a STR_r STR_y STR_o STR_p STR_e STR_r STR_a STR_t STR_o STR_r "\0"
+#define STRING_imperialaramaic0 STR_i STR_m STR_p STR_e STR_r STR_i STR_a STR_l STR_a STR_r STR_a STR_m STR_a STR_i STR_c "\0"
+#define STRING_inherited0 STR_i STR_n STR_h STR_e STR_r STR_i STR_t STR_e STR_d "\0"
+#define STRING_inscriptionalpahlavi0 STR_i STR_n STR_s STR_c STR_r STR_i STR_p STR_t STR_i STR_o STR_n STR_a STR_l STR_p STR_a STR_h STR_l STR_a STR_v STR_i "\0"
+#define STRING_inscriptionalparthian0 STR_i STR_n STR_s STR_c STR_r STR_i STR_p STR_t STR_i STR_o STR_n STR_a STR_l STR_p STR_a STR_r STR_t STR_h STR_i STR_a STR_n "\0"
+#define STRING_ital0 STR_i STR_t STR_a STR_l "\0"
+#define STRING_java0 STR_j STR_a STR_v STR_a "\0"
+#define STRING_javanese0 STR_j STR_a STR_v STR_a STR_n STR_e STR_s STR_e "\0"
+#define STRING_joinc0 STR_j STR_o STR_i STR_n STR_c "\0"
+#define STRING_joincontrol0 STR_j STR_o STR_i STR_n STR_c STR_o STR_n STR_t STR_r STR_o STR_l "\0"
+#define STRING_kaithi0 STR_k STR_a STR_i STR_t STR_h STR_i "\0"
+#define STRING_kali0 STR_k STR_a STR_l STR_i "\0"
+#define STRING_kana0 STR_k STR_a STR_n STR_a "\0"
+#define STRING_kannada0 STR_k STR_a STR_n STR_n STR_a STR_d STR_a "\0"
+#define STRING_katakana0 STR_k STR_a STR_t STR_a STR_k STR_a STR_n STR_a "\0"
+#define STRING_kayahli0 STR_k STR_a STR_y STR_a STR_h STR_l STR_i "\0"
+#define STRING_khar0 STR_k STR_h STR_a STR_r "\0"
+#define STRING_kharoshthi0 STR_k STR_h STR_a STR_r STR_o STR_s STR_h STR_t STR_h STR_i "\0"
+#define STRING_khitansmallscript0 STR_k STR_h STR_i STR_t STR_a STR_n STR_s STR_m STR_a STR_l STR_l STR_s STR_c STR_r STR_i STR_p STR_t "\0"
+#define STRING_khmer0 STR_k STR_h STR_m STR_e STR_r "\0"
+#define STRING_khmr0 STR_k STR_h STR_m STR_r "\0"
+#define STRING_khoj0 STR_k STR_h STR_o STR_j "\0"
+#define STRING_khojki0 STR_k STR_h STR_o STR_j STR_k STR_i "\0"
+#define STRING_khudawadi0 STR_k STR_h STR_u STR_d STR_a STR_w STR_a STR_d STR_i "\0"
+#define STRING_kits0 STR_k STR_i STR_t STR_s "\0"
+#define STRING_knda0 STR_k STR_n STR_d STR_a "\0"
+#define STRING_kthi0 STR_k STR_t STR_h STR_i "\0"
+#define STRING_l0 STR_l "\0"
+#define STRING_l_AMPERSAND0 STR_l STR_AMPERSAND "\0"
+#define STRING_lana0 STR_l STR_a STR_n STR_a "\0"
+#define STRING_lao0 STR_l STR_a STR_o "\0"
+#define STRING_laoo0 STR_l STR_a STR_o STR_o "\0"
+#define STRING_latin0 STR_l STR_a STR_t STR_i STR_n "\0"
+#define STRING_latn0 STR_l STR_a STR_t STR_n "\0"
+#define STRING_lc0 STR_l STR_c "\0"
+#define STRING_lepc0 STR_l STR_e STR_p STR_c "\0"
+#define STRING_lepcha0 STR_l STR_e STR_p STR_c STR_h STR_a "\0"
+#define STRING_limb0 STR_l STR_i STR_m STR_b "\0"
+#define STRING_limbu0 STR_l STR_i STR_m STR_b STR_u "\0"
+#define STRING_lina0 STR_l STR_i STR_n STR_a "\0"
+#define STRING_linb0 STR_l STR_i STR_n STR_b "\0"
+#define STRING_lineara0 STR_l STR_i STR_n STR_e STR_a STR_r STR_a "\0"
+#define STRING_linearb0 STR_l STR_i STR_n STR_e STR_a STR_r STR_b "\0"
+#define STRING_lisu0 STR_l STR_i STR_s STR_u "\0"
+#define STRING_ll0 STR_l STR_l "\0"
+#define STRING_lm0 STR_l STR_m "\0"
+#define STRING_lo0 STR_l STR_o "\0"
+#define STRING_loe0 STR_l STR_o STR_e "\0"
+#define STRING_logicalorderexception0 STR_l STR_o STR_g STR_i STR_c STR_a STR_l STR_o STR_r STR_d STR_e STR_r STR_e STR_x STR_c STR_e STR_p STR_t STR_i STR_o STR_n "\0"
+#define STRING_lower0 STR_l STR_o STR_w STR_e STR_r "\0"
+#define STRING_lowercase0 STR_l STR_o STR_w STR_e STR_r STR_c STR_a STR_s STR_e "\0"
+#define STRING_lt0 STR_l STR_t "\0"
+#define STRING_lu0 STR_l STR_u "\0"
+#define STRING_lyci0 STR_l STR_y STR_c STR_i "\0"
+#define STRING_lycian0 STR_l STR_y STR_c STR_i STR_a STR_n "\0"
+#define STRING_lydi0 STR_l STR_y STR_d STR_i "\0"
+#define STRING_lydian0 STR_l STR_y STR_d STR_i STR_a STR_n "\0"
+#define STRING_m0 STR_m "\0"
+#define STRING_mahajani0 STR_m STR_a STR_h STR_a STR_j STR_a STR_n STR_i "\0"
+#define STRING_mahj0 STR_m STR_a STR_h STR_j "\0"
+#define STRING_maka0 STR_m STR_a STR_k STR_a "\0"
+#define STRING_makasar0 STR_m STR_a STR_k STR_a STR_s STR_a STR_r "\0"
+#define STRING_malayalam0 STR_m STR_a STR_l STR_a STR_y STR_a STR_l STR_a STR_m "\0"
+#define STRING_mand0 STR_m STR_a STR_n STR_d "\0"
+#define STRING_mandaic0 STR_m STR_a STR_n STR_d STR_a STR_i STR_c "\0"
+#define STRING_mani0 STR_m STR_a STR_n STR_i "\0"
+#define STRING_manichaean0 STR_m STR_a STR_n STR_i STR_c STR_h STR_a STR_e STR_a STR_n "\0"
+#define STRING_marc0 STR_m STR_a STR_r STR_c "\0"
+#define STRING_marchen0 STR_m STR_a STR_r STR_c STR_h STR_e STR_n "\0"
+#define STRING_masaramgondi0 STR_m STR_a STR_s STR_a STR_r STR_a STR_m STR_g STR_o STR_n STR_d STR_i "\0"
+#define STRING_math0 STR_m STR_a STR_t STR_h "\0"
+#define STRING_mc0 STR_m STR_c "\0"
+#define STRING_me0 STR_m STR_e "\0"
+#define STRING_medefaidrin0 STR_m STR_e STR_d STR_e STR_f STR_a STR_i STR_d STR_r STR_i STR_n "\0"
+#define STRING_medf0 STR_m STR_e STR_d STR_f "\0"
+#define STRING_meeteimayek0 STR_m STR_e STR_e STR_t STR_e STR_i STR_m STR_a STR_y STR_e STR_k "\0"
+#define STRING_mend0 STR_m STR_e STR_n STR_d "\0"
+#define STRING_mendekikakui0 STR_m STR_e STR_n STR_d STR_e STR_k STR_i STR_k STR_a STR_k STR_u STR_i "\0"
+#define STRING_merc0 STR_m STR_e STR_r STR_c "\0"
+#define STRING_mero0 STR_m STR_e STR_r STR_o "\0"
+#define STRING_meroiticcursive0 STR_m STR_e STR_r STR_o STR_i STR_t STR_i STR_c STR_c STR_u STR_r STR_s STR_i STR_v STR_e "\0"
+#define STRING_meroitichieroglyphs0 STR_m STR_e STR_r STR_o STR_i STR_t STR_i STR_c STR_h STR_i STR_e STR_r STR_o STR_g STR_l STR_y STR_p STR_h STR_s "\0"
+#define STRING_miao0 STR_m STR_i STR_a STR_o "\0"
+#define STRING_mlym0 STR_m STR_l STR_y STR_m "\0"
+#define STRING_mn0 STR_m STR_n "\0"
+#define STRING_modi0 STR_m STR_o STR_d STR_i "\0"
+#define STRING_mong0 STR_m STR_o STR_n STR_g "\0"
+#define STRING_mongolian0 STR_m STR_o STR_n STR_g STR_o STR_l STR_i STR_a STR_n "\0"
+#define STRING_mro0 STR_m STR_r STR_o "\0"
+#define STRING_mroo0 STR_m STR_r STR_o STR_o "\0"
+#define STRING_mtei0 STR_m STR_t STR_e STR_i "\0"
+#define STRING_mult0 STR_m STR_u STR_l STR_t "\0"
+#define STRING_multani0 STR_m STR_u STR_l STR_t STR_a STR_n STR_i "\0"
+#define STRING_myanmar0 STR_m STR_y STR_a STR_n STR_m STR_a STR_r "\0"
+#define STRING_mymr0 STR_m STR_y STR_m STR_r "\0"
+#define STRING_n0 STR_n "\0"
+#define STRING_nabataean0 STR_n STR_a STR_b STR_a STR_t STR_a STR_e STR_a STR_n "\0"
+#define STRING_nand0 STR_n STR_a STR_n STR_d "\0"
+#define STRING_nandinagari0 STR_n STR_a STR_n STR_d STR_i STR_n STR_a STR_g STR_a STR_r STR_i "\0"
+#define STRING_narb0 STR_n STR_a STR_r STR_b "\0"
+#define STRING_nbat0 STR_n STR_b STR_a STR_t "\0"
+#define STRING_nchar0 STR_n STR_c STR_h STR_a STR_r "\0"
+#define STRING_nd0 STR_n STR_d "\0"
+#define STRING_newa0 STR_n STR_e STR_w STR_a "\0"
+#define STRING_newtailue0 STR_n STR_e STR_w STR_t STR_a STR_i STR_l STR_u STR_e "\0"
+#define STRING_nko0 STR_n STR_k STR_o "\0"
+#define STRING_nkoo0 STR_n STR_k STR_o STR_o "\0"
+#define STRING_nl0 STR_n STR_l "\0"
+#define STRING_no0 STR_n STR_o "\0"
+#define STRING_noncharactercodepoint0 STR_n STR_o STR_n STR_c STR_h STR_a STR_r STR_a STR_c STR_t STR_e STR_r STR_c STR_o STR_d STR_e STR_p STR_o STR_i STR_n STR_t "\0"
+#define STRING_nshu0 STR_n STR_s STR_h STR_u "\0"
+#define STRING_nushu0 STR_n STR_u STR_s STR_h STR_u "\0"
+#define STRING_nyiakengpuachuehmong0 STR_n STR_y STR_i STR_a STR_k STR_e STR_n STR_g STR_p STR_u STR_a STR_c STR_h STR_u STR_e STR_h STR_m STR_o STR_n STR_g "\0"
+#define STRING_ogam0 STR_o STR_g STR_a STR_m "\0"
+#define STRING_ogham0 STR_o STR_g STR_h STR_a STR_m "\0"
+#define STRING_olchiki0 STR_o STR_l STR_c STR_h STR_i STR_k STR_i "\0"
+#define STRING_olck0 STR_o STR_l STR_c STR_k "\0"
+#define STRING_oldhungarian0 STR_o STR_l STR_d STR_h STR_u STR_n STR_g STR_a STR_r STR_i STR_a STR_n "\0"
+#define STRING_olditalic0 STR_o STR_l STR_d STR_i STR_t STR_a STR_l STR_i STR_c "\0"
+#define STRING_oldnortharabian0 STR_o STR_l STR_d STR_n STR_o STR_r STR_t STR_h STR_a STR_r STR_a STR_b STR_i STR_a STR_n "\0"
+#define STRING_oldpermic0 STR_o STR_l STR_d STR_p STR_e STR_r STR_m STR_i STR_c "\0"
+#define STRING_oldpersian0 STR_o STR_l STR_d STR_p STR_e STR_r STR_s STR_i STR_a STR_n "\0"
+#define STRING_oldsogdian0 STR_o STR_l STR_d STR_s STR_o STR_g STR_d STR_i STR_a STR_n "\0"
+#define STRING_oldsoutharabian0 STR_o STR_l STR_d STR_s STR_o STR_u STR_t STR_h STR_a STR_r STR_a STR_b STR_i STR_a STR_n "\0"
+#define STRING_oldturkic0 STR_o STR_l STR_d STR_t STR_u STR_r STR_k STR_i STR_c "\0"
+#define STRING_olduyghur0 STR_o STR_l STR_d STR_u STR_y STR_g STR_h STR_u STR_r "\0"
+#define STRING_oriya0 STR_o STR_r STR_i STR_y STR_a "\0"
+#define STRING_orkh0 STR_o STR_r STR_k STR_h "\0"
+#define STRING_orya0 STR_o STR_r STR_y STR_a "\0"
+#define STRING_osage0 STR_o STR_s STR_a STR_g STR_e "\0"
+#define STRING_osge0 STR_o STR_s STR_g STR_e "\0"
+#define STRING_osma0 STR_o STR_s STR_m STR_a "\0"
+#define STRING_osmanya0 STR_o STR_s STR_m STR_a STR_n STR_y STR_a "\0"
+#define STRING_ougr0 STR_o STR_u STR_g STR_r "\0"
+#define STRING_p0 STR_p "\0"
+#define STRING_pahawhhmong0 STR_p STR_a STR_h STR_a STR_w STR_h STR_h STR_m STR_o STR_n STR_g "\0"
+#define STRING_palm0 STR_p STR_a STR_l STR_m "\0"
+#define STRING_palmyrene0 STR_p STR_a STR_l STR_m STR_y STR_r STR_e STR_n STR_e "\0"
+#define STRING_patsyn0 STR_p STR_a STR_t STR_s STR_y STR_n "\0"
+#define STRING_patternsyntax0 STR_p STR_a STR_t STR_t STR_e STR_r STR_n STR_s STR_y STR_n STR_t STR_a STR_x "\0"
+#define STRING_patternwhitespace0 STR_p STR_a STR_t STR_t STR_e STR_r STR_n STR_w STR_h STR_i STR_t STR_e STR_s STR_p STR_a STR_c STR_e "\0"
+#define STRING_patws0 STR_p STR_a STR_t STR_w STR_s "\0"
+#define STRING_pauc0 STR_p STR_a STR_u STR_c "\0"
+#define STRING_paucinhau0 STR_p STR_a STR_u STR_c STR_i STR_n STR_h STR_a STR_u "\0"
+#define STRING_pc0 STR_p STR_c "\0"
+#define STRING_pcm0 STR_p STR_c STR_m "\0"
+#define STRING_pd0 STR_p STR_d "\0"
+#define STRING_pe0 STR_p STR_e "\0"
+#define STRING_perm0 STR_p STR_e STR_r STR_m "\0"
+#define STRING_pf0 STR_p STR_f "\0"
+#define STRING_phag0 STR_p STR_h STR_a STR_g "\0"
+#define STRING_phagspa0 STR_p STR_h STR_a STR_g STR_s STR_p STR_a "\0"
+#define STRING_phli0 STR_p STR_h STR_l STR_i "\0"
+#define STRING_phlp0 STR_p STR_h STR_l STR_p "\0"
+#define STRING_phnx0 STR_p STR_h STR_n STR_x "\0"
+#define STRING_phoenician0 STR_p STR_h STR_o STR_e STR_n STR_i STR_c STR_i STR_a STR_n "\0"
+#define STRING_pi0 STR_p STR_i "\0"
+#define STRING_plrd0 STR_p STR_l STR_r STR_d "\0"
+#define STRING_po0 STR_p STR_o "\0"
+#define STRING_prependedconcatenationmark0 STR_p STR_r STR_e STR_p STR_e STR_n STR_d STR_e STR_d STR_c STR_o STR_n STR_c STR_a STR_t STR_e STR_n STR_a STR_t STR_i STR_o STR_n STR_m STR_a STR_r STR_k "\0"
+#define STRING_prti0 STR_p STR_r STR_t STR_i "\0"
+#define STRING_ps0 STR_p STR_s "\0"
+#define STRING_psalterpahlavi0 STR_p STR_s STR_a STR_l STR_t STR_e STR_r STR_p STR_a STR_h STR_l STR_a STR_v STR_i "\0"
+#define STRING_qaac0 STR_q STR_a STR_a STR_c "\0"
+#define STRING_qaai0 STR_q STR_a STR_a STR_i "\0"
+#define STRING_qmark0 STR_q STR_m STR_a STR_r STR_k "\0"
+#define STRING_quotationmark0 STR_q STR_u STR_o STR_t STR_a STR_t STR_i STR_o STR_n STR_m STR_a STR_r STR_k "\0"
+#define STRING_radical0 STR_r STR_a STR_d STR_i STR_c STR_a STR_l "\0"
+#define STRING_regionalindicator0 STR_r STR_e STR_g STR_i STR_o STR_n STR_a STR_l STR_i STR_n STR_d STR_i STR_c STR_a STR_t STR_o STR_r "\0"
+#define STRING_rejang0 STR_r STR_e STR_j STR_a STR_n STR_g "\0"
+#define STRING_ri0 STR_r STR_i "\0"
+#define STRING_rjng0 STR_r STR_j STR_n STR_g "\0"
+#define STRING_rohg0 STR_r STR_o STR_h STR_g "\0"
+#define STRING_runic0 STR_r STR_u STR_n STR_i STR_c "\0"
+#define STRING_runr0 STR_r STR_u STR_n STR_r "\0"
+#define STRING_s0 STR_s "\0"
+#define STRING_samaritan0 STR_s STR_a STR_m STR_a STR_r STR_i STR_t STR_a STR_n "\0"
+#define STRING_samr0 STR_s STR_a STR_m STR_r "\0"
+#define STRING_sarb0 STR_s STR_a STR_r STR_b "\0"
+#define STRING_saur0 STR_s STR_a STR_u STR_r "\0"
+#define STRING_saurashtra0 STR_s STR_a STR_u STR_r STR_a STR_s STR_h STR_t STR_r STR_a "\0"
+#define STRING_sc0 STR_s STR_c "\0"
+#define STRING_sd0 STR_s STR_d "\0"
+#define STRING_sentenceterminal0 STR_s STR_e STR_n STR_t STR_e STR_n STR_c STR_e STR_t STR_e STR_r STR_m STR_i STR_n STR_a STR_l "\0"
+#define STRING_sgnw0 STR_s STR_g STR_n STR_w "\0"
+#define STRING_sharada0 STR_s STR_h STR_a STR_r STR_a STR_d STR_a "\0"
+#define STRING_shavian0 STR_s STR_h STR_a STR_v STR_i STR_a STR_n "\0"
+#define STRING_shaw0 STR_s STR_h STR_a STR_w "\0"
+#define STRING_shrd0 STR_s STR_h STR_r STR_d "\0"
+#define STRING_sidd0 STR_s STR_i STR_d STR_d "\0"
+#define STRING_siddham0 STR_s STR_i STR_d STR_d STR_h STR_a STR_m "\0"
+#define STRING_signwriting0 STR_s STR_i STR_g STR_n STR_w STR_r STR_i STR_t STR_i STR_n STR_g "\0"
+#define STRING_sind0 STR_s STR_i STR_n STR_d "\0"
+#define STRING_sinh0 STR_s STR_i STR_n STR_h "\0"
+#define STRING_sinhala0 STR_s STR_i STR_n STR_h STR_a STR_l STR_a "\0"
+#define STRING_sk0 STR_s STR_k "\0"
+#define STRING_sm0 STR_s STR_m "\0"
+#define STRING_so0 STR_s STR_o "\0"
+#define STRING_softdotted0 STR_s STR_o STR_f STR_t STR_d STR_o STR_t STR_t STR_e STR_d "\0"
+#define STRING_sogd0 STR_s STR_o STR_g STR_d "\0"
+#define STRING_sogdian0 STR_s STR_o STR_g STR_d STR_i STR_a STR_n "\0"
+#define STRING_sogo0 STR_s STR_o STR_g STR_o "\0"
+#define STRING_sora0 STR_s STR_o STR_r STR_a "\0"
+#define STRING_sorasompeng0 STR_s STR_o STR_r STR_a STR_s STR_o STR_m STR_p STR_e STR_n STR_g "\0"
+#define STRING_soyo0 STR_s STR_o STR_y STR_o "\0"
+#define STRING_soyombo0 STR_s STR_o STR_y STR_o STR_m STR_b STR_o "\0"
+#define STRING_space0 STR_s STR_p STR_a STR_c STR_e "\0"
+#define STRING_sterm0 STR_s STR_t STR_e STR_r STR_m "\0"
+#define STRING_sund0 STR_s STR_u STR_n STR_d "\0"
+#define STRING_sundanese0 STR_s STR_u STR_n STR_d STR_a STR_n STR_e STR_s STR_e "\0"
+#define STRING_sylo0 STR_s STR_y STR_l STR_o "\0"
+#define STRING_sylotinagri0 STR_s STR_y STR_l STR_o STR_t STR_i STR_n STR_a STR_g STR_r STR_i "\0"
+#define STRING_syrc0 STR_s STR_y STR_r STR_c "\0"
+#define STRING_syriac0 STR_s STR_y STR_r STR_i STR_a STR_c "\0"
+#define STRING_tagalog0 STR_t STR_a STR_g STR_a STR_l STR_o STR_g "\0"
+#define STRING_tagb0 STR_t STR_a STR_g STR_b "\0"
+#define STRING_tagbanwa0 STR_t STR_a STR_g STR_b STR_a STR_n STR_w STR_a "\0"
+#define STRING_taile0 STR_t STR_a STR_i STR_l STR_e "\0"
+#define STRING_taitham0 STR_t STR_a STR_i STR_t STR_h STR_a STR_m "\0"
+#define STRING_taiviet0 STR_t STR_a STR_i STR_v STR_i STR_e STR_t "\0"
+#define STRING_takr0 STR_t STR_a STR_k STR_r "\0"
+#define STRING_takri0 STR_t STR_a STR_k STR_r STR_i "\0"
+#define STRING_tale0 STR_t STR_a STR_l STR_e "\0"
+#define STRING_talu0 STR_t STR_a STR_l STR_u "\0"
+#define STRING_tamil0 STR_t STR_a STR_m STR_i STR_l "\0"
+#define STRING_taml0 STR_t STR_a STR_m STR_l "\0"
+#define STRING_tang0 STR_t STR_a STR_n STR_g "\0"
+#define STRING_tangsa0 STR_t STR_a STR_n STR_g STR_s STR_a "\0"
+#define STRING_tangut0 STR_t STR_a STR_n STR_g STR_u STR_t "\0"
+#define STRING_tavt0 STR_t STR_a STR_v STR_t "\0"
+#define STRING_telu0 STR_t STR_e STR_l STR_u "\0"
+#define STRING_telugu0 STR_t STR_e STR_l STR_u STR_g STR_u "\0"
+#define STRING_term0 STR_t STR_e STR_r STR_m "\0"
+#define STRING_terminalpunctuation0 STR_t STR_e STR_r STR_m STR_i STR_n STR_a STR_l STR_p STR_u STR_n STR_c STR_t STR_u STR_a STR_t STR_i STR_o STR_n "\0"
+#define STRING_tfng0 STR_t STR_f STR_n STR_g "\0"
+#define STRING_tglg0 STR_t STR_g STR_l STR_g "\0"
+#define STRING_thaa0 STR_t STR_h STR_a STR_a "\0"
+#define STRING_thaana0 STR_t STR_h STR_a STR_a STR_n STR_a "\0"
+#define STRING_thai0 STR_t STR_h STR_a STR_i "\0"
+#define STRING_tibetan0 STR_t STR_i STR_b STR_e STR_t STR_a STR_n "\0"
+#define STRING_tibt0 STR_t STR_i STR_b STR_t "\0"
+#define STRING_tifinagh0 STR_t STR_i STR_f STR_i STR_n STR_a STR_g STR_h "\0"
+#define STRING_tirh0 STR_t STR_i STR_r STR_h "\0"
+#define STRING_tirhuta0 STR_t STR_i STR_r STR_h STR_u STR_t STR_a "\0"
+#define STRING_tnsa0 STR_t STR_n STR_s STR_a "\0"
+#define STRING_toto0 STR_t STR_o STR_t STR_o "\0"
+#define STRING_ugar0 STR_u STR_g STR_a STR_r "\0"
+#define STRING_ugaritic0 STR_u STR_g STR_a STR_r STR_i STR_t STR_i STR_c "\0"
+#define STRING_uideo0 STR_u STR_i STR_d STR_e STR_o "\0"
+#define STRING_unifiedideograph0 STR_u STR_n STR_i STR_f STR_i STR_e STR_d STR_i STR_d STR_e STR_o STR_g STR_r STR_a STR_p STR_h "\0"
+#define STRING_unknown0 STR_u STR_n STR_k STR_n STR_o STR_w STR_n "\0"
+#define STRING_upper0 STR_u STR_p STR_p STR_e STR_r "\0"
+#define STRING_uppercase0 STR_u STR_p STR_p STR_e STR_r STR_c STR_a STR_s STR_e "\0"
+#define STRING_vai0 STR_v STR_a STR_i "\0"
+#define STRING_vaii0 STR_v STR_a STR_i STR_i "\0"
+#define STRING_variationselector0 STR_v STR_a STR_r STR_i STR_a STR_t STR_i STR_o STR_n STR_s STR_e STR_l STR_e STR_c STR_t STR_o STR_r "\0"
+#define STRING_vith0 STR_v STR_i STR_t STR_h "\0"
+#define STRING_vithkuqi0 STR_v STR_i STR_t STR_h STR_k STR_u STR_q STR_i "\0"
+#define STRING_vs0 STR_v STR_s "\0"
+#define STRING_wancho0 STR_w STR_a STR_n STR_c STR_h STR_o "\0"
+#define STRING_wara0 STR_w STR_a STR_r STR_a "\0"
+#define STRING_warangciti0 STR_w STR_a STR_r STR_a STR_n STR_g STR_c STR_i STR_t STR_i "\0"
+#define STRING_wcho0 STR_w STR_c STR_h STR_o "\0"
+#define STRING_whitespace0 STR_w STR_h STR_i STR_t STR_e STR_s STR_p STR_a STR_c STR_e "\0"
+#define STRING_wspace0 STR_w STR_s STR_p STR_a STR_c STR_e "\0"
+#define STRING_xan0 STR_x STR_a STR_n "\0"
+#define STRING_xidc0 STR_x STR_i STR_d STR_c "\0"
+#define STRING_xidcontinue0 STR_x STR_i STR_d STR_c STR_o STR_n STR_t STR_i STR_n STR_u STR_e "\0"
+#define STRING_xids0 STR_x STR_i STR_d STR_s "\0"
+#define STRING_xidstart0 STR_x STR_i STR_d STR_s STR_t STR_a STR_r STR_t "\0"
+#define STRING_xpeo0 STR_x STR_p STR_e STR_o "\0"
+#define STRING_xps0 STR_x STR_p STR_s "\0"
+#define STRING_xsp0 STR_x STR_s STR_p "\0"
+#define STRING_xsux0 STR_x STR_s STR_u STR_x "\0"
+#define STRING_xuc0 STR_x STR_u STR_c "\0"
+#define STRING_xwd0 STR_x STR_w STR_d "\0"
+#define STRING_yezi0 STR_y STR_e STR_z STR_i "\0"
+#define STRING_yezidi0 STR_y STR_e STR_z STR_i STR_d STR_i "\0"
+#define STRING_yi0 STR_y STR_i "\0"
+#define STRING_yiii0 STR_y STR_i STR_i STR_i "\0"
+#define STRING_z0 STR_z "\0"
+#define STRING_zanabazarsquare0 STR_z STR_a STR_n STR_a STR_b STR_a STR_z STR_a STR_r STR_s STR_q STR_u STR_a STR_r STR_e "\0"
+#define STRING_zanb0 STR_z STR_a STR_n STR_b "\0"
+#define STRING_zinh0 STR_z STR_i STR_n STR_h "\0"
+#define STRING_zl0 STR_z STR_l "\0"
+#define STRING_zp0 STR_z STR_p "\0"
+#define STRING_zs0 STR_z STR_s "\0"
+#define STRING_zyyy0 STR_z STR_y STR_y STR_y "\0"
+#define STRING_zzzz0 STR_z STR_z STR_z STR_z "\0"
+
+const char PRIV(utt_names)[] =
+ STRING_adlam0
+ STRING_adlm0
+ STRING_aghb0
+ STRING_ahex0
+ STRING_ahom0
+ STRING_alpha0
+ STRING_alphabetic0
+ STRING_anatolianhieroglyphs0
+ STRING_any0
+ STRING_arab0
+ STRING_arabic0
+ STRING_armenian0
+ STRING_armi0
+ STRING_armn0
+ STRING_ascii0
+ STRING_asciihexdigit0
+ STRING_avestan0
+ STRING_avst0
+ STRING_bali0
+ STRING_balinese0
+ STRING_bamu0
+ STRING_bamum0
+ STRING_bass0
+ STRING_bassavah0
+ STRING_batak0
+ STRING_batk0
+ STRING_beng0
+ STRING_bengali0
+ STRING_bhaiksuki0
+ STRING_bhks0
+ STRING_bidial0
+ STRING_bidian0
+ STRING_bidib0
+ STRING_bidibn0
+ STRING_bidic0
+ STRING_bidicontrol0
+ STRING_bidics0
+ STRING_bidien0
+ STRING_bidies0
+ STRING_bidiet0
+ STRING_bidifsi0
+ STRING_bidil0
+ STRING_bidilre0
+ STRING_bidilri0
+ STRING_bidilro0
+ STRING_bidim0
+ STRING_bidimirrored0
+ STRING_bidinsm0
+ STRING_bidion0
+ STRING_bidipdf0
+ STRING_bidipdi0
+ STRING_bidir0
+ STRING_bidirle0
+ STRING_bidirli0
+ STRING_bidirlo0
+ STRING_bidis0
+ STRING_bidiws0
+ STRING_bopo0
+ STRING_bopomofo0
+ STRING_brah0
+ STRING_brahmi0
+ STRING_brai0
+ STRING_braille0
+ STRING_bugi0
+ STRING_buginese0
+ STRING_buhd0
+ STRING_buhid0
+ STRING_c0
+ STRING_cakm0
+ STRING_canadianaboriginal0
+ STRING_cans0
+ STRING_cari0
+ STRING_carian0
+ STRING_cased0
+ STRING_caseignorable0
+ STRING_caucasianalbanian0
+ STRING_cc0
+ STRING_cf0
+ STRING_chakma0
+ STRING_cham0
+ STRING_changeswhencasefolded0
+ STRING_changeswhencasemapped0
+ STRING_changeswhenlowercased0
+ STRING_changeswhentitlecased0
+ STRING_changeswhenuppercased0
+ STRING_cher0
+ STRING_cherokee0
+ STRING_chorasmian0
+ STRING_chrs0
+ STRING_ci0
+ STRING_cn0
+ STRING_co0
+ STRING_common0
+ STRING_copt0
+ STRING_coptic0
+ STRING_cpmn0
+ STRING_cprt0
+ STRING_cs0
+ STRING_cuneiform0
+ STRING_cwcf0
+ STRING_cwcm0
+ STRING_cwl0
+ STRING_cwt0
+ STRING_cwu0
+ STRING_cypriot0
+ STRING_cyprominoan0
+ STRING_cyrillic0
+ STRING_cyrl0
+ STRING_dash0
+ STRING_defaultignorablecodepoint0
+ STRING_dep0
+ STRING_deprecated0
+ STRING_deseret0
+ STRING_deva0
+ STRING_devanagari0
+ STRING_di0
+ STRING_dia0
+ STRING_diacritic0
+ STRING_diak0
+ STRING_divesakuru0
+ STRING_dogr0
+ STRING_dogra0
+ STRING_dsrt0
+ STRING_dupl0
+ STRING_duployan0
+ STRING_ebase0
+ STRING_ecomp0
+ STRING_egyp0
+ STRING_egyptianhieroglyphs0
+ STRING_elba0
+ STRING_elbasan0
+ STRING_elym0
+ STRING_elymaic0
+ STRING_emod0
+ STRING_emoji0
+ STRING_emojicomponent0
+ STRING_emojimodifier0
+ STRING_emojimodifierbase0
+ STRING_emojipresentation0
+ STRING_epres0
+ STRING_ethi0
+ STRING_ethiopic0
+ STRING_ext0
+ STRING_extendedpictographic0
+ STRING_extender0
+ STRING_extpict0
+ STRING_geor0
+ STRING_georgian0
+ STRING_glag0
+ STRING_glagolitic0
+ STRING_gong0
+ STRING_gonm0
+ STRING_goth0
+ STRING_gothic0
+ STRING_gran0
+ STRING_grantha0
+ STRING_graphemebase0
+ STRING_graphemeextend0
+ STRING_graphemelink0
+ STRING_grbase0
+ STRING_greek0
+ STRING_grek0
+ STRING_grext0
+ STRING_grlink0
+ STRING_gujarati0
+ STRING_gujr0
+ STRING_gunjalagondi0
+ STRING_gurmukhi0
+ STRING_guru0
+ STRING_han0
+ STRING_hang0
+ STRING_hangul0
+ STRING_hani0
+ STRING_hanifirohingya0
+ STRING_hano0
+ STRING_hanunoo0
+ STRING_hatr0
+ STRING_hatran0
+ STRING_hebr0
+ STRING_hebrew0
+ STRING_hex0
+ STRING_hexdigit0
+ STRING_hira0
+ STRING_hiragana0
+ STRING_hluw0
+ STRING_hmng0
+ STRING_hmnp0
+ STRING_hung0
+ STRING_idc0
+ STRING_idcontinue0
+ STRING_ideo0
+ STRING_ideographic0
+ STRING_ids0
+ STRING_idsb0
+ STRING_idsbinaryoperator0
+ STRING_idst0
+ STRING_idstart0
+ STRING_idstrinaryoperator0
+ STRING_imperialaramaic0
+ STRING_inherited0
+ STRING_inscriptionalpahlavi0
+ STRING_inscriptionalparthian0
+ STRING_ital0
+ STRING_java0
+ STRING_javanese0
+ STRING_joinc0
+ STRING_joincontrol0
+ STRING_kaithi0
+ STRING_kali0
+ STRING_kana0
+ STRING_kannada0
+ STRING_katakana0
+ STRING_kayahli0
+ STRING_khar0
+ STRING_kharoshthi0
+ STRING_khitansmallscript0
+ STRING_khmer0
+ STRING_khmr0
+ STRING_khoj0
+ STRING_khojki0
+ STRING_khudawadi0
+ STRING_kits0
+ STRING_knda0
+ STRING_kthi0
+ STRING_l0
+ STRING_l_AMPERSAND0
+ STRING_lana0
+ STRING_lao0
+ STRING_laoo0
+ STRING_latin0
+ STRING_latn0
+ STRING_lc0
+ STRING_lepc0
+ STRING_lepcha0
+ STRING_limb0
+ STRING_limbu0
+ STRING_lina0
+ STRING_linb0
+ STRING_lineara0
+ STRING_linearb0
+ STRING_lisu0
+ STRING_ll0
+ STRING_lm0
+ STRING_lo0
+ STRING_loe0
+ STRING_logicalorderexception0
+ STRING_lower0
+ STRING_lowercase0
+ STRING_lt0
+ STRING_lu0
+ STRING_lyci0
+ STRING_lycian0
+ STRING_lydi0
+ STRING_lydian0
+ STRING_m0
+ STRING_mahajani0
+ STRING_mahj0
+ STRING_maka0
+ STRING_makasar0
+ STRING_malayalam0
+ STRING_mand0
+ STRING_mandaic0
+ STRING_mani0
+ STRING_manichaean0
+ STRING_marc0
+ STRING_marchen0
+ STRING_masaramgondi0
+ STRING_math0
+ STRING_mc0
+ STRING_me0
+ STRING_medefaidrin0
+ STRING_medf0
+ STRING_meeteimayek0
+ STRING_mend0
+ STRING_mendekikakui0
+ STRING_merc0
+ STRING_mero0
+ STRING_meroiticcursive0
+ STRING_meroitichieroglyphs0
+ STRING_miao0
+ STRING_mlym0
+ STRING_mn0
+ STRING_modi0
+ STRING_mong0
+ STRING_mongolian0
+ STRING_mro0
+ STRING_mroo0
+ STRING_mtei0
+ STRING_mult0
+ STRING_multani0
+ STRING_myanmar0
+ STRING_mymr0
+ STRING_n0
+ STRING_nabataean0
+ STRING_nand0
+ STRING_nandinagari0
+ STRING_narb0
+ STRING_nbat0
+ STRING_nchar0
+ STRING_nd0
+ STRING_newa0
+ STRING_newtailue0
+ STRING_nko0
+ STRING_nkoo0
+ STRING_nl0
+ STRING_no0
+ STRING_noncharactercodepoint0
+ STRING_nshu0
+ STRING_nushu0
+ STRING_nyiakengpuachuehmong0
+ STRING_ogam0
+ STRING_ogham0
+ STRING_olchiki0
+ STRING_olck0
+ STRING_oldhungarian0
+ STRING_olditalic0
+ STRING_oldnortharabian0
+ STRING_oldpermic0
+ STRING_oldpersian0
+ STRING_oldsogdian0
+ STRING_oldsoutharabian0
+ STRING_oldturkic0
+ STRING_olduyghur0
+ STRING_oriya0
+ STRING_orkh0
+ STRING_orya0
+ STRING_osage0
+ STRING_osge0
+ STRING_osma0
+ STRING_osmanya0
+ STRING_ougr0
+ STRING_p0
+ STRING_pahawhhmong0
+ STRING_palm0
+ STRING_palmyrene0
+ STRING_patsyn0
+ STRING_patternsyntax0
+ STRING_patternwhitespace0
+ STRING_patws0
+ STRING_pauc0
+ STRING_paucinhau0
+ STRING_pc0
+ STRING_pcm0
+ STRING_pd0
+ STRING_pe0
+ STRING_perm0
+ STRING_pf0
+ STRING_phag0
+ STRING_phagspa0
+ STRING_phli0
+ STRING_phlp0
+ STRING_phnx0
+ STRING_phoenician0
+ STRING_pi0
+ STRING_plrd0
+ STRING_po0
+ STRING_prependedconcatenationmark0
+ STRING_prti0
+ STRING_ps0
+ STRING_psalterpahlavi0
+ STRING_qaac0
+ STRING_qaai0
+ STRING_qmark0
+ STRING_quotationmark0
+ STRING_radical0
+ STRING_regionalindicator0
+ STRING_rejang0
+ STRING_ri0
+ STRING_rjng0
+ STRING_rohg0
+ STRING_runic0
+ STRING_runr0
+ STRING_s0
+ STRING_samaritan0
+ STRING_samr0
+ STRING_sarb0
+ STRING_saur0
+ STRING_saurashtra0
+ STRING_sc0
+ STRING_sd0
+ STRING_sentenceterminal0
+ STRING_sgnw0
+ STRING_sharada0
+ STRING_shavian0
+ STRING_shaw0
+ STRING_shrd0
+ STRING_sidd0
+ STRING_siddham0
+ STRING_signwriting0
+ STRING_sind0
+ STRING_sinh0
+ STRING_sinhala0
+ STRING_sk0
+ STRING_sm0
+ STRING_so0
+ STRING_softdotted0
+ STRING_sogd0
+ STRING_sogdian0
+ STRING_sogo0
+ STRING_sora0
+ STRING_sorasompeng0
+ STRING_soyo0
+ STRING_soyombo0
+ STRING_space0
+ STRING_sterm0
+ STRING_sund0
+ STRING_sundanese0
+ STRING_sylo0
+ STRING_sylotinagri0
+ STRING_syrc0
+ STRING_syriac0
+ STRING_tagalog0
+ STRING_tagb0
+ STRING_tagbanwa0
+ STRING_taile0
+ STRING_taitham0
+ STRING_taiviet0
+ STRING_takr0
+ STRING_takri0
+ STRING_tale0
+ STRING_talu0
+ STRING_tamil0
+ STRING_taml0
+ STRING_tang0
+ STRING_tangsa0
+ STRING_tangut0
+ STRING_tavt0
+ STRING_telu0
+ STRING_telugu0
+ STRING_term0
+ STRING_terminalpunctuation0
+ STRING_tfng0
+ STRING_tglg0
+ STRING_thaa0
+ STRING_thaana0
+ STRING_thai0
+ STRING_tibetan0
+ STRING_tibt0
+ STRING_tifinagh0
+ STRING_tirh0
+ STRING_tirhuta0
+ STRING_tnsa0
+ STRING_toto0
+ STRING_ugar0
+ STRING_ugaritic0
+ STRING_uideo0
+ STRING_unifiedideograph0
+ STRING_unknown0
+ STRING_upper0
+ STRING_uppercase0
+ STRING_vai0
+ STRING_vaii0
+ STRING_variationselector0
+ STRING_vith0
+ STRING_vithkuqi0
+ STRING_vs0
+ STRING_wancho0
+ STRING_wara0
+ STRING_warangciti0
+ STRING_wcho0
+ STRING_whitespace0
+ STRING_wspace0
+ STRING_xan0
+ STRING_xidc0
+ STRING_xidcontinue0
+ STRING_xids0
+ STRING_xidstart0
+ STRING_xpeo0
+ STRING_xps0
+ STRING_xsp0
+ STRING_xsux0
+ STRING_xuc0
+ STRING_xwd0
+ STRING_yezi0
+ STRING_yezidi0
+ STRING_yi0
+ STRING_yiii0
+ STRING_z0
+ STRING_zanabazarsquare0
+ STRING_zanb0
+ STRING_zinh0
+ STRING_zl0
+ STRING_zp0
+ STRING_zs0
+ STRING_zyyy0
+ STRING_zzzz0;
+
+const ucp_type_table PRIV(utt)[] = {
+ { 0, PT_SCX, ucp_Adlam },
+ { 6, PT_SCX, ucp_Adlam },
+ { 11, PT_SC, ucp_Caucasian_Albanian },
+ { 16, PT_BOOL, ucp_ASCII_Hex_Digit },
+ { 21, PT_SC, ucp_Ahom },
+ { 26, PT_BOOL, ucp_Alphabetic },
+ { 32, PT_BOOL, ucp_Alphabetic },
+ { 43, PT_SC, ucp_Anatolian_Hieroglyphs },
+ { 64, PT_ANY, 0 },
+ { 68, PT_SCX, ucp_Arabic },
+ { 73, PT_SCX, ucp_Arabic },
+ { 80, PT_SC, ucp_Armenian },
+ { 89, PT_SC, ucp_Imperial_Aramaic },
+ { 94, PT_SC, ucp_Armenian },
+ { 99, PT_BOOL, ucp_ASCII },
+ { 105, PT_BOOL, ucp_ASCII_Hex_Digit },
+ { 119, PT_SC, ucp_Avestan },
+ { 127, PT_SC, ucp_Avestan },
+ { 132, PT_SC, ucp_Balinese },
+ { 137, PT_SC, ucp_Balinese },
+ { 146, PT_SC, ucp_Bamum },
+ { 151, PT_SC, ucp_Bamum },
+ { 157, PT_SC, ucp_Bassa_Vah },
+ { 162, PT_SC, ucp_Bassa_Vah },
+ { 171, PT_SC, ucp_Batak },
+ { 177, PT_SC, ucp_Batak },
+ { 182, PT_SCX, ucp_Bengali },
+ { 187, PT_SCX, ucp_Bengali },
+ { 195, PT_SC, ucp_Bhaiksuki },
+ { 205, PT_SC, ucp_Bhaiksuki },
+ { 210, PT_BIDICL, ucp_bidiAL },
+ { 217, PT_BIDICL, ucp_bidiAN },
+ { 224, PT_BIDICL, ucp_bidiB },
+ { 230, PT_BIDICL, ucp_bidiBN },
+ { 237, PT_BOOL, ucp_Bidi_Control },
+ { 243, PT_BOOL, ucp_Bidi_Control },
+ { 255, PT_BIDICL, ucp_bidiCS },
+ { 262, PT_BIDICL, ucp_bidiEN },
+ { 269, PT_BIDICL, ucp_bidiES },
+ { 276, PT_BIDICL, ucp_bidiET },
+ { 283, PT_BIDICL, ucp_bidiFSI },
+ { 291, PT_BIDICL, ucp_bidiL },
+ { 297, PT_BIDICL, ucp_bidiLRE },
+ { 305, PT_BIDICL, ucp_bidiLRI },
+ { 313, PT_BIDICL, ucp_bidiLRO },
+ { 321, PT_BOOL, ucp_Bidi_Mirrored },
+ { 327, PT_BOOL, ucp_Bidi_Mirrored },
+ { 340, PT_BIDICL, ucp_bidiNSM },
+ { 348, PT_BIDICL, ucp_bidiON },
+ { 355, PT_BIDICL, ucp_bidiPDF },
+ { 363, PT_BIDICL, ucp_bidiPDI },
+ { 371, PT_BIDICL, ucp_bidiR },
+ { 377, PT_BIDICL, ucp_bidiRLE },
+ { 385, PT_BIDICL, ucp_bidiRLI },
+ { 393, PT_BIDICL, ucp_bidiRLO },
+ { 401, PT_BIDICL, ucp_bidiS },
+ { 407, PT_BIDICL, ucp_bidiWS },
+ { 414, PT_SCX, ucp_Bopomofo },
+ { 419, PT_SCX, ucp_Bopomofo },
+ { 428, PT_SC, ucp_Brahmi },
+ { 433, PT_SC, ucp_Brahmi },
+ { 440, PT_SC, ucp_Braille },
+ { 445, PT_SC, ucp_Braille },
+ { 453, PT_SCX, ucp_Buginese },
+ { 458, PT_SCX, ucp_Buginese },
+ { 467, PT_SCX, ucp_Buhid },
+ { 472, PT_SCX, ucp_Buhid },
+ { 478, PT_GC, ucp_C },
+ { 480, PT_SCX, ucp_Chakma },
+ { 485, PT_SC, ucp_Canadian_Aboriginal },
+ { 504, PT_SC, ucp_Canadian_Aboriginal },
+ { 509, PT_SC, ucp_Carian },
+ { 514, PT_SC, ucp_Carian },
+ { 521, PT_BOOL, ucp_Cased },
+ { 527, PT_BOOL, ucp_Case_Ignorable },
+ { 541, PT_SC, ucp_Caucasian_Albanian },
+ { 559, PT_PC, ucp_Cc },
+ { 562, PT_PC, ucp_Cf },
+ { 565, PT_SCX, ucp_Chakma },
+ { 572, PT_SC, ucp_Cham },
+ { 577, PT_BOOL, ucp_Changes_When_Casefolded },
+ { 599, PT_BOOL, ucp_Changes_When_Casemapped },
+ { 621, PT_BOOL, ucp_Changes_When_Lowercased },
+ { 643, PT_BOOL, ucp_Changes_When_Titlecased },
+ { 665, PT_BOOL, ucp_Changes_When_Uppercased },
+ { 687, PT_SC, ucp_Cherokee },
+ { 692, PT_SC, ucp_Cherokee },
+ { 701, PT_SC, ucp_Chorasmian },
+ { 712, PT_SC, ucp_Chorasmian },
+ { 717, PT_BOOL, ucp_Case_Ignorable },
+ { 720, PT_PC, ucp_Cn },
+ { 723, PT_PC, ucp_Co },
+ { 726, PT_SC, ucp_Common },
+ { 733, PT_SCX, ucp_Coptic },
+ { 738, PT_SCX, ucp_Coptic },
+ { 745, PT_SCX, ucp_Cypro_Minoan },
+ { 750, PT_SCX, ucp_Cypriot },
+ { 755, PT_PC, ucp_Cs },
+ { 758, PT_SC, ucp_Cuneiform },
+ { 768, PT_BOOL, ucp_Changes_When_Casefolded },
+ { 773, PT_BOOL, ucp_Changes_When_Casemapped },
+ { 778, PT_BOOL, ucp_Changes_When_Lowercased },
+ { 782, PT_BOOL, ucp_Changes_When_Titlecased },
+ { 786, PT_BOOL, ucp_Changes_When_Uppercased },
+ { 790, PT_SCX, ucp_Cypriot },
+ { 798, PT_SCX, ucp_Cypro_Minoan },
+ { 810, PT_SCX, ucp_Cyrillic },
+ { 819, PT_SCX, ucp_Cyrillic },
+ { 824, PT_BOOL, ucp_Dash },
+ { 829, PT_BOOL, ucp_Default_Ignorable_Code_Point },
+ { 855, PT_BOOL, ucp_Deprecated },
+ { 859, PT_BOOL, ucp_Deprecated },
+ { 870, PT_SC, ucp_Deseret },
+ { 878, PT_SCX, ucp_Devanagari },
+ { 883, PT_SCX, ucp_Devanagari },
+ { 894, PT_BOOL, ucp_Default_Ignorable_Code_Point },
+ { 897, PT_BOOL, ucp_Diacritic },
+ { 901, PT_BOOL, ucp_Diacritic },
+ { 911, PT_SC, ucp_Dives_Akuru },
+ { 916, PT_SC, ucp_Dives_Akuru },
+ { 927, PT_SCX, ucp_Dogra },
+ { 932, PT_SCX, ucp_Dogra },
+ { 938, PT_SC, ucp_Deseret },
+ { 943, PT_SCX, ucp_Duployan },
+ { 948, PT_SCX, ucp_Duployan },
+ { 957, PT_BOOL, ucp_Emoji_Modifier_Base },
+ { 963, PT_BOOL, ucp_Emoji_Component },
+ { 969, PT_SC, ucp_Egyptian_Hieroglyphs },
+ { 974, PT_SC, ucp_Egyptian_Hieroglyphs },
+ { 994, PT_SC, ucp_Elbasan },
+ { 999, PT_SC, ucp_Elbasan },
+ { 1007, PT_SC, ucp_Elymaic },
+ { 1012, PT_SC, ucp_Elymaic },
+ { 1020, PT_BOOL, ucp_Emoji_Modifier },
+ { 1025, PT_BOOL, ucp_Emoji },
+ { 1031, PT_BOOL, ucp_Emoji_Component },
+ { 1046, PT_BOOL, ucp_Emoji_Modifier },
+ { 1060, PT_BOOL, ucp_Emoji_Modifier_Base },
+ { 1078, PT_BOOL, ucp_Emoji_Presentation },
+ { 1096, PT_BOOL, ucp_Emoji_Presentation },
+ { 1102, PT_SC, ucp_Ethiopic },
+ { 1107, PT_SC, ucp_Ethiopic },
+ { 1116, PT_BOOL, ucp_Extender },
+ { 1120, PT_BOOL, ucp_Extended_Pictographic },
+ { 1141, PT_BOOL, ucp_Extender },
+ { 1150, PT_BOOL, ucp_Extended_Pictographic },
+ { 1158, PT_SCX, ucp_Georgian },
+ { 1163, PT_SCX, ucp_Georgian },
+ { 1172, PT_SCX, ucp_Glagolitic },
+ { 1177, PT_SCX, ucp_Glagolitic },
+ { 1188, PT_SCX, ucp_Gunjala_Gondi },
+ { 1193, PT_SCX, ucp_Masaram_Gondi },
+ { 1198, PT_SC, ucp_Gothic },
+ { 1203, PT_SC, ucp_Gothic },
+ { 1210, PT_SCX, ucp_Grantha },
+ { 1215, PT_SCX, ucp_Grantha },
+ { 1223, PT_BOOL, ucp_Grapheme_Base },
+ { 1236, PT_BOOL, ucp_Grapheme_Extend },
+ { 1251, PT_BOOL, ucp_Grapheme_Link },
+ { 1264, PT_BOOL, ucp_Grapheme_Base },
+ { 1271, PT_SCX, ucp_Greek },
+ { 1277, PT_SCX, ucp_Greek },
+ { 1282, PT_BOOL, ucp_Grapheme_Extend },
+ { 1288, PT_BOOL, ucp_Grapheme_Link },
+ { 1295, PT_SCX, ucp_Gujarati },
+ { 1304, PT_SCX, ucp_Gujarati },
+ { 1309, PT_SCX, ucp_Gunjala_Gondi },
+ { 1322, PT_SCX, ucp_Gurmukhi },
+ { 1331, PT_SCX, ucp_Gurmukhi },
+ { 1336, PT_SCX, ucp_Han },
+ { 1340, PT_SCX, ucp_Hangul },
+ { 1345, PT_SCX, ucp_Hangul },
+ { 1352, PT_SCX, ucp_Han },
+ { 1357, PT_SCX, ucp_Hanifi_Rohingya },
+ { 1372, PT_SCX, ucp_Hanunoo },
+ { 1377, PT_SCX, ucp_Hanunoo },
+ { 1385, PT_SC, ucp_Hatran },
+ { 1390, PT_SC, ucp_Hatran },
+ { 1397, PT_SC, ucp_Hebrew },
+ { 1402, PT_SC, ucp_Hebrew },
+ { 1409, PT_BOOL, ucp_Hex_Digit },
+ { 1413, PT_BOOL, ucp_Hex_Digit },
+ { 1422, PT_SCX, ucp_Hiragana },
+ { 1427, PT_SCX, ucp_Hiragana },
+ { 1436, PT_SC, ucp_Anatolian_Hieroglyphs },
+ { 1441, PT_SC, ucp_Pahawh_Hmong },
+ { 1446, PT_SC, ucp_Nyiakeng_Puachue_Hmong },
+ { 1451, PT_SC, ucp_Old_Hungarian },
+ { 1456, PT_BOOL, ucp_ID_Continue },
+ { 1460, PT_BOOL, ucp_ID_Continue },
+ { 1471, PT_BOOL, ucp_Ideographic },
+ { 1476, PT_BOOL, ucp_Ideographic },
+ { 1488, PT_BOOL, ucp_ID_Start },
+ { 1492, PT_BOOL, ucp_IDS_Binary_Operator },
+ { 1497, PT_BOOL, ucp_IDS_Binary_Operator },
+ { 1515, PT_BOOL, ucp_IDS_Trinary_Operator },
+ { 1520, PT_BOOL, ucp_ID_Start },
+ { 1528, PT_BOOL, ucp_IDS_Trinary_Operator },
+ { 1547, PT_SC, ucp_Imperial_Aramaic },
+ { 1563, PT_SC, ucp_Inherited },
+ { 1573, PT_SC, ucp_Inscriptional_Pahlavi },
+ { 1594, PT_SC, ucp_Inscriptional_Parthian },
+ { 1616, PT_SC, ucp_Old_Italic },
+ { 1621, PT_SCX, ucp_Javanese },
+ { 1626, PT_SCX, ucp_Javanese },
+ { 1635, PT_BOOL, ucp_Join_Control },
+ { 1641, PT_BOOL, ucp_Join_Control },
+ { 1653, PT_SCX, ucp_Kaithi },
+ { 1660, PT_SCX, ucp_Kayah_Li },
+ { 1665, PT_SCX, ucp_Katakana },
+ { 1670, PT_SCX, ucp_Kannada },
+ { 1678, PT_SCX, ucp_Katakana },
+ { 1687, PT_SCX, ucp_Kayah_Li },
+ { 1695, PT_SC, ucp_Kharoshthi },
+ { 1700, PT_SC, ucp_Kharoshthi },
+ { 1711, PT_SC, ucp_Khitan_Small_Script },
+ { 1729, PT_SC, ucp_Khmer },
+ { 1735, PT_SC, ucp_Khmer },
+ { 1740, PT_SCX, ucp_Khojki },
+ { 1745, PT_SCX, ucp_Khojki },
+ { 1752, PT_SCX, ucp_Khudawadi },
+ { 1762, PT_SC, ucp_Khitan_Small_Script },
+ { 1767, PT_SCX, ucp_Kannada },
+ { 1772, PT_SCX, ucp_Kaithi },
+ { 1777, PT_GC, ucp_L },
+ { 1779, PT_LAMP, 0 },
+ { 1782, PT_SC, ucp_Tai_Tham },
+ { 1787, PT_SC, ucp_Lao },
+ { 1791, PT_SC, ucp_Lao },
+ { 1796, PT_SCX, ucp_Latin },
+ { 1802, PT_SCX, ucp_Latin },
+ { 1807, PT_LAMP, 0 },
+ { 1810, PT_SC, ucp_Lepcha },
+ { 1815, PT_SC, ucp_Lepcha },
+ { 1822, PT_SCX, ucp_Limbu },
+ { 1827, PT_SCX, ucp_Limbu },
+ { 1833, PT_SCX, ucp_Linear_A },
+ { 1838, PT_SCX, ucp_Linear_B },
+ { 1843, PT_SCX, ucp_Linear_A },
+ { 1851, PT_SCX, ucp_Linear_B },
+ { 1859, PT_SC, ucp_Lisu },
+ { 1864, PT_PC, ucp_Ll },
+ { 1867, PT_PC, ucp_Lm },
+ { 1870, PT_PC, ucp_Lo },
+ { 1873, PT_BOOL, ucp_Logical_Order_Exception },
+ { 1877, PT_BOOL, ucp_Logical_Order_Exception },
+ { 1899, PT_BOOL, ucp_Lowercase },
+ { 1905, PT_BOOL, ucp_Lowercase },
+ { 1915, PT_PC, ucp_Lt },
+ { 1918, PT_PC, ucp_Lu },
+ { 1921, PT_SC, ucp_Lycian },
+ { 1926, PT_SC, ucp_Lycian },
+ { 1933, PT_SC, ucp_Lydian },
+ { 1938, PT_SC, ucp_Lydian },
+ { 1945, PT_GC, ucp_M },
+ { 1947, PT_SCX, ucp_Mahajani },
+ { 1956, PT_SCX, ucp_Mahajani },
+ { 1961, PT_SC, ucp_Makasar },
+ { 1966, PT_SC, ucp_Makasar },
+ { 1974, PT_SCX, ucp_Malayalam },
+ { 1984, PT_SCX, ucp_Mandaic },
+ { 1989, PT_SCX, ucp_Mandaic },
+ { 1997, PT_SCX, ucp_Manichaean },
+ { 2002, PT_SCX, ucp_Manichaean },
+ { 2013, PT_SC, ucp_Marchen },
+ { 2018, PT_SC, ucp_Marchen },
+ { 2026, PT_SCX, ucp_Masaram_Gondi },
+ { 2039, PT_BOOL, ucp_Math },
+ { 2044, PT_PC, ucp_Mc },
+ { 2047, PT_PC, ucp_Me },
+ { 2050, PT_SC, ucp_Medefaidrin },
+ { 2062, PT_SC, ucp_Medefaidrin },
+ { 2067, PT_SC, ucp_Meetei_Mayek },
+ { 2079, PT_SC, ucp_Mende_Kikakui },
+ { 2084, PT_SC, ucp_Mende_Kikakui },
+ { 2097, PT_SC, ucp_Meroitic_Cursive },
+ { 2102, PT_SC, ucp_Meroitic_Hieroglyphs },
+ { 2107, PT_SC, ucp_Meroitic_Cursive },
+ { 2123, PT_SC, ucp_Meroitic_Hieroglyphs },
+ { 2143, PT_SC, ucp_Miao },
+ { 2148, PT_SCX, ucp_Malayalam },
+ { 2153, PT_PC, ucp_Mn },
+ { 2156, PT_SCX, ucp_Modi },
+ { 2161, PT_SCX, ucp_Mongolian },
+ { 2166, PT_SCX, ucp_Mongolian },
+ { 2176, PT_SC, ucp_Mro },
+ { 2180, PT_SC, ucp_Mro },
+ { 2185, PT_SC, ucp_Meetei_Mayek },
+ { 2190, PT_SCX, ucp_Multani },
+ { 2195, PT_SCX, ucp_Multani },
+ { 2203, PT_SCX, ucp_Myanmar },
+ { 2211, PT_SCX, ucp_Myanmar },
+ { 2216, PT_GC, ucp_N },
+ { 2218, PT_SC, ucp_Nabataean },
+ { 2228, PT_SCX, ucp_Nandinagari },
+ { 2233, PT_SCX, ucp_Nandinagari },
+ { 2245, PT_SC, ucp_Old_North_Arabian },
+ { 2250, PT_SC, ucp_Nabataean },
+ { 2255, PT_BOOL, ucp_Noncharacter_Code_Point },
+ { 2261, PT_PC, ucp_Nd },
+ { 2264, PT_SC, ucp_Newa },
+ { 2269, PT_SC, ucp_New_Tai_Lue },
+ { 2279, PT_SCX, ucp_Nko },
+ { 2283, PT_SCX, ucp_Nko },
+ { 2288, PT_PC, ucp_Nl },
+ { 2291, PT_PC, ucp_No },
+ { 2294, PT_BOOL, ucp_Noncharacter_Code_Point },
+ { 2316, PT_SC, ucp_Nushu },
+ { 2321, PT_SC, ucp_Nushu },
+ { 2327, PT_SC, ucp_Nyiakeng_Puachue_Hmong },
+ { 2348, PT_SC, ucp_Ogham },
+ { 2353, PT_SC, ucp_Ogham },
+ { 2359, PT_SC, ucp_Ol_Chiki },
+ { 2367, PT_SC, ucp_Ol_Chiki },
+ { 2372, PT_SC, ucp_Old_Hungarian },
+ { 2385, PT_SC, ucp_Old_Italic },
+ { 2395, PT_SC, ucp_Old_North_Arabian },
+ { 2411, PT_SCX, ucp_Old_Permic },
+ { 2421, PT_SC, ucp_Old_Persian },
+ { 2432, PT_SC, ucp_Old_Sogdian },
+ { 2443, PT_SC, ucp_Old_South_Arabian },
+ { 2459, PT_SC, ucp_Old_Turkic },
+ { 2469, PT_SCX, ucp_Old_Uyghur },
+ { 2479, PT_SCX, ucp_Oriya },
+ { 2485, PT_SC, ucp_Old_Turkic },
+ { 2490, PT_SCX, ucp_Oriya },
+ { 2495, PT_SC, ucp_Osage },
+ { 2501, PT_SC, ucp_Osage },
+ { 2506, PT_SC, ucp_Osmanya },
+ { 2511, PT_SC, ucp_Osmanya },
+ { 2519, PT_SCX, ucp_Old_Uyghur },
+ { 2524, PT_GC, ucp_P },
+ { 2526, PT_SC, ucp_Pahawh_Hmong },
+ { 2538, PT_SC, ucp_Palmyrene },
+ { 2543, PT_SC, ucp_Palmyrene },
+ { 2553, PT_BOOL, ucp_Pattern_Syntax },
+ { 2560, PT_BOOL, ucp_Pattern_Syntax },
+ { 2574, PT_BOOL, ucp_Pattern_White_Space },
+ { 2592, PT_BOOL, ucp_Pattern_White_Space },
+ { 2598, PT_SC, ucp_Pau_Cin_Hau },
+ { 2603, PT_SC, ucp_Pau_Cin_Hau },
+ { 2613, PT_PC, ucp_Pc },
+ { 2616, PT_BOOL, ucp_Prepended_Concatenation_Mark },
+ { 2620, PT_PC, ucp_Pd },
+ { 2623, PT_PC, ucp_Pe },
+ { 2626, PT_SCX, ucp_Old_Permic },
+ { 2631, PT_PC, ucp_Pf },
+ { 2634, PT_SCX, ucp_Phags_Pa },
+ { 2639, PT_SCX, ucp_Phags_Pa },
+ { 2647, PT_SC, ucp_Inscriptional_Pahlavi },
+ { 2652, PT_SCX, ucp_Psalter_Pahlavi },
+ { 2657, PT_SC, ucp_Phoenician },
+ { 2662, PT_SC, ucp_Phoenician },
+ { 2673, PT_PC, ucp_Pi },
+ { 2676, PT_SC, ucp_Miao },
+ { 2681, PT_PC, ucp_Po },
+ { 2684, PT_BOOL, ucp_Prepended_Concatenation_Mark },
+ { 2711, PT_SC, ucp_Inscriptional_Parthian },
+ { 2716, PT_PC, ucp_Ps },
+ { 2719, PT_SCX, ucp_Psalter_Pahlavi },
+ { 2734, PT_SCX, ucp_Coptic },
+ { 2739, PT_SC, ucp_Inherited },
+ { 2744, PT_BOOL, ucp_Quotation_Mark },
+ { 2750, PT_BOOL, ucp_Quotation_Mark },
+ { 2764, PT_BOOL, ucp_Radical },
+ { 2772, PT_BOOL, ucp_Regional_Indicator },
+ { 2790, PT_SC, ucp_Rejang },
+ { 2797, PT_BOOL, ucp_Regional_Indicator },
+ { 2800, PT_SC, ucp_Rejang },
+ { 2805, PT_SCX, ucp_Hanifi_Rohingya },
+ { 2810, PT_SC, ucp_Runic },
+ { 2816, PT_SC, ucp_Runic },
+ { 2821, PT_GC, ucp_S },
+ { 2823, PT_SC, ucp_Samaritan },
+ { 2833, PT_SC, ucp_Samaritan },
+ { 2838, PT_SC, ucp_Old_South_Arabian },
+ { 2843, PT_SC, ucp_Saurashtra },
+ { 2848, PT_SC, ucp_Saurashtra },
+ { 2859, PT_PC, ucp_Sc },
+ { 2862, PT_BOOL, ucp_Soft_Dotted },
+ { 2865, PT_BOOL, ucp_Sentence_Terminal },
+ { 2882, PT_SC, ucp_SignWriting },
+ { 2887, PT_SCX, ucp_Sharada },
+ { 2895, PT_SC, ucp_Shavian },
+ { 2903, PT_SC, ucp_Shavian },
+ { 2908, PT_SCX, ucp_Sharada },
+ { 2913, PT_SC, ucp_Siddham },
+ { 2918, PT_SC, ucp_Siddham },
+ { 2926, PT_SC, ucp_SignWriting },
+ { 2938, PT_SCX, ucp_Khudawadi },
+ { 2943, PT_SCX, ucp_Sinhala },
+ { 2948, PT_SCX, ucp_Sinhala },
+ { 2956, PT_PC, ucp_Sk },
+ { 2959, PT_PC, ucp_Sm },
+ { 2962, PT_PC, ucp_So },
+ { 2965, PT_BOOL, ucp_Soft_Dotted },
+ { 2976, PT_SCX, ucp_Sogdian },
+ { 2981, PT_SCX, ucp_Sogdian },
+ { 2989, PT_SC, ucp_Old_Sogdian },
+ { 2994, PT_SC, ucp_Sora_Sompeng },
+ { 2999, PT_SC, ucp_Sora_Sompeng },
+ { 3011, PT_SC, ucp_Soyombo },
+ { 3016, PT_SC, ucp_Soyombo },
+ { 3024, PT_BOOL, ucp_White_Space },
+ { 3030, PT_BOOL, ucp_Sentence_Terminal },
+ { 3036, PT_SC, ucp_Sundanese },
+ { 3041, PT_SC, ucp_Sundanese },
+ { 3051, PT_SCX, ucp_Syloti_Nagri },
+ { 3056, PT_SCX, ucp_Syloti_Nagri },
+ { 3068, PT_SCX, ucp_Syriac },
+ { 3073, PT_SCX, ucp_Syriac },
+ { 3080, PT_SCX, ucp_Tagalog },
+ { 3088, PT_SCX, ucp_Tagbanwa },
+ { 3093, PT_SCX, ucp_Tagbanwa },
+ { 3102, PT_SCX, ucp_Tai_Le },
+ { 3108, PT_SC, ucp_Tai_Tham },
+ { 3116, PT_SC, ucp_Tai_Viet },
+ { 3124, PT_SCX, ucp_Takri },
+ { 3129, PT_SCX, ucp_Takri },
+ { 3135, PT_SCX, ucp_Tai_Le },
+ { 3140, PT_SC, ucp_New_Tai_Lue },
+ { 3145, PT_SCX, ucp_Tamil },
+ { 3151, PT_SCX, ucp_Tamil },
+ { 3156, PT_SC, ucp_Tangut },
+ { 3161, PT_SC, ucp_Tangsa },
+ { 3168, PT_SC, ucp_Tangut },
+ { 3175, PT_SC, ucp_Tai_Viet },
+ { 3180, PT_SCX, ucp_Telugu },
+ { 3185, PT_SCX, ucp_Telugu },
+ { 3192, PT_BOOL, ucp_Terminal_Punctuation },
+ { 3197, PT_BOOL, ucp_Terminal_Punctuation },
+ { 3217, PT_SC, ucp_Tifinagh },
+ { 3222, PT_SCX, ucp_Tagalog },
+ { 3227, PT_SCX, ucp_Thaana },
+ { 3232, PT_SCX, ucp_Thaana },
+ { 3239, PT_SC, ucp_Thai },
+ { 3244, PT_SC, ucp_Tibetan },
+ { 3252, PT_SC, ucp_Tibetan },
+ { 3257, PT_SC, ucp_Tifinagh },
+ { 3266, PT_SCX, ucp_Tirhuta },
+ { 3271, PT_SCX, ucp_Tirhuta },
+ { 3279, PT_SC, ucp_Tangsa },
+ { 3284, PT_SC, ucp_Toto },
+ { 3289, PT_SC, ucp_Ugaritic },
+ { 3294, PT_SC, ucp_Ugaritic },
+ { 3303, PT_BOOL, ucp_Unified_Ideograph },
+ { 3309, PT_BOOL, ucp_Unified_Ideograph },
+ { 3326, PT_SC, ucp_Unknown },
+ { 3334, PT_BOOL, ucp_Uppercase },
+ { 3340, PT_BOOL, ucp_Uppercase },
+ { 3350, PT_SC, ucp_Vai },
+ { 3354, PT_SC, ucp_Vai },
+ { 3359, PT_BOOL, ucp_Variation_Selector },
+ { 3377, PT_SC, ucp_Vithkuqi },
+ { 3382, PT_SC, ucp_Vithkuqi },
+ { 3391, PT_BOOL, ucp_Variation_Selector },
+ { 3394, PT_SC, ucp_Wancho },
+ { 3401, PT_SC, ucp_Warang_Citi },
+ { 3406, PT_SC, ucp_Warang_Citi },
+ { 3417, PT_SC, ucp_Wancho },
+ { 3422, PT_BOOL, ucp_White_Space },
+ { 3433, PT_BOOL, ucp_White_Space },
+ { 3440, PT_ALNUM, 0 },
+ { 3444, PT_BOOL, ucp_XID_Continue },
+ { 3449, PT_BOOL, ucp_XID_Continue },
+ { 3461, PT_BOOL, ucp_XID_Start },
+ { 3466, PT_BOOL, ucp_XID_Start },
+ { 3475, PT_SC, ucp_Old_Persian },
+ { 3480, PT_PXSPACE, 0 },
+ { 3484, PT_SPACE, 0 },
+ { 3488, PT_SC, ucp_Cuneiform },
+ { 3493, PT_UCNC, 0 },
+ { 3497, PT_WORD, 0 },
+ { 3501, PT_SCX, ucp_Yezidi },
+ { 3506, PT_SCX, ucp_Yezidi },
+ { 3513, PT_SCX, ucp_Yi },
+ { 3516, PT_SCX, ucp_Yi },
+ { 3521, PT_GC, ucp_Z },
+ { 3523, PT_SC, ucp_Zanabazar_Square },
+ { 3539, PT_SC, ucp_Zanabazar_Square },
+ { 3544, PT_SC, ucp_Inherited },
+ { 3549, PT_PC, ucp_Zl },
+ { 3552, PT_PC, ucp_Zp },
+ { 3555, PT_PC, ucp_Zs },
+ { 3558, PT_SC, ucp_Common },
+ { 3563, PT_SC, ucp_Unknown }
+};
+
+const size_t PRIV(utt_size) = sizeof(PRIV(utt)) / sizeof(ucp_type_table);
+
+#endif /* SUPPORT_UNICODE */
+
+/* End of pcre2_ucptables.c */
diff --git a/contrib/libs/pcre2/src/pcre2_valid_utf.c b/contrib/libs/pcre2/src/pcre2_valid_utf.c
new file mode 100644
index 00000000000..9373f71099d
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_valid_utf.c
@@ -0,0 +1,398 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2020 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This module contains an internal function for validating UTF character
+strings. This file is also #included by the pcre2test program, which uses
+macros to change names from _pcre2_xxx to xxxx, thereby avoiding name clashes
+with the library. In this case, PCRE2_PCRE2TEST is defined. */
+
+#ifndef PCRE2_PCRE2TEST /* We're compiling the library */
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+#include "pcre2_internal.h"
+#endif /* PCRE2_PCRE2TEST */
+
+
+#ifndef SUPPORT_UNICODE
+/*************************************************
+* Dummy function when Unicode is not supported *
+*************************************************/
+
+/* This function should never be called when Unicode is not supported. */
+
+int
+PRIV(valid_utf)(PCRE2_SPTR string, PCRE2_SIZE length, PCRE2_SIZE *erroroffset)
+{
+(void)string;
+(void)length;
+(void)erroroffset;
+return 0;
+}
+#else /* UTF is supported */
+
+
+
+/*************************************************
+* Validate a UTF string *
+*************************************************/
+
+/* This function is called (optionally) at the start of compile or match, to
+check that a supposed UTF string is actually valid. The early check means
+that subsequent code can assume it is dealing with a valid string. The check
+can be turned off for maximum performance, but the consequences of supplying an
+invalid string are then undefined.
+
+Arguments:
+ string points to the string
+ length length of string
+ errp pointer to an error position offset variable
+
+Returns: == 0 if the string is a valid UTF string
+ != 0 otherwise, setting the offset of the bad character
+*/
+
+int
+PRIV(valid_utf)(PCRE2_SPTR string, PCRE2_SIZE length, PCRE2_SIZE *erroroffset)
+{
+PCRE2_SPTR p;
+uint32_t c;
+
+/* ----------------- Check a UTF-8 string ----------------- */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+
+/* Originally, this function checked according to RFC 2279, allowing for values
+in the range 0 to 0x7fffffff, up to 6 bytes long, but ensuring that they were
+in the canonical format. Once somebody had pointed out RFC 3629 to me (it
+obsoletes 2279), additional restrictions were applied. The values are now
+limited to be between 0 and 0x0010ffff, no more than 4 bytes long, and the
+subrange 0xd000 to 0xdfff is excluded. However, the format of 5-byte and 6-byte
+characters is still checked. Error returns are as follows:
+
+PCRE2_ERROR_UTF8_ERR1 Missing 1 byte at the end of the string
+PCRE2_ERROR_UTF8_ERR2 Missing 2 bytes at the end of the string
+PCRE2_ERROR_UTF8_ERR3 Missing 3 bytes at the end of the string
+PCRE2_ERROR_UTF8_ERR4 Missing 4 bytes at the end of the string
+PCRE2_ERROR_UTF8_ERR5 Missing 5 bytes at the end of the string
+PCRE2_ERROR_UTF8_ERR6 2nd-byte's two top bits are not 0x80
+PCRE2_ERROR_UTF8_ERR7 3rd-byte's two top bits are not 0x80
+PCRE2_ERROR_UTF8_ERR8 4th-byte's two top bits are not 0x80
+PCRE2_ERROR_UTF8_ERR9 5th-byte's two top bits are not 0x80
+PCRE2_ERROR_UTF8_ERR10 6th-byte's two top bits are not 0x80
+PCRE2_ERROR_UTF8_ERR11 5-byte character is not permitted by RFC 3629
+PCRE2_ERROR_UTF8_ERR12 6-byte character is not permitted by RFC 3629
+PCRE2_ERROR_UTF8_ERR13 4-byte character with value > 0x10ffff is not permitted
+PCRE2_ERROR_UTF8_ERR14 3-byte character with value 0xd800-0xdfff is not permitted
+PCRE2_ERROR_UTF8_ERR15 Overlong 2-byte sequence
+PCRE2_ERROR_UTF8_ERR16 Overlong 3-byte sequence
+PCRE2_ERROR_UTF8_ERR17 Overlong 4-byte sequence
+PCRE2_ERROR_UTF8_ERR18 Overlong 5-byte sequence (won't ever occur)
+PCRE2_ERROR_UTF8_ERR19 Overlong 6-byte sequence (won't ever occur)
+PCRE2_ERROR_UTF8_ERR20 Isolated 0x80 byte (not within UTF-8 character)
+PCRE2_ERROR_UTF8_ERR21 Byte with the illegal value 0xfe or 0xff
+*/
+
+for (p = string; length > 0; p++)
+ {
+ uint32_t ab, d;
+
+ c = *p;
+ length--;
+
+ if (c < 128) continue; /* ASCII character */
+
+ if (c < 0xc0) /* Isolated 10xx xxxx byte */
+ {
+ *erroroffset = (PCRE2_SIZE)(p - string);
+ return PCRE2_ERROR_UTF8_ERR20;
+ }
+
+ if (c >= 0xfe) /* Invalid 0xfe or 0xff bytes */
+ {
+ *erroroffset = (PCRE2_SIZE)(p - string);
+ return PCRE2_ERROR_UTF8_ERR21;
+ }
+
+ ab = PRIV(utf8_table4)[c & 0x3f]; /* Number of additional bytes (1-5) */
+ if (length < ab) /* Missing bytes */
+ {
+ *erroroffset = (PCRE2_SIZE)(p - string);
+ switch(ab - length)
+ {
+ case 1: return PCRE2_ERROR_UTF8_ERR1;
+ case 2: return PCRE2_ERROR_UTF8_ERR2;
+ case 3: return PCRE2_ERROR_UTF8_ERR3;
+ case 4: return PCRE2_ERROR_UTF8_ERR4;
+ case 5: return PCRE2_ERROR_UTF8_ERR5;
+ }
+ }
+ length -= ab; /* Length remaining */
+
+ /* Check top bits in the second byte */
+
+ if (((d = *(++p)) & 0xc0) != 0x80)
+ {
+ *erroroffset = (int)(p - string) - 1;
+ return PCRE2_ERROR_UTF8_ERR6;
+ }
+
+ /* For each length, check that the remaining bytes start with the 0x80 bit
+ set and not the 0x40 bit. Then check for an overlong sequence, and for the
+ excluded range 0xd800 to 0xdfff. */
+
+ switch (ab)
+ {
+ /* 2-byte character. No further bytes to check for 0x80. Check first byte
+ for for xx00 000x (overlong sequence). */
+
+ case 1: if ((c & 0x3e) == 0)
+ {
+ *erroroffset = (int)(p - string) - 1;
+ return PCRE2_ERROR_UTF8_ERR15;
+ }
+ break;
+
+ /* 3-byte character. Check third byte for 0x80. Then check first 2 bytes
+ for 1110 0000, xx0x xxxx (overlong sequence) or
+ 1110 1101, 1010 xxxx (0xd800 - 0xdfff) */
+
+ case 2:
+ if ((*(++p) & 0xc0) != 0x80) /* Third byte */
+ {
+ *erroroffset = (int)(p - string) - 2;
+ return PCRE2_ERROR_UTF8_ERR7;
+ }
+ if (c == 0xe0 && (d & 0x20) == 0)
+ {
+ *erroroffset = (int)(p - string) - 2;
+ return PCRE2_ERROR_UTF8_ERR16;
+ }
+ if (c == 0xed && d >= 0xa0)
+ {
+ *erroroffset = (int)(p - string) - 2;
+ return PCRE2_ERROR_UTF8_ERR14;
+ }
+ break;
+
+ /* 4-byte character. Check 3rd and 4th bytes for 0x80. Then check first 2
+ bytes for for 1111 0000, xx00 xxxx (overlong sequence), then check for a
+ character greater than 0x0010ffff (f4 8f bf bf) */
+
+ case 3:
+ if ((*(++p) & 0xc0) != 0x80) /* Third byte */
+ {
+ *erroroffset = (int)(p - string) - 2;
+ return PCRE2_ERROR_UTF8_ERR7;
+ }
+ if ((*(++p) & 0xc0) != 0x80) /* Fourth byte */
+ {
+ *erroroffset = (int)(p - string) - 3;
+ return PCRE2_ERROR_UTF8_ERR8;
+ }
+ if (c == 0xf0 && (d & 0x30) == 0)
+ {
+ *erroroffset = (int)(p - string) - 3;
+ return PCRE2_ERROR_UTF8_ERR17;
+ }
+ if (c > 0xf4 || (c == 0xf4 && d > 0x8f))
+ {
+ *erroroffset = (int)(p - string) - 3;
+ return PCRE2_ERROR_UTF8_ERR13;
+ }
+ break;
+
+ /* 5-byte and 6-byte characters are not allowed by RFC 3629, and will be
+ rejected by the length test below. However, we do the appropriate tests
+ here so that overlong sequences get diagnosed, and also in case there is
+ ever an option for handling these larger code points. */
+
+ /* 5-byte character. Check 3rd, 4th, and 5th bytes for 0x80. Then check for
+ 1111 1000, xx00 0xxx */
+
+ case 4:
+ if ((*(++p) & 0xc0) != 0x80) /* Third byte */
+ {
+ *erroroffset = (int)(p - string) - 2;
+ return PCRE2_ERROR_UTF8_ERR7;
+ }
+ if ((*(++p) & 0xc0) != 0x80) /* Fourth byte */
+ {
+ *erroroffset = (int)(p - string) - 3;
+ return PCRE2_ERROR_UTF8_ERR8;
+ }
+ if ((*(++p) & 0xc0) != 0x80) /* Fifth byte */
+ {
+ *erroroffset = (int)(p - string) - 4;
+ return PCRE2_ERROR_UTF8_ERR9;
+ }
+ if (c == 0xf8 && (d & 0x38) == 0)
+ {
+ *erroroffset = (int)(p - string) - 4;
+ return PCRE2_ERROR_UTF8_ERR18;
+ }
+ break;
+
+ /* 6-byte character. Check 3rd-6th bytes for 0x80. Then check for
+ 1111 1100, xx00 00xx. */
+
+ case 5:
+ if ((*(++p) & 0xc0) != 0x80) /* Third byte */
+ {
+ *erroroffset = (int)(p - string) - 2;
+ return PCRE2_ERROR_UTF8_ERR7;
+ }
+ if ((*(++p) & 0xc0) != 0x80) /* Fourth byte */
+ {
+ *erroroffset = (int)(p - string) - 3;
+ return PCRE2_ERROR_UTF8_ERR8;
+ }
+ if ((*(++p) & 0xc0) != 0x80) /* Fifth byte */
+ {
+ *erroroffset = (int)(p - string) - 4;
+ return PCRE2_ERROR_UTF8_ERR9;
+ }
+ if ((*(++p) & 0xc0) != 0x80) /* Sixth byte */
+ {
+ *erroroffset = (int)(p - string) - 5;
+ return PCRE2_ERROR_UTF8_ERR10;
+ }
+ if (c == 0xfc && (d & 0x3c) == 0)
+ {
+ *erroroffset = (int)(p - string) - 5;
+ return PCRE2_ERROR_UTF8_ERR19;
+ }
+ break;
+ }
+
+ /* Character is valid under RFC 2279, but 4-byte and 5-byte characters are
+ excluded by RFC 3629. The pointer p is currently at the last byte of the
+ character. */
+
+ if (ab > 3)
+ {
+ *erroroffset = (int)(p - string) - ab;
+ return (ab == 4)? PCRE2_ERROR_UTF8_ERR11 : PCRE2_ERROR_UTF8_ERR12;
+ }
+ }
+return 0;
+
+
+/* ----------------- Check a UTF-16 string ----------------- */
+
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+
+/* There's not so much work, nor so many errors, for UTF-16.
+PCRE2_ERROR_UTF16_ERR1 Missing low surrogate at the end of the string
+PCRE2_ERROR_UTF16_ERR2 Invalid low surrogate
+PCRE2_ERROR_UTF16_ERR3 Isolated low surrogate
+*/
+
+for (p = string; length > 0; p++)
+ {
+ c = *p;
+ length--;
+
+ if ((c & 0xf800) != 0xd800)
+ {
+ /* Normal UTF-16 code point. Neither high nor low surrogate. */
+ }
+ else if ((c & 0x0400) == 0)
+ {
+ /* High surrogate. Must be a followed by a low surrogate. */
+ if (length == 0)
+ {
+ *erroroffset = p - string;
+ return PCRE2_ERROR_UTF16_ERR1;
+ }
+ p++;
+ length--;
+ if ((*p & 0xfc00) != 0xdc00)
+ {
+ *erroroffset = p - string - 1;
+ return PCRE2_ERROR_UTF16_ERR2;
+ }
+ }
+ else
+ {
+ /* Isolated low surrogate. Always an error. */
+ *erroroffset = p - string;
+ return PCRE2_ERROR_UTF16_ERR3;
+ }
+ }
+return 0;
+
+
+
+/* ----------------- Check a UTF-32 string ----------------- */
+
+#else
+
+/* There is very little to do for a UTF-32 string.
+PCRE2_ERROR_UTF32_ERR1 Surrogate character
+PCRE2_ERROR_UTF32_ERR2 Character > 0x10ffff
+*/
+
+for (p = string; length > 0; length--, p++)
+ {
+ c = *p;
+ if ((c & 0xfffff800u) != 0xd800u)
+ {
+ /* Normal UTF-32 code point. Neither high nor low surrogate. */
+ if (c > 0x10ffffu)
+ {
+ *erroroffset = p - string;
+ return PCRE2_ERROR_UTF32_ERR2;
+ }
+ }
+ else
+ {
+ /* A surrogate */
+ *erroroffset = p - string;
+ return PCRE2_ERROR_UTF32_ERR1;
+ }
+ }
+return 0;
+#endif /* CODE_UNIT_WIDTH */
+}
+#endif /* SUPPORT_UNICODE */
+
+/* End of pcre2_valid_utf.c */
diff --git a/contrib/libs/pcre2/src/pcre2_xclass.c b/contrib/libs/pcre2/src/pcre2_xclass.c
new file mode 100644
index 00000000000..cd9331c33b5
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_xclass.c
@@ -0,0 +1,289 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains an internal function that is used to match an extended
+class. It is used by pcre2_auto_possessify() and by both pcre2_match() and
+pcre2_def_match(). */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+
+#include "pcre2_internal.h"
+
+/*************************************************
+* Match character against an XCLASS *
+*************************************************/
+
+/* This function is called to match a character against an extended class that
+might contain codepoints above 255 and/or Unicode properties.
+
+Arguments:
+ c the character
+ data points to the flag code unit of the XCLASS data
+ utf TRUE if in UTF mode
+
+Returns: TRUE if character matches, else FALSE
+*/
+
+BOOL
+PRIV(xclass)(uint32_t c, PCRE2_SPTR data, BOOL utf)
+{
+PCRE2_UCHAR t;
+BOOL negated = (*data & XCL_NOT) != 0;
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+/* In 8 bit mode, this must always be TRUE. Help the compiler to know that. */
+utf = TRUE;
+#endif
+
+/* Code points < 256 are matched against a bitmap, if one is present. If not,
+we still carry on, because there may be ranges that start below 256 in the
+additional data. */
+
+if (c < 256)
+ {
+ if ((*data & XCL_HASPROP) == 0)
+ {
+ if ((*data & XCL_MAP) == 0) return negated;
+ return (((uint8_t *)(data + 1))[c/8] & (1u << (c&7))) != 0;
+ }
+ if ((*data & XCL_MAP) != 0 &&
+ (((uint8_t *)(data + 1))[c/8] & (1u << (c&7))) != 0)
+ return !negated; /* char found */
+ }
+
+/* First skip the bit map if present. Then match against the list of Unicode
+properties or large chars or ranges that end with a large char. We won't ever
+encounter XCL_PROP or XCL_NOTPROP when UTF support is not compiled. */
+
+if ((*data++ & XCL_MAP) != 0) data += 32 / sizeof(PCRE2_UCHAR);
+
+while ((t = *data++) != XCL_END)
+ {
+ uint32_t x, y;
+ if (t == XCL_SINGLE)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ GETCHARINC(x, data); /* macro generates multiple statements */
+ }
+ else
+#endif
+ x = *data++;
+ if (c == x) return !negated;
+ }
+ else if (t == XCL_RANGE)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ GETCHARINC(x, data); /* macro generates multiple statements */
+ GETCHARINC(y, data); /* macro generates multiple statements */
+ }
+ else
+#endif
+ {
+ x = *data++;
+ y = *data++;
+ }
+ if (c >= x && c <= y) return !negated;
+ }
+
+#ifdef SUPPORT_UNICODE
+ else /* XCL_PROP & XCL_NOTPROP */
+ {
+ const ucd_record *prop = GET_UCD(c);
+ BOOL isprop = t == XCL_PROP;
+ BOOL ok;
+
+ switch(*data)
+ {
+ case PT_ANY:
+ if (isprop) return !negated;
+ break;
+
+ case PT_LAMP:
+ if ((prop->chartype == ucp_Lu || prop->chartype == ucp_Ll ||
+ prop->chartype == ucp_Lt) == isprop) return !negated;
+ break;
+
+ case PT_GC:
+ if ((data[1] == PRIV(ucp_gentype)[prop->chartype]) == isprop)
+ return !negated;
+ break;
+
+ case PT_PC:
+ if ((data[1] == prop->chartype) == isprop) return !negated;
+ break;
+
+ case PT_SC:
+ if ((data[1] == prop->script) == isprop) return !negated;
+ break;
+
+ case PT_SCX:
+ ok = (data[1] == prop->script ||
+ MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), data[1]) != 0);
+ if (ok == isprop) return !negated;
+ break;
+
+ case PT_ALNUM:
+ if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N) == isprop)
+ return !negated;
+ break;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ switch(c)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ if (isprop) return !negated;
+ break;
+
+ default:
+ if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z) == isprop)
+ return !negated;
+ break;
+ }
+ break;
+
+ case PT_WORD:
+ if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N || c == CHAR_UNDERSCORE)
+ == isprop)
+ return !negated;
+ break;
+
+ case PT_UCNC:
+ if (c < 0xa0)
+ {
+ if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT ||
+ c == CHAR_GRAVE_ACCENT) == isprop)
+ return !negated;
+ }
+ else
+ {
+ if ((c < 0xd800 || c > 0xdfff) == isprop)
+ return !negated;
+ }
+ break;
+
+ case PT_BIDICL:
+ if ((UCD_BIDICLASS_PROP(prop) == data[1]) == isprop)
+ return !negated;
+ break;
+
+ case PT_BOOL:
+ ok = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), data[1]) != 0;
+ if (ok == isprop) return !negated;
+ break;
+
+ /* The following three properties can occur only in an XCLASS, as there
+ is no \p or \P coding for them. */
+
+ /* Graphic character. Implement this as not Z (space or separator) and
+ not C (other), except for Cf (format) with a few exceptions. This seems
+ to be what Perl does. The exceptional characters are:
+
+ U+061C Arabic Letter Mark
+ U+180E Mongolian Vowel Separator
+ U+2066 - U+2069 Various "isolate"s
+ */
+
+ case PT_PXGRAPH:
+ if ((PRIV(ucp_gentype)[prop->chartype] != ucp_Z &&
+ (PRIV(ucp_gentype)[prop->chartype] != ucp_C ||
+ (prop->chartype == ucp_Cf &&
+ c != 0x061c && c != 0x180e && (c < 0x2066 || c > 0x2069))
+ )) == isprop)
+ return !negated;
+ break;
+
+ /* Printable character: same as graphic, with the addition of Zs, i.e.
+ not Zl and not Zp, and U+180E. */
+
+ case PT_PXPRINT:
+ if ((prop->chartype != ucp_Zl &&
+ prop->chartype != ucp_Zp &&
+ (PRIV(ucp_gentype)[prop->chartype] != ucp_C ||
+ (prop->chartype == ucp_Cf &&
+ c != 0x061c && (c < 0x2066 || c > 0x2069))
+ )) == isprop)
+ return !negated;
+ break;
+
+ /* Punctuation: all Unicode punctuation, plus ASCII characters that
+ Unicode treats as symbols rather than punctuation, for Perl
+ compatibility (these are $+<=>^`|~). */
+
+ case PT_PXPUNCT:
+ if ((PRIV(ucp_gentype)[prop->chartype] == ucp_P ||
+ (c < 128 && PRIV(ucp_gentype)[prop->chartype] == ucp_S)) == isprop)
+ return !negated;
+ break;
+
+ /* This should never occur, but compilers may mutter if there is no
+ default. */
+
+ default:
+ return FALSE;
+ }
+
+ data += 2;
+ }
+#else
+ (void)utf; /* Avoid compiler warning */
+#endif /* SUPPORT_UNICODE */
+ }
+
+return negated; /* char did not match */
+}
+
+/* End of pcre2_xclass.c */
diff --git a/contrib/libs/pcre2/src/sljit/sljitConfig.h b/contrib/libs/pcre2/src/sljit/sljitConfig.h
new file mode 100644
index 00000000000..5fba7aa6380
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitConfig.h
@@ -0,0 +1,162 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SLJIT_CONFIG_H_
+#define SLJIT_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ This file contains the basic configuration options for the SLJIT compiler
+ and their default values. These options can be overridden in the
+ sljitConfigPre.h header file when SLJIT_HAVE_CONFIG_PRE is set to a
+ non-zero value.
+*/
+
+/* --------------------------------------------------------------------- */
+/* Architecture */
+/* --------------------------------------------------------------------- */
+
+/* Architecture selection. */
+/* #define SLJIT_CONFIG_X86_32 1 */
+/* #define SLJIT_CONFIG_X86_64 1 */
+/* #define SLJIT_CONFIG_ARM_V5 1 */
+/* #define SLJIT_CONFIG_ARM_V7 1 */
+/* #define SLJIT_CONFIG_ARM_THUMB2 1 */
+/* #define SLJIT_CONFIG_ARM_64 1 */
+/* #define SLJIT_CONFIG_PPC_32 1 */
+/* #define SLJIT_CONFIG_PPC_64 1 */
+/* #define SLJIT_CONFIG_MIPS_32 1 */
+/* #define SLJIT_CONFIG_MIPS_64 1 */
+/* #define SLJIT_CONFIG_RISCV_32 1 */
+/* #define SLJIT_CONFIG_RISCV_64 1 */
+/* #define SLJIT_CONFIG_S390X 1 */
+
+/* #define SLJIT_CONFIG_AUTO 1 */
+/* #define SLJIT_CONFIG_UNSUPPORTED 1 */
+
+/* --------------------------------------------------------------------- */
+/* Utilities */
+/* --------------------------------------------------------------------- */
+
+/* Implements a stack like data structure (by using mmap / VirtualAlloc */
+/* or a custom allocator). */
+#ifndef SLJIT_UTIL_STACK
+/* Enabled by default */
+#define SLJIT_UTIL_STACK 1
+#endif
+
+/* Uses user provided allocator to allocate the stack (see SLJIT_UTIL_STACK) */
+#ifndef SLJIT_UTIL_SIMPLE_STACK_ALLOCATION
+/* Disabled by default */
+#define SLJIT_UTIL_SIMPLE_STACK_ALLOCATION 0
+#endif
+
+/* Single threaded application. Does not require any locks. */
+#ifndef SLJIT_SINGLE_THREADED
+/* Disabled by default. */
+#define SLJIT_SINGLE_THREADED 0
+#endif
+
+/* --------------------------------------------------------------------- */
+/* Configuration */
+/* --------------------------------------------------------------------- */
+
+/* If SLJIT_STD_MACROS_DEFINED is not defined, the application should
+ define SLJIT_MALLOC, SLJIT_FREE, SLJIT_MEMCPY, and NULL. */
+#ifndef SLJIT_STD_MACROS_DEFINED
+/* Disabled by default. */
+#define SLJIT_STD_MACROS_DEFINED 0
+#endif
+
+/* Executable code allocation:
+ If SLJIT_EXECUTABLE_ALLOCATOR is not defined, the application should
+ define SLJIT_MALLOC_EXEC, SLJIT_FREE_EXEC, and SLJIT_EXEC_OFFSET. */
+#ifndef SLJIT_EXECUTABLE_ALLOCATOR
+/* Enabled by default. */
+#define SLJIT_EXECUTABLE_ALLOCATOR 1
+
+/* When SLJIT_PROT_EXECUTABLE_ALLOCATOR is enabled SLJIT uses
+ an allocator which does not set writable and executable
+ permission flags at the same time.
+ Instead, it creates a shared memory segment (usually backed by a file)
+ and maps it twice, with different permissions, depending on the use
+ case.
+ The trade-off is increased use of virtual memory, incompatibility with
+ fork(), and some possible additional security risks by the use of
+ publicly accessible files for the generated code. */
+#ifndef SLJIT_PROT_EXECUTABLE_ALLOCATOR
+/* Disabled by default. */
+#define SLJIT_PROT_EXECUTABLE_ALLOCATOR 0
+#endif
+
+/* When SLJIT_WX_EXECUTABLE_ALLOCATOR is enabled SLJIT uses an
+ allocator which does not set writable and executable permission
+ flags at the same time.
+ Instead, it creates a new independent map on each invocation and
+ switches permissions at the underlying pages as needed.
+ The trade-off is increased memory use and degraded performance. */
+#ifndef SLJIT_WX_EXECUTABLE_ALLOCATOR
+/* Disabled by default. */
+#define SLJIT_WX_EXECUTABLE_ALLOCATOR 0
+#endif
+
+#endif /* !SLJIT_EXECUTABLE_ALLOCATOR */
+
+/* Return with error when an invalid argument is passed. */
+#ifndef SLJIT_ARGUMENT_CHECKS
+/* Disabled by default */
+#define SLJIT_ARGUMENT_CHECKS 0
+#endif
+
+/* Debug checks (assertions, etc.). */
+#ifndef SLJIT_DEBUG
+/* Enabled by default */
+#define SLJIT_DEBUG 1
+#endif
+
+/* Verbose operations. */
+#ifndef SLJIT_VERBOSE
+/* Enabled by default */
+#define SLJIT_VERBOSE 1
+#endif
+
+/*
+ SLJIT_IS_FPU_AVAILABLE
+ The availability of the FPU can be controlled by SLJIT_IS_FPU_AVAILABLE.
+ zero value - FPU is NOT present.
+ nonzero value - FPU is present.
+*/
+
+/* For further configurations, see the beginning of sljitConfigInternal.h */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SLJIT_CONFIG_H_ */
diff --git a/contrib/libs/pcre2/src/sljit/sljitConfigInternal.h b/contrib/libs/pcre2/src/sljit/sljitConfigInternal.h
new file mode 100644
index 00000000000..cd3ce697346
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitConfigInternal.h
@@ -0,0 +1,851 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SLJIT_CONFIG_INTERNAL_H_
+#define SLJIT_CONFIG_INTERNAL_H_
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_DEBUG && SLJIT_DEBUG && (!defined(SLJIT_ASSERT) || !defined(SLJIT_UNREACHABLE)))
+#include <stdio.h>
+#endif
+
+#if (defined SLJIT_DEBUG && SLJIT_DEBUG \
+ && (!defined(SLJIT_ASSERT) || !defined(SLJIT_UNREACHABLE) || !defined(SLJIT_HALT_PROCESS)))
+#include <stdlib.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ SLJIT defines the following architecture dependent types and macros:
+
+ Types:
+ sljit_s8, sljit_u8 : signed and unsigned 8 bit integer type
+ sljit_s16, sljit_u16 : signed and unsigned 16 bit integer type
+ sljit_s32, sljit_u32 : signed and unsigned 32 bit integer type
+ sljit_sw, sljit_uw : signed and unsigned machine word, enough to store a pointer
+ sljit_p : unsgined pointer value (usually the same as sljit_uw, but
+ some 64 bit ABIs may use 32 bit pointers)
+ sljit_f32 : 32 bit single precision floating point value
+ sljit_f64 : 64 bit double precision floating point value
+
+ Macros for feature detection (boolean):
+ SLJIT_32BIT_ARCHITECTURE : 32 bit architecture
+ SLJIT_64BIT_ARCHITECTURE : 64 bit architecture
+ SLJIT_LITTLE_ENDIAN : little endian architecture
+ SLJIT_BIG_ENDIAN : big endian architecture
+ SLJIT_UNALIGNED : unaligned memory accesses for non-fpu operations are supported
+ SLJIT_FPU_UNALIGNED : unaligned memory accesses for fpu operations are supported
+ SLJIT_INDIRECT_CALL : see SLJIT_FUNC_ADDR() for more information
+
+ Constants:
+ SLJIT_NUMBER_OF_REGISTERS : number of available registers
+ SLJIT_NUMBER_OF_SCRATCH_REGISTERS : number of available scratch registers
+ SLJIT_NUMBER_OF_SAVED_REGISTERS : number of available saved registers
+ SLJIT_NUMBER_OF_FLOAT_REGISTERS : number of available floating point registers
+ SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS : number of available floating point scratch registers
+ SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS : number of available floating point saved registers
+ SLJIT_WORD_SHIFT : the shift required to apply when accessing a sljit_sw/sljit_uw array by index
+ SLJIT_F32_SHIFT : the shift required to apply when accessing
+ a single precision floating point array by index
+ SLJIT_F64_SHIFT : the shift required to apply when accessing
+ a double precision floating point array by index
+ SLJIT_PREF_SHIFT_REG : x86 systems prefers ecx for shifting by register
+ the scratch register index of ecx is stored in this variable
+ SLJIT_LOCALS_OFFSET : local space starting offset (SLJIT_SP + SLJIT_LOCALS_OFFSET)
+ SLJIT_RETURN_ADDRESS_OFFSET : a return instruction always adds this offset to the return address
+
+ Other macros:
+ SLJIT_FUNC : calling convention attribute for both calling JIT from C and C calling back from JIT
+ SLJIT_W(number) : defining 64 bit constants on 64 bit architectures (platform independent helper)
+*/
+
+/*****************/
+/* Sanity check. */
+/*****************/
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ + (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ + (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) \
+ + (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ + (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ + (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ + (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ + (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ + (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ + (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ + (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) \
+ + (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) \
+ + (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) \
+ + (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \
+ + (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) >= 2
+#error "Multiple architectures are selected"
+#endif
+
+#if !(defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ && !(defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ && !(defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) \
+ && !(defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ && !(defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ && !(defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ && !(defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ && !(defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ && !(defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ && !(defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) \
+ && !(defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) \
+ && !(defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) \
+ && !(defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) \
+ && !(defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO)
+#if defined SLJIT_CONFIG_AUTO && !SLJIT_CONFIG_AUTO
+#error "An architecture must be selected"
+#else /* SLJIT_CONFIG_AUTO */
+#define SLJIT_CONFIG_AUTO 1
+#endif /* !SLJIT_CONFIG_AUTO */
+#endif /* !SLJIT_CONFIG */
+
+/********************************************************/
+/* Automatic CPU detection (requires compiler support). */
+/********************************************************/
+
+#if (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO)
+
+#ifndef _WIN32
+
+#if defined(__i386__) || defined(__i386)
+#define SLJIT_CONFIG_X86_32 1
+#elif defined(__x86_64__)
+#define SLJIT_CONFIG_X86_64 1
+#elif defined(__arm__) || defined(__ARM__)
+#ifdef __thumb2__
+#define SLJIT_CONFIG_ARM_THUMB2 1
+#elif defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__)
+#define SLJIT_CONFIG_ARM_V7 1
+#else
+#define SLJIT_CONFIG_ARM_V5 1
+#endif
+#elif defined (__aarch64__)
+#define SLJIT_CONFIG_ARM_64 1
+#elif defined(__ppc64__) || defined(__powerpc64__) || (defined(_ARCH_PPC64) && defined(__64BIT__)) || (defined(_POWER) && defined(__64BIT__))
+#define SLJIT_CONFIG_PPC_64 1
+#elif defined(__ppc__) || defined(__powerpc__) || defined(_ARCH_PPC) || defined(_ARCH_PWR) || defined(_ARCH_PWR2) || defined(_POWER)
+#define SLJIT_CONFIG_PPC_32 1
+#elif defined(__mips__) && !defined(_LP64)
+#define SLJIT_CONFIG_MIPS_32 1
+#elif defined(__mips64)
+#define SLJIT_CONFIG_MIPS_64 1
+#elif defined (__riscv_xlen) && (__riscv_xlen == 32)
+#define SLJIT_CONFIG_RISCV_32 1
+#elif defined (__riscv_xlen) && (__riscv_xlen == 64)
+#define SLJIT_CONFIG_RISCV_64 1
+#elif defined(__s390x__)
+#define SLJIT_CONFIG_S390X 1
+#else
+/* Unsupported architecture */
+#define SLJIT_CONFIG_UNSUPPORTED 1
+#endif
+
+#else /* _WIN32 */
+
+#if defined(_M_X64) || defined(__x86_64__)
+#define SLJIT_CONFIG_X86_64 1
+#elif (defined(_M_ARM) && _M_ARM >= 7 && defined(_M_ARMT)) || defined(__thumb2__)
+#define SLJIT_CONFIG_ARM_THUMB2 1
+#elif (defined(_M_ARM) && _M_ARM >= 7)
+#define SLJIT_CONFIG_ARM_V7 1
+#elif defined(_ARM_)
+#define SLJIT_CONFIG_ARM_V5 1
+#elif defined(_M_ARM64) || defined(__aarch64__)
+#define SLJIT_CONFIG_ARM_64 1
+#else
+#define SLJIT_CONFIG_X86_32 1
+#endif
+
+#endif /* !_WIN32 */
+#endif /* SLJIT_CONFIG_AUTO */
+
+#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+#undef SLJIT_EXECUTABLE_ALLOCATOR
+#endif
+
+/******************************/
+/* CPU family type detection. */
+/******************************/
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+#define SLJIT_CONFIG_ARM_32 1
+#endif
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+#define SLJIT_CONFIG_X86 1
+#elif (defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+#define SLJIT_CONFIG_ARM 1
+#elif (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define SLJIT_CONFIG_PPC 1
+#elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+#define SLJIT_CONFIG_MIPS 1
+#elif (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) || (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+#define SLJIT_CONFIG_RISCV 1
+#endif
+
+/***********************************************************/
+/* Intel Control-flow Enforcement Technology (CET) spport. */
+/***********************************************************/
+
+#ifdef SLJIT_CONFIG_X86
+
+#if defined(__CET__) && !(defined SLJIT_CONFIG_X86_CET && SLJIT_CONFIG_X86_CET)
+#define SLJIT_CONFIG_X86_CET 1
+#endif
+
+#if (defined SLJIT_CONFIG_X86_CET && SLJIT_CONFIG_X86_CET) && defined(__GNUC__)
+#include <x86intrin.h>
+#endif
+
+#endif /* SLJIT_CONFIG_X86 */
+
+/**********************************/
+/* External function definitions. */
+/**********************************/
+
+/* General macros:
+ Note: SLJIT is designed to be independent from them as possible.
+
+ In release mode (SLJIT_DEBUG is not defined) only the following
+ external functions are needed:
+*/
+
+#ifndef SLJIT_MALLOC
+#define SLJIT_MALLOC(size, allocator_data) malloc(size)
+#endif
+
+#ifndef SLJIT_FREE
+#define SLJIT_FREE(ptr, allocator_data) free(ptr)
+#endif
+
+#ifndef SLJIT_MEMCPY
+#define SLJIT_MEMCPY(dest, src, len) memcpy(dest, src, len)
+#endif
+
+#ifndef SLJIT_MEMMOVE
+#define SLJIT_MEMMOVE(dest, src, len) memmove(dest, src, len)
+#endif
+
+#ifndef SLJIT_ZEROMEM
+#define SLJIT_ZEROMEM(dest, len) memset(dest, 0, len)
+#endif
+
+/***************************/
+/* Compiler helper macros. */
+/***************************/
+
+#if !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY)
+
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+#define SLJIT_LIKELY(x) __builtin_expect((x), 1)
+#define SLJIT_UNLIKELY(x) __builtin_expect((x), 0)
+#else
+#define SLJIT_LIKELY(x) (x)
+#define SLJIT_UNLIKELY(x) (x)
+#endif
+
+#endif /* !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY) */
+
+#ifndef SLJIT_INLINE
+/* Inline functions. Some old compilers do not support them. */
+#ifdef __SUNPRO_C
+#if __SUNPRO_C < 0x560
+#define SLJIT_INLINE
+#else
+#define SLJIT_INLINE inline
+#endif /* __SUNPRO_C */
+#else
+#define SLJIT_INLINE __inline
+#endif
+#endif /* !SLJIT_INLINE */
+
+#ifndef SLJIT_NOINLINE
+/* Not inline functions. */
+#if defined(__GNUC__)
+#define SLJIT_NOINLINE __attribute__ ((noinline))
+#else
+#define SLJIT_NOINLINE
+#endif
+#endif /* !SLJIT_INLINE */
+
+#ifndef SLJIT_UNUSED_ARG
+/* Unused arguments. */
+#define SLJIT_UNUSED_ARG(arg) (void)arg
+#endif
+
+/*********************************/
+/* Type of public API functions. */
+/*********************************/
+
+#ifndef SLJIT_API_FUNC_ATTRIBUTE
+#if (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC)
+/* Static ABI functions. For all-in-one programs. */
+
+#if defined(__GNUC__)
+/* Disable unused warnings in gcc. */
+#define SLJIT_API_FUNC_ATTRIBUTE static __attribute__((unused))
+#else
+#define SLJIT_API_FUNC_ATTRIBUTE static
+#endif
+
+#else
+#define SLJIT_API_FUNC_ATTRIBUTE
+#endif /* (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC) */
+#endif /* defined SLJIT_API_FUNC_ATTRIBUTE */
+
+/****************************/
+/* Instruction cache flush. */
+/****************************/
+
+/*
+ * TODO:
+ *
+ * clang >= 15 could be safe to enable below
+ * older versions are known to abort in some targets
+ * https://github.com/PhilipHazel/pcre2/issues/92
+ *
+ * beware some vendors (ex: Microsoft, Apple) are known to have
+ * removed the code to support this builtin even if the call for
+ * __has_builtin reports it is available.
+ *
+ * make sure linking doesn't fail because __clear_cache() is
+ * missing before changing it or add an exception so that the
+ * system provided method that should be defined below is used
+ * instead.
+ */
+#if (!defined SLJIT_CACHE_FLUSH && defined __has_builtin)
+#if __has_builtin(__builtin___clear_cache) && !defined(__clang__)
+
+/*
+ * https://gcc.gnu.org/bugzilla//show_bug.cgi?id=91248
+ * https://gcc.gnu.org/bugzilla//show_bug.cgi?id=93811
+ * gcc's clear_cache builtin for power is broken
+ */
+#if !defined(SLJIT_CONFIG_PPC)
+#define SLJIT_CACHE_FLUSH(from, to) \
+ __builtin___clear_cache((char*)(from), (char*)(to))
+#endif
+
+#endif /* gcc >= 10 */
+#endif /* (!defined SLJIT_CACHE_FLUSH && defined __has_builtin) */
+
+#ifndef SLJIT_CACHE_FLUSH
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
+ || (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+
+/* Not required to implement on archs with unified caches. */
+#define SLJIT_CACHE_FLUSH(from, to)
+
+#elif defined __APPLE__
+
+/* Supported by all macs since Mac OS 10.5.
+ However, it does not work on non-jailbroken iOS devices,
+ although the compilation is successful. */
+#include <libkern/OSCacheControl.h>
+#define SLJIT_CACHE_FLUSH(from, to) \
+ sys_icache_invalidate((void*)(from), (size_t)((char*)(to) - (char*)(from)))
+
+#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+/* The __clear_cache() implementation of GCC is a dummy function on PowerPC. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ ppc_cache_flush((from), (to))
+#define SLJIT_CACHE_FLUSH_OWN_IMPL 1
+
+#elif defined(_WIN32)
+
+#define SLJIT_CACHE_FLUSH(from, to) \
+ FlushInstructionCache(GetCurrentProcess(), (void*)(from), (char*)(to) - (char*)(from))
+
+#elif (defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) || defined(__clang__)
+
+#define SLJIT_CACHE_FLUSH(from, to) \
+ __builtin___clear_cache((char*)(from), (char*)(to))
+
+#elif defined __ANDROID__
+
+/* Android ARMv7 with gcc lacks __clear_cache; use cacheflush instead. */
+#include <sys/cachectl.h>
+#define SLJIT_CACHE_FLUSH(from, to) \
+ cacheflush((long)(from), (long)(to), 0)
+
+#else
+
+/* Call __ARM_NR_cacheflush on ARM-Linux or the corresponding MIPS syscall. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ __clear_cache((char*)(from), (char*)(to))
+
+#endif
+
+#endif /* !SLJIT_CACHE_FLUSH */
+
+/******************************************************/
+/* Integer and floating point type definitions. */
+/******************************************************/
+
+/* 8 bit byte type. */
+typedef unsigned char sljit_u8;
+typedef signed char sljit_s8;
+
+/* 16 bit half-word type. */
+typedef unsigned short int sljit_u16;
+typedef signed short int sljit_s16;
+
+/* 32 bit integer type. */
+typedef unsigned int sljit_u32;
+typedef signed int sljit_s32;
+
+/* Machine word type. Enough for storing a pointer.
+ 32 bit for 32 bit machines.
+ 64 bit for 64 bit machines. */
+#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+/* Just to have something. */
+#define SLJIT_WORD_SHIFT 0
+typedef unsigned long int sljit_uw;
+typedef long int sljit_sw;
+#elif !(defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ && !(defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ && !(defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ && !(defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) \
+ && !(defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+#define SLJIT_32BIT_ARCHITECTURE 1
+#define SLJIT_WORD_SHIFT 2
+typedef unsigned int sljit_uw;
+typedef int sljit_sw;
+#else
+#define SLJIT_64BIT_ARCHITECTURE 1
+#define SLJIT_WORD_SHIFT 3
+#ifdef _WIN32
+#ifdef __GNUC__
+/* These types do not require windows.h */
+typedef unsigned long long sljit_uw;
+typedef long long sljit_sw;
+#else
+typedef unsigned __int64 sljit_uw;
+typedef __int64 sljit_sw;
+#endif
+#else /* !_WIN32 */
+typedef unsigned long int sljit_uw;
+typedef long int sljit_sw;
+#endif /* _WIN32 */
+#endif
+
+typedef sljit_uw sljit_p;
+
+/* Floating point types. */
+typedef float sljit_f32;
+typedef double sljit_f64;
+
+/* Shift for pointer sized data. */
+#define SLJIT_POINTER_SHIFT SLJIT_WORD_SHIFT
+
+/* Shift for double precision sized data. */
+#define SLJIT_F32_SHIFT 2
+#define SLJIT_F64_SHIFT 3
+
+#ifndef SLJIT_W
+
+/* Defining long constants. */
+#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+#define SLJIT_W(w) (w##l)
+#elif (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+#ifdef _WIN64
+#define SLJIT_W(w) (w##ll)
+#else /* !windows */
+#define SLJIT_W(w) (w##l)
+#endif /* windows */
+#else /* 32 bit */
+#define SLJIT_W(w) (w)
+#endif /* unknown */
+
+#endif /* !SLJIT_W */
+
+/*************************/
+/* Endianness detection. */
+/*************************/
+
+#if !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN)
+
+/* These macros are mostly useful for the applications. */
+#if (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+#ifdef __LITTLE_ENDIAN__
+#define SLJIT_LITTLE_ENDIAN 1
+#else
+#define SLJIT_BIG_ENDIAN 1
+#endif
+
+#elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+
+#ifdef __MIPSEL__
+#define SLJIT_LITTLE_ENDIAN 1
+#else
+#define SLJIT_BIG_ENDIAN 1
+#endif
+
+#ifndef SLJIT_MIPS_REV
+
+/* Auto detecting mips revision. */
+#if (defined __mips_isa_rev) && (__mips_isa_rev >= 6)
+#define SLJIT_MIPS_REV 6
+#elif (defined __mips_isa_rev && __mips_isa_rev >= 1) \
+ || (defined __clang__ && defined _MIPS_ARCH_OCTEON) \
+ || (defined __clang__ && defined _MIPS_ARCH_P5600)
+/* clang either forgets to define (clang-7) __mips_isa_rev at all
+ * or sets it to zero (clang-8,-9) for -march=octeon (MIPS64 R2+)
+ * and -march=p5600 (MIPS32 R5).
+ * It also sets the __mips macro to 64 or 32 for -mipsN when N <= 5
+ * (should be set to N exactly) so we cannot rely on this too.
+ */
+#define SLJIT_MIPS_REV 1
+#endif
+
+#endif /* !SLJIT_MIPS_REV */
+
+#elif (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+
+#define SLJIT_BIG_ENDIAN 1
+
+#else
+#define SLJIT_LITTLE_ENDIAN 1
+#endif
+
+#endif /* !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN) */
+
+/* Sanity check. */
+#if (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#error "Exactly one endianness must be selected"
+#endif
+
+#if !(defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && !(defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#error "Exactly one endianness must be selected"
+#endif
+
+#ifndef SLJIT_UNALIGNED
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
+ || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ || (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \
+ || (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) \
+ || (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+#define SLJIT_UNALIGNED 1
+#endif
+
+#endif /* !SLJIT_UNALIGNED */
+
+#ifndef SLJIT_FPU_UNALIGNED
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
+ || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ || (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \
+ || (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) \
+ || (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+#define SLJIT_FPU_UNALIGNED 1
+#endif
+
+#endif /* !SLJIT_FPU_UNALIGNED */
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+/* Auto detect SSE2 support using CPUID.
+ On 64 bit x86 cpus, sse2 must be present. */
+#define SLJIT_DETECT_SSE2 1
+#endif
+
+/*****************************************************************************************/
+/* Calling convention of functions generated by SLJIT or called from the generated code. */
+/*****************************************************************************************/
+
+#ifndef SLJIT_FUNC
+#define SLJIT_FUNC
+#endif /* !SLJIT_FUNC */
+
+#ifndef SLJIT_INDIRECT_CALL
+#if ((defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) && (!defined _CALL_ELF || _CALL_ELF == 1)) \
+ || ((defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) && defined _AIX)
+/* It seems certain ppc compilers use an indirect addressing for functions
+ which makes things complicated. */
+#define SLJIT_INDIRECT_CALL 1
+#endif
+#endif /* SLJIT_INDIRECT_CALL */
+
+/* The offset which needs to be subtracted from the return address to
+determine the next executed instruction after return. */
+#ifndef SLJIT_RETURN_ADDRESS_OFFSET
+#define SLJIT_RETURN_ADDRESS_OFFSET 0
+#endif /* SLJIT_RETURN_ADDRESS_OFFSET */
+
+/***************************************************/
+/* Functions of the built-in executable allocator. */
+/***************************************************/
+
+#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size);
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr);
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void);
+#define SLJIT_BUILTIN_MALLOC_EXEC(size, exec_allocator_data) sljit_malloc_exec(size)
+#define SLJIT_BUILTIN_FREE_EXEC(ptr, exec_allocator_data) sljit_free_exec(ptr)
+
+#ifndef SLJIT_MALLOC_EXEC
+#define SLJIT_MALLOC_EXEC(size, exec_allocator_data) SLJIT_BUILTIN_MALLOC_EXEC((size), (exec_allocator_data))
+#endif /* SLJIT_MALLOC_EXEC */
+
+#ifndef SLJIT_FREE_EXEC
+#define SLJIT_FREE_EXEC(ptr, exec_allocator_data) SLJIT_BUILTIN_FREE_EXEC((ptr), (exec_allocator_data))
+#endif /* SLJIT_FREE_EXEC */
+
+#if (defined SLJIT_PROT_EXECUTABLE_ALLOCATOR && SLJIT_PROT_EXECUTABLE_ALLOCATOR)
+SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr);
+#define SLJIT_EXEC_OFFSET(ptr) sljit_exec_offset(ptr)
+#else
+#define SLJIT_EXEC_OFFSET(ptr) 0
+#endif
+
+#endif /* SLJIT_EXECUTABLE_ALLOCATOR */
+
+/**********************************************/
+/* Registers and locals offset determination. */
+/**********************************************/
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 7
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 0
+#define SLJIT_LOCALS_OFFSET_BASE (8 * SSIZE_OF(sw))
+#define SLJIT_PREF_SHIFT_REG SLJIT_R2
+
+#elif (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+
+#define SLJIT_NUMBER_OF_REGISTERS 13
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 15
+#ifndef _WIN64
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 6
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 0
+#define SLJIT_LOCALS_OFFSET_BASE 0
+#else /* _WIN64 */
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 10
+#define SLJIT_LOCALS_OFFSET_BASE (4 * SSIZE_OF(sw))
+#endif /* !_WIN64 */
+#define SLJIT_PREF_SHIFT_REG SLJIT_R3
+
+#elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 14
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#elif (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 14
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#elif (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+
+#define SLJIT_NUMBER_OF_REGISTERS 26
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 10
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 30
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE (2 * (sljit_s32)sizeof(sljit_sw))
+
+#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+#define SLJIT_NUMBER_OF_REGISTERS 23
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 17
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 30
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 18
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) || (defined _AIX)
+#define SLJIT_LOCALS_OFFSET_BASE ((6 + 8) * (sljit_s32)sizeof(sljit_sw))
+#elif (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+/* Add +1 for double alignment. */
+#define SLJIT_LOCALS_OFFSET_BASE ((3 + 1) * (sljit_s32)sizeof(sljit_sw))
+#else
+#define SLJIT_LOCALS_OFFSET_BASE (3 * (sljit_s32)sizeof(sljit_sw))
+#endif /* SLJIT_CONFIG_PPC_64 || _AIX */
+
+#elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+
+#define SLJIT_NUMBER_OF_REGISTERS 21
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#define SLJIT_LOCALS_OFFSET_BASE (4 * (sljit_s32)sizeof(sljit_sw))
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 13
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 6
+#else
+#define SLJIT_LOCALS_OFFSET_BASE 0
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 29
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 8
+#endif
+
+#elif (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV)
+
+#define SLJIT_NUMBER_OF_REGISTERS 23
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 12
+#define SLJIT_LOCALS_OFFSET_BASE 0
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 30
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 12
+
+#elif (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+
+/*
+ * https://refspecs.linuxbase.org/ELF/zSeries/lzsabi0_zSeries.html#STACKFRAME
+ *
+ * 160
+ * .. FR6
+ * .. FR4
+ * .. FR2
+ * 128 FR0
+ * 120 R15 (used for SP)
+ * 112 R14
+ * 104 R13
+ * 96 R12
+ * ..
+ * 48 R6
+ * ..
+ * 16 R2
+ * 8 RESERVED
+ * 0 SP
+ */
+#define SLJIT_S390X_DEFAULT_STACK_FRAME_SIZE 160
+
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 15
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE SLJIT_S390X_DEFAULT_STACK_FRAME_SIZE
+
+#elif (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+
+#define SLJIT_NUMBER_OF_REGISTERS 0
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 0
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 0
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 0
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#endif
+
+#define SLJIT_LOCALS_OFFSET (SLJIT_LOCALS_OFFSET_BASE)
+
+#define SLJIT_NUMBER_OF_SCRATCH_REGISTERS \
+ (SLJIT_NUMBER_OF_REGISTERS - SLJIT_NUMBER_OF_SAVED_REGISTERS)
+
+#define SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS \
+ (SLJIT_NUMBER_OF_FLOAT_REGISTERS - SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS)
+
+/********************************/
+/* CPU status flags management. */
+/********************************/
+
+#if (defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM) \
+ || (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \
+ || (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) \
+ || (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) \
+ || (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+#define SLJIT_HAS_STATUS_FLAGS_STATE 1
+#endif
+
+/*************************************/
+/* Debug and verbose related macros. */
+/*************************************/
+
+#if (defined SLJIT_DEBUG && SLJIT_DEBUG)
+
+#if !defined(SLJIT_ASSERT) || !defined(SLJIT_UNREACHABLE)
+
+/* SLJIT_HALT_PROCESS must halt the process. */
+#ifndef SLJIT_HALT_PROCESS
+#define SLJIT_HALT_PROCESS() \
+ abort();
+#endif /* !SLJIT_HALT_PROCESS */
+
+#endif /* !SLJIT_ASSERT || !SLJIT_UNREACHABLE */
+
+/* Feel free to redefine these two macros. */
+#ifndef SLJIT_ASSERT
+
+#define SLJIT_ASSERT(x) \
+ do { \
+ if (SLJIT_UNLIKELY(!(x))) { \
+ printf("Assertion failed at " __FILE__ ":%d\n", __LINE__); \
+ SLJIT_HALT_PROCESS(); \
+ } \
+ } while (0)
+
+#endif /* !SLJIT_ASSERT */
+
+#ifndef SLJIT_UNREACHABLE
+
+#define SLJIT_UNREACHABLE() \
+ do { \
+ printf("Should never been reached " __FILE__ ":%d\n", __LINE__); \
+ SLJIT_HALT_PROCESS(); \
+ } while (0)
+
+#endif /* !SLJIT_UNREACHABLE */
+
+#else /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
+
+/* Forcing empty, but valid statements. */
+#undef SLJIT_ASSERT
+#undef SLJIT_UNREACHABLE
+
+#define SLJIT_ASSERT(x) \
+ do { } while (0)
+#define SLJIT_UNREACHABLE() \
+ do { } while (0)
+
+#endif /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
+
+#ifndef SLJIT_COMPILE_ASSERT
+
+#define SLJIT_COMPILE_ASSERT(x, description) \
+ switch(0) { case 0: case ((x) ? 1 : 0): break; }
+
+#endif /* !SLJIT_COMPILE_ASSERT */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SLJIT_CONFIG_INTERNAL_H_ */
diff --git a/contrib/libs/pcre2/src/sljit/sljitExecAllocator.c b/contrib/libs/pcre2/src/sljit/sljitExecAllocator.c
new file mode 100644
index 00000000000..92d940ddc24
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitExecAllocator.c
@@ -0,0 +1,411 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ This file contains a simple executable memory allocator
+
+ It is assumed, that executable code blocks are usually medium (or sometimes
+ large) memory blocks, and the allocator is not too frequently called (less
+ optimized than other allocators). Thus, using it as a generic allocator is
+ not suggested.
+
+ How does it work:
+ Memory is allocated in continuous memory areas called chunks by alloc_chunk()
+ Chunk format:
+ [ block ][ block ] ... [ block ][ block terminator ]
+
+ All blocks and the block terminator is started with block_header. The block
+ header contains the size of the previous and the next block. These sizes
+ can also contain special values.
+ Block size:
+ 0 - The block is a free_block, with a different size member.
+ 1 - The block is a block terminator.
+ n - The block is used at the moment, and the value contains its size.
+ Previous block size:
+ 0 - This is the first block of the memory chunk.
+ n - The size of the previous block.
+
+ Using these size values we can go forward or backward on the block chain.
+ The unused blocks are stored in a chain list pointed by free_blocks. This
+ list is useful if we need to find a suitable memory area when the allocator
+ is called.
+
+ When a block is freed, the new free block is connected to its adjacent free
+ blocks if possible.
+
+ [ free block ][ used block ][ free block ]
+ and "used block" is freed, the three blocks are connected together:
+ [ one big free block ]
+*/
+
+/* --------------------------------------------------------------------- */
+/* System (OS) functions */
+/* --------------------------------------------------------------------- */
+
+/* 64 KByte. */
+#define CHUNK_SIZE (sljit_uw)0x10000u
+
+/*
+ alloc_chunk / free_chunk :
+ * allocate executable system memory chunks
+ * the size is always divisible by CHUNK_SIZE
+ SLJIT_ALLOCATOR_LOCK / SLJIT_ALLOCATOR_UNLOCK :
+ * provided as part of sljitUtils
+ * only the allocator requires this lock, sljit is fully thread safe
+ as it only uses local variables
+*/
+
+#ifdef _WIN32
+#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec)
+
+static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
+{
+ return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+}
+
+static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
+{
+ SLJIT_UNUSED_ARG(size);
+ VirtualFree(chunk, 0, MEM_RELEASE);
+}
+
+#else /* POSIX */
+
+#if defined(__APPLE__) && defined(MAP_JIT)
+/*
+ On macOS systems, returns MAP_JIT if it is defined _and_ we're running on a
+ version where it's OK to have more than one JIT block or where MAP_JIT is
+ required.
+ On non-macOS systems, returns MAP_JIT if it is defined.
+*/
+#include <TargetConditionals.h>
+#if TARGET_OS_OSX
+#if defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86
+#ifdef MAP_ANON
+#include <sys/utsname.h>
+#include <stdlib.h>
+
+#define SLJIT_MAP_JIT (get_map_jit_flag())
+
+static SLJIT_INLINE int get_map_jit_flag()
+{
+ size_t page_size;
+ void *ptr;
+ struct utsname name;
+ static int map_jit_flag = -1;
+
+ if (map_jit_flag < 0) {
+ map_jit_flag = 0;
+ uname(&name);
+
+ /* Kernel version for 10.14.0 (Mojave) or later */
+ if (atoi(name.release) >= 18) {
+ page_size = get_page_alignment() + 1;
+ /* Only use MAP_JIT if a hardened runtime is used */
+ ptr = mmap(NULL, page_size, PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+
+ if (ptr != MAP_FAILED)
+ munmap(ptr, page_size);
+ else
+ map_jit_flag = MAP_JIT;
+ }
+ }
+ return map_jit_flag;
+}
+#endif /* MAP_ANON */
+#else /* !SLJIT_CONFIG_X86 */
+#if !(defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM)
+#error "Unsupported architecture"
+#endif /* SLJIT_CONFIG_ARM */
+#include <AvailabilityMacros.h>
+#include <pthread.h>
+
+#define SLJIT_MAP_JIT (MAP_JIT)
+#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec) \
+ apple_update_wx_flags(enable_exec)
+
+static SLJIT_INLINE void apple_update_wx_flags(sljit_s32 enable_exec)
+{
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 110000
+ pthread_jit_write_protect_np(enable_exec);
+#else
+#error "Must target Big Sur or newer"
+#endif /* BigSur */
+}
+#endif /* SLJIT_CONFIG_X86 */
+#else /* !TARGET_OS_OSX */
+#define SLJIT_MAP_JIT (MAP_JIT)
+#endif /* TARGET_OS_OSX */
+#endif /* __APPLE__ && MAP_JIT */
+#ifndef SLJIT_UPDATE_WX_FLAGS
+#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec)
+#endif /* !SLJIT_UPDATE_WX_FLAGS */
+#ifndef SLJIT_MAP_JIT
+#define SLJIT_MAP_JIT (0)
+#endif /* !SLJIT_MAP_JIT */
+
+static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
+{
+ void *retval;
+ int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
+ int flags = MAP_PRIVATE;
+ int fd = -1;
+
+#ifdef PROT_MAX
+ prot |= PROT_MAX(prot);
+#endif
+
+#ifdef MAP_ANON
+ flags |= MAP_ANON | SLJIT_MAP_JIT;
+#else /* !MAP_ANON */
+ if (SLJIT_UNLIKELY((dev_zero < 0) && open_dev_zero()))
+ return NULL;
+
+ fd = dev_zero;
+#endif /* MAP_ANON */
+
+ retval = mmap(NULL, size, prot, flags, fd, 0);
+ if (retval == MAP_FAILED)
+ return NULL;
+
+#ifdef __FreeBSD__
+ /* HardenedBSD's mmap lies, so check permissions again */
+ if (mprotect(retval, size, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) {
+ munmap(retval, size);
+ return NULL;
+ }
+#endif /* FreeBSD */
+
+ SLJIT_UPDATE_WX_FLAGS(retval, (uint8_t *)retval + size, 0);
+
+ return retval;
+}
+
+static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
+{
+ munmap(chunk, size);
+}
+
+#endif /* windows */
+
+/* --------------------------------------------------------------------- */
+/* Common functions */
+/* --------------------------------------------------------------------- */
+
+#define CHUNK_MASK (~(CHUNK_SIZE - 1))
+
+struct block_header {
+ sljit_uw size;
+ sljit_uw prev_size;
+};
+
+struct free_block {
+ struct block_header header;
+ struct free_block *next;
+ struct free_block *prev;
+ sljit_uw size;
+};
+
+#define AS_BLOCK_HEADER(base, offset) \
+ ((struct block_header*)(((sljit_u8*)base) + offset))
+#define AS_FREE_BLOCK(base, offset) \
+ ((struct free_block*)(((sljit_u8*)base) + offset))
+#define MEM_START(base) ((void*)(((sljit_u8*)base) + sizeof(struct block_header)))
+#define ALIGN_SIZE(size) (((size) + sizeof(struct block_header) + 7u) & ~(sljit_uw)7)
+
+static struct free_block* free_blocks;
+static sljit_uw allocated_size;
+static sljit_uw total_size;
+
+static SLJIT_INLINE void sljit_insert_free_block(struct free_block *free_block, sljit_uw size)
+{
+ free_block->header.size = 0;
+ free_block->size = size;
+
+ free_block->next = free_blocks;
+ free_block->prev = NULL;
+ if (free_blocks)
+ free_blocks->prev = free_block;
+ free_blocks = free_block;
+}
+
+static SLJIT_INLINE void sljit_remove_free_block(struct free_block *free_block)
+{
+ if (free_block->next)
+ free_block->next->prev = free_block->prev;
+
+ if (free_block->prev)
+ free_block->prev->next = free_block->next;
+ else {
+ SLJIT_ASSERT(free_blocks == free_block);
+ free_blocks = free_block->next;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
+{
+ struct block_header *header;
+ struct block_header *next_header;
+ struct free_block *free_block;
+ sljit_uw chunk_size;
+
+ SLJIT_ALLOCATOR_LOCK();
+ if (size < (64 - sizeof(struct block_header)))
+ size = (64 - sizeof(struct block_header));
+ size = ALIGN_SIZE(size);
+
+ free_block = free_blocks;
+ while (free_block) {
+ if (free_block->size >= size) {
+ chunk_size = free_block->size;
+ SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 0);
+ if (chunk_size > size + 64) {
+ /* We just cut a block from the end of the free block. */
+ chunk_size -= size;
+ free_block->size = chunk_size;
+ header = AS_BLOCK_HEADER(free_block, chunk_size);
+ header->prev_size = chunk_size;
+ AS_BLOCK_HEADER(header, size)->prev_size = size;
+ }
+ else {
+ sljit_remove_free_block(free_block);
+ header = (struct block_header*)free_block;
+ size = chunk_size;
+ }
+ allocated_size += size;
+ header->size = size;
+ SLJIT_ALLOCATOR_UNLOCK();
+ return MEM_START(header);
+ }
+ free_block = free_block->next;
+ }
+
+ chunk_size = (size + sizeof(struct block_header) + CHUNK_SIZE - 1) & CHUNK_MASK;
+ header = (struct block_header*)alloc_chunk(chunk_size);
+ if (!header) {
+ SLJIT_ALLOCATOR_UNLOCK();
+ return NULL;
+ }
+
+ chunk_size -= sizeof(struct block_header);
+ total_size += chunk_size;
+
+ header->prev_size = 0;
+ if (chunk_size > size + 64) {
+ /* Cut the allocated space into a free and a used block. */
+ allocated_size += size;
+ header->size = size;
+ chunk_size -= size;
+
+ free_block = AS_FREE_BLOCK(header, size);
+ free_block->header.prev_size = size;
+ sljit_insert_free_block(free_block, chunk_size);
+ next_header = AS_BLOCK_HEADER(free_block, chunk_size);
+ }
+ else {
+ /* All space belongs to this allocation. */
+ allocated_size += chunk_size;
+ header->size = chunk_size;
+ next_header = AS_BLOCK_HEADER(header, chunk_size);
+ }
+ next_header->size = 1;
+ next_header->prev_size = chunk_size;
+ SLJIT_ALLOCATOR_UNLOCK();
+ return MEM_START(header);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
+{
+ struct block_header *header;
+ struct free_block* free_block;
+
+ SLJIT_ALLOCATOR_LOCK();
+ header = AS_BLOCK_HEADER(ptr, -(sljit_sw)sizeof(struct block_header));
+ allocated_size -= header->size;
+
+ /* Connecting free blocks together if possible. */
+ SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 0);
+
+ /* If header->prev_size == 0, free_block will equal to header.
+ In this case, free_block->header.size will be > 0. */
+ free_block = AS_FREE_BLOCK(header, -(sljit_sw)header->prev_size);
+ if (SLJIT_UNLIKELY(!free_block->header.size)) {
+ free_block->size += header->size;
+ header = AS_BLOCK_HEADER(free_block, free_block->size);
+ header->prev_size = free_block->size;
+ }
+ else {
+ free_block = (struct free_block*)header;
+ sljit_insert_free_block(free_block, header->size);
+ }
+
+ header = AS_BLOCK_HEADER(free_block, free_block->size);
+ if (SLJIT_UNLIKELY(!header->size)) {
+ free_block->size += ((struct free_block*)header)->size;
+ sljit_remove_free_block((struct free_block*)header);
+ header = AS_BLOCK_HEADER(free_block, free_block->size);
+ header->prev_size = free_block->size;
+ }
+
+ /* The whole chunk is free. */
+ if (SLJIT_UNLIKELY(!free_block->header.prev_size && header->size == 1)) {
+ /* If this block is freed, we still have (allocated_size / 2) free space. */
+ if (total_size - free_block->size > (allocated_size * 3 / 2)) {
+ total_size -= free_block->size;
+ sljit_remove_free_block(free_block);
+ free_chunk(free_block, free_block->size + sizeof(struct block_header));
+ }
+ }
+
+ SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 1);
+ SLJIT_ALLOCATOR_UNLOCK();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
+{
+ struct free_block* free_block;
+ struct free_block* next_free_block;
+
+ SLJIT_ALLOCATOR_LOCK();
+ SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 0);
+
+ free_block = free_blocks;
+ while (free_block) {
+ next_free_block = free_block->next;
+ if (!free_block->header.prev_size &&
+ AS_BLOCK_HEADER(free_block, free_block->size)->size == 1) {
+ total_size -= free_block->size;
+ sljit_remove_free_block(free_block);
+ free_chunk(free_block, free_block->size + sizeof(struct block_header));
+ }
+ free_block = next_free_block;
+ }
+
+ SLJIT_ASSERT((total_size && free_blocks) || (!total_size && !free_blocks));
+ SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 1);
+ SLJIT_ALLOCATOR_UNLOCK();
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitLir.c b/contrib/libs/pcre2/src/sljit/sljitLir.c
new file mode 100644
index 00000000000..abafe1add97
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitLir.c
@@ -0,0 +1,3136 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "sljitLir.h"
+
+#ifdef _WIN32
+
+#include <windows.h>
+
+#endif /* _WIN32 */
+
+#if !(defined SLJIT_STD_MACROS_DEFINED && SLJIT_STD_MACROS_DEFINED)
+
+/* These libraries are needed for the macros below. */
+#include <stdlib.h>
+#include <string.h>
+
+#endif /* SLJIT_STD_MACROS_DEFINED */
+
+#define CHECK_ERROR() \
+ do { \
+ if (SLJIT_UNLIKELY(compiler->error)) \
+ return compiler->error; \
+ } while (0)
+
+#define CHECK_ERROR_PTR() \
+ do { \
+ if (SLJIT_UNLIKELY(compiler->error)) \
+ return NULL; \
+ } while (0)
+
+#define FAIL_IF(expr) \
+ do { \
+ if (SLJIT_UNLIKELY(expr)) \
+ return compiler->error; \
+ } while (0)
+
+#define PTR_FAIL_IF(expr) \
+ do { \
+ if (SLJIT_UNLIKELY(expr)) \
+ return NULL; \
+ } while (0)
+
+#define FAIL_IF_NULL(ptr) \
+ do { \
+ if (SLJIT_UNLIKELY(!(ptr))) { \
+ compiler->error = SLJIT_ERR_ALLOC_FAILED; \
+ return SLJIT_ERR_ALLOC_FAILED; \
+ } \
+ } while (0)
+
+#define PTR_FAIL_IF_NULL(ptr) \
+ do { \
+ if (SLJIT_UNLIKELY(!(ptr))) { \
+ compiler->error = SLJIT_ERR_ALLOC_FAILED; \
+ return NULL; \
+ } \
+ } while (0)
+
+#define PTR_FAIL_WITH_EXEC_IF(ptr) \
+ do { \
+ if (SLJIT_UNLIKELY(!(ptr))) { \
+ compiler->error = SLJIT_ERR_EX_ALLOC_FAILED; \
+ return NULL; \
+ } \
+ } while (0)
+
+#if !(defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+
+#define SSIZE_OF(type) ((sljit_s32)sizeof(sljit_ ## type))
+
+#define VARIABLE_FLAG_SHIFT (10)
+#define VARIABLE_FLAG_MASK (0x3f << VARIABLE_FLAG_SHIFT)
+#define GET_FLAG_TYPE(op) ((op) >> VARIABLE_FLAG_SHIFT)
+
+#define GET_OPCODE(op) \
+ ((op) & ~(SLJIT_32 | SLJIT_SET_Z | VARIABLE_FLAG_MASK))
+
+#define HAS_FLAGS(op) \
+ ((op) & (SLJIT_SET_Z | VARIABLE_FLAG_MASK))
+
+#define GET_ALL_FLAGS(op) \
+ ((op) & (SLJIT_32 | SLJIT_SET_Z | VARIABLE_FLAG_MASK))
+
+#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+#define TYPE_CAST_NEEDED(op) \
+ ((op) >= SLJIT_MOV_U8 && (op) <= SLJIT_MOV_S32)
+#else /* !SLJIT_64BIT_ARCHITECTURE */
+#define TYPE_CAST_NEEDED(op) \
+ ((op) >= SLJIT_MOV_U8 && (op) <= SLJIT_MOV_S16)
+#endif /* SLJIT_64BIT_ARCHITECTURE */
+
+#define BUF_SIZE 4096
+
+#if (defined SLJIT_32BIT_ARCHITECTURE && SLJIT_32BIT_ARCHITECTURE)
+#define ABUF_SIZE 2048
+#else
+#define ABUF_SIZE 4096
+#endif
+
+/* Parameter parsing. */
+#define REG_MASK 0x3f
+#define OFFS_REG(reg) (((reg) >> 8) & REG_MASK)
+#define OFFS_REG_MASK (REG_MASK << 8)
+#define TO_OFFS_REG(reg) ((reg) << 8)
+/* When reg cannot be unused. */
+#define FAST_IS_REG(reg) ((reg) <= REG_MASK)
+
+/* Mask for argument types. */
+#define SLJIT_ARG_MASK 0x7
+#define SLJIT_ARG_FULL_MASK (SLJIT_ARG_MASK | SLJIT_ARG_TYPE_SCRATCH_REG)
+
+/* Mask for sljit_emit_mem. */
+#define REG_PAIR_MASK 0xff00
+#define REG_PAIR_FIRST(reg) ((reg) & 0xff)
+#define REG_PAIR_SECOND(reg) ((reg) >> 8)
+
+/* Mask for sljit_emit_enter. */
+#define SLJIT_KEPT_SAVEDS_COUNT(options) ((options) & 0x3)
+
+/* Jump flags. */
+#define JUMP_LABEL 0x1
+#define JUMP_ADDR 0x2
+/* SLJIT_REWRITABLE_JUMP is 0x1000. */
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+# define PATCH_MB 0x4
+# define PATCH_MW 0x8
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+# define PATCH_MD 0x10
+#endif
+# define TYPE_SHIFT 13
+#endif /* SLJIT_CONFIG_X86 */
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+# define IS_BL 0x4
+# define PATCH_B 0x8
+#endif /* SLJIT_CONFIG_ARM_V5 || SLJIT_CONFIG_ARM_V7 */
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+# define CPOOL_SIZE 512
+#endif /* SLJIT_CONFIG_ARM_V5 */
+
+#if (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+# define IS_COND 0x04
+# define IS_BL 0x08
+ /* conditional + imm8 */
+# define PATCH_TYPE1 0x10
+ /* conditional + imm20 */
+# define PATCH_TYPE2 0x20
+ /* IT + imm24 */
+# define PATCH_TYPE3 0x30
+ /* imm11 */
+# define PATCH_TYPE4 0x40
+ /* imm24 */
+# define PATCH_TYPE5 0x50
+ /* BL + imm24 */
+# define PATCH_BL 0x60
+ /* 0xf00 cc code for branches */
+#endif /* SLJIT_CONFIG_ARM_THUMB2 */
+
+#if (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+# define IS_COND 0x004
+# define IS_CBZ 0x008
+# define IS_BL 0x010
+# define PATCH_B 0x020
+# define PATCH_COND 0x040
+# define PATCH_ABS48 0x080
+# define PATCH_ABS64 0x100
+#endif /* SLJIT_CONFIG_ARM_64 */
+
+#if (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+# define IS_COND 0x004
+# define IS_CALL 0x008
+# define PATCH_B 0x010
+# define PATCH_ABS_B 0x020
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+# define PATCH_ABS32 0x040
+# define PATCH_ABS48 0x080
+#endif /* SLJIT_CONFIG_PPC_64 */
+# define REMOVE_COND 0x100
+#endif /* SLJIT_CONFIG_PPC */
+
+#if (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+# define IS_MOVABLE 0x004
+# define IS_JAL 0x008
+# define IS_CALL 0x010
+# define IS_BIT26_COND 0x020
+# define IS_BIT16_COND 0x040
+# define IS_BIT23_COND 0x080
+
+# define IS_COND (IS_BIT26_COND | IS_BIT16_COND | IS_BIT23_COND)
+
+# define PATCH_B 0x100
+# define PATCH_J 0x200
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+# define PATCH_ABS32 0x400
+# define PATCH_ABS48 0x800
+#endif /* SLJIT_CONFIG_MIPS_64 */
+
+ /* instruction types */
+# define MOVABLE_INS 0
+ /* 1 - 31 last destination register */
+ /* no destination (i.e: store) */
+# define UNMOVABLE_INS 32
+ /* FPU status register */
+# define FCSR_FCC 33
+#endif /* SLJIT_CONFIG_MIPS */
+
+#if (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV)
+# define IS_COND 0x004
+# define IS_CALL 0x008
+
+# define PATCH_B 0x010
+# define PATCH_J 0x020
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+# define PATCH_REL32 0x040
+# define PATCH_ABS32 0x080
+# define PATCH_ABS44 0x100
+# define PATCH_ABS52 0x200
+#else /* !SLJIT_CONFIG_RISCV_64 */
+# define PATCH_REL32 0x0
+#endif /* SLJIT_CONFIG_RISCV_64 */
+#endif /* SLJIT_CONFIG_RISCV */
+
+/* Stack management. */
+
+#define GET_SAVED_REGISTERS_SIZE(scratches, saveds, extra) \
+ (((scratches < SLJIT_NUMBER_OF_SCRATCH_REGISTERS ? 0 : (scratches - SLJIT_NUMBER_OF_SCRATCH_REGISTERS)) + \
+ (saveds) + (sljit_s32)(extra)) * (sljit_s32)sizeof(sljit_sw))
+
+#define GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, size) \
+ (((fscratches < SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS ? 0 : (fscratches - SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS)) + \
+ (fsaveds)) * (sljit_s32)(size))
+
+#define ADJUST_LOCAL_OFFSET(p, i) \
+ if ((p) == (SLJIT_MEM1(SLJIT_SP))) \
+ (i) += SLJIT_LOCALS_OFFSET;
+
+#endif /* !(defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) */
+
+/* Utils can still be used even if SLJIT_CONFIG_UNSUPPORTED is set. */
+#include "sljitUtils.c"
+
+#if !(defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+
+#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
+
+#if (defined SLJIT_PROT_EXECUTABLE_ALLOCATOR && SLJIT_PROT_EXECUTABLE_ALLOCATOR)
+#include "sljitProtExecAllocator.c"
+#elif (defined SLJIT_WX_EXECUTABLE_ALLOCATOR && SLJIT_WX_EXECUTABLE_ALLOCATOR)
+#include "sljitWXExecAllocator.c"
+#else
+#include "sljitExecAllocator.c"
+#endif
+
+#endif
+
+#if (defined SLJIT_PROT_EXECUTABLE_ALLOCATOR && SLJIT_PROT_EXECUTABLE_ALLOCATOR)
+#define SLJIT_ADD_EXEC_OFFSET(ptr, exec_offset) ((sljit_u8 *)(ptr) + (exec_offset))
+#else
+#define SLJIT_ADD_EXEC_OFFSET(ptr, exec_offset) ((sljit_u8 *)(ptr))
+#endif
+
+#ifndef SLJIT_UPDATE_WX_FLAGS
+#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec)
+#endif
+
+/* Argument checking features. */
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+
+/* Returns with error when an invalid argument is passed. */
+
+#define CHECK_ARGUMENT(x) \
+ do { \
+ if (SLJIT_UNLIKELY(!(x))) \
+ return 1; \
+ } while (0)
+
+#define CHECK_RETURN_TYPE sljit_s32
+#define CHECK_RETURN_OK return 0
+
+#define CHECK(x) \
+ do { \
+ if (SLJIT_UNLIKELY(x)) { \
+ compiler->error = SLJIT_ERR_BAD_ARGUMENT; \
+ return SLJIT_ERR_BAD_ARGUMENT; \
+ } \
+ } while (0)
+
+#define CHECK_PTR(x) \
+ do { \
+ if (SLJIT_UNLIKELY(x)) { \
+ compiler->error = SLJIT_ERR_BAD_ARGUMENT; \
+ return NULL; \
+ } \
+ } while (0)
+
+#define CHECK_REG_INDEX(x) \
+ do { \
+ if (SLJIT_UNLIKELY(x)) { \
+ return -2; \
+ } \
+ } while (0)
+
+#elif (defined SLJIT_DEBUG && SLJIT_DEBUG)
+
+/* Assertion failure occures if an invalid argument is passed. */
+#undef SLJIT_ARGUMENT_CHECKS
+#define SLJIT_ARGUMENT_CHECKS 1
+
+#define CHECK_ARGUMENT(x) SLJIT_ASSERT(x)
+#define CHECK_RETURN_TYPE void
+#define CHECK_RETURN_OK return
+#define CHECK(x) x
+#define CHECK_PTR(x) x
+#define CHECK_REG_INDEX(x) x
+
+#elif (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+
+/* Arguments are not checked. */
+#define CHECK_RETURN_TYPE void
+#define CHECK_RETURN_OK return
+#define CHECK(x) x
+#define CHECK_PTR(x) x
+#define CHECK_REG_INDEX(x) x
+
+#else
+
+/* Arguments are not checked. */
+#define CHECK(x)
+#define CHECK_PTR(x)
+#define CHECK_REG_INDEX(x)
+
+#endif /* SLJIT_ARGUMENT_CHECKS */
+
+/* --------------------------------------------------------------------- */
+/* Public functions */
+/* --------------------------------------------------------------------- */
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+#define SLJIT_NEEDS_COMPILER_INIT 1
+static sljit_s32 compiler_initialized = 0;
+/* A thread safe initialization. */
+static void init_compiler(void);
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void *allocator_data, void *exec_allocator_data)
+{
+ struct sljit_compiler *compiler = (struct sljit_compiler*)SLJIT_MALLOC(sizeof(struct sljit_compiler), allocator_data);
+ if (!compiler)
+ return NULL;
+ SLJIT_ZEROMEM(compiler, sizeof(struct sljit_compiler));
+
+ SLJIT_COMPILE_ASSERT(
+ sizeof(sljit_s8) == 1 && sizeof(sljit_u8) == 1
+ && sizeof(sljit_s16) == 2 && sizeof(sljit_u16) == 2
+ && sizeof(sljit_s32) == 4 && sizeof(sljit_u32) == 4
+ && (sizeof(sljit_p) == 4 || sizeof(sljit_p) == 8)
+ && sizeof(sljit_p) <= sizeof(sljit_sw)
+ && (sizeof(sljit_sw) == 4 || sizeof(sljit_sw) == 8)
+ && (sizeof(sljit_uw) == 4 || sizeof(sljit_uw) == 8),
+ invalid_integer_types);
+ SLJIT_COMPILE_ASSERT(SLJIT_REWRITABLE_JUMP != SLJIT_32,
+ rewritable_jump_and_single_op_must_not_be_the_same);
+ SLJIT_COMPILE_ASSERT(!(SLJIT_EQUAL & 0x1) && !(SLJIT_LESS & 0x1) && !(SLJIT_F_EQUAL & 0x1) && !(SLJIT_JUMP & 0x1),
+ conditional_flags_must_be_even_numbers);
+
+ /* Only the non-zero members must be set. */
+ compiler->error = SLJIT_SUCCESS;
+
+ compiler->allocator_data = allocator_data;
+ compiler->exec_allocator_data = exec_allocator_data;
+ compiler->buf = (struct sljit_memory_fragment*)SLJIT_MALLOC(BUF_SIZE, allocator_data);
+ compiler->abuf = (struct sljit_memory_fragment*)SLJIT_MALLOC(ABUF_SIZE, allocator_data);
+
+ if (!compiler->buf || !compiler->abuf) {
+ if (compiler->buf)
+ SLJIT_FREE(compiler->buf, allocator_data);
+ if (compiler->abuf)
+ SLJIT_FREE(compiler->abuf, allocator_data);
+ SLJIT_FREE(compiler, allocator_data);
+ return NULL;
+ }
+
+ compiler->buf->next = NULL;
+ compiler->buf->used_size = 0;
+ compiler->abuf->next = NULL;
+ compiler->abuf->used_size = 0;
+
+ compiler->scratches = -1;
+ compiler->saveds = -1;
+ compiler->fscratches = -1;
+ compiler->fsaveds = -1;
+ compiler->local_size = -1;
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ compiler->args_size = -1;
+#endif
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ compiler->cpool = (sljit_uw*)SLJIT_MALLOC(CPOOL_SIZE * sizeof(sljit_uw)
+ + CPOOL_SIZE * sizeof(sljit_u8), allocator_data);
+ if (!compiler->cpool) {
+ SLJIT_FREE(compiler->buf, allocator_data);
+ SLJIT_FREE(compiler->abuf, allocator_data);
+ SLJIT_FREE(compiler, allocator_data);
+ return NULL;
+ }
+ compiler->cpool_unique = (sljit_u8*)(compiler->cpool + CPOOL_SIZE);
+ compiler->cpool_diff = 0xffffffff;
+#endif
+
+#if (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+ compiler->delay_slot = UNMOVABLE_INS;
+#endif
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \
+ || (defined SLJIT_DEBUG && SLJIT_DEBUG)
+ compiler->last_flags = 0;
+ compiler->last_return = -1;
+ compiler->logical_local_size = 0;
+#endif
+
+#if (defined SLJIT_NEEDS_COMPILER_INIT && SLJIT_NEEDS_COMPILER_INIT)
+ if (!compiler_initialized) {
+ init_compiler();
+ compiler_initialized = 1;
+ }
+#endif
+
+ return compiler;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_compiler(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf;
+ struct sljit_memory_fragment *curr;
+ void *allocator_data = compiler->allocator_data;
+ SLJIT_UNUSED_ARG(allocator_data);
+
+ buf = compiler->buf;
+ while (buf) {
+ curr = buf;
+ buf = buf->next;
+ SLJIT_FREE(curr, allocator_data);
+ }
+
+ buf = compiler->abuf;
+ while (buf) {
+ curr = buf;
+ buf = buf->next;
+ SLJIT_FREE(curr, allocator_data);
+ }
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ SLJIT_FREE(compiler->cpool, allocator_data);
+#endif
+ SLJIT_FREE(compiler, allocator_data);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_compiler_memory_error(struct sljit_compiler *compiler)
+{
+ if (compiler->error == SLJIT_SUCCESS)
+ compiler->error = SLJIT_ERR_ALLOC_FAILED;
+}
+
+#if (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code, void *exec_allocator_data)
+{
+ SLJIT_UNUSED_ARG(exec_allocator_data);
+
+ /* Remove thumb mode flag. */
+ SLJIT_FREE_EXEC((void*)((sljit_uw)code & ~(sljit_uw)0x1), exec_allocator_data);
+}
+#elif (defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL)
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code, void *exec_allocator_data)
+{
+ SLJIT_UNUSED_ARG(exec_allocator_data);
+
+ /* Resolve indirection. */
+ code = (void*)(*(sljit_uw*)code);
+ SLJIT_FREE_EXEC(code, exec_allocator_data);
+}
+#else
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code, void *exec_allocator_data)
+{
+ SLJIT_UNUSED_ARG(exec_allocator_data);
+
+ SLJIT_FREE_EXEC(code, exec_allocator_data);
+}
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_label(struct sljit_jump *jump, struct sljit_label* label)
+{
+ if (SLJIT_LIKELY(!!jump) && SLJIT_LIKELY(!!label)) {
+ jump->flags &= (sljit_uw)~JUMP_ADDR;
+ jump->flags |= JUMP_LABEL;
+ jump->u.label = label;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_target(struct sljit_jump *jump, sljit_uw target)
+{
+ if (SLJIT_LIKELY(!!jump)) {
+ jump->flags &= (sljit_uw)~JUMP_LABEL;
+ jump->flags |= JUMP_ADDR;
+ jump->u.target = target;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_put_label(struct sljit_put_label *put_label, struct sljit_label *label)
+{
+ if (SLJIT_LIKELY(!!put_label))
+ put_label->label = label;
+}
+
+#define SLJIT_CURRENT_FLAGS_ALL \
+ (SLJIT_CURRENT_FLAGS_32 | SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB | SLJIT_CURRENT_FLAGS_COMPARE)
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_current_flags(struct sljit_compiler *compiler, sljit_s32 current_flags)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(current_flags);
+
+#if (defined SLJIT_HAS_STATUS_FLAGS_STATE && SLJIT_HAS_STATUS_FLAGS_STATE)
+ compiler->status_flags_state = current_flags;
+#endif
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->last_flags = 0;
+ if ((current_flags & ~(VARIABLE_FLAG_MASK | SLJIT_SET_Z | SLJIT_CURRENT_FLAGS_ALL)) == 0) {
+ compiler->last_flags = GET_FLAG_TYPE(current_flags) | (current_flags & (SLJIT_32 | SLJIT_SET_Z));
+ }
+#endif
+}
+
+/* --------------------------------------------------------------------- */
+/* Private functions */
+/* --------------------------------------------------------------------- */
+
+static void* ensure_buf(struct sljit_compiler *compiler, sljit_uw size)
+{
+ sljit_u8 *ret;
+ struct sljit_memory_fragment *new_frag;
+
+ SLJIT_ASSERT(size <= 256);
+ if (compiler->buf->used_size + size <= (BUF_SIZE - (sljit_uw)SLJIT_OFFSETOF(struct sljit_memory_fragment, memory))) {
+ ret = compiler->buf->memory + compiler->buf->used_size;
+ compiler->buf->used_size += size;
+ return ret;
+ }
+ new_frag = (struct sljit_memory_fragment*)SLJIT_MALLOC(BUF_SIZE, compiler->allocator_data);
+ PTR_FAIL_IF_NULL(new_frag);
+ new_frag->next = compiler->buf;
+ compiler->buf = new_frag;
+ new_frag->used_size = size;
+ return new_frag->memory;
+}
+
+static void* ensure_abuf(struct sljit_compiler *compiler, sljit_uw size)
+{
+ sljit_u8 *ret;
+ struct sljit_memory_fragment *new_frag;
+
+ SLJIT_ASSERT(size <= 256);
+ if (compiler->abuf->used_size + size <= (ABUF_SIZE - (sljit_uw)SLJIT_OFFSETOF(struct sljit_memory_fragment, memory))) {
+ ret = compiler->abuf->memory + compiler->abuf->used_size;
+ compiler->abuf->used_size += size;
+ return ret;
+ }
+ new_frag = (struct sljit_memory_fragment*)SLJIT_MALLOC(ABUF_SIZE, compiler->allocator_data);
+ PTR_FAIL_IF_NULL(new_frag);
+ new_frag->next = compiler->abuf;
+ compiler->abuf = new_frag;
+ new_frag->used_size = size;
+ return new_frag->memory;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_alloc_memory(struct sljit_compiler *compiler, sljit_s32 size)
+{
+ CHECK_ERROR_PTR();
+
+#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+ if (size <= 0 || size > 128)
+ return NULL;
+ size = (size + 7) & ~7;
+#else
+ if (size <= 0 || size > 64)
+ return NULL;
+ size = (size + 3) & ~3;
+#endif
+ return ensure_abuf(compiler, (sljit_uw)size);
+}
+
+static SLJIT_INLINE void reverse_buf(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf = compiler->buf;
+ struct sljit_memory_fragment *prev = NULL;
+ struct sljit_memory_fragment *tmp;
+
+ do {
+ tmp = buf->next;
+ buf->next = prev;
+ prev = buf;
+ buf = tmp;
+ } while (buf != NULL);
+
+ compiler->buf = prev;
+}
+
+/* Only used in RISC architectures where the instruction size is constant */
+#if !(defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
+ && !(defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+
+static SLJIT_INLINE sljit_uw compute_next_addr(struct sljit_label *label, struct sljit_jump *jump,
+ struct sljit_const *const_, struct sljit_put_label *put_label)
+{
+ sljit_uw result = ~(sljit_uw)0;
+
+ if (label)
+ result = label->size;
+
+ if (jump && jump->addr < result)
+ result = jump->addr;
+
+ if (const_ && const_->addr < result)
+ result = const_->addr;
+
+ if (put_label && put_label->addr < result)
+ result = put_label->addr;
+
+ return result;
+}
+
+#endif /* !SLJIT_CONFIG_X86 && !SLJIT_CONFIG_S390X */
+
+static SLJIT_INLINE void set_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ SLJIT_UNUSED_ARG(args);
+ SLJIT_UNUSED_ARG(local_size);
+
+ compiler->options = options;
+ compiler->scratches = scratches;
+ compiler->saveds = saveds;
+ compiler->fscratches = fscratches;
+ compiler->fsaveds = fsaveds;
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->last_return = args & SLJIT_ARG_MASK;
+ compiler->logical_local_size = local_size;
+#endif
+}
+
+static SLJIT_INLINE void set_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ SLJIT_UNUSED_ARG(args);
+ SLJIT_UNUSED_ARG(local_size);
+
+ compiler->options = options;
+ compiler->scratches = scratches;
+ compiler->saveds = saveds;
+ compiler->fscratches = fscratches;
+ compiler->fsaveds = fsaveds;
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->last_return = args & SLJIT_ARG_MASK;
+ compiler->logical_local_size = local_size;
+#endif
+}
+
+static SLJIT_INLINE void set_label(struct sljit_label *label, struct sljit_compiler *compiler)
+{
+ label->next = NULL;
+ label->size = compiler->size;
+ if (compiler->last_label)
+ compiler->last_label->next = label;
+ else
+ compiler->labels = label;
+ compiler->last_label = label;
+}
+
+static SLJIT_INLINE void set_jump(struct sljit_jump *jump, struct sljit_compiler *compiler, sljit_u32 flags)
+{
+ jump->next = NULL;
+ jump->flags = flags;
+ if (compiler->last_jump)
+ compiler->last_jump->next = jump;
+ else
+ compiler->jumps = jump;
+ compiler->last_jump = jump;
+}
+
+static SLJIT_INLINE void set_const(struct sljit_const *const_, struct sljit_compiler *compiler)
+{
+ const_->next = NULL;
+ const_->addr = compiler->size;
+ if (compiler->last_const)
+ compiler->last_const->next = const_;
+ else
+ compiler->consts = const_;
+ compiler->last_const = const_;
+}
+
+static SLJIT_INLINE void set_put_label(struct sljit_put_label *put_label, struct sljit_compiler *compiler, sljit_uw offset)
+{
+ put_label->next = NULL;
+ put_label->label = NULL;
+ put_label->addr = compiler->size - offset;
+ put_label->flags = 0;
+ if (compiler->last_put_label)
+ compiler->last_put_label->next = put_label;
+ else
+ compiler->put_labels = put_label;
+ compiler->last_put_label = put_label;
+}
+
+#define ADDRESSING_DEPENDS_ON(exp, reg) \
+ (((exp) & SLJIT_MEM) && (((exp) & REG_MASK) == reg || OFFS_REG(exp) == reg))
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+
+static sljit_s32 function_check_arguments(sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches)
+{
+ sljit_s32 word_arg_count, scratch_arg_end, saved_arg_count, float_arg_count, curr_type;
+
+ curr_type = (arg_types & SLJIT_ARG_FULL_MASK);
+
+ if (curr_type >= SLJIT_ARG_TYPE_F64) {
+ if (curr_type > SLJIT_ARG_TYPE_F32 || fscratches == 0)
+ return 0;
+ } else if (curr_type >= SLJIT_ARG_TYPE_W) {
+ if (scratches == 0)
+ return 0;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ word_arg_count = 0;
+ scratch_arg_end = 0;
+ saved_arg_count = 0;
+ float_arg_count = 0;
+ while (arg_types != 0) {
+ if (word_arg_count + float_arg_count >= 4)
+ return 0;
+
+ curr_type = (arg_types & SLJIT_ARG_MASK);
+
+ if (arg_types & SLJIT_ARG_TYPE_SCRATCH_REG) {
+ if (saveds == -1 || curr_type < SLJIT_ARG_TYPE_W || curr_type > SLJIT_ARG_TYPE_P)
+ return 0;
+
+ word_arg_count++;
+ scratch_arg_end = word_arg_count;
+ } else {
+ if (curr_type < SLJIT_ARG_TYPE_W || curr_type > SLJIT_ARG_TYPE_F32)
+ return 0;
+
+ if (curr_type < SLJIT_ARG_TYPE_F64) {
+ word_arg_count++;
+ saved_arg_count++;
+ } else
+ float_arg_count++;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ if (saveds == -1)
+ return (word_arg_count <= scratches && float_arg_count <= fscratches);
+
+ return (saved_arg_count <= saveds && scratch_arg_end <= scratches && float_arg_count <= fscratches);
+}
+
+#define FUNCTION_CHECK_IS_REG(r) \
+ (((r) >= SLJIT_R0 && (r) < (SLJIT_R0 + compiler->scratches)) \
+ || ((r) > (SLJIT_S0 - compiler->saveds) && (r) <= SLJIT_S0))
+
+#define FUNCTION_CHECK_IS_FREG(fr) \
+ (((fr) >= SLJIT_FR0 && (fr) < (SLJIT_FR0 + compiler->fscratches)) \
+ || ((fr) > (SLJIT_FS0 - compiler->fsaveds) && (fr) <= SLJIT_FS0))
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+#define CHECK_IF_VIRTUAL_REGISTER(p) ((p) <= SLJIT_S3 && (p) >= SLJIT_S8)
+#else
+#define CHECK_IF_VIRTUAL_REGISTER(p) 0
+#endif
+
+static sljit_s32 function_check_src_mem(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i)
+{
+ if (compiler->scratches == -1 || compiler->saveds == -1)
+ return 0;
+
+ if (!(p & SLJIT_MEM))
+ return 0;
+
+ if (p == SLJIT_MEM1(SLJIT_SP))
+ return (i >= 0 && i < compiler->logical_local_size);
+
+ if (!(!(p & REG_MASK) || FUNCTION_CHECK_IS_REG(p & REG_MASK)))
+ return 0;
+
+ if (CHECK_IF_VIRTUAL_REGISTER(p & REG_MASK))
+ return 0;
+
+ if (p & OFFS_REG_MASK) {
+ if (!(p & REG_MASK))
+ return 0;
+
+ if (!(FUNCTION_CHECK_IS_REG(OFFS_REG(p))))
+ return 0;
+
+ if (CHECK_IF_VIRTUAL_REGISTER(OFFS_REG(p)))
+ return 0;
+
+ if ((i & ~0x3) != 0)
+ return 0;
+ }
+
+ return (p & ~(SLJIT_MEM | REG_MASK | OFFS_REG_MASK)) == 0;
+}
+
+#define FUNCTION_CHECK_SRC_MEM(p, i) \
+ CHECK_ARGUMENT(function_check_src_mem(compiler, p, i));
+
+static sljit_s32 function_check_src(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i)
+{
+ if (compiler->scratches == -1 || compiler->saveds == -1)
+ return 0;
+
+ if (FUNCTION_CHECK_IS_REG(p))
+ return (i == 0);
+
+ if (p == SLJIT_IMM)
+ return 1;
+
+ return function_check_src_mem(compiler, p, i);
+}
+
+#define FUNCTION_CHECK_SRC(p, i) \
+ CHECK_ARGUMENT(function_check_src(compiler, p, i));
+
+static sljit_s32 function_check_dst(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i)
+{
+ if (compiler->scratches == -1 || compiler->saveds == -1)
+ return 0;
+
+ if (FUNCTION_CHECK_IS_REG(p))
+ return (i == 0);
+
+ return function_check_src_mem(compiler, p, i);
+}
+
+#define FUNCTION_CHECK_DST(p, i) \
+ CHECK_ARGUMENT(function_check_dst(compiler, p, i));
+
+static sljit_s32 function_fcheck(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i)
+{
+ if (compiler->scratches == -1 || compiler->saveds == -1)
+ return 0;
+
+ if (FUNCTION_CHECK_IS_FREG(p))
+ return (i == 0);
+
+ return function_check_src_mem(compiler, p, i);
+}
+
+#define FUNCTION_FCHECK(p, i) \
+ CHECK_ARGUMENT(function_fcheck(compiler, p, i));
+
+#endif /* SLJIT_ARGUMENT_CHECKS */
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_compiler_verbose(struct sljit_compiler *compiler, FILE* verbose)
+{
+ compiler->verbose = verbose;
+}
+
+#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+#ifdef _WIN64
+#ifdef __GNUC__
+# define SLJIT_PRINT_D "ll"
+#else
+# define SLJIT_PRINT_D "I64"
+#endif
+#else
+# define SLJIT_PRINT_D "l"
+#endif
+#else
+# define SLJIT_PRINT_D ""
+#endif
+
+static void sljit_verbose_reg(struct sljit_compiler *compiler, sljit_s32 r)
+{
+ if (r < (SLJIT_R0 + compiler->scratches))
+ fprintf(compiler->verbose, "r%d", r - SLJIT_R0);
+ else if (r != SLJIT_SP)
+ fprintf(compiler->verbose, "s%d", SLJIT_NUMBER_OF_REGISTERS - r);
+ else
+ fprintf(compiler->verbose, "sp");
+}
+
+static void sljit_verbose_freg(struct sljit_compiler *compiler, sljit_s32 r)
+{
+ if (r < (SLJIT_FR0 + compiler->fscratches))
+ fprintf(compiler->verbose, "fr%d", r - SLJIT_FR0);
+ else
+ fprintf(compiler->verbose, "fs%d", SLJIT_NUMBER_OF_FLOAT_REGISTERS - r);
+}
+
+static void sljit_verbose_param(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i)
+{
+ if ((p) & SLJIT_IMM)
+ fprintf(compiler->verbose, "#%" SLJIT_PRINT_D "d", (i));
+ else if ((p) & SLJIT_MEM) {
+ if ((p) & REG_MASK) {
+ fputc('[', compiler->verbose);
+ sljit_verbose_reg(compiler, (p) & REG_MASK);
+ if ((p) & OFFS_REG_MASK) {
+ fprintf(compiler->verbose, " + ");
+ sljit_verbose_reg(compiler, OFFS_REG(p));
+ if (i)
+ fprintf(compiler->verbose, " * %d", 1 << (i));
+ }
+ else if (i)
+ fprintf(compiler->verbose, " + %" SLJIT_PRINT_D "d", (i));
+ fputc(']', compiler->verbose);
+ }
+ else
+ fprintf(compiler->verbose, "[#%" SLJIT_PRINT_D "d]", (i));
+ } else
+ sljit_verbose_reg(compiler, p);
+}
+
+static void sljit_verbose_fparam(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i)
+{
+ if ((p) & SLJIT_MEM) {
+ if ((p) & REG_MASK) {
+ fputc('[', compiler->verbose);
+ sljit_verbose_reg(compiler, (p) & REG_MASK);
+ if ((p) & OFFS_REG_MASK) {
+ fprintf(compiler->verbose, " + ");
+ sljit_verbose_reg(compiler, OFFS_REG(p));
+ if (i)
+ fprintf(compiler->verbose, "%d", 1 << (i));
+ }
+ else if (i)
+ fprintf(compiler->verbose, " + %" SLJIT_PRINT_D "d", (i));
+ fputc(']', compiler->verbose);
+ }
+ else
+ fprintf(compiler->verbose, "[#%" SLJIT_PRINT_D "d]", (i));
+ }
+ else
+ sljit_verbose_freg(compiler, p);
+}
+
+static const char* op0_names[] = {
+ "breakpoint", "nop", "lmul.uw", "lmul.sw",
+ "divmod.u", "divmod.s", "div.u", "div.s",
+ "endbr", "skip_frames_before_return"
+};
+
+static const char* op1_names[] = {
+ "", ".u8", ".s8", ".u16",
+ ".s16", ".u32", ".s32", "32",
+ ".p", "not", "clz", "ctz"
+};
+
+static const char* op2_names[] = {
+ "add", "addc", "sub", "subc",
+ "mul", "and", "or", "xor",
+ "shl", "mshl", "lshr", "mlshr",
+ "ashr", "mashr", "rotl", "rotr"
+};
+
+static const char* op_src_names[] = {
+ "fast_return", "skip_frames_before_fast_return",
+ "prefetch_l1", "prefetch_l2",
+ "prefetch_l3", "prefetch_once",
+};
+
+static const char* fop1_names[] = {
+ "mov", "conv", "conv", "conv",
+ "conv", "conv", "cmp", "neg",
+ "abs",
+};
+
+static const char* fop2_names[] = {
+ "add", "sub", "mul", "div"
+};
+
+static const char* jump_names[] = {
+ "equal", "not_equal",
+ "less", "greater_equal",
+ "greater", "less_equal",
+ "sig_less", "sig_greater_equal",
+ "sig_greater", "sig_less_equal",
+ "overflow", "not_overflow",
+ "carry", "",
+ "f_equal", "f_not_equal",
+ "f_less", "f_greater_equal",
+ "f_greater", "f_less_equal",
+ "unordered", "ordered",
+ "ordered_equal", "unordered_or_not_equal",
+ "ordered_less", "unordered_or_greater_equal",
+ "ordered_greater", "unordered_or_less_equal",
+ "unordered_or_equal", "ordered_not_equal",
+ "unordered_or_less", "ordered_greater_equal",
+ "unordered_or_greater", "ordered_less_equal",
+ "jump", "fast_call",
+ "call", "call_reg_arg"
+};
+
+static const char* call_arg_names[] = {
+ "void", "w", "32", "p", "f64", "f32"
+};
+
+#endif /* SLJIT_VERBOSE */
+
+/* --------------------------------------------------------------------- */
+/* Arch dependent */
+/* --------------------------------------------------------------------- */
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \
+ || (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+
+#define SLJIT_SKIP_CHECKS(compiler) (compiler)->skip_checks = 1
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_generate_code(struct sljit_compiler *compiler)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ struct sljit_jump *jump;
+#endif
+
+ SLJIT_UNUSED_ARG(compiler);
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(compiler->size > 0);
+ jump = compiler->jumps;
+ while (jump) {
+ /* All jumps have target. */
+ CHECK_ARGUMENT(jump->flags & (JUMP_LABEL | JUMP_ADDR));
+ jump = jump->next;
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ SLJIT_UNUSED_ARG(compiler);
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ if (options & SLJIT_ENTER_REG_ARG) {
+ CHECK_ARGUMENT(!(options & ~(0x3 | SLJIT_ENTER_REG_ARG)));
+ } else {
+ CHECK_ARGUMENT(options == 0);
+ }
+ CHECK_ARGUMENT(SLJIT_KEPT_SAVEDS_COUNT(options) <= 3 && SLJIT_KEPT_SAVEDS_COUNT(options) <= saveds);
+ CHECK_ARGUMENT(scratches >= 0 && scratches <= SLJIT_NUMBER_OF_REGISTERS);
+ CHECK_ARGUMENT(saveds >= 0 && saveds <= SLJIT_NUMBER_OF_SAVED_REGISTERS);
+ CHECK_ARGUMENT(scratches + saveds <= SLJIT_NUMBER_OF_REGISTERS);
+ CHECK_ARGUMENT(fscratches >= 0 && fscratches <= SLJIT_NUMBER_OF_FLOAT_REGISTERS);
+ CHECK_ARGUMENT(fsaveds >= 0 && fsaveds <= SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS);
+ CHECK_ARGUMENT(fscratches + fsaveds <= SLJIT_NUMBER_OF_FLOAT_REGISTERS);
+ CHECK_ARGUMENT(local_size >= 0 && local_size <= SLJIT_MAX_LOCAL_SIZE);
+ CHECK_ARGUMENT((arg_types & SLJIT_ARG_FULL_MASK) <= SLJIT_ARG_TYPE_F32);
+ CHECK_ARGUMENT(function_check_arguments(arg_types, scratches, (options & SLJIT_ENTER_REG_ARG) ? 0 : saveds, fscratches));
+
+ compiler->last_flags = 0;
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " enter ret[%s", call_arg_names[arg_types & SLJIT_ARG_MASK]);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ if (arg_types) {
+ fprintf(compiler->verbose, "], args[");
+ do {
+ fprintf(compiler->verbose, "%s%s", call_arg_names[arg_types & SLJIT_ARG_MASK],
+ (arg_types & SLJIT_ARG_TYPE_SCRATCH_REG) ? "_r" : "");
+ arg_types >>= SLJIT_ARG_SHIFT;
+ if (arg_types)
+ fprintf(compiler->verbose, ",");
+ } while (arg_types);
+ }
+
+ fprintf(compiler->verbose, "],");
+
+ if (options & SLJIT_ENTER_REG_ARG) {
+ fprintf(compiler->verbose, " enter:reg_arg,");
+
+ if (SLJIT_KEPT_SAVEDS_COUNT(options) > 0)
+ fprintf(compiler->verbose, " keep:%d,", SLJIT_KEPT_SAVEDS_COUNT(options));
+ }
+
+ fprintf(compiler->verbose, "scratches:%d, saveds:%d, fscratches:%d, fsaveds:%d, local_size:%d\n",
+ scratches, saveds, fscratches, fsaveds, local_size);
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ SLJIT_UNUSED_ARG(compiler);
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ if (options & SLJIT_ENTER_REG_ARG) {
+ CHECK_ARGUMENT(!(options & ~(0x3 | SLJIT_ENTER_REG_ARG)));
+ } else {
+ CHECK_ARGUMENT(options == 0);
+ }
+ CHECK_ARGUMENT(SLJIT_KEPT_SAVEDS_COUNT(options) <= 3 && SLJIT_KEPT_SAVEDS_COUNT(options) <= saveds);
+ CHECK_ARGUMENT(scratches >= 0 && scratches <= SLJIT_NUMBER_OF_REGISTERS);
+ CHECK_ARGUMENT(saveds >= 0 && saveds <= SLJIT_NUMBER_OF_SAVED_REGISTERS);
+ CHECK_ARGUMENT(scratches + saveds <= SLJIT_NUMBER_OF_REGISTERS);
+ CHECK_ARGUMENT(fscratches >= 0 && fscratches <= SLJIT_NUMBER_OF_FLOAT_REGISTERS);
+ CHECK_ARGUMENT(fsaveds >= 0 && fsaveds <= SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS);
+ CHECK_ARGUMENT(fscratches + fsaveds <= SLJIT_NUMBER_OF_FLOAT_REGISTERS);
+ CHECK_ARGUMENT(local_size >= 0 && local_size <= SLJIT_MAX_LOCAL_SIZE);
+ CHECK_ARGUMENT((arg_types & SLJIT_ARG_FULL_MASK) < SLJIT_ARG_TYPE_F64);
+ CHECK_ARGUMENT(function_check_arguments(arg_types, scratches, (options & SLJIT_ENTER_REG_ARG) ? 0 : saveds, fscratches));
+
+ compiler->last_flags = 0;
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " set_context ret[%s", call_arg_names[arg_types & SLJIT_ARG_MASK]);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ if (arg_types) {
+ fprintf(compiler->verbose, "], args[");
+ do {
+ fprintf(compiler->verbose, "%s%s", call_arg_names[arg_types & SLJIT_ARG_MASK],
+ (arg_types & SLJIT_ARG_TYPE_SCRATCH_REG) ? "_r" : "");
+ arg_types >>= SLJIT_ARG_SHIFT;
+ if (arg_types)
+ fprintf(compiler->verbose, ",");
+ } while (arg_types);
+ }
+
+ fprintf(compiler->verbose, "],");
+
+ if (options & SLJIT_ENTER_REG_ARG) {
+ fprintf(compiler->verbose, " enter:reg_arg,");
+
+ if (SLJIT_KEPT_SAVEDS_COUNT(options) > 0)
+ fprintf(compiler->verbose, " keep:%d,", SLJIT_KEPT_SAVEDS_COUNT(options));
+ }
+
+ fprintf(compiler->verbose, " scratches:%d, saveds:%d, fscratches:%d, fsaveds:%d, local_size:%d\n",
+ scratches, saveds, fscratches, fsaveds, local_size);
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(compiler->last_return == SLJIT_ARG_TYPE_VOID);
+#endif
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " return_void\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(compiler->scratches >= 0);
+
+ switch (compiler->last_return) {
+ case SLJIT_ARG_TYPE_W:
+ CHECK_ARGUMENT(op >= SLJIT_MOV && op <= SLJIT_MOV_S32);
+ break;
+ case SLJIT_ARG_TYPE_32:
+ CHECK_ARGUMENT(op == SLJIT_MOV32 || (op >= SLJIT_MOV32_U8 && op <= SLJIT_MOV32_S16));
+ break;
+ case SLJIT_ARG_TYPE_P:
+ CHECK_ARGUMENT(op == SLJIT_MOV_P);
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
+ CHECK_ARGUMENT(op == SLJIT_MOV_F64);
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
+ CHECK_ARGUMENT(op == SLJIT_MOV_F32);
+ break;
+ default:
+ /* Context not initialized, void, etc. */
+ CHECK_ARGUMENT(0);
+ break;
+ }
+
+ if (GET_OPCODE(op) < SLJIT_MOV_F64) {
+ FUNCTION_CHECK_SRC(src, srcw);
+ } else {
+ FUNCTION_FCHECK(src, srcw);
+ }
+ compiler->last_flags = 0;
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ if (GET_OPCODE(op) < SLJIT_MOV_F64) {
+ fprintf(compiler->verbose, " return%s%s ", !(op & SLJIT_32) ? "" : "32",
+ op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE]);
+ sljit_verbose_param(compiler, src, srcw);
+ } else {
+ fprintf(compiler->verbose, " return%s ", !(op & SLJIT_32) ? ".f64" : ".f32");
+ sljit_verbose_fparam(compiler, src, srcw);
+ }
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ FUNCTION_CHECK_SRC(src, srcw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " return_to ");
+ sljit_verbose_param(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ FUNCTION_CHECK_DST(dst, dstw);
+ compiler->last_flags = 0;
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " fast_enter ");
+ sljit_verbose_param(compiler, dst, dstw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT((op >= SLJIT_BREAKPOINT && op <= SLJIT_LMUL_SW)
+ || ((op & ~SLJIT_32) >= SLJIT_DIVMOD_UW && (op & ~SLJIT_32) <= SLJIT_DIV_SW)
+ || (op >= SLJIT_ENDBR && op <= SLJIT_SKIP_FRAMES_BEFORE_RETURN));
+ CHECK_ARGUMENT(GET_OPCODE(op) < SLJIT_LMUL_UW || GET_OPCODE(op) >= SLJIT_ENDBR || compiler->scratches >= 2);
+ if ((GET_OPCODE(op) >= SLJIT_LMUL_UW && GET_OPCODE(op) <= SLJIT_DIV_SW) || op == SLJIT_SKIP_FRAMES_BEFORE_RETURN)
+ compiler->last_flags = 0;
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose))
+ {
+ fprintf(compiler->verbose, " %s", op0_names[GET_OPCODE(op) - SLJIT_OP0_BASE]);
+ if (GET_OPCODE(op) >= SLJIT_DIVMOD_UW && GET_OPCODE(op) <= SLJIT_DIV_SW) {
+ fprintf(compiler->verbose, (op & SLJIT_32) ? "32" : "w");
+ }
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_MOV && GET_OPCODE(op) <= SLJIT_CTZ);
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_NOT:
+ /* Only SLJIT_32 and SLJIT_SET_Z are allowed. */
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK));
+ break;
+ case SLJIT_MOV:
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_P:
+ /* Nothing allowed */
+ CHECK_ARGUMENT(!(op & (SLJIT_32 | SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
+ break;
+ default:
+ /* Only SLJIT_32 is allowed. */
+ CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
+ break;
+ }
+
+ FUNCTION_CHECK_DST(dst, dstw);
+ FUNCTION_CHECK_SRC(src, srcw);
+
+ if (GET_OPCODE(op) >= SLJIT_NOT) {
+ CHECK_ARGUMENT(src != SLJIT_IMM);
+ compiler->last_flags = GET_FLAG_TYPE(op) | (op & (SLJIT_32 | SLJIT_SET_Z));
+ }
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ if (GET_OPCODE(op) <= SLJIT_MOV_P)
+ {
+ fprintf(compiler->verbose, " mov%s%s ", !(op & SLJIT_32) ? "" : "32",
+ op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE]);
+ }
+ else
+ {
+ fprintf(compiler->verbose, " %s%s%s%s%s ", op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE], !(op & SLJIT_32) ? "" : "32",
+ !(op & SLJIT_SET_Z) ? "" : ".z", !(op & VARIABLE_FLAG_MASK) ? "" : ".",
+ !(op & VARIABLE_FLAG_MASK) ? "" : jump_names[GET_FLAG_TYPE(op)]);
+ }
+
+ sljit_verbose_param(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 unset,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_ADD && GET_OPCODE(op) <= SLJIT_ROTR);
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_AND:
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK));
+ break;
+ case SLJIT_MUL:
+ CHECK_ARGUMENT(!(op & SLJIT_SET_Z));
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)
+ || GET_FLAG_TYPE(op) == SLJIT_OVERFLOW);
+ break;
+ case SLJIT_ADD:
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)
+ || GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY)
+ || GET_FLAG_TYPE(op) == SLJIT_OVERFLOW);
+ break;
+ case SLJIT_SUB:
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)
+ || (GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_OVERFLOW)
+ || GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY));
+ break;
+ case SLJIT_ADDC:
+ case SLJIT_SUBC:
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)
+ || GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY));
+ CHECK_ARGUMENT((compiler->last_flags & 0xff) == GET_FLAG_TYPE(SLJIT_SET_CARRY));
+ CHECK_ARGUMENT((op & SLJIT_32) == (compiler->last_flags & SLJIT_32));
+ break;
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+ if (unset) {
+ CHECK_ARGUMENT(HAS_FLAGS(op));
+ } else {
+ FUNCTION_CHECK_DST(dst, dstw);
+ }
+ FUNCTION_CHECK_SRC(src1, src1w);
+ FUNCTION_CHECK_SRC(src2, src2w);
+ compiler->last_flags = GET_FLAG_TYPE(op) | (op & (SLJIT_32 | SLJIT_SET_Z));
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s%s%s%s%s ", op2_names[GET_OPCODE(op) - SLJIT_OP2_BASE], !(op & SLJIT_32) ? "" : "32",
+ !(op & SLJIT_SET_Z) ? "" : ".z", !(op & VARIABLE_FLAG_MASK) ? "" : ".",
+ !(op & VARIABLE_FLAG_MASK) ? "" : jump_names[GET_FLAG_TYPE(op)]);
+ if (unset)
+ fprintf(compiler->verbose, "unset");
+ else
+ sljit_verbose_param(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, src1, src1w);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, src2, src2w);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(GET_OPCODE(op) == SLJIT_SHL || GET_OPCODE(op) == SLJIT_LSHR
+ || GET_OPCODE(op) == SLJIT_MSHL || GET_OPCODE(op) == SLJIT_MLSHR);
+ CHECK_ARGUMENT((op & ~(0xff | SLJIT_32 | SLJIT_SHIFT_INTO_NON_ZERO)) == 0);
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(src_dst));
+ FUNCTION_CHECK_SRC(src1, src1w);
+ FUNCTION_CHECK_SRC(src2, src2w);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s%s.into%s ", op2_names[GET_OPCODE(op) - SLJIT_OP2_BASE], !(op & SLJIT_32) ? "" : "32",
+ (op & SLJIT_SHIFT_INTO_NON_ZERO) ? ".nz" : "");
+
+ sljit_verbose_reg(compiler, src_dst);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, src1, src1w);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, src2, src2w);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(op >= SLJIT_FAST_RETURN && op <= SLJIT_PREFETCH_ONCE);
+ FUNCTION_CHECK_SRC(src, srcw);
+
+ if (op == SLJIT_FAST_RETURN || op == SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN)
+ {
+ CHECK_ARGUMENT(src != SLJIT_IMM);
+ compiler->last_flags = 0;
+ }
+ else if (op >= SLJIT_PREFETCH_L1 && op <= SLJIT_PREFETCH_ONCE)
+ {
+ CHECK_ARGUMENT(src & SLJIT_MEM);
+ }
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s ", op_src_names[op - SLJIT_OP_SRC_BASE]);
+ sljit_verbose_param(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_get_register_index(sljit_s32 reg)
+{
+ SLJIT_UNUSED_ARG(reg);
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(reg > 0 && reg <= SLJIT_NUMBER_OF_REGISTERS);
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_get_float_register_index(sljit_s32 reg)
+{
+ SLJIT_UNUSED_ARG(reg);
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(reg > 0 && reg <= SLJIT_NUMBER_OF_FLOAT_REGISTERS);
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ sljit_u32 i;
+#endif
+
+ SLJIT_UNUSED_ARG(compiler);
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(instruction);
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+ CHECK_ARGUMENT(size > 0 && size < 16);
+#elif (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+ CHECK_ARGUMENT((size == 2 && (((sljit_sw)instruction) & 0x1) == 0)
+ || (size == 4 && (((sljit_sw)instruction) & 0x3) == 0));
+#elif (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+ CHECK_ARGUMENT(size == 2 || size == 4 || size == 6);
+#else
+ CHECK_ARGUMENT(size == 4 && (((sljit_sw)instruction) & 0x3) == 0);
+#endif
+
+ compiler->last_flags = 0;
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " op_custom");
+ for (i = 0; i < size; i++)
+ fprintf(compiler->verbose, " 0x%x", ((sljit_u8*)instruction)[i]);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
+ CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_MOV_F64 && GET_OPCODE(op) <= SLJIT_ABS_F64);
+ CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
+ FUNCTION_FCHECK(src, srcw);
+ FUNCTION_FCHECK(dst, dstw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32)
+ fprintf(compiler->verbose, " %s%s ", fop1_names[SLJIT_CONV_F64_FROM_F32 - SLJIT_FOP1_BASE],
+ (op & SLJIT_32) ? ".f32.from.f64" : ".f64.from.f32");
+ else
+ fprintf(compiler->verbose, " %s%s ", fop1_names[GET_OPCODE(op) - SLJIT_FOP1_BASE],
+ (op & SLJIT_32) ? ".f32" : ".f64");
+
+ sljit_verbose_fparam(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_fparam(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->last_flags = GET_FLAG_TYPE(op) | (op & SLJIT_32);
+#endif
+
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
+ CHECK_ARGUMENT(GET_OPCODE(op) == SLJIT_CMP_F64);
+ CHECK_ARGUMENT(!(op & SLJIT_SET_Z));
+ CHECK_ARGUMENT((op & VARIABLE_FLAG_MASK)
+ || (GET_FLAG_TYPE(op) >= SLJIT_F_EQUAL && GET_FLAG_TYPE(op) <= SLJIT_ORDERED_LESS_EQUAL));
+ FUNCTION_FCHECK(src1, src1w);
+ FUNCTION_FCHECK(src2, src2w);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s%s", fop1_names[SLJIT_CMP_F64 - SLJIT_FOP1_BASE], (op & SLJIT_32) ? ".f32" : ".f64");
+ if (op & VARIABLE_FLAG_MASK) {
+ fprintf(compiler->verbose, ".%s", jump_names[GET_FLAG_TYPE(op)]);
+ }
+ fprintf(compiler->verbose, " ");
+ sljit_verbose_fparam(compiler, src1, src1w);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_fparam(compiler, src2, src2w);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
+ CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_CONV_SW_FROM_F64 && GET_OPCODE(op) <= SLJIT_CONV_S32_FROM_F64);
+ CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
+ FUNCTION_FCHECK(src, srcw);
+ FUNCTION_CHECK_DST(dst, dstw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s%s.from%s ", fop1_names[GET_OPCODE(op) - SLJIT_FOP1_BASE],
+ (GET_OPCODE(op) == SLJIT_CONV_S32_FROM_F64) ? ".s32" : ".sw",
+ (op & SLJIT_32) ? ".f32" : ".f64");
+ sljit_verbose_param(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_fparam(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
+ CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_CONV_F64_FROM_SW && GET_OPCODE(op) <= SLJIT_CONV_F64_FROM_S32);
+ CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
+ FUNCTION_CHECK_SRC(src, srcw);
+ FUNCTION_FCHECK(dst, dstw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s%s.from%s ", fop1_names[GET_OPCODE(op) - SLJIT_FOP1_BASE],
+ (op & SLJIT_32) ? ".f32" : ".f64",
+ (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32) ? ".s32" : ".sw");
+ sljit_verbose_fparam(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
+ CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_ADD_F64 && GET_OPCODE(op) <= SLJIT_DIV_F64);
+ CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
+ FUNCTION_FCHECK(src1, src1w);
+ FUNCTION_FCHECK(src2, src2w);
+ FUNCTION_FCHECK(dst, dstw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s%s ", fop2_names[GET_OPCODE(op) - SLJIT_FOP2_BASE], (op & SLJIT_32) ? ".f32" : ".f64");
+ sljit_verbose_fparam(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_fparam(compiler, src1, src1w);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_fparam(compiler, src2, src2w);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_label(struct sljit_compiler *compiler)
+{
+ SLJIT_UNUSED_ARG(compiler);
+
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->last_flags = 0;
+#endif
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose))
+ fprintf(compiler->verbose, "label:\n");
+#endif
+ CHECK_RETURN_OK;
+}
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
+ || (defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM)
+#define CHECK_UNORDERED(type, last_flags) \
+ ((((type) & 0xff) == SLJIT_UNORDERED || ((type) & 0xff) == SLJIT_ORDERED) && \
+ ((last_flags) & 0xff) >= SLJIT_UNORDERED && ((last_flags) & 0xff) <= SLJIT_ORDERED_LESS_EQUAL)
+#else
+#define CHECK_UNORDERED(type, last_flags) 0
+#endif
+#endif /* SLJIT_ARGUMENT_CHECKS */
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP)));
+ CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_FAST_CALL);
+
+ if ((type & 0xff) < SLJIT_JUMP) {
+ if ((type & 0xff) <= SLJIT_NOT_ZERO)
+ CHECK_ARGUMENT(compiler->last_flags & SLJIT_SET_Z);
+ else if ((compiler->last_flags & 0xff) == SLJIT_CARRY) {
+ CHECK_ARGUMENT((type & 0xff) == SLJIT_CARRY || (type & 0xff) == SLJIT_NOT_CARRY);
+ compiler->last_flags = 0;
+ } else
+ CHECK_ARGUMENT((type & 0xff) == (compiler->last_flags & 0xff)
+ || ((type & 0xff) == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW)
+ || CHECK_UNORDERED(type, compiler->last_flags));
+ }
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose))
+ fprintf(compiler->verbose, " jump%s %s\n", !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r",
+ jump_names[type & 0xff]);
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP | SLJIT_CALL_RETURN)));
+ CHECK_ARGUMENT((type & 0xff) >= SLJIT_CALL && (type & 0xff) <= SLJIT_CALL_REG_ARG);
+ CHECK_ARGUMENT(function_check_arguments(arg_types, compiler->scratches, -1, compiler->fscratches));
+
+ if (type & SLJIT_CALL_RETURN) {
+ CHECK_ARGUMENT((arg_types & SLJIT_ARG_MASK) == compiler->last_return);
+
+ if (compiler->options & SLJIT_ENTER_REG_ARG) {
+ CHECK_ARGUMENT((type & 0xff) == SLJIT_CALL_REG_ARG);
+ } else {
+ CHECK_ARGUMENT((type & 0xff) != SLJIT_CALL_REG_ARG);
+ }
+ }
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s%s%s ret[%s", jump_names[type & 0xff],
+ !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r",
+ !(type & SLJIT_CALL_RETURN) ? "" : ".ret",
+ call_arg_names[arg_types & SLJIT_ARG_MASK]);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ if (arg_types) {
+ fprintf(compiler->verbose, "], args[");
+ do {
+ fprintf(compiler->verbose, "%s", call_arg_names[arg_types & SLJIT_ARG_MASK]);
+ arg_types >>= SLJIT_ARG_SHIFT;
+ if (arg_types)
+ fprintf(compiler->verbose, ",");
+ } while (arg_types);
+ }
+ fprintf(compiler->verbose, "]\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP | SLJIT_32)));
+ CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_SIG_LESS_EQUAL);
+ FUNCTION_CHECK_SRC(src1, src1w);
+ FUNCTION_CHECK_SRC(src2, src2w);
+ compiler->last_flags = 0;
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " cmp%s%s %s, ", (type & SLJIT_32) ? "32" : "",
+ !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r", jump_names[type & 0xff]);
+ sljit_verbose_param(compiler, src1, src1w);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, src2, src2w);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fcmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
+ CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP | SLJIT_32)));
+ CHECK_ARGUMENT((type & 0xff) >= SLJIT_F_EQUAL && (type & 0xff) <= SLJIT_ORDERED_LESS_EQUAL
+ && ((type & 0xff) <= SLJIT_ORDERED || sljit_cmp_info(type & 0xff)));
+ FUNCTION_FCHECK(src1, src1w);
+ FUNCTION_FCHECK(src2, src2w);
+ compiler->last_flags = 0;
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " fcmp%s%s %s, ", (type & SLJIT_32) ? ".f32" : ".f64",
+ !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r", jump_names[type & 0xff]);
+ sljit_verbose_fparam(compiler, src1, src1w);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_fparam(compiler, src2, src2w);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(type >= SLJIT_JUMP && type <= SLJIT_FAST_CALL);
+ FUNCTION_CHECK_SRC(src, srcw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " ijump.%s ", jump_names[type]);
+ sljit_verbose_param(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_CALL_RETURN)));
+ CHECK_ARGUMENT((type & 0xff) >= SLJIT_CALL && (type & 0xff) <= SLJIT_CALL_REG_ARG);
+ CHECK_ARGUMENT(function_check_arguments(arg_types, compiler->scratches, -1, compiler->fscratches));
+ FUNCTION_CHECK_SRC(src, srcw);
+
+ if (type & SLJIT_CALL_RETURN) {
+ CHECK_ARGUMENT((arg_types & SLJIT_ARG_MASK) == compiler->last_return);
+
+ if (compiler->options & SLJIT_ENTER_REG_ARG) {
+ CHECK_ARGUMENT((type & 0xff) == SLJIT_CALL_REG_ARG);
+ } else {
+ CHECK_ARGUMENT((type & 0xff) != SLJIT_CALL_REG_ARG);
+ }
+ }
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " i%s%s ret[%s", jump_names[type & 0xff],
+ !(type & SLJIT_CALL_RETURN) ? "" : ".ret",
+ call_arg_names[arg_types & SLJIT_ARG_MASK]);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ if (arg_types) {
+ fprintf(compiler->verbose, "], args[");
+ do {
+ fprintf(compiler->verbose, "%s", call_arg_names[arg_types & SLJIT_ARG_MASK]);
+ arg_types >>= SLJIT_ARG_SHIFT;
+ if (arg_types)
+ fprintf(compiler->verbose, ",");
+ } while (arg_types);
+ }
+ fprintf(compiler->verbose, "], ");
+ sljit_verbose_param(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(type >= SLJIT_EQUAL && type <= SLJIT_ORDERED_LESS_EQUAL);
+ CHECK_ARGUMENT(op == SLJIT_MOV || op == SLJIT_MOV32
+ || (GET_OPCODE(op) >= SLJIT_AND && GET_OPCODE(op) <= SLJIT_XOR));
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK));
+
+ if (type <= SLJIT_NOT_ZERO)
+ CHECK_ARGUMENT(compiler->last_flags & SLJIT_SET_Z);
+ else
+ CHECK_ARGUMENT(type == (compiler->last_flags & 0xff)
+ || (type == SLJIT_NOT_CARRY && (compiler->last_flags & 0xff) == SLJIT_CARRY)
+ || (type == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW)
+ || CHECK_UNORDERED(type, compiler->last_flags));
+
+ FUNCTION_CHECK_DST(dst, dstw);
+
+ if (GET_OPCODE(op) >= SLJIT_ADD)
+ compiler->last_flags = GET_FLAG_TYPE(op) | (op & (SLJIT_32 | SLJIT_SET_Z));
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " flags.%s%s%s ",
+ GET_OPCODE(op) < SLJIT_OP2_BASE ? "mov" : op2_names[GET_OPCODE(op) - SLJIT_OP2_BASE],
+ GET_OPCODE(op) < SLJIT_OP2_BASE ? op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE] : ((op & SLJIT_32) ? "32" : ""),
+ !(op & SLJIT_SET_Z) ? "" : ".z");
+ sljit_verbose_param(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", %s\n", jump_names[type]);
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ sljit_s32 cond = type & ~SLJIT_32;
+
+ CHECK_ARGUMENT(cond >= SLJIT_EQUAL && cond <= SLJIT_ORDERED_LESS_EQUAL);
+
+ CHECK_ARGUMENT(compiler->scratches != -1 && compiler->saveds != -1);
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(dst_reg));
+ if (src != SLJIT_IMM) {
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(src));
+ CHECK_ARGUMENT(srcw == 0);
+ }
+
+ if (cond <= SLJIT_NOT_ZERO)
+ CHECK_ARGUMENT(compiler->last_flags & SLJIT_SET_Z);
+ else
+ CHECK_ARGUMENT(cond == (compiler->last_flags & 0xff)
+ || (cond == SLJIT_NOT_CARRY && (compiler->last_flags & 0xff) == SLJIT_CARRY)
+ || (cond == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW)
+ || CHECK_UNORDERED(cond, compiler->last_flags));
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " cmov%s %s, ",
+ !(type & SLJIT_32) ? "" : "32",
+ jump_names[type & ~SLJIT_32]);
+ sljit_verbose_reg(compiler, dst_reg);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ sljit_s32 allowed_flags;
+
+ if (type & SLJIT_MEM_UNALIGNED) {
+ CHECK_ARGUMENT(!(type & (SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32)));
+ } else if (type & SLJIT_MEM_UNALIGNED_16) {
+ CHECK_ARGUMENT(!(type & SLJIT_MEM_UNALIGNED_32));
+ } else {
+ CHECK_ARGUMENT((reg & REG_PAIR_MASK) || (type & SLJIT_MEM_UNALIGNED_32));
+ }
+
+ allowed_flags = SLJIT_MEM_UNALIGNED;
+
+ switch (type & 0xff) {
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ allowed_flags = SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16;
+ break;
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+ allowed_flags = SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32;
+ break;
+ }
+
+ CHECK_ARGUMENT((type & ~(0xff | SLJIT_32 | SLJIT_MEM_STORE | allowed_flags)) == 0);
+
+ if (reg & REG_PAIR_MASK) {
+ CHECK_ARGUMENT((type & 0xff) == SLJIT_MOV);
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(REG_PAIR_FIRST(reg)));
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(REG_PAIR_SECOND(reg)));
+ CHECK_ARGUMENT(REG_PAIR_FIRST(reg) != REG_PAIR_SECOND(reg));
+ } else {
+ CHECK_ARGUMENT((type & 0xff) >= SLJIT_MOV && (type & 0xff) <= SLJIT_MOV_P);
+ CHECK_ARGUMENT(!(type & SLJIT_32) || ((type & 0xff) >= SLJIT_MOV_U8 && (type & 0xff) <= SLJIT_MOV_S16));
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(reg));
+ }
+
+ FUNCTION_CHECK_SRC_MEM(mem, memw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ if ((type & 0xff) == SLJIT_MOV32)
+ fprintf(compiler->verbose, " %s32",
+ (type & SLJIT_MEM_STORE) ? "store" : "load");
+ else
+ fprintf(compiler->verbose, " %s%s%s",
+ (type & SLJIT_MEM_STORE) ? "store" : "load",
+ !(type & SLJIT_32) ? "" : "32",
+ op1_names[(type & 0xff) - SLJIT_OP1_BASE]);
+
+ if (type & SLJIT_MEM_UNALIGNED)
+ printf(".un");
+ else if (type & SLJIT_MEM_UNALIGNED_16)
+ printf(".un16");
+ else if (type & SLJIT_MEM_UNALIGNED_32)
+ printf(".un32");
+
+ if (reg & REG_PAIR_MASK) {
+ fprintf(compiler->verbose, " {");
+ sljit_verbose_reg(compiler, REG_PAIR_FIRST(reg));
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_reg(compiler, REG_PAIR_SECOND(reg));
+ fprintf(compiler->verbose, "}, ");
+ } else {
+ fprintf(compiler->verbose, " ");
+ sljit_verbose_reg(compiler, reg);
+ fprintf(compiler->verbose, ", ");
+ }
+ sljit_verbose_param(compiler, mem, memw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT((type & 0xff) >= SLJIT_MOV && (type & 0xff) <= SLJIT_MOV_P);
+ CHECK_ARGUMENT((type & ~(0xff | SLJIT_32 | SLJIT_MEM_STORE | SLJIT_MEM_SUPP | SLJIT_MEM_POST)) == 0);
+ CHECK_ARGUMENT((mem & REG_MASK) != 0 && (mem & REG_MASK) != reg);
+
+ FUNCTION_CHECK_SRC_MEM(mem, memw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ if (type & SLJIT_MEM_SUPP)
+ CHECK_RETURN_OK;
+ if (sljit_emit_mem_update(compiler, type | SLJIT_MEM_SUPP, reg, mem, memw) == SLJIT_ERR_UNSUPPORTED) {
+ fprintf(compiler->verbose, " # mem: unsupported form, no instructions are emitted\n");
+ CHECK_RETURN_OK;
+ }
+
+ if ((type & 0xff) == SLJIT_MOV32)
+ fprintf(compiler->verbose, " %s32.%s ",
+ (type & SLJIT_MEM_STORE) ? "store" : "load",
+ (type & SLJIT_MEM_POST) ? "post" : "pre");
+ else
+ fprintf(compiler->verbose, " %s%s%s.%s ",
+ (type & SLJIT_MEM_STORE) ? "store" : "load",
+ !(type & SLJIT_32) ? "" : "32",
+ op1_names[(type & 0xff) - SLJIT_OP1_BASE],
+ (type & SLJIT_MEM_POST) ? "post" : "pre");
+
+ sljit_verbose_reg(compiler, reg);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, mem, memw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT((type & 0xff) == SLJIT_MOV_F64);
+
+ if (type & SLJIT_MEM_UNALIGNED) {
+ CHECK_ARGUMENT(!(type & (SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32)));
+ } else if (type & SLJIT_MEM_UNALIGNED_16) {
+ CHECK_ARGUMENT(!(type & SLJIT_MEM_UNALIGNED_32));
+ } else {
+ CHECK_ARGUMENT(type & SLJIT_MEM_UNALIGNED_32);
+ CHECK_ARGUMENT(!(type & SLJIT_32));
+ }
+
+ CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_32 | SLJIT_MEM_STORE | SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32)));
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_FREG(freg));
+ FUNCTION_CHECK_SRC_MEM(mem, memw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s.%s",
+ (type & SLJIT_MEM_STORE) ? "store" : "load",
+ !(type & SLJIT_32) ? "f64" : "f32");
+
+ if (type & SLJIT_MEM_UNALIGNED)
+ printf(".un");
+ else if (type & SLJIT_MEM_UNALIGNED_16)
+ printf(".un16");
+ else if (type & SLJIT_MEM_UNALIGNED_32)
+ printf(".un32");
+
+ fprintf(compiler->verbose, " ");
+ sljit_verbose_freg(compiler, freg);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, mem, memw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT((type & 0xff) == SLJIT_MOV_F64);
+ CHECK_ARGUMENT((type & ~(0xff | SLJIT_32 | SLJIT_MEM_STORE | SLJIT_MEM_SUPP | SLJIT_MEM_POST)) == 0);
+ FUNCTION_CHECK_SRC_MEM(mem, memw);
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_FREG(freg));
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ if (type & SLJIT_MEM_SUPP)
+ CHECK_RETURN_OK;
+ if (sljit_emit_fmem_update(compiler, type | SLJIT_MEM_SUPP, freg, mem, memw) == SLJIT_ERR_UNSUPPORTED) {
+ fprintf(compiler->verbose, " # fmem: unsupported form, no instructions are emitted\n");
+ CHECK_RETURN_OK;
+ }
+
+ fprintf(compiler->verbose, " %s.%s.%s ",
+ (type & SLJIT_MEM_STORE) ? "store" : "load",
+ !(type & SLJIT_32) ? "f64" : "f32",
+ (type & SLJIT_MEM_POST) ? "post" : "pre");
+
+ sljit_verbose_freg(compiler, freg);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, mem, memw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset)
+{
+ /* Any offset is allowed. */
+ SLJIT_UNUSED_ARG(offset);
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ FUNCTION_CHECK_DST(dst, dstw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " local_base ");
+ sljit_verbose_param(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", #%" SLJIT_PRINT_D "d\n", offset);
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ SLJIT_UNUSED_ARG(init_value);
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ FUNCTION_CHECK_DST(dst, dstw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " const ");
+ sljit_verbose_param(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", #%" SLJIT_PRINT_D "d\n", init_value);
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ FUNCTION_CHECK_DST(dst, dstw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " put_label ");
+ sljit_verbose_param(compiler, dst, dstw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+#else /* !SLJIT_ARGUMENT_CHECKS && !SLJIT_VERBOSE */
+
+#define SLJIT_SKIP_CHECKS(compiler)
+
+#endif /* SLJIT_ARGUMENT_CHECKS || SLJIT_VERBOSE */
+
+#define SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw) \
+ SLJIT_COMPILE_ASSERT(!(SLJIT_CONV_SW_FROM_F64 & 0x1) && !(SLJIT_CONV_F64_FROM_SW & 0x1), \
+ invalid_float_opcodes); \
+ if (GET_OPCODE(op) >= SLJIT_CONV_SW_FROM_F64 && GET_OPCODE(op) <= SLJIT_CMP_F64) { \
+ if (GET_OPCODE(op) == SLJIT_CMP_F64) { \
+ CHECK(check_sljit_emit_fop1_cmp(compiler, op, dst, dstw, src, srcw)); \
+ ADJUST_LOCAL_OFFSET(dst, dstw); \
+ ADJUST_LOCAL_OFFSET(src, srcw); \
+ return sljit_emit_fop1_cmp(compiler, op, dst, dstw, src, srcw); \
+ } \
+ if ((GET_OPCODE(op) | 0x1) == SLJIT_CONV_S32_FROM_F64) { \
+ CHECK(check_sljit_emit_fop1_conv_sw_from_f64(compiler, op, dst, dstw, src, srcw)); \
+ ADJUST_LOCAL_OFFSET(dst, dstw); \
+ ADJUST_LOCAL_OFFSET(src, srcw); \
+ return sljit_emit_fop1_conv_sw_from_f64(compiler, op, dst, dstw, src, srcw); \
+ } \
+ CHECK(check_sljit_emit_fop1_conv_f64_from_sw(compiler, op, dst, dstw, src, srcw)); \
+ ADJUST_LOCAL_OFFSET(dst, dstw); \
+ ADJUST_LOCAL_OFFSET(src, srcw); \
+ return sljit_emit_fop1_conv_f64_from_sw(compiler, op, dst, dstw, src, srcw); \
+ } \
+ CHECK(check_sljit_emit_fop1(compiler, op, dst, dstw, src, srcw)); \
+ ADJUST_LOCAL_OFFSET(dst, dstw); \
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
+ || (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \
+ || ((defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) && !(defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1 && SLJIT_MIPS_REV < 6)) \
+ || (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) \
+ || (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+
+static SLJIT_INLINE sljit_s32 sljit_emit_cmov_generic(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ sljit_s32 op = (type & SLJIT_32) ? SLJIT_MOV32 : SLJIT_MOV;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ jump = sljit_emit_jump(compiler, (type & ~SLJIT_32) ^ 0x1);
+ FAIL_IF(!jump);
+
+ SLJIT_SKIP_CHECKS(compiler);
+ FAIL_IF(sljit_emit_op1(compiler, op, dst_reg, 0, src, srcw));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ label = sljit_emit_label(compiler);
+ FAIL_IF(!label);
+
+ sljit_set_label(jump, label);
+ return SLJIT_SUCCESS;
+}
+
+#endif
+
+#if (!(defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) || (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)) \
+ && !(defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+
+static sljit_s32 sljit_emit_mem_unaligned(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ SLJIT_SKIP_CHECKS(compiler);
+
+ if (type & SLJIT_MEM_STORE)
+ return sljit_emit_op1(compiler, type & (0xff | SLJIT_32), mem, memw, reg, 0);
+ return sljit_emit_op1(compiler, type & (0xff | SLJIT_32), reg, 0, mem, memw);
+}
+
+#endif /* (!SLJIT_CONFIG_MIPS || SLJIT_MIPS_REV >= 6) && !SLJIT_CONFIG_ARM_V5 */
+
+#if (!(defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) || (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)) \
+ && !(defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32)
+
+static sljit_s32 sljit_emit_fmem_unaligned(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ SLJIT_SKIP_CHECKS(compiler);
+
+ if (type & SLJIT_MEM_STORE)
+ return sljit_emit_fop1(compiler, type & (0xff | SLJIT_32), mem, memw, freg, 0);
+ return sljit_emit_fop1(compiler, type & (0xff | SLJIT_32), freg, 0, mem, memw);
+}
+
+#endif /* (!SLJIT_CONFIG_MIPS || SLJIT_MIPS_REV >= 6) && !SLJIT_CONFIG_ARM */
+
+/* CPU description section */
+
+#if (defined SLJIT_32BIT_ARCHITECTURE && SLJIT_32BIT_ARCHITECTURE)
+#define SLJIT_CPUINFO_PART1 " 32bit ("
+#elif (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+#define SLJIT_CPUINFO_PART1 " 64bit ("
+#else
+#error "Internal error: CPU type info missing"
+#endif
+
+#if (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#define SLJIT_CPUINFO_PART2 "little endian + "
+#elif (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN)
+#define SLJIT_CPUINFO_PART2 "big endian + "
+#else
+#error "Internal error: CPU type info missing"
+#endif
+
+#if (defined SLJIT_UNALIGNED && SLJIT_UNALIGNED)
+#define SLJIT_CPUINFO_PART3 "unaligned)"
+#else
+#define SLJIT_CPUINFO_PART3 "aligned)"
+#endif
+
+#define SLJIT_CPUINFO SLJIT_CPUINFO_PART1 SLJIT_CPUINFO_PART2 SLJIT_CPUINFO_PART3
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+# include "sljitNativeX86_common.c"
+#elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+# include "sljitNativeARM_32.c"
+#elif (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+# include "sljitNativeARM_32.c"
+#elif (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+# include "sljitNativeARM_T2_32.c"
+#elif (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+# include "sljitNativeARM_64.c"
+#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+# include "sljitNativePPC_common.c"
+#elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+# include "sljitNativeMIPS_common.c"
+#elif (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV)
+# include "sljitNativeRISCV_common.c"
+#elif (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+# include "sljitNativeS390X.c"
+#endif
+
+static SLJIT_INLINE sljit_s32 emit_mov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+ /* At the moment the pointer size is always equal to sljit_sw. May be changed in the future. */
+ if (src == SLJIT_RETURN_REG && (op == SLJIT_MOV || op == SLJIT_MOV_P))
+ return SLJIT_SUCCESS;
+#else
+ if (src == SLJIT_RETURN_REG && (op == SLJIT_MOV || op == SLJIT_MOV_U32 || op == SLJIT_MOV_S32 || op == SLJIT_MOV_P))
+ return SLJIT_SUCCESS;
+#endif
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op1(compiler, op, SLJIT_RETURN_REG, 0, src, srcw);
+}
+
+#if !(defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ && !((defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) && defined __SOFTFP__)
+
+static SLJIT_INLINE sljit_s32 emit_fmov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+ if (src == SLJIT_FR0)
+ return SLJIT_SUCCESS;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_fop1(compiler, op, SLJIT_RETURN_FREG, 0, src, srcw);
+}
+
+#endif /* !SLJIT_CONFIG_X86_32 && !(SLJIT_CONFIG_ARM_32 && __SOFTFP__) */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return(compiler, op, src, srcw));
+
+ if (GET_OPCODE(op) < SLJIT_MOV_F64) {
+ FAIL_IF(emit_mov_before_return(compiler, op, src, srcw));
+ } else {
+ FAIL_IF(emit_fmov_before_return(compiler, op, src, srcw));
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_return_void(compiler);
+}
+
+#if !(defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) \
+ && !(defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV)
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ /* Default compare for most architectures. */
+ sljit_s32 flags, tmp_src, condition;
+ sljit_sw tmp_srcw;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_cmp(compiler, type, src1, src1w, src2, src2w));
+
+ condition = type & 0xff;
+#if (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+ if ((condition == SLJIT_EQUAL || condition == SLJIT_NOT_EQUAL)) {
+ if ((src1 & SLJIT_IMM) && !src1w) {
+ src1 = src2;
+ src1w = src2w;
+ src2 = SLJIT_IMM;
+ src2w = 0;
+ }
+ if ((src2 & SLJIT_IMM) && !src2w)
+ return emit_cmp_to0(compiler, type, src1, src1w);
+ }
+#endif
+
+ if (SLJIT_UNLIKELY((src1 & SLJIT_IMM) && !(src2 & SLJIT_IMM))) {
+ /* Immediate is preferred as second argument by most architectures. */
+ switch (condition) {
+ case SLJIT_LESS:
+ condition = SLJIT_GREATER;
+ break;
+ case SLJIT_GREATER_EQUAL:
+ condition = SLJIT_LESS_EQUAL;
+ break;
+ case SLJIT_GREATER:
+ condition = SLJIT_LESS;
+ break;
+ case SLJIT_LESS_EQUAL:
+ condition = SLJIT_GREATER_EQUAL;
+ break;
+ case SLJIT_SIG_LESS:
+ condition = SLJIT_SIG_GREATER;
+ break;
+ case SLJIT_SIG_GREATER_EQUAL:
+ condition = SLJIT_SIG_LESS_EQUAL;
+ break;
+ case SLJIT_SIG_GREATER:
+ condition = SLJIT_SIG_LESS;
+ break;
+ case SLJIT_SIG_LESS_EQUAL:
+ condition = SLJIT_SIG_GREATER_EQUAL;
+ break;
+ }
+
+ type = condition | (type & (SLJIT_32 | SLJIT_REWRITABLE_JUMP));
+ tmp_src = src1;
+ src1 = src2;
+ src2 = tmp_src;
+ tmp_srcw = src1w;
+ src1w = src2w;
+ src2w = tmp_srcw;
+ }
+
+ if (condition <= SLJIT_NOT_ZERO)
+ flags = SLJIT_SET_Z;
+ else
+ flags = condition << VARIABLE_FLAG_SHIFT;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ PTR_FAIL_IF(sljit_emit_op2u(compiler,
+ SLJIT_SUB | flags | (type & SLJIT_32), src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, condition | (type & (SLJIT_REWRITABLE_JUMP | SLJIT_32)));
+}
+
+#endif /* !SLJIT_CONFIG_MIPS */
+
+#if (defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type)
+{
+ if (type < SLJIT_UNORDERED || type > SLJIT_ORDERED_LESS_EQUAL)
+ return 0;
+
+ switch (type) {
+ case SLJIT_UNORDERED_OR_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ return 0;
+ }
+
+ return 1;
+}
+
+#endif /* SLJIT_CONFIG_ARM */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_fcmp(compiler, type, src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ sljit_emit_fop1(compiler, SLJIT_CMP_F64 | ((type & 0xff) << VARIABLE_FLAG_SHIFT) | (type & SLJIT_32), src1, src1w, src2, src2w);
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+}
+
+#if !(defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM) \
+ && !(defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem_update(compiler, type, reg, mem, memw));
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(reg);
+ SLJIT_UNUSED_ARG(mem);
+ SLJIT_UNUSED_ARG(memw);
+
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+#endif /* !SLJIT_CONFIG_ARM && !SLJIT_CONFIG_PPC */
+
+#if !(defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) \
+ && !(defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw));
+
+ return sljit_emit_fmem_unaligned(compiler, type, freg, mem, memw);
+}
+
+#endif /* !SLJIT_CONFIG_ARM_32 && !SLJIT_CONFIG_MIPS */
+
+#if !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ && !(defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fmem_update(compiler, type, freg, mem, memw));
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(freg);
+ SLJIT_UNUSED_ARG(mem);
+ SLJIT_UNUSED_ARG(memw);
+
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+#endif /* !SLJIT_CONFIG_ARM_64 && !SLJIT_CONFIG_PPC */
+
+#if !(defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
+ && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_get_local_base(compiler, dst, dstw, offset));
+
+ ADJUST_LOCAL_OFFSET(SLJIT_MEM1(SLJIT_SP), offset);
+
+ SLJIT_SKIP_CHECKS(compiler);
+
+ if (offset != 0)
+ return sljit_emit_op2(compiler, SLJIT_ADD, dst, dstw, SLJIT_SP, 0, SLJIT_IMM, offset);
+ return sljit_emit_op1(compiler, SLJIT_MOV, dst, dstw, SLJIT_SP, 0);
+}
+
+#endif
+
+#else /* SLJIT_CONFIG_UNSUPPORTED */
+
+/* Empty function bodies for those machines, which are not (yet) supported. */
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+ return "unsupported";
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void *allocator_data, void *exec_allocator_data)
+{
+ SLJIT_UNUSED_ARG(allocator_data);
+ SLJIT_UNUSED_ARG(exec_allocator_data);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_compiler(struct sljit_compiler *compiler)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNREACHABLE();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_compiler_memory_error(struct sljit_compiler *compiler)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNREACHABLE();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_alloc_memory(struct sljit_compiler *compiler, sljit_s32 size)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(size);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+SLJIT_API_FUNC_ATTRIBUTE void sljit_compiler_verbose(struct sljit_compiler *compiler, FILE* verbose)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(verbose);
+ SLJIT_UNREACHABLE();
+}
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ SLJIT_UNUSED_ARG(feature_type);
+ SLJIT_UNREACHABLE();
+ return 0;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type)
+{
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNREACHABLE();
+ return 0;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code, void *exec_allocator_data)
+{
+ SLJIT_UNUSED_ARG(code);
+ SLJIT_UNUSED_ARG(exec_allocator_data);
+ SLJIT_UNREACHABLE();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(options);
+ SLJIT_UNUSED_ARG(arg_types);
+ SLJIT_UNUSED_ARG(scratches);
+ SLJIT_UNUSED_ARG(saveds);
+ SLJIT_UNUSED_ARG(fscratches);
+ SLJIT_UNUSED_ARG(fsaveds);
+ SLJIT_UNUSED_ARG(local_size);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(options);
+ SLJIT_UNUSED_ARG(arg_types);
+ SLJIT_UNUSED_ARG(scratches);
+ SLJIT_UNUSED_ARG(saveds);
+ SLJIT_UNUSED_ARG(fscratches);
+ SLJIT_UNUSED_ARG(fsaveds);
+ SLJIT_UNUSED_ARG(local_size);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ SLJIT_UNUSED_ARG(src1);
+ SLJIT_UNUSED_ARG(src1w);
+ SLJIT_UNUSED_ARG(src2);
+ SLJIT_UNUSED_ARG(src2w);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(src1);
+ SLJIT_UNUSED_ARG(src1w);
+ SLJIT_UNUSED_ARG(src2);
+ SLJIT_UNUSED_ARG(src2w);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(src_dst);
+ SLJIT_UNUSED_ARG(src1);
+ SLJIT_UNUSED_ARG(src1w);
+ SLJIT_UNUSED_ARG(src2);
+ SLJIT_UNUSED_ARG(src2w);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ SLJIT_UNREACHABLE();
+ return reg;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(instruction);
+ SLJIT_UNUSED_ARG(size);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_current_flags(struct sljit_compiler *compiler, sljit_s32 current_flags)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(current_flags);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ SLJIT_UNUSED_ARG(src1);
+ SLJIT_UNUSED_ARG(src1w);
+ SLJIT_UNUSED_ARG(src2);
+ SLJIT_UNUSED_ARG(src2w);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(arg_types);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(src1);
+ SLJIT_UNUSED_ARG(src1w);
+ SLJIT_UNUSED_ARG(src2);
+ SLJIT_UNUSED_ARG(src2w);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(src1);
+ SLJIT_UNUSED_ARG(src1w);
+ SLJIT_UNUSED_ARG(src2);
+ SLJIT_UNUSED_ARG(src2w);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_label(struct sljit_jump *jump, struct sljit_label* label)
+{
+ SLJIT_UNUSED_ARG(jump);
+ SLJIT_UNUSED_ARG(label);
+ SLJIT_UNREACHABLE();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_target(struct sljit_jump *jump, sljit_uw target)
+{
+ SLJIT_UNUSED_ARG(jump);
+ SLJIT_UNUSED_ARG(target);
+ SLJIT_UNREACHABLE();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_put_label(struct sljit_put_label *put_label, struct sljit_label *label)
+{
+ SLJIT_UNUSED_ARG(put_label);
+ SLJIT_UNUSED_ARG(label);
+ SLJIT_UNREACHABLE();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(arg_types);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(dst_reg);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 reg, sljit_s32 mem, sljit_sw memw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(reg);
+ SLJIT_UNUSED_ARG(mem);
+ SLJIT_UNUSED_ARG(memw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 reg, sljit_s32 mem, sljit_sw memw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(reg);
+ SLJIT_UNUSED_ARG(mem);
+ SLJIT_UNUSED_ARG(memw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 freg, sljit_s32 mem, sljit_sw memw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(freg);
+ SLJIT_UNUSED_ARG(mem);
+ SLJIT_UNUSED_ARG(memw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 freg, sljit_s32 mem, sljit_sw memw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(freg);
+ SLJIT_UNUSED_ARG(mem);
+ SLJIT_UNUSED_ARG(memw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ SLJIT_UNUSED_ARG(offset);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw initval)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ SLJIT_UNUSED_ARG(initval);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ SLJIT_UNUSED_ARG(addr);
+ SLJIT_UNUSED_ARG(new_target);
+ SLJIT_UNUSED_ARG(executable_offset);
+ SLJIT_UNREACHABLE();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ SLJIT_UNUSED_ARG(addr);
+ SLJIT_UNUSED_ARG(new_constant);
+ SLJIT_UNUSED_ARG(executable_offset);
+ SLJIT_UNREACHABLE();
+}
+
+#endif /* !SLJIT_CONFIG_UNSUPPORTED */
diff --git a/contrib/libs/pcre2/src/sljit/sljitLir.h b/contrib/libs/pcre2/src/sljit/sljitLir.h
new file mode 100644
index 00000000000..a666a2643f2
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitLir.h
@@ -0,0 +1,1823 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SLJIT_LIR_H_
+#define SLJIT_LIR_H_
+
+/*
+ ------------------------------------------------------------------------
+ Stack-Less JIT compiler for multiple architectures (x86, ARM, PowerPC)
+ ------------------------------------------------------------------------
+
+ Short description
+ Advantages:
+ - The execution can be continued from any LIR instruction. In other
+ words, it is possible to jump to any label from anywhere, even from
+ a code fragment, which is compiled later, as long as the compiling
+ context is the same. See sljit_emit_enter for more details.
+ - Supports self modifying code: target of any jump and call
+ instructions and some constant values can be dynamically modified
+ during runtime. See SLJIT_REWRITABLE_JUMP.
+ - although it is not suggested to do it frequently
+ - can be used for inline caching: save an important value once
+ in the instruction stream
+ - A fixed stack space can be allocated for local variables
+ - The compiler is thread-safe
+ - The compiler is highly configurable through preprocessor macros.
+ You can disable unneeded features (multithreading in single
+ threaded applications), and you can use your own system functions
+ (including memory allocators). See sljitConfig.h.
+ Disadvantages:
+ - The compiler is more like a platform independent assembler, so
+ there is no built-in variable management. Registers and stack must
+ be managed manually (the name of the compiler refers to this).
+ In practice:
+ - This approach is very effective for interpreters
+ - One of the saved registers typically points to a stack interface
+ - It can jump to any exception handler anytime (even if it belongs
+ to another function)
+ - Hot paths can be modified during runtime reflecting the changes
+ of the fastest execution path of the dynamic language
+ - SLJIT supports complex memory addressing modes
+ - mainly position and context independent code (except some cases)
+
+ For valgrind users:
+ - pass --smc-check=all argument to valgrind, since JIT is a "self-modifying code"
+*/
+
+#if (defined SLJIT_HAVE_CONFIG_PRE && SLJIT_HAVE_CONFIG_PRE)
+#error #include "sljitConfigPre.h"
+#endif /* SLJIT_HAVE_CONFIG_PRE */
+
+#include "sljitConfig.h"
+
+/* The following header file defines useful macros for fine tuning
+SLJIT based code generators. They are listed in the beginning
+of sljitConfigInternal.h */
+
+#include "sljitConfigInternal.h"
+
+#if (defined SLJIT_HAVE_CONFIG_POST && SLJIT_HAVE_CONFIG_POST)
+#error #include "sljitConfigPost.h"
+#endif /* SLJIT_HAVE_CONFIG_POST */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Version numbers. */
+#define SLJIT_MAJOR_VERSION 0
+#define SLJIT_MINOR_VERSION 95
+
+/* --------------------------------------------------------------------- */
+/* Error codes */
+/* --------------------------------------------------------------------- */
+
+/* Indicates no error. */
+#define SLJIT_SUCCESS 0
+/* After the call of sljit_generate_code(), the error code of the compiler
+ is set to this value to avoid further code generation.
+ The complier should be freed after sljit_generate_code(). */
+#define SLJIT_ERR_COMPILED 1
+/* Cannot allocate non-executable memory. */
+#define SLJIT_ERR_ALLOC_FAILED 2
+/* Cannot allocate executable memory.
+ Only sljit_generate_code() returns with this error code. */
+#define SLJIT_ERR_EX_ALLOC_FAILED 3
+/* Return value for SLJIT_CONFIG_UNSUPPORTED placeholder architecture. */
+#define SLJIT_ERR_UNSUPPORTED 4
+/* An ivalid argument is passed to any SLJIT function. */
+#define SLJIT_ERR_BAD_ARGUMENT 5
+
+/* --------------------------------------------------------------------- */
+/* Registers */
+/* --------------------------------------------------------------------- */
+
+/*
+ Scratch (R) registers: registers which may not preserve their values
+ across function calls.
+
+ Saved (S) registers: registers which preserve their values across
+ function calls.
+
+ The scratch and saved register sets overlap. The last scratch register
+ is the first saved register, the one before the last is the second saved
+ register, and so on.
+
+ If an architecture provides two scratch and three saved registers,
+ its scratch and saved register sets are the following:
+
+ R0 | | R0 is always a scratch register
+ R1 | | R1 is always a scratch register
+ [R2] | S2 | R2 and S2 represent the same physical register
+ [R3] | S1 | R3 and S1 represent the same physical register
+ [R4] | S0 | R4 and S0 represent the same physical register
+
+ Note: SLJIT_NUMBER_OF_SCRATCH_REGISTERS would be 2 and
+ SLJIT_NUMBER_OF_SAVED_REGISTERS would be 3 for this architecture.
+
+ Note: On all supported architectures SLJIT_NUMBER_OF_REGISTERS >= 12
+ and SLJIT_NUMBER_OF_SAVED_REGISTERS >= 6. However, 6 registers
+ are virtual on x86-32. See below.
+
+ The purpose of this definition is convenience: saved registers can
+ be used as extra scratch registers. For example four registers can
+ be specified as scratch registers and the fifth one as saved register
+ on the CPU above and any user code which requires four scratch
+ registers can run unmodified. The SLJIT compiler automatically saves
+ the content of the two extra scratch register on the stack. Scratch
+ registers can also be preserved by saving their value on the stack
+ but this needs to be done manually.
+
+ Note: To emphasize that registers assigned to R2-R4 are saved
+ registers, they are enclosed by square brackets.
+
+ Note: sljit_emit_enter and sljit_set_context defines whether a register
+ is S or R register. E.g: when 3 scratches and 1 saved is mapped
+ by sljit_emit_enter, the allowed register set will be: R0-R2 and
+ S0. Although S2 is mapped to the same position as R2, it does not
+ available in the current configuration. Furthermore the S1 register
+ is not available at all.
+*/
+
+/* Scratch registers. */
+#define SLJIT_R0 1
+#define SLJIT_R1 2
+#define SLJIT_R2 3
+/* Note: on x86-32, R3 - R6 (same as S3 - S6) are emulated (they
+ are allocated on the stack). These registers are called virtual
+ and cannot be used for memory addressing (cannot be part of
+ any SLJIT_MEM1, SLJIT_MEM2 construct). There is no such
+ limitation on other CPUs. See sljit_get_register_index(). */
+#define SLJIT_R3 4
+#define SLJIT_R4 5
+#define SLJIT_R5 6
+#define SLJIT_R6 7
+#define SLJIT_R7 8
+#define SLJIT_R8 9
+#define SLJIT_R9 10
+/* All R registers provided by the architecture can be accessed by SLJIT_R(i)
+ The i parameter must be >= 0 and < SLJIT_NUMBER_OF_REGISTERS. */
+#define SLJIT_R(i) (1 + (i))
+
+/* Saved registers. */
+#define SLJIT_S0 (SLJIT_NUMBER_OF_REGISTERS)
+#define SLJIT_S1 (SLJIT_NUMBER_OF_REGISTERS - 1)
+#define SLJIT_S2 (SLJIT_NUMBER_OF_REGISTERS - 2)
+/* Note: on x86-32, S3 - S6 (same as R3 - R6) are emulated (they
+ are allocated on the stack). These registers are called virtual
+ and cannot be used for memory addressing (cannot be part of
+ any SLJIT_MEM1, SLJIT_MEM2 construct). There is no such
+ limitation on other CPUs. See sljit_get_register_index(). */
+#define SLJIT_S3 (SLJIT_NUMBER_OF_REGISTERS - 3)
+#define SLJIT_S4 (SLJIT_NUMBER_OF_REGISTERS - 4)
+#define SLJIT_S5 (SLJIT_NUMBER_OF_REGISTERS - 5)
+#define SLJIT_S6 (SLJIT_NUMBER_OF_REGISTERS - 6)
+#define SLJIT_S7 (SLJIT_NUMBER_OF_REGISTERS - 7)
+#define SLJIT_S8 (SLJIT_NUMBER_OF_REGISTERS - 8)
+#define SLJIT_S9 (SLJIT_NUMBER_OF_REGISTERS - 9)
+/* All S registers provided by the architecture can be accessed by SLJIT_S(i)
+ The i parameter must be >= 0 and < SLJIT_NUMBER_OF_SAVED_REGISTERS. */
+#define SLJIT_S(i) (SLJIT_NUMBER_OF_REGISTERS - (i))
+
+/* Registers >= SLJIT_FIRST_SAVED_REG are saved registers. */
+#define SLJIT_FIRST_SAVED_REG (SLJIT_S0 - SLJIT_NUMBER_OF_SAVED_REGISTERS + 1)
+
+/* The SLJIT_SP provides direct access to the linear stack space allocated by
+ sljit_emit_enter. It can only be used in the following form: SLJIT_MEM1(SLJIT_SP).
+ The immediate offset is extended by the relative stack offset automatically.
+ The sljit_get_local_base can be used to obtain the real address of a value. */
+#define SLJIT_SP (SLJIT_NUMBER_OF_REGISTERS + 1)
+
+/* Return with machine word. */
+
+#define SLJIT_RETURN_REG SLJIT_R0
+
+/* --------------------------------------------------------------------- */
+/* Floating point registers */
+/* --------------------------------------------------------------------- */
+
+/* Each floating point register can store a 32 or a 64 bit precision
+ value. The FR and FS register sets are overlap in the same way as R
+ and S register sets. See above. */
+
+/* Floating point scratch registers. */
+#define SLJIT_FR0 1
+#define SLJIT_FR1 2
+#define SLJIT_FR2 3
+#define SLJIT_FR3 4
+#define SLJIT_FR4 5
+#define SLJIT_FR5 6
+/* All FR registers provided by the architecture can be accessed by SLJIT_FR(i)
+ The i parameter must be >= 0 and < SLJIT_NUMBER_OF_FLOAT_REGISTERS. */
+#define SLJIT_FR(i) (1 + (i))
+
+/* Floating point saved registers. */
+#define SLJIT_FS0 (SLJIT_NUMBER_OF_FLOAT_REGISTERS)
+#define SLJIT_FS1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS - 1)
+#define SLJIT_FS2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS - 2)
+#define SLJIT_FS3 (SLJIT_NUMBER_OF_FLOAT_REGISTERS - 3)
+#define SLJIT_FS4 (SLJIT_NUMBER_OF_FLOAT_REGISTERS - 4)
+#define SLJIT_FS5 (SLJIT_NUMBER_OF_FLOAT_REGISTERS - 5)
+/* All S registers provided by the architecture can be accessed by SLJIT_FS(i)
+ The i parameter must be >= 0 and < SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS. */
+#define SLJIT_FS(i) (SLJIT_NUMBER_OF_FLOAT_REGISTERS - (i))
+
+/* Float registers >= SLJIT_FIRST_SAVED_FLOAT_REG are saved registers. */
+#define SLJIT_FIRST_SAVED_FLOAT_REG (SLJIT_FS0 - SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS + 1)
+
+/* Return with floating point arg. */
+
+#define SLJIT_RETURN_FREG SLJIT_FR0
+
+/* --------------------------------------------------------------------- */
+/* Argument type definitions */
+/* --------------------------------------------------------------------- */
+
+/* The following argument type definitions are used by sljit_emit_enter,
+ sljit_set_context, sljit_emit_call, and sljit_emit_icall functions.
+
+ As for sljit_emit_call and sljit_emit_icall, the first integer argument
+ must be placed into SLJIT_R0, the second one into SLJIT_R1, and so on.
+ Similarly the first floating point argument must be placed into SLJIT_FR0,
+ the second one into SLJIT_FR1, and so on.
+
+ As for sljit_emit_enter, the integer arguments can be stored in scratch
+ or saved registers. The first integer argument without _R postfix is
+ stored in SLJIT_S0, the next one in SLJIT_S1, and so on. The integer
+ arguments with _R postfix are placed into scratch registers. The index
+ of the scratch register is the count of the previous integer arguments
+ starting from SLJIT_R0. The floating point arguments are always placed
+ into SLJIT_FR0, SLJIT_FR1, and so on.
+
+ Note: if a function is called by sljit_emit_call/sljit_emit_icall and
+ an argument is stored in a scratch register by sljit_emit_enter,
+ that argument uses the same scratch register index for both
+ integer and floating point arguments.
+
+ Example function definition:
+ sljit_f32 SLJIT_FUNC example_c_callback(void *arg_a,
+ sljit_f64 arg_b, sljit_u32 arg_c, sljit_f32 arg_d);
+
+ Argument type definition:
+ SLJIT_ARG_RETURN(SLJIT_ARG_TYPE_F32)
+ | SLJIT_ARG_VALUE(SLJIT_ARG_TYPE_P, 1) | SLJIT_ARG_VALUE(SLJIT_ARG_TYPE_F64, 2)
+ | SLJIT_ARG_VALUE(SLJIT_ARG_TYPE_32, 3) | SLJIT_ARG_VALUE(SLJIT_ARG_TYPE_F32, 4)
+
+ Short form of argument type definition:
+ SLJIT_ARGS4(32, P, F64, 32, F32)
+
+ Argument passing:
+ arg_a must be placed in SLJIT_R0
+ arg_c must be placed in SLJIT_R1
+ arg_b must be placed in SLJIT_FR0
+ arg_d must be placed in SLJIT_FR1
+
+ Examples for argument processing by sljit_emit_enter:
+ SLJIT_ARGS4(VOID, P, 32_R, F32, W)
+ Arguments are placed into: SLJIT_S0, SLJIT_R1, SLJIT_FR0, SLJIT_S1
+
+ SLJIT_ARGS4(VOID, W, W_R, W, W_R)
+ Arguments are placed into: SLJIT_S0, SLJIT_R1, SLJIT_S1, SLJIT_R3
+
+ SLJIT_ARGS4(VOID, F64, W, F32, W_R)
+ Arguments are placed into: SLJIT_FR0, SLJIT_S0, SLJIT_FR1, SLJIT_R1
+
+ Note: it is recommended to pass the scratch arguments first
+ followed by the saved arguments:
+
+ SLJIT_ARGS4(VOID, W_R, W_R, W, W)
+ Arguments are placed into: SLJIT_R0, SLJIT_R1, SLJIT_S0, SLJIT_S1
+*/
+
+/* The following flag is only allowed for the integer arguments of
+ sljit_emit_enter. When the flag is set, the integer argument is
+ stored in a scratch register instead of a saved register. */
+#define SLJIT_ARG_TYPE_SCRATCH_REG 0x8
+
+/* Void result, can only be used by SLJIT_ARG_RETURN. */
+#define SLJIT_ARG_TYPE_VOID 0
+/* Machine word sized integer argument or result. */
+#define SLJIT_ARG_TYPE_W 1
+#define SLJIT_ARG_TYPE_W_R (SLJIT_ARG_TYPE_W | SLJIT_ARG_TYPE_SCRATCH_REG)
+/* 32 bit integer argument or result. */
+#define SLJIT_ARG_TYPE_32 2
+#define SLJIT_ARG_TYPE_32_R (SLJIT_ARG_TYPE_32 | SLJIT_ARG_TYPE_SCRATCH_REG)
+/* Pointer sized integer argument or result. */
+#define SLJIT_ARG_TYPE_P 3
+#define SLJIT_ARG_TYPE_P_R (SLJIT_ARG_TYPE_P | SLJIT_ARG_TYPE_SCRATCH_REG)
+/* 64 bit floating point argument or result. */
+#define SLJIT_ARG_TYPE_F64 4
+/* 32 bit floating point argument or result. */
+#define SLJIT_ARG_TYPE_F32 5
+
+#define SLJIT_ARG_SHIFT 4
+#define SLJIT_ARG_RETURN(type) (type)
+#define SLJIT_ARG_VALUE(type, idx) ((type) << ((idx) * SLJIT_ARG_SHIFT))
+
+/* Simplified argument list definitions.
+
+ The following definition:
+ SLJIT_ARG_RETURN(SLJIT_ARG_TYPE_W) | SLJIT_ARG_VALUE(SLJIT_ARG_TYPE_F32, 1)
+
+ can be shortened to:
+ SLJIT_ARGS1(W, F32)
+*/
+
+#define SLJIT_ARG_TO_TYPE(type) SLJIT_ARG_TYPE_ ## type
+
+#define SLJIT_ARGS0(ret) \
+ SLJIT_ARG_RETURN(SLJIT_ARG_TO_TYPE(ret))
+
+#define SLJIT_ARGS1(ret, arg1) \
+ (SLJIT_ARGS0(ret) | SLJIT_ARG_VALUE(SLJIT_ARG_TO_TYPE(arg1), 1))
+
+#define SLJIT_ARGS2(ret, arg1, arg2) \
+ (SLJIT_ARGS1(ret, arg1) | SLJIT_ARG_VALUE(SLJIT_ARG_TO_TYPE(arg2), 2))
+
+#define SLJIT_ARGS3(ret, arg1, arg2, arg3) \
+ (SLJIT_ARGS2(ret, arg1, arg2) | SLJIT_ARG_VALUE(SLJIT_ARG_TO_TYPE(arg3), 3))
+
+#define SLJIT_ARGS4(ret, arg1, arg2, arg3, arg4) \
+ (SLJIT_ARGS3(ret, arg1, arg2, arg3) | SLJIT_ARG_VALUE(SLJIT_ARG_TO_TYPE(arg4), 4))
+
+/* --------------------------------------------------------------------- */
+/* Main structures and functions */
+/* --------------------------------------------------------------------- */
+
+/*
+ The following structures are private, and can be changed in the
+ future. Keeping them here allows code inlining.
+*/
+
+struct sljit_memory_fragment {
+ struct sljit_memory_fragment *next;
+ sljit_uw used_size;
+ /* Must be aligned to sljit_sw. */
+ sljit_u8 memory[1];
+};
+
+struct sljit_label {
+ struct sljit_label *next;
+ sljit_uw addr;
+ /* The maximum size difference. */
+ sljit_uw size;
+};
+
+struct sljit_jump {
+ struct sljit_jump *next;
+ sljit_uw addr;
+ /* Architecture dependent flags. */
+ sljit_uw flags;
+ union {
+ sljit_uw target;
+ struct sljit_label *label;
+ } u;
+};
+
+struct sljit_put_label {
+ struct sljit_put_label *next;
+ struct sljit_label *label;
+ sljit_uw addr;
+ sljit_uw flags;
+};
+
+struct sljit_const {
+ struct sljit_const *next;
+ sljit_uw addr;
+};
+
+struct sljit_compiler {
+ sljit_s32 error;
+ sljit_s32 options;
+
+ struct sljit_label *labels;
+ struct sljit_jump *jumps;
+ struct sljit_put_label *put_labels;
+ struct sljit_const *consts;
+ struct sljit_label *last_label;
+ struct sljit_jump *last_jump;
+ struct sljit_const *last_const;
+ struct sljit_put_label *last_put_label;
+
+ void *allocator_data;
+ void *exec_allocator_data;
+ struct sljit_memory_fragment *buf;
+ struct sljit_memory_fragment *abuf;
+
+ /* Available scratch registers. */
+ sljit_s32 scratches;
+ /* Available saved registers. */
+ sljit_s32 saveds;
+ /* Available float scratch registers. */
+ sljit_s32 fscratches;
+ /* Available float saved registers. */
+ sljit_s32 fsaveds;
+ /* Local stack size. */
+ sljit_s32 local_size;
+ /* Maximum code size. */
+ sljit_uw size;
+ /* Relative offset of the executable mapping from the writable mapping. */
+ sljit_sw executable_offset;
+ /* Executable size for statistical purposes. */
+ sljit_uw executable_size;
+
+#if (defined SLJIT_HAS_STATUS_FLAGS_STATE && SLJIT_HAS_STATUS_FLAGS_STATE)
+ sljit_s32 status_flags_state;
+#endif
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ sljit_s32 args_size;
+#endif
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ sljit_s32 mode32;
+#endif
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ /* Constant pool handling. */
+ sljit_uw *cpool;
+ sljit_u8 *cpool_unique;
+ sljit_uw cpool_diff;
+ sljit_uw cpool_fill;
+ /* Other members. */
+ /* Contains pointer, "ldr pc, [...]" pairs. */
+ sljit_uw patches;
+#endif
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+ /* Temporary fields. */
+ sljit_uw shift_imm;
+#endif /* SLJIT_CONFIG_ARM_V5 || SLJIT_CONFIG_ARM_V7 */
+
+#if (defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) && (defined __SOFTFP__)
+ sljit_uw args_size;
+#endif
+
+#if (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+ sljit_u32 imm;
+#endif
+
+#if (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+ sljit_s32 delay_slot;
+ sljit_s32 cache_arg;
+ sljit_sw cache_argw;
+#endif
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ sljit_uw args_size;
+#endif
+
+#if (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV)
+ sljit_s32 cache_arg;
+ sljit_sw cache_argw;
+#endif
+
+#if (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+ /* Need to allocate register save area to make calls. */
+ sljit_s32 mode;
+#endif
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ FILE* verbose;
+#endif
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \
+ || (defined SLJIT_DEBUG && SLJIT_DEBUG)
+ /* Flags specified by the last arithmetic instruction.
+ It contains the type of the variable flag. */
+ sljit_s32 last_flags;
+ /* Return value type set by entry functions. */
+ sljit_s32 last_return;
+ /* Local size passed to entry functions. */
+ sljit_s32 logical_local_size;
+#endif
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \
+ || (defined SLJIT_DEBUG && SLJIT_DEBUG) \
+ || (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ /* Trust arguments when an API function is called.
+ Used internally for calling API functions. */
+ sljit_s32 skip_checks;
+#endif
+};
+
+/* --------------------------------------------------------------------- */
+/* Main functions */
+/* --------------------------------------------------------------------- */
+
+/* Creates an SLJIT compiler. The allocator_data is required by some
+ custom memory managers. This pointer is passed to SLJIT_MALLOC
+ and SLJIT_FREE macros. Most allocators (including the default
+ one) ignores this value, and it is recommended to pass NULL
+ as a dummy value for allocator_data. The exec_allocator_data
+ has the same purpose but this one is passed to SLJIT_MALLOC_EXEC /
+ SLJIT_MALLOC_FREE functions.
+
+ Returns NULL if failed. */
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void *allocator_data, void *exec_allocator_data);
+
+/* Frees everything except the compiled machine code. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_compiler(struct sljit_compiler *compiler);
+
+/* Returns the current error code. If an error occurres, future calls
+ which uses the same compiler argument returns early with the same
+ error code. Thus there is no need for checking the error after every
+ call, it is enough to do it after the code is compiled. Removing
+ these checks increases the performance of the compiling process. */
+static SLJIT_INLINE sljit_s32 sljit_get_compiler_error(struct sljit_compiler *compiler) { return compiler->error; }
+
+/* Sets the compiler error code to SLJIT_ERR_ALLOC_FAILED except
+ if an error was detected before. After the error code is set
+ the compiler behaves as if the allocation failure happened
+ during an SLJIT function call. This can greatly simplify error
+ checking, since it is enough to check the compiler status
+ after the code is compiled. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_compiler_memory_error(struct sljit_compiler *compiler);
+
+/*
+ Allocate a small amount of memory. The size must be <= 64 bytes on 32 bit,
+ and <= 128 bytes on 64 bit architectures. The memory area is owned by the
+ compiler, and freed by sljit_free_compiler. The returned pointer is
+ sizeof(sljit_sw) aligned. Excellent for allocating small blocks during
+ compiling, and no need to worry about freeing them. The size is enough
+ to contain at most 16 pointers. If the size is outside of the range,
+ the function will return with NULL. However, this return value does not
+ indicate that there is no more memory (does not set the current error code
+ of the compiler to out-of-memory status).
+*/
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_alloc_memory(struct sljit_compiler *compiler, sljit_s32 size);
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+/* Passing NULL disables verbose. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_compiler_verbose(struct sljit_compiler *compiler, FILE* verbose);
+#endif
+
+/*
+ Create executable code from the instruction stream. This is the final step
+ of the code generation so no more instructions can be emitted after this call.
+*/
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler);
+
+/* Free executable code. */
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code, void *exec_allocator_data);
+
+/*
+ When the protected executable allocator is used the JIT code is mapped
+ twice. The first mapping has read/write and the second mapping has read/exec
+ permissions. This function returns with the relative offset of the executable
+ mapping using the writable mapping as the base after the machine code is
+ successfully generated. The returned value is always 0 for the normal executable
+ allocator, since it uses only one mapping with read/write/exec permissions.
+ Dynamic code modifications requires this value.
+
+ Before a successful code generation, this function returns with 0.
+*/
+static SLJIT_INLINE sljit_sw sljit_get_executable_offset(struct sljit_compiler *compiler) { return compiler->executable_offset; }
+
+/*
+ The executable memory consumption of the generated code can be retrieved by
+ this function. The returned value can be used for statistical purposes.
+
+ Before a successful code generation, this function returns with 0.
+*/
+static SLJIT_INLINE sljit_uw sljit_get_generated_code_size(struct sljit_compiler *compiler) { return compiler->executable_size; }
+
+/* Returns with non-zero if the feature or limitation type passed as its
+ argument is present on the current CPU. The return value is one, if a
+ feature is fully supported, and it is two, if partially supported.
+
+ Some features (e.g. floating point operations) require hardware (CPU)
+ support while others (e.g. move with update) are emulated if not available.
+ However, even when a feature is emulated, specialized code paths may be
+ faster than the emulation. Some limitations are emulated as well so their
+ general case is supported but it has extra performance costs. */
+
+/* [Not emulated] Floating-point support is available. */
+#define SLJIT_HAS_FPU 0
+/* [Limitation] Some registers are virtual registers. */
+#define SLJIT_HAS_VIRTUAL_REGISTERS 1
+/* [Emulated] Has zero register (setting a memory location to zero is efficient). */
+#define SLJIT_HAS_ZERO_REGISTER 2
+/* [Emulated] Count leading zero is supported. */
+#define SLJIT_HAS_CLZ 3
+/* [Emulated] Count trailing zero is supported. */
+#define SLJIT_HAS_CTZ 4
+/* [Emulated] Rotate left/right is supported. */
+#define SLJIT_HAS_ROT 5
+/* [Emulated] Conditional move is supported. */
+#define SLJIT_HAS_CMOV 6
+/* [Emulated] Prefetch instruction is available (emulated as a nop). */
+#define SLJIT_HAS_PREFETCH 7
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+/* [Not emulated] SSE2 support is available on x86. */
+#define SLJIT_HAS_SSE2 100
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type);
+
+/* If type is between SLJIT_ORDERED_EQUAL and SLJIT_ORDERED_LESS_EQUAL,
+ sljit_cmp_info returns one, if the cpu supports the passed floating
+ point comparison type.
+
+ If type is SLJIT_UNORDERED or SLJIT_ORDERED, sljit_cmp_info returns
+ one, if the cpu supports checking the unordered comparison result
+ regardless of the comparison type passed to the comparison instruction.
+ The returned value is always one, if there is at least one type between
+ SLJIT_ORDERED_EQUAL and SLJIT_ORDERED_LESS_EQUAL where sljit_cmp_info
+ returns with a zero value.
+
+ Otherwise it returns zero. */
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type);
+
+/* The following functions generate machine code. If there is no
+ error, they return with SLJIT_SUCCESS, otherwise they return
+ with an error code. */
+
+/*
+ The executable code is a function from the viewpoint of the C
+ language. The function calls must obey to the ABI (Application
+ Binary Interface) of the platform, which specify the purpose of
+ machine registers and stack handling among other things. The
+ sljit_emit_enter function emits the necessary instructions for
+ setting up a new context for the executable code. This is often
+ called as function prologue. Furthermore the options argument
+ can be used to pass configuration options to the compiler. The
+ available options are listed before sljit_emit_enter.
+
+ The function argument list is specified by the SLJIT_ARGSx
+ (SLJIT_ARGS0 .. SLJIT_ARGS4) macros. Currently maximum four
+ arguments are supported. See the description of SLJIT_ARGSx
+ macros about argument passing. Furthermore the register set
+ used by the function must be declared as well. The number of
+ scratch and saved registers available to the function must
+ be passed to sljit_emit_enter. Only R registers between R0
+ and "scratches" argument can be used later. E.g. if "scratches"
+ is set to two, the scratch register set will be limited to
+ SLJIT_R0 and SLJIT_R1. The S registers and the floating point
+ registers ("fscratches" and "fsaveds") are specified in a
+ similar manner. The sljit_emit_enter is also capable of
+ allocating a stack space for local data. The "local_size"
+ argument contains the size in bytes of this local area, and
+ it can be accessed using SLJIT_MEM1(SLJIT_SP). The memory
+ area between SLJIT_SP (inclusive) and SLJIT_SP + local_size
+ (exclusive) can be modified freely until the function returns.
+ The stack space is not initialized to zero.
+
+ Note: the following conditions must met:
+ 0 <= scratches <= SLJIT_NUMBER_OF_REGISTERS
+ 0 <= saveds <= SLJIT_NUMBER_OF_SAVED_REGISTERS
+ scratches + saveds <= SLJIT_NUMBER_OF_REGISTERS
+ 0 <= fscratches <= SLJIT_NUMBER_OF_FLOAT_REGISTERS
+ 0 <= fsaveds <= SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS
+ fscratches + fsaveds <= SLJIT_NUMBER_OF_FLOAT_REGISTERS
+
+ Note: the compiler can use saved registers as scratch registers,
+ but the opposite is not supported
+
+ Note: every call of sljit_emit_enter and sljit_set_context
+ overwrites the previous context.
+*/
+
+/* Saved registers between SLJIT_S0 and SLJIT_S(n - 1) (inclusive)
+ are not saved / restored on function enter / return. Instead,
+ these registers can be used to pass / return data (such as
+ global / local context pointers) across function calls. The
+ value of n must be between 1 and 3. This option is only
+ supported by SLJIT_ENTER_REG_ARG calling convention. */
+#define SLJIT_ENTER_KEEP(n) (n)
+
+/* The compiled function uses an SLJIT specific register argument
+ calling convention. This is a lightweight function call type where
+ both the caller and the called functions must be compiled by
+ SLJIT. The type argument of the call must be SLJIT_CALL_REG_ARG
+ and all arguments must be stored in scratch registers. */
+#define SLJIT_ENTER_REG_ARG 0x00000004
+
+/* The local_size must be >= 0 and <= SLJIT_MAX_LOCAL_SIZE. */
+#define SLJIT_MAX_LOCAL_SIZE 65536
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size);
+
+/* The SLJIT compiler has a current context (which contains the local
+ stack space size, number of used registers, etc.) which is initialized
+ by sljit_emit_enter. Several functions (such as sljit_emit_return)
+ requires this context to be able to generate the appropriate code.
+ However, some code fragments (compiled separately) may have no
+ normal entry point so their context is unknown for the compiler.
+
+ The sljit_set_context and sljit_emit_enter have the same arguments,
+ but sljit_set_context does not generate any machine code.
+
+ Note: every call of sljit_emit_enter and sljit_set_context overwrites
+ the previous context. */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size);
+
+/* Return to the caller function. The sljit_emit_return_void function
+ does not return with any value. The sljit_emit_return function returns
+ with a single value loaded from its source operand. The load operation
+ can be between SLJIT_MOV and SLJIT_MOV_P (see sljit_emit_op1) and
+ SLJIT_MOV_F32/SLJIT_MOV_F64 (see sljit_emit_fop1) depending on the
+ return value specified by sljit_emit_enter/sljit_set_context. */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler);
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw);
+
+/* Restores the saved registers and free the stack area, then the execution
+ continues from the address specified by the source operand. This
+ operation is similar to sljit_emit_return, but it ignores the return
+ address. The code where the exection continues should use the same context
+ as the caller function (see sljit_set_context). A word (pointer) value
+ can be passed in the SLJIT_RETURN_REG register. This function can be used
+ to jump to exception handlers. */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw);
+
+/* Generating entry and exit points for fast call functions (see SLJIT_FAST_CALL).
+ Both sljit_emit_fast_enter and SLJIT_FAST_RETURN operations preserve the
+ values of all registers and stack frame. The return address is stored in the
+ dst argument of sljit_emit_fast_enter, and this return address can be passed
+ to SLJIT_FAST_RETURN to continue the execution after the fast call.
+
+ Fast calls are cheap operations (usually only a single call instruction is
+ emitted) but they do not preserve any registers. However the callee function
+ can freely use / update any registers and the local area which can be
+ efficiently exploited by various optimizations. Registers can be saved
+ and restored manually if needed.
+
+ Although returning to different address by SLJIT_FAST_RETURN is possible,
+ this address usually cannot be predicted by the return address predictor of
+ modern CPUs which may reduce performance. Furthermore certain security
+ enhancement technologies such as Intel Control-flow Enforcement Technology
+ (CET) may disallow returning to a different address.
+
+ Flags: - (does not modify flags). */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw);
+
+/*
+ Source and destination operands for arithmetical instructions
+ imm - a simple immediate value (cannot be used as a destination)
+ reg - any of the available registers (immediate argument must be 0)
+ [imm] - absolute memory address
+ [reg+imm] - indirect memory address
+ [reg+(reg<<imm)] - indirect indexed memory address (shift must be between 0 and 3)
+ useful for accessing arrays (fully supported by both x86 and
+ ARM architectures, and cheap operation on others)
+*/
+
+/*
+ IMPORTANT NOTE: memory accesses MUST be naturally aligned unless
+ SLJIT_UNALIGNED macro is defined and its value is 1.
+
+ length | alignment
+ ---------+-----------
+ byte | 1 byte (any physical_address is accepted)
+ half | 2 byte (physical_address & 0x1 == 0)
+ int | 4 byte (physical_address & 0x3 == 0)
+ word | 4 byte if SLJIT_32BIT_ARCHITECTURE is defined and its value is 1
+ | 8 byte if SLJIT_64BIT_ARCHITECTURE is defined and its value is 1
+ pointer | size of sljit_p type (4 byte on 32 bit machines, 4 or 8 byte
+ | on 64 bit machines)
+
+ Note: Different architectures have different addressing limitations.
+ A single instruction is enough for the following addressing
+ modes. Other adrressing modes are emulated by instruction
+ sequences. This information could help to improve those code
+ generators which focuses only a few architectures.
+
+ x86: [reg+imm], -2^32+1 <= imm <= 2^32-1 (full address space on x86-32)
+ [reg+(reg<<imm)] is supported
+ [imm], -2^32+1 <= imm <= 2^32-1 is supported
+ Write-back is not supported
+ arm: [reg+imm], -4095 <= imm <= 4095 or -255 <= imm <= 255 for signed
+ bytes, any halfs or floating point values)
+ [reg+(reg<<imm)] is supported
+ Write-back is supported
+ arm-t2: [reg+imm], -255 <= imm <= 4095
+ [reg+(reg<<imm)] is supported
+ Write back is supported only for [reg+imm], where -255 <= imm <= 255
+ arm64: [reg+imm], -256 <= imm <= 255, 0 <= aligned imm <= 4095 * alignment
+ [reg+(reg<<imm)] is supported
+ Write back is supported only for [reg+imm], where -256 <= imm <= 255
+ ppc: [reg+imm], -65536 <= imm <= 65535. 64 bit loads/stores and 32 bit
+ signed load on 64 bit requires immediates divisible by 4.
+ [reg+imm] is not supported for signed 8 bit values.
+ [reg+reg] is supported
+ Write-back is supported except for one instruction: 32 bit signed
+ load with [reg+imm] addressing mode on 64 bit.
+ mips: [reg+imm], -65536 <= imm <= 65535
+ Write-back is not supported
+ riscv: [reg+imm], -2048 <= imm <= 2047
+ Write-back is not supported
+ s390x: [reg+imm], -2^19 <= imm < 2^19
+ [reg+reg] is supported
+ Write-back is not supported
+*/
+
+/* Macros for specifying operand types. */
+#define SLJIT_MEM 0x80
+#define SLJIT_MEM0() (SLJIT_MEM)
+#define SLJIT_MEM1(r1) (SLJIT_MEM | (r1))
+#define SLJIT_MEM2(r1, r2) (SLJIT_MEM | (r1) | ((r2) << 8))
+#define SLJIT_IMM 0x40
+#define SLJIT_REG_PAIR(r1, r2) ((r1) | ((r2) << 8))
+
+/* Sets 32 bit operation mode on 64 bit CPUs. This option is ignored on
+ 32 bit CPUs. When this option is set for an arithmetic operation, only
+ the lower 32 bits of the input registers are used, and the CPU status
+ flags are set according to the 32 bit result. Although the higher 32 bit
+ of the input and the result registers are not defined by SLJIT, it might
+ be defined by the CPU architecture (e.g. MIPS). To satisfy these CPU
+ requirements all source registers must be the result of those operations
+ where this option was also set. Memory loads read 32 bit values rather
+ than 64 bit ones. In other words 32 bit and 64 bit operations cannot be
+ mixed. The only exception is SLJIT_MOV32 which source register can hold
+ any 32 or 64 bit value, and it is converted to a 32 bit compatible format
+ first. When the source and destination registers are the same, this
+ conversion is free (no instructions are emitted) on most CPUs. A 32 bit
+ value can also be converted to a 64 bit value by SLJIT_MOV_S32
+ (sign extension) or SLJIT_MOV_U32 (zero extension).
+
+ As for floating-point operations, this option sets 32 bit single
+ precision mode. Similar to the integer operations, all register arguments
+ must be the result of those operations where this option was also set.
+
+ Note: memory addressing always uses 64 bit values on 64 bit systems so
+ the result of a 32 bit operation must not be used with SLJIT_MEMx
+ macros.
+
+ This option is part of the instruction name, so there is no need to
+ manually set it. E.g:
+
+ SLJIT_ADD32 == (SLJIT_ADD | SLJIT_32) */
+#define SLJIT_32 0x100
+
+/* Many CPUs (x86, ARM, PPC) have status flag bits which can be set according
+ to the result of an operation. Other CPUs (MIPS) do not have status
+ flag bits, and results must be stored in registers. To cover both
+ architecture types efficiently only two flags are defined by SLJIT:
+
+ * Zero (equal) flag: it is set if the result is zero
+ * Variable flag: its value is defined by the arithmetic operation
+
+ SLJIT instructions can set any or both of these flags. The value of
+ these flags is undefined if the instruction does not specify their
+ value. The description of each instruction contains the list of
+ allowed flag types.
+
+ Note: the logical or operation can be used to set flags.
+
+ Example: SLJIT_ADD can set the Z, OVERFLOW, CARRY flags hence
+
+ sljit_op2(..., SLJIT_ADD, ...)
+ Both the zero and variable flags are undefined so they can
+ have any value after the operation is completed.
+
+ sljit_op2(..., SLJIT_ADD | SLJIT_SET_Z, ...)
+ Sets the zero flag if the result is zero, clears it otherwise.
+ The variable flag is undefined.
+
+ sljit_op2(..., SLJIT_ADD | SLJIT_SET_OVERFLOW, ...)
+ Sets the variable flag if an integer overflow occurs, clears
+ it otherwise. The zero flag is undefined.
+
+ sljit_op2(..., SLJIT_ADD | SLJIT_SET_Z | SLJIT_SET_CARRY, ...)
+ Sets the zero flag if the result is zero, clears it otherwise.
+ Sets the variable flag if unsigned overflow (carry) occurs,
+ clears it otherwise.
+
+ Certain instructions (e.g. SLJIT_MOV) does not modify flags, so
+ status flags are unchanged.
+
+ Example:
+
+ sljit_op2(..., SLJIT_ADD | SLJIT_SET_Z, ...)
+ sljit_op1(..., SLJIT_MOV, ...)
+ Zero flag is set according to the result of SLJIT_ADD.
+
+ sljit_op2(..., SLJIT_ADD | SLJIT_SET_Z, ...)
+ sljit_op2(..., SLJIT_ADD, ...)
+ Zero flag has unknown value.
+
+ These flags can be used for code optimization. E.g. a fast loop can be
+ implemented by decreasing a counter register and set the zero flag
+ using a single instruction. The zero register can be used by a
+ conditional jump to restart the loop. A single comparison can set a
+ zero and less flags to check if a value is less, equal, or greater
+ than another value.
+
+ Motivation: although some CPUs can set a large number of flag bits,
+ usually their values are ignored or only a few of them are used. Emulating
+ a large number of flags on systems without a flag register is complicated
+ so SLJIT instructions must specify the flag they want to use and only
+ that flag is computed. The last arithmetic instruction can be repeated if
+ multiple flags need to be checked.
+*/
+
+/* Set Zero status flag. */
+#define SLJIT_SET_Z 0x0200
+/* Set the variable status flag if condition is true.
+ See comparison types (e.g. SLJIT_SET_LESS, SLJIT_SET_F_EQUAL). */
+#define SLJIT_SET(condition) ((condition) << 10)
+
+/* Starting index of opcodes for sljit_emit_op0. */
+#define SLJIT_OP0_BASE 0
+
+/* Flags: - (does not modify flags)
+ Note: breakpoint instruction is not supported by all architectures (e.g. ppc)
+ It falls back to SLJIT_NOP in those cases. */
+#define SLJIT_BREAKPOINT (SLJIT_OP0_BASE + 0)
+/* Flags: - (does not modify flags)
+ Note: may or may not cause an extra cycle wait
+ it can even decrease the runtime in a few cases. */
+#define SLJIT_NOP (SLJIT_OP0_BASE + 1)
+/* Flags: - (may destroy flags)
+ Unsigned multiplication of SLJIT_R0 and SLJIT_R1.
+ Result is placed into SLJIT_R1:SLJIT_R0 (high:low) word */
+#define SLJIT_LMUL_UW (SLJIT_OP0_BASE + 2)
+/* Flags: - (may destroy flags)
+ Signed multiplication of SLJIT_R0 and SLJIT_R1.
+ Result is placed into SLJIT_R1:SLJIT_R0 (high:low) word */
+#define SLJIT_LMUL_SW (SLJIT_OP0_BASE + 3)
+/* Flags: - (may destroy flags)
+ Unsigned divide of the value in SLJIT_R0 by the value in SLJIT_R1.
+ The result is placed into SLJIT_R0 and the remainder into SLJIT_R1.
+ Note: if SLJIT_R1 is 0, the behaviour is undefined. */
+#define SLJIT_DIVMOD_UW (SLJIT_OP0_BASE + 4)
+#define SLJIT_DIVMOD_U32 (SLJIT_DIVMOD_UW | SLJIT_32)
+/* Flags: - (may destroy flags)
+ Signed divide of the value in SLJIT_R0 by the value in SLJIT_R1.
+ The result is placed into SLJIT_R0 and the remainder into SLJIT_R1.
+ Note: if SLJIT_R1 is 0, the behaviour is undefined.
+ Note: if SLJIT_R1 is -1 and SLJIT_R0 is integer min (0x800..00),
+ the behaviour is undefined. */
+#define SLJIT_DIVMOD_SW (SLJIT_OP0_BASE + 5)
+#define SLJIT_DIVMOD_S32 (SLJIT_DIVMOD_SW | SLJIT_32)
+/* Flags: - (may destroy flags)
+ Unsigned divide of the value in SLJIT_R0 by the value in SLJIT_R1.
+ The result is placed into SLJIT_R0. SLJIT_R1 preserves its value.
+ Note: if SLJIT_R1 is 0, the behaviour is undefined. */
+#define SLJIT_DIV_UW (SLJIT_OP0_BASE + 6)
+#define SLJIT_DIV_U32 (SLJIT_DIV_UW | SLJIT_32)
+/* Flags: - (may destroy flags)
+ Signed divide of the value in SLJIT_R0 by the value in SLJIT_R1.
+ The result is placed into SLJIT_R0. SLJIT_R1 preserves its value.
+ Note: if SLJIT_R1 is 0, the behaviour is undefined.
+ Note: if SLJIT_R1 is -1 and SLJIT_R0 is integer min (0x800..00),
+ the behaviour is undefined. */
+#define SLJIT_DIV_SW (SLJIT_OP0_BASE + 7)
+#define SLJIT_DIV_S32 (SLJIT_DIV_SW | SLJIT_32)
+/* Flags: - (does not modify flags)
+ ENDBR32 instruction for x86-32 and ENDBR64 instruction for x86-64
+ when Intel Control-flow Enforcement Technology (CET) is enabled.
+ No instructions are emitted for other architectures. */
+#define SLJIT_ENDBR (SLJIT_OP0_BASE + 8)
+/* Flags: - (may destroy flags)
+ Skip stack frames before return when Intel Control-flow
+ Enforcement Technology (CET) is enabled. No instructions
+ are emitted for other architectures. */
+#define SLJIT_SKIP_FRAMES_BEFORE_RETURN (SLJIT_OP0_BASE + 9)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op);
+
+/* Starting index of opcodes for sljit_emit_op1. */
+#define SLJIT_OP1_BASE 32
+
+/* The MOV instruction transfers data from source to destination.
+
+ MOV instruction suffixes:
+
+ U8 - unsigned 8 bit data transfer
+ S8 - signed 8 bit data transfer
+ U16 - unsigned 16 bit data transfer
+ S16 - signed 16 bit data transfer
+ U32 - unsigned int (32 bit) data transfer
+ S32 - signed int (32 bit) data transfer
+ P - pointer (sljit_p) data transfer
+*/
+
+/* Flags: - (does not modify flags) */
+#define SLJIT_MOV (SLJIT_OP1_BASE + 0)
+/* Flags: - (does not modify flags) */
+#define SLJIT_MOV_U8 (SLJIT_OP1_BASE + 1)
+#define SLJIT_MOV32_U8 (SLJIT_MOV_U8 | SLJIT_32)
+/* Flags: - (does not modify flags) */
+#define SLJIT_MOV_S8 (SLJIT_OP1_BASE + 2)
+#define SLJIT_MOV32_S8 (SLJIT_MOV_S8 | SLJIT_32)
+/* Flags: - (does not modify flags) */
+#define SLJIT_MOV_U16 (SLJIT_OP1_BASE + 3)
+#define SLJIT_MOV32_U16 (SLJIT_MOV_U16 | SLJIT_32)
+/* Flags: - (does not modify flags) */
+#define SLJIT_MOV_S16 (SLJIT_OP1_BASE + 4)
+#define SLJIT_MOV32_S16 (SLJIT_MOV_S16 | SLJIT_32)
+/* Flags: - (does not modify flags)
+ Note: no SLJIT_MOV32_U32 form, since it is the same as SLJIT_MOV32 */
+#define SLJIT_MOV_U32 (SLJIT_OP1_BASE + 5)
+/* Flags: - (does not modify flags)
+ Note: no SLJIT_MOV32_S32 form, since it is the same as SLJIT_MOV32 */
+#define SLJIT_MOV_S32 (SLJIT_OP1_BASE + 6)
+/* Flags: - (does not modify flags) */
+#define SLJIT_MOV32 (SLJIT_OP1_BASE + 7)
+/* Flags: - (does not modify flags)
+ Note: loads a pointer sized data, useful on x32 mode (a 64 bit mode
+ on x86-64 which uses 32 bit pointers) or similar compiling modes */
+#define SLJIT_MOV_P (SLJIT_OP1_BASE + 8)
+/* Flags: Z
+ Note: immediate source argument is not supported */
+#define SLJIT_NOT (SLJIT_OP1_BASE + 9)
+#define SLJIT_NOT32 (SLJIT_NOT | SLJIT_32)
+/* Count leading zeroes
+ Flags: - (may destroy flags)
+ Note: immediate source argument is not supported */
+#define SLJIT_CLZ (SLJIT_OP1_BASE + 10)
+#define SLJIT_CLZ32 (SLJIT_CLZ | SLJIT_32)
+/* Count trailing zeroes
+ Flags: - (may destroy flags)
+ Note: immediate source argument is not supported */
+#define SLJIT_CTZ (SLJIT_OP1_BASE + 11)
+#define SLJIT_CTZ32 (SLJIT_CTZ | SLJIT_32)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw);
+
+/* Starting index of opcodes for sljit_emit_op2. */
+#define SLJIT_OP2_BASE 96
+
+/* Flags: Z | OVERFLOW | CARRY */
+#define SLJIT_ADD (SLJIT_OP2_BASE + 0)
+#define SLJIT_ADD32 (SLJIT_ADD | SLJIT_32)
+/* Flags: CARRY */
+#define SLJIT_ADDC (SLJIT_OP2_BASE + 1)
+#define SLJIT_ADDC32 (SLJIT_ADDC | SLJIT_32)
+/* Flags: Z | LESS | GREATER_EQUAL | GREATER | LESS_EQUAL
+ SIG_LESS | SIG_GREATER_EQUAL | SIG_GREATER
+ SIG_LESS_EQUAL | OVERFLOW | CARRY */
+#define SLJIT_SUB (SLJIT_OP2_BASE + 2)
+#define SLJIT_SUB32 (SLJIT_SUB | SLJIT_32)
+/* Flags: CARRY */
+#define SLJIT_SUBC (SLJIT_OP2_BASE + 3)
+#define SLJIT_SUBC32 (SLJIT_SUBC | SLJIT_32)
+/* Note: integer mul
+ Flags: OVERFLOW */
+#define SLJIT_MUL (SLJIT_OP2_BASE + 4)
+#define SLJIT_MUL32 (SLJIT_MUL | SLJIT_32)
+/* Flags: Z */
+#define SLJIT_AND (SLJIT_OP2_BASE + 5)
+#define SLJIT_AND32 (SLJIT_AND | SLJIT_32)
+/* Flags: Z */
+#define SLJIT_OR (SLJIT_OP2_BASE + 6)
+#define SLJIT_OR32 (SLJIT_OR | SLJIT_32)
+/* Flags: Z */
+#define SLJIT_XOR (SLJIT_OP2_BASE + 7)
+#define SLJIT_XOR32 (SLJIT_XOR | SLJIT_32)
+/* Flags: Z
+ Let bit_length be the length of the shift operation: 32 or 64.
+ If src2 is immediate, src2w is masked by (bit_length - 1).
+ Otherwise, if the content of src2 is outside the range from 0
+ to bit_length - 1, the result is undefined. */
+#define SLJIT_SHL (SLJIT_OP2_BASE + 8)
+#define SLJIT_SHL32 (SLJIT_SHL | SLJIT_32)
+/* Flags: Z
+ Same as SLJIT_SHL, except the the second operand is
+ always masked by the length of the shift operation. */
+#define SLJIT_MSHL (SLJIT_OP2_BASE + 9)
+#define SLJIT_MSHL32 (SLJIT_MSHL | SLJIT_32)
+/* Flags: Z
+ Let bit_length be the length of the shift operation: 32 or 64.
+ If src2 is immediate, src2w is masked by (bit_length - 1).
+ Otherwise, if the content of src2 is outside the range from 0
+ to bit_length - 1, the result is undefined. */
+#define SLJIT_LSHR (SLJIT_OP2_BASE + 10)
+#define SLJIT_LSHR32 (SLJIT_LSHR | SLJIT_32)
+/* Flags: Z
+ Same as SLJIT_LSHR, except the the second operand is
+ always masked by the length of the shift operation. */
+#define SLJIT_MLSHR (SLJIT_OP2_BASE + 11)
+#define SLJIT_MLSHR32 (SLJIT_MLSHR | SLJIT_32)
+/* Flags: Z
+ Let bit_length be the length of the shift operation: 32 or 64.
+ If src2 is immediate, src2w is masked by (bit_length - 1).
+ Otherwise, if the content of src2 is outside the range from 0
+ to bit_length - 1, the result is undefined. */
+#define SLJIT_ASHR (SLJIT_OP2_BASE + 12)
+#define SLJIT_ASHR32 (SLJIT_ASHR | SLJIT_32)
+/* Flags: Z
+ Same as SLJIT_ASHR, except the the second operand is
+ always masked by the length of the shift operation. */
+#define SLJIT_MASHR (SLJIT_OP2_BASE + 13)
+#define SLJIT_MASHR32 (SLJIT_MASHR | SLJIT_32)
+/* Flags: - (may destroy flags)
+ Let bit_length be the length of the rotate operation: 32 or 64.
+ The second operand is always masked by (bit_length - 1). */
+#define SLJIT_ROTL (SLJIT_OP2_BASE + 14)
+#define SLJIT_ROTL32 (SLJIT_ROTL | SLJIT_32)
+/* Flags: - (may destroy flags)
+ Let bit_length be the length of the rotate operation: 32 or 64.
+ The second operand is always masked by (bit_length - 1). */
+#define SLJIT_ROTR (SLJIT_OP2_BASE + 15)
+#define SLJIT_ROTR32 (SLJIT_ROTR | SLJIT_32)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w);
+
+/* The sljit_emit_op2u function is the same as sljit_emit_op2
+ except the result is discarded. */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w);
+
+/* Emit a left or right shift operation, where the bits shifted
+ in comes from a separate source operand. All operands are
+ interpreted as unsigned integers.
+
+ In the followings the value_mask variable is 31 for 32 bit
+ operations and word_size - 1 otherwise.
+
+ op must be one of the following operations:
+ SLJIT_SHL or SLJIT_SHL32:
+ src_dst <<= src2
+ src_dst |= ((src1 >> 1) >> (src2 ^ value_mask))
+ SLJIT_MSHL or SLJIT_MSHL32:
+ src2 &= value_mask
+ perform the SLJIT_SHL or SLJIT_SHL32 operation
+ SLJIT_LSHR or SLJIT_LSHR32:
+ src_dst >>= src2
+ src_dst |= ((src1 << 1) << (src2 ^ value_mask))
+ SLJIT_MLSHR or SLJIT_MLSHR32:
+ src2 &= value_mask
+ perform the SLJIT_LSHR or SLJIT_LSHR32 operation
+
+ op can be combined (or'ed) with SLJIT_SHIFT_INTO_NON_ZERO
+
+ src_dst must be a register which content is updated after
+ the operation is completed
+ src1 / src1w contains the bits which shifted into src_dst
+ src2 / src2w contains the shift amount
+
+ Note: a rotate operation can be performed if src_dst and
+ src1 are set to the same register
+
+ Flags: - (may destroy flags) */
+
+/* The src2 contains a non-zero value. Improves the generated
+ code on certain architectures, which provides a small
+ performance improvement. */
+#define SLJIT_SHIFT_INTO_NON_ZERO 0x200
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w);
+
+/* Starting index of opcodes for sljit_emit_op2. */
+#define SLJIT_OP_SRC_BASE 128
+
+/* Note: src cannot be an immedate value
+ Flags: - (does not modify flags) */
+#define SLJIT_FAST_RETURN (SLJIT_OP_SRC_BASE + 0)
+/* Skip stack frames before fast return.
+ Note: src cannot be an immedate value
+ Flags: may destroy flags. */
+#define SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN (SLJIT_OP_SRC_BASE + 1)
+/* Prefetch value into the level 1 data cache
+ Note: if the target CPU does not support data prefetch,
+ no instructions are emitted.
+ Note: this instruction never fails, even if the memory address is invalid.
+ Flags: - (does not modify flags) */
+#define SLJIT_PREFETCH_L1 (SLJIT_OP_SRC_BASE + 2)
+/* Prefetch value into the level 2 data cache
+ Note: same as SLJIT_PREFETCH_L1 if the target CPU
+ does not support this instruction form.
+ Note: this instruction never fails, even if the memory address is invalid.
+ Flags: - (does not modify flags) */
+#define SLJIT_PREFETCH_L2 (SLJIT_OP_SRC_BASE + 3)
+/* Prefetch value into the level 3 data cache
+ Note: same as SLJIT_PREFETCH_L2 if the target CPU
+ does not support this instruction form.
+ Note: this instruction never fails, even if the memory address is invalid.
+ Flags: - (does not modify flags) */
+#define SLJIT_PREFETCH_L3 (SLJIT_OP_SRC_BASE + 4)
+/* Prefetch a value which is only used once (and can be discarded afterwards)
+ Note: same as SLJIT_PREFETCH_L1 if the target CPU
+ does not support this instruction form.
+ Note: this instruction never fails, even if the memory address is invalid.
+ Flags: - (does not modify flags) */
+#define SLJIT_PREFETCH_ONCE (SLJIT_OP_SRC_BASE + 5)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw);
+
+/* Starting index of opcodes for sljit_emit_fop1. */
+#define SLJIT_FOP1_BASE 160
+
+/* Flags: - (does not modify flags) */
+#define SLJIT_MOV_F64 (SLJIT_FOP1_BASE + 0)
+#define SLJIT_MOV_F32 (SLJIT_MOV_F64 | SLJIT_32)
+/* Convert opcodes: CONV[DST_TYPE].FROM[SRC_TYPE]
+ SRC/DST TYPE can be: F64, F32, S32, SW
+ Rounding mode when the destination is SW or S32: round towards zero. */
+/* Flags: - (may destroy flags) */
+#define SLJIT_CONV_F64_FROM_F32 (SLJIT_FOP1_BASE + 1)
+#define SLJIT_CONV_F32_FROM_F64 (SLJIT_CONV_F64_FROM_F32 | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_CONV_SW_FROM_F64 (SLJIT_FOP1_BASE + 2)
+#define SLJIT_CONV_SW_FROM_F32 (SLJIT_CONV_SW_FROM_F64 | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_CONV_S32_FROM_F64 (SLJIT_FOP1_BASE + 3)
+#define SLJIT_CONV_S32_FROM_F32 (SLJIT_CONV_S32_FROM_F64 | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_CONV_F64_FROM_SW (SLJIT_FOP1_BASE + 4)
+#define SLJIT_CONV_F32_FROM_SW (SLJIT_CONV_F64_FROM_SW | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_CONV_F64_FROM_S32 (SLJIT_FOP1_BASE + 5)
+#define SLJIT_CONV_F32_FROM_S32 (SLJIT_CONV_F64_FROM_S32 | SLJIT_32)
+/* Note: dst is the left and src is the right operand for SLJIT_CMP_F32/64.
+ Flags: EQUAL_F | LESS_F | GREATER_EQUAL_F | GREATER_F | LESS_EQUAL_F */
+#define SLJIT_CMP_F64 (SLJIT_FOP1_BASE + 6)
+#define SLJIT_CMP_F32 (SLJIT_CMP_F64 | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_NEG_F64 (SLJIT_FOP1_BASE + 7)
+#define SLJIT_NEG_F32 (SLJIT_NEG_F64 | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_ABS_F64 (SLJIT_FOP1_BASE + 8)
+#define SLJIT_ABS_F32 (SLJIT_ABS_F64 | SLJIT_32)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw);
+
+/* Starting index of opcodes for sljit_emit_fop2. */
+#define SLJIT_FOP2_BASE 192
+
+/* Flags: - (may destroy flags) */
+#define SLJIT_ADD_F64 (SLJIT_FOP2_BASE + 0)
+#define SLJIT_ADD_F32 (SLJIT_ADD_F64 | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_SUB_F64 (SLJIT_FOP2_BASE + 1)
+#define SLJIT_SUB_F32 (SLJIT_SUB_F64 | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_MUL_F64 (SLJIT_FOP2_BASE + 2)
+#define SLJIT_MUL_F32 (SLJIT_MUL_F64 | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_DIV_F64 (SLJIT_FOP2_BASE + 3)
+#define SLJIT_DIV_F32 (SLJIT_DIV_F64 | SLJIT_32)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w);
+
+/* Label and jump instructions. */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler);
+
+/* Invert (negate) conditional type: xor (^) with 0x1 */
+
+/* Integer comparison types. */
+#define SLJIT_EQUAL 0
+#define SLJIT_ZERO SLJIT_EQUAL
+#define SLJIT_NOT_EQUAL 1
+#define SLJIT_NOT_ZERO SLJIT_NOT_EQUAL
+
+#define SLJIT_LESS 2
+#define SLJIT_SET_LESS SLJIT_SET(SLJIT_LESS)
+#define SLJIT_GREATER_EQUAL 3
+#define SLJIT_SET_GREATER_EQUAL SLJIT_SET(SLJIT_GREATER_EQUAL)
+#define SLJIT_GREATER 4
+#define SLJIT_SET_GREATER SLJIT_SET(SLJIT_GREATER)
+#define SLJIT_LESS_EQUAL 5
+#define SLJIT_SET_LESS_EQUAL SLJIT_SET(SLJIT_LESS_EQUAL)
+#define SLJIT_SIG_LESS 6
+#define SLJIT_SET_SIG_LESS SLJIT_SET(SLJIT_SIG_LESS)
+#define SLJIT_SIG_GREATER_EQUAL 7
+#define SLJIT_SET_SIG_GREATER_EQUAL SLJIT_SET(SLJIT_SIG_GREATER_EQUAL)
+#define SLJIT_SIG_GREATER 8
+#define SLJIT_SET_SIG_GREATER SLJIT_SET(SLJIT_SIG_GREATER)
+#define SLJIT_SIG_LESS_EQUAL 9
+#define SLJIT_SET_SIG_LESS_EQUAL SLJIT_SET(SLJIT_SIG_LESS_EQUAL)
+
+#define SLJIT_OVERFLOW 10
+#define SLJIT_SET_OVERFLOW SLJIT_SET(SLJIT_OVERFLOW)
+#define SLJIT_NOT_OVERFLOW 11
+
+/* Unlike other flags, sljit_emit_jump may destroy the carry flag. */
+#define SLJIT_CARRY 12
+#define SLJIT_SET_CARRY SLJIT_SET(SLJIT_CARRY)
+#define SLJIT_NOT_CARRY 13
+
+/* Basic floating point comparison types.
+
+ Note: when the comparison result is unordered, their behaviour is unspecified. */
+
+#define SLJIT_F_EQUAL 14
+#define SLJIT_SET_F_EQUAL SLJIT_SET(SLJIT_F_EQUAL)
+#define SLJIT_F_NOT_EQUAL 15
+#define SLJIT_SET_F_NOT_EQUAL SLJIT_SET(SLJIT_F_NOT_EQUAL)
+#define SLJIT_F_LESS 16
+#define SLJIT_SET_F_LESS SLJIT_SET(SLJIT_F_LESS)
+#define SLJIT_F_GREATER_EQUAL 17
+#define SLJIT_SET_F_GREATER_EQUAL SLJIT_SET(SLJIT_F_GREATER_EQUAL)
+#define SLJIT_F_GREATER 18
+#define SLJIT_SET_F_GREATER SLJIT_SET(SLJIT_F_GREATER)
+#define SLJIT_F_LESS_EQUAL 19
+#define SLJIT_SET_F_LESS_EQUAL SLJIT_SET(SLJIT_F_LESS_EQUAL)
+
+/* Jumps when either argument contains a NaN value. */
+#define SLJIT_UNORDERED 20
+#define SLJIT_SET_UNORDERED SLJIT_SET(SLJIT_UNORDERED)
+/* Jumps when neither argument contains a NaN value. */
+#define SLJIT_ORDERED 21
+#define SLJIT_SET_ORDERED SLJIT_SET(SLJIT_ORDERED)
+
+/* Ordered / unordered floating point comparison types.
+
+ Note: each comparison type has an ordered and unordered form. Some
+ architectures supports only either of them (see: sljit_cmp_info). */
+
+#define SLJIT_ORDERED_EQUAL 22
+#define SLJIT_SET_ORDERED_EQUAL SLJIT_SET(SLJIT_ORDERED_EQUAL)
+#define SLJIT_UNORDERED_OR_NOT_EQUAL 23
+#define SLJIT_SET_UNORDERED_OR_NOT_EQUAL SLJIT_SET(SLJIT_UNORDERED_OR_NOT_EQUAL)
+#define SLJIT_ORDERED_LESS 24
+#define SLJIT_SET_ORDERED_LESS SLJIT_SET(SLJIT_ORDERED_LESS)
+#define SLJIT_UNORDERED_OR_GREATER_EQUAL 25
+#define SLJIT_SET_UNORDERED_OR_GREATER_EQUAL SLJIT_SET(SLJIT_UNORDERED_OR_GREATER_EQUAL)
+#define SLJIT_ORDERED_GREATER 26
+#define SLJIT_SET_ORDERED_GREATER SLJIT_SET(SLJIT_ORDERED_GREATER)
+#define SLJIT_UNORDERED_OR_LESS_EQUAL 27
+#define SLJIT_SET_UNORDERED_OR_LESS_EQUAL SLJIT_SET(SLJIT_UNORDERED_OR_LESS_EQUAL)
+
+#define SLJIT_UNORDERED_OR_EQUAL 28
+#define SLJIT_SET_UNORDERED_OR_EQUAL SLJIT_SET(SLJIT_UNORDERED_OR_EQUAL)
+#define SLJIT_ORDERED_NOT_EQUAL 29
+#define SLJIT_SET_ORDERED_NOT_EQUAL SLJIT_SET(SLJIT_ORDERED_NOT_EQUAL)
+#define SLJIT_UNORDERED_OR_LESS 30
+#define SLJIT_SET_UNORDERED_OR_LESS SLJIT_SET(SLJIT_UNORDERED_OR_LESS)
+#define SLJIT_ORDERED_GREATER_EQUAL 31
+#define SLJIT_SET_ORDERED_GREATER_EQUAL SLJIT_SET(SLJIT_ORDERED_GREATER_EQUAL)
+#define SLJIT_UNORDERED_OR_GREATER 32
+#define SLJIT_SET_UNORDERED_OR_GREATER SLJIT_SET(SLJIT_UNORDERED_OR_GREATER)
+#define SLJIT_ORDERED_LESS_EQUAL 33
+#define SLJIT_SET_ORDERED_LESS_EQUAL SLJIT_SET(SLJIT_ORDERED_LESS_EQUAL)
+
+/* Unconditional jump types. */
+#define SLJIT_JUMP 34
+/* Fast calling method. See sljit_emit_fast_enter / SLJIT_FAST_RETURN. */
+#define SLJIT_FAST_CALL 35
+/* Default C calling convention. */
+#define SLJIT_CALL 36
+/* Called function must be compiled by SLJIT.
+ See SLJIT_ENTER_REG_ARG option. */
+#define SLJIT_CALL_REG_ARG 37
+
+/* The target can be changed during runtime (see: sljit_set_jump_addr). */
+#define SLJIT_REWRITABLE_JUMP 0x1000
+/* When this flag is passed, the execution of the current function ends and
+ the called function returns to the caller of the current function. The
+ stack usage is reduced before the call, but it is not necessarily reduced
+ to zero. In the latter case the compiler needs to allocate space for some
+ arguments and the return address must be stored on the stack as well. */
+#define SLJIT_CALL_RETURN 0x2000
+
+/* Emit a jump instruction. The destination is not set, only the type of the jump.
+ type must be between SLJIT_EQUAL and SLJIT_FAST_CALL
+ type can be combined (or'ed) with SLJIT_REWRITABLE_JUMP
+
+ Flags: does not modify flags. */
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type);
+
+/* Emit a C compiler (ABI) compatible function call.
+ type must be SLJIT_CALL or SLJIT_CALL_REG_ARG
+ type can be combined (or'ed) with SLJIT_REWRITABLE_JUMP and/or SLJIT_CALL_RETURN
+ arg_types can be specified by SLJIT_ARGSx (SLJIT_ARG_RETURN / SLJIT_ARG_VALUE) macros
+
+ Flags: destroy all flags. */
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 arg_types);
+
+/* Basic arithmetic comparison. In most architectures it is implemented as
+ a compare operation followed by a sljit_emit_jump. However some
+ architectures (i.e: ARM64 or MIPS) may employ special optimizations
+ here. It is suggested to use this comparison form when appropriate.
+ type must be between SLJIT_EQUAL and SLJIT_SIG_LESS_EQUAL
+ type can be combined (or'ed) with SLJIT_REWRITABLE_JUMP
+
+ Flags: may destroy flags. */
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w);
+
+/* Basic floating point comparison. In most architectures it is implemented as
+ a SLJIT_CMP_F32/64 operation (setting appropriate flags) followed by a
+ sljit_emit_jump. However some architectures (i.e: MIPS) may employ
+ special optimizations here. It is suggested to use this comparison form
+ when appropriate.
+ type must be between SLJIT_F_EQUAL and SLJIT_ORDERED_LESS_EQUAL
+ type can be combined (or'ed) with SLJIT_REWRITABLE_JUMP
+ Flags: destroy flags.
+ Note: when an operand is NaN the behaviour depends on the comparison type. */
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w);
+
+/* Set the destination of the jump to this label. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_label(struct sljit_jump *jump, struct sljit_label* label);
+/* Set the destination address of the jump to this label. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_target(struct sljit_jump *jump, sljit_uw target);
+
+/* Emit an indirect jump or fast call.
+ Direct form: set src to SLJIT_IMM() and srcw to the address
+ Indirect form: any other valid addressing mode
+ type must be between SLJIT_JUMP and SLJIT_FAST_CALL
+
+ Flags: does not modify flags. */
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw);
+
+/* Emit a C compiler (ABI) compatible function call.
+ Direct form: set src to SLJIT_IMM() and srcw to the address
+ Indirect form: any other valid addressing mode
+ type must be SLJIT_CALL or SLJIT_CALL_REG_ARG
+ type can be combined (or'ed) with SLJIT_CALL_RETURN
+ arg_types can be specified by SLJIT_ARGSx (SLJIT_ARG_RETURN / SLJIT_ARG_VALUE) macros
+
+ Flags: destroy all flags. */
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 arg_types, sljit_s32 src, sljit_sw srcw);
+
+/* Perform an operation using the conditional flags as the second argument.
+ Type must always be between SLJIT_EQUAL and SLJIT_ORDERED_LESS_EQUAL.
+ The value represented by the type is 1, if the condition represented
+ by the type is fulfilled, and 0 otherwise.
+
+ When op is SLJIT_MOV or SLJIT_MOV32:
+ Set dst to the value represented by the type (0 or 1).
+ Flags: - (does not modify flags)
+ When op is SLJIT_AND, SLJIT_AND32, SLJIT_OR, SLJIT_OR32, SLJIT_XOR, or SLJIT_XOR32
+ Performs the binary operation using dst as the first, and the value
+ represented by type as the second argument. Result is written into dst.
+ Flags: Z (may destroy flags) */
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type);
+
+/* Emit a conditional mov instruction which moves source to destination,
+ if the condition is satisfied. Unlike other arithmetic operations this
+ instruction does not support memory access.
+
+ type must be between SLJIT_EQUAL and SLJIT_ORDERED_LESS_EQUAL
+ type can be combined (or'ed) with SLJIT_32
+ dst_reg must be a valid register
+ src must be a valid register or immediate (SLJIT_IMM)
+
+ Flags: - (does not modify flags) */
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw);
+
+/* The following flags are used by sljit_emit_mem(), sljit_emit_mem_update(),
+ sljit_emit_fmem(), and sljit_emit_fmem_update(). */
+
+/* Memory load operation. This is the default. */
+#define SLJIT_MEM_LOAD 0x000000
+/* Memory store operation. */
+#define SLJIT_MEM_STORE 0x000200
+
+/* The following flags are used by sljit_emit_mem() and sljit_emit_fmem(). */
+
+/* Load or stora data from an unaligned (byte aligned) address. */
+#define SLJIT_MEM_UNALIGNED 0x000400
+/* Load or stora data from a 16 bit aligned address. */
+#define SLJIT_MEM_UNALIGNED_16 0x000800
+/* Load or stora data from a 32 bit aligned address. */
+#define SLJIT_MEM_UNALIGNED_32 0x001000
+
+/* The following flags are used by sljit_emit_mem_update(),
+ and sljit_emit_fmem_update(). */
+
+/* Base register is updated before the memory access (default). */
+#define SLJIT_MEM_PRE 0x000000
+/* Base register is updated after the memory access. */
+#define SLJIT_MEM_POST 0x000400
+
+/* When SLJIT_MEM_SUPP is passed, no instructions are emitted.
+ Instead the function returns with SLJIT_SUCCESS if the instruction
+ form is supported and SLJIT_ERR_UNSUPPORTED otherwise. This flag
+ allows runtime checking of available instruction forms. */
+#define SLJIT_MEM_SUPP 0x000800
+
+/* The sljit_emit_mem emits instructions for various memory operations:
+
+ When SLJIT_MEM_UNALIGNED / SLJIT_MEM_UNALIGNED_16 /
+ SLJIT_MEM_UNALIGNED_32 is set in type argument:
+ Emit instructions for unaligned memory loads or stores. When
+ SLJIT_UNALIGNED is not defined, the only way to access unaligned
+ memory data is using sljit_emit_mem. Otherwise all operations (e.g.
+ sljit_emit_op1/2, or sljit_emit_fop1/2) supports unaligned access.
+ In general, the performance of unaligned memory accesses are often
+ lower than aligned and should be avoided.
+
+ When a pair of registers is passed in reg argument:
+ Emit instructions for moving data between a register pair and
+ memory. The register pair can be specified by the SLJIT_REG_PAIR
+ macro. The first register is loaded from or stored into the
+ location specified by the mem/memw arguments, and the end address
+ of this operation is the starting address of the data transfer
+ between the second register and memory. The type argument must
+ be SLJIT_MOV. The SLJIT_MEM_UNALIGNED* options are allowed for
+ this operation.
+
+ type must be between SLJIT_MOV and SLJIT_MOV_P and can be
+ combined (or'ed) with SLJIT_MEM_* flags
+ reg is a register or register pair, which is the source or
+ destination of the operation
+ mem must be a memory operand
+
+ Flags: - (does not modify flags) */
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw);
+
+/* Emit a single memory load or store with update instruction.
+ When the requested instruction form is not supported by the CPU,
+ it returns with SLJIT_ERR_UNSUPPORTED instead of emulating the
+ instruction. This allows specializing tight loops based on
+ the supported instruction forms (see SLJIT_MEM_SUPP flag).
+ Absolute address (SLJIT_MEM0) forms are never supported
+ and the base (first) register specified by the mem argument
+ must not be SLJIT_SP and must also be different from the
+ register specified by the reg argument.
+
+ type must be between SLJIT_MOV and SLJIT_MOV_P and can be
+ combined (or'ed) with SLJIT_MEM_* flags
+ reg is the source or destination register of the operation
+ mem must be a memory operand
+
+ Flags: - (does not modify flags) */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw);
+
+/* Same as sljit_emit_mem except the followings:
+
+ Loading or storing a pair of registers is not supported.
+
+ type must be SLJIT_MOV_F64 or SLJIT_MOV_F32 and can be
+ combined (or'ed) with SLJIT_MEM_* flags.
+ freg is the source or destination floating point register
+ of the operation
+ mem must be a memory operand
+
+ Flags: - (does not modify flags) */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw);
+
+/* Same as sljit_emit_mem_update except the followings:
+
+ type must be SLJIT_MOV_F64 or SLJIT_MOV_F32 and can be
+ combined (or'ed) with SLJIT_MEM_* flags
+ freg is the source or destination floating point register
+ of the operation
+ mem must be a memory operand
+
+ Flags: - (does not modify flags) */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw);
+
+/* Copies the base address of SLJIT_SP + offset to dst. The offset can
+ represent the starting address of a value in the local data (stack).
+ The offset is not limited by the local data limits, it can be any value.
+ For example if an array of bytes are stored on the stack from
+ offset 0x40, and R0 contains the offset of an array item plus 0x120,
+ this item can be changed by two SLJIT instructions:
+
+ sljit_get_local_base(compiler, SLJIT_R1, 0, 0x40 - 0x120);
+ sljit_emit_op1(compiler, SLJIT_MOV_U8, SLJIT_MEM2(SLJIT_R1, SLJIT_R0), 0, SLJIT_IMM, 0x5);
+
+ Flags: - (may destroy flags) */
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset);
+
+/* Store a value that can be changed runtime (see: sljit_get_const_addr / sljit_set_const)
+ Flags: - (does not modify flags) */
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value);
+
+/* Store the value of a label (see: sljit_set_put_label)
+ Flags: - (does not modify flags) */
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw);
+
+/* Set the value stored by put_label to this label. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_put_label(struct sljit_put_label *put_label, struct sljit_label *label);
+
+/* After the code generation the address for label, jump and const instructions
+ are computed. Since these structures are freed by sljit_free_compiler, the
+ addresses must be preserved by the user program elsewere. */
+static SLJIT_INLINE sljit_uw sljit_get_label_addr(struct sljit_label *label) { return label->addr; }
+static SLJIT_INLINE sljit_uw sljit_get_jump_addr(struct sljit_jump *jump) { return jump->addr; }
+static SLJIT_INLINE sljit_uw sljit_get_const_addr(struct sljit_const *const_) { return const_->addr; }
+
+/* Only the address and executable offset are required to perform dynamic
+ code modifications. See sljit_get_executable_offset function. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset);
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset);
+
+/* --------------------------------------------------------------------- */
+/* CPU specific functions */
+/* --------------------------------------------------------------------- */
+
+/* The following function is a helper function for sljit_emit_op_custom.
+ It returns with the real machine register index ( >=0 ) of any SLJIT_R,
+ SLJIT_S and SLJIT_SP registers.
+
+ Note: it returns with -1 for virtual registers (only on x86-32). */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg);
+
+/* The following function is a helper function for sljit_emit_op_custom.
+ It returns with the real machine register ( >= 0 ) index of any SLJIT_FR,
+ and SLJIT_FS register.
+
+ Note: the index is always an even number on ARM-32, MIPS. */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg);
+
+/* Any instruction can be inserted into the instruction stream by
+ sljit_emit_op_custom. It has a similar purpose as inline assembly.
+ The size parameter must match to the instruction size of the target
+ architecture:
+
+ x86: 0 < size <= 15. The instruction argument can be byte aligned.
+ Thumb2: if size == 2, the instruction argument must be 2 byte aligned.
+ if size == 4, the instruction argument must be 4 byte aligned.
+ Otherwise: size must be 4 and instruction argument must be 4 byte aligned. */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size);
+
+/* Flags were set by a 32 bit operation. */
+#define SLJIT_CURRENT_FLAGS_32 SLJIT_32
+
+/* Flags were set by an ADD or ADDC operations. */
+#define SLJIT_CURRENT_FLAGS_ADD 0x01
+/* Flags were set by a SUB, SUBC, or NEG operation. */
+#define SLJIT_CURRENT_FLAGS_SUB 0x02
+
+/* Flags were set by sljit_emit_op2u with SLJIT_SUB opcode.
+ Must be combined with SLJIT_CURRENT_FLAGS_SUB. */
+#define SLJIT_CURRENT_FLAGS_COMPARE 0x04
+
+/* Define the currently available CPU status flags. It is usually used after
+ an sljit_emit_label or sljit_emit_op_custom operations to define which CPU
+ status flags are available.
+
+ The current_flags must be a valid combination of SLJIT_SET_* and
+ SLJIT_CURRENT_FLAGS_* constants. */
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_current_flags(struct sljit_compiler *compiler,
+ sljit_s32 current_flags);
+
+/* --------------------------------------------------------------------- */
+/* Miscellaneous utility functions */
+/* --------------------------------------------------------------------- */
+
+/* Get the human readable name of the platform. Can be useful on platforms
+ like ARM, where ARM and Thumb2 functions can be mixed, and it is useful
+ to know the type of the code generator. */
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void);
+
+/* Portable helper function to get an offset of a member. */
+#define SLJIT_OFFSETOF(base, member) ((sljit_sw)(&((base*)0x10)->member) - 0x10)
+
+#if (defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK)
+
+/* The sljit_stack structure and its manipulation functions provides
+ an implementation for a top-down stack. The stack top is stored
+ in the end field of the sljit_stack structure and the stack goes
+ down to the min_start field, so the memory region reserved for
+ this stack is between min_start (inclusive) and end (exclusive)
+ fields. However the application can only use the region between
+ start (inclusive) and end (exclusive) fields. The sljit_stack_resize
+ function can be used to extend this region up to min_start.
+
+ This feature uses the "address space reserve" feature of modern
+ operating systems. Instead of allocating a large memory block
+ applications can allocate a small memory region and extend it
+ later without moving the content of the memory area. Therefore
+ after a successful resize by sljit_stack_resize all pointers into
+ this region are still valid.
+
+ Note:
+ this structure may not be supported by all operating systems.
+ end and max_limit fields are aligned to PAGE_SIZE bytes (usually
+ 4 Kbyte or more).
+ stack should grow in larger steps, e.g. 4Kbyte, 16Kbyte or more. */
+
+struct sljit_stack {
+ /* User data, anything can be stored here.
+ Initialized to the same value as the end field. */
+ sljit_u8 *top;
+/* These members are read only. */
+ /* End address of the stack */
+ sljit_u8 *end;
+ /* Current start address of the stack. */
+ sljit_u8 *start;
+ /* Lowest start address of the stack. */
+ sljit_u8 *min_start;
+};
+
+/* Allocates a new stack. Returns NULL if unsuccessful.
+ Note: see sljit_create_compiler for the explanation of allocator_data. */
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_FUNC sljit_allocate_stack(sljit_uw start_size, sljit_uw max_size, void *allocator_data);
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_free_stack(struct sljit_stack *stack, void *allocator_data);
+
+/* Can be used to increase (extend) or decrease (shrink) the stack
+ memory area. Returns with new_start if successful and NULL otherwise.
+ It always fails if new_start is less than min_start or greater or equal
+ than end fields. The fields of the stack are not changed if the returned
+ value is NULL (the current memory content is never lost). */
+SLJIT_API_FUNC_ATTRIBUTE sljit_u8 *SLJIT_FUNC sljit_stack_resize(struct sljit_stack *stack, sljit_u8 *new_start);
+
+#endif /* (defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK) */
+
+#if !(defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL)
+
+/* Get the entry address of a given function (signed, unsigned result). */
+#define SLJIT_FUNC_ADDR(func_name) ((sljit_sw)func_name)
+#define SLJIT_FUNC_UADDR(func_name) ((sljit_uw)func_name)
+
+#else /* !(defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL) */
+
+/* All JIT related code should be placed in the same context (library, binary, etc.). */
+
+/* Get the entry address of a given function (signed, unsigned result). */
+#define SLJIT_FUNC_ADDR(func_name) (*(sljit_sw*)(void*)func_name)
+#define SLJIT_FUNC_UADDR(func_name) (*(sljit_uw*)(void*)func_name)
+
+/* For powerpc64, the function pointers point to a context descriptor. */
+struct sljit_function_context {
+ sljit_uw addr;
+ sljit_uw r2;
+ sljit_uw r11;
+};
+
+/* Fill the context arguments using the addr and the function.
+ If func_ptr is NULL, it will not be set to the address of context
+ If addr is NULL, the function address also comes from the func pointer. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_function_context(void** func_ptr, struct sljit_function_context* context, sljit_uw addr, void* func);
+
+#endif /* !(defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL) */
+
+#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
+/* Free unused executable memory. The allocator keeps some free memory
+ around to reduce the number of OS executable memory allocations.
+ This improves performance since these calls are costly. However
+ it is sometimes desired to free all unused memory regions, e.g.
+ before the application terminates. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void);
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SLJIT_LIR_H_ */
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeARM_32.c b/contrib/libs/pcre2/src/sljit/sljitNativeARM_32.c
new file mode 100644
index 00000000000..54b8ade0636
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeARM_32.c
@@ -0,0 +1,3700 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef __SOFTFP__
+#define ARM_ABI_INFO " ABI:softfp"
+#else
+#define ARM_ABI_INFO " ABI:hardfp"
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+ return "ARMv7" SLJIT_CPUINFO ARM_ABI_INFO;
+#elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ return "ARMv5" SLJIT_CPUINFO ARM_ABI_INFO;
+#else
+#error "Internal error: Unknown ARM architecture"
+#endif
+}
+
+/* Last register + 1. */
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
+#define TMP_PC (SLJIT_NUMBER_OF_REGISTERS + 4)
+
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
+
+/* In ARM instruction words.
+ Cache lines are usually 32 byte aligned. */
+#define CONST_POOL_ALIGNMENT 8
+#define CONST_POOL_EMPTY 0xffffffff
+
+#define ALIGN_INSTRUCTION(ptr) \
+ (sljit_uw*)(((sljit_uw)(ptr) + (CONST_POOL_ALIGNMENT * sizeof(sljit_uw)) - 1) & ~((CONST_POOL_ALIGNMENT * sizeof(sljit_uw)) - 1))
+#define MAX_DIFFERENCE(max_diff) \
+ (((max_diff) / (sljit_s32)sizeof(sljit_uw)) - (CONST_POOL_ALIGNMENT - 1))
+
+/* See sljit_emit_enter and sljit_emit_op0 if you want to change them. */
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = {
+ 0, 0, 1, 2, 3, 11, 10, 9, 8, 7, 6, 5, 4, 13, 12, 14, 15
+};
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = {
+ 0, 0, 1, 2, 3, 4, 5, 15, 14, 13, 12, 11, 10, 9, 8, 6, 7
+};
+
+#define RM(rm) ((sljit_uw)reg_map[rm])
+#define RM8(rm) ((sljit_uw)reg_map[rm] << 8)
+#define RD(rd) ((sljit_uw)reg_map[rd] << 12)
+#define RN(rn) ((sljit_uw)reg_map[rn] << 16)
+
+#define VM(rm) ((sljit_uw)freg_map[rm])
+#define VD(rd) ((sljit_uw)freg_map[rd] << 12)
+#define VN(rn) ((sljit_uw)freg_map[rn] << 16)
+
+/* --------------------------------------------------------------------- */
+/* Instrucion forms */
+/* --------------------------------------------------------------------- */
+
+/* The instruction includes the AL condition.
+ INST_NAME - CONDITIONAL remove this flag. */
+#define COND_MASK 0xf0000000
+#define CONDITIONAL 0xe0000000
+#define PUSH_POOL 0xff000000
+
+#define ADC 0xe0a00000
+#define ADD 0xe0800000
+#define AND 0xe0000000
+#define B 0xea000000
+#define BIC 0xe1c00000
+#define BL 0xeb000000
+#define BLX 0xe12fff30
+#define BX 0xe12fff10
+#define CLZ 0xe16f0f10
+#define CMN 0xe1600000
+#define CMP 0xe1400000
+#define BKPT 0xe1200070
+#define EOR 0xe0200000
+#define LDR 0xe5100000
+#define LDR_POST 0xe4100000
+#define MOV 0xe1a00000
+#define MUL 0xe0000090
+#define MVN 0xe1e00000
+#define NOP 0xe1a00000
+#define ORR 0xe1800000
+#define PUSH 0xe92d0000
+#define POP 0xe8bd0000
+#define RBIT 0xe6ff0f30
+#define RSB 0xe0600000
+#define RSC 0xe0e00000
+#define SBC 0xe0c00000
+#define SMULL 0xe0c00090
+#define STR 0xe5000000
+#define SUB 0xe0400000
+#define TST 0xe1000000
+#define UMULL 0xe0800090
+#define VABS_F32 0xeeb00ac0
+#define VADD_F32 0xee300a00
+#define VCMP_F32 0xeeb40a40
+#define VCVT_F32_S32 0xeeb80ac0
+#define VCVT_F64_F32 0xeeb70ac0
+#define VCVT_S32_F32 0xeebd0ac0
+#define VDIV_F32 0xee800a00
+#define VLDR_F32 0xed100a00
+#define VMOV_F32 0xeeb00a40
+#define VMOV 0xee000a10
+#define VMOV2 0xec400a10
+#define VMRS 0xeef1fa10
+#define VMUL_F32 0xee200a00
+#define VNEG_F32 0xeeb10a40
+#define VPOP 0xecbd0b00
+#define VPUSH 0xed2d0b00
+#define VSTR_F32 0xed000a00
+#define VSUB_F32 0xee300a40
+
+#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+/* Arm v7 specific instructions. */
+#define MOVW 0xe3000000
+#define MOVT 0xe3400000
+#define SXTB 0xe6af0070
+#define SXTH 0xe6bf0070
+#define UXTB 0xe6ef0070
+#define UXTH 0xe6ff0070
+#endif
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+
+static sljit_s32 push_cpool(struct sljit_compiler *compiler)
+{
+ /* Pushing the constant pool into the instruction stream. */
+ sljit_uw* inst;
+ sljit_uw* cpool_ptr;
+ sljit_uw* cpool_end;
+ sljit_s32 i;
+
+ /* The label could point the address after the constant pool. */
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ compiler->last_label->size += compiler->cpool_fill + (CONST_POOL_ALIGNMENT - 1) + 1;
+
+ SLJIT_ASSERT(compiler->cpool_fill > 0 && compiler->cpool_fill <= CPOOL_SIZE);
+ inst = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
+ FAIL_IF(!inst);
+ compiler->size++;
+ *inst = 0xff000000 | compiler->cpool_fill;
+
+ for (i = 0; i < CONST_POOL_ALIGNMENT - 1; i++) {
+ inst = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
+ FAIL_IF(!inst);
+ compiler->size++;
+ *inst = 0;
+ }
+
+ cpool_ptr = compiler->cpool;
+ cpool_end = cpool_ptr + compiler->cpool_fill;
+ while (cpool_ptr < cpool_end) {
+ inst = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
+ FAIL_IF(!inst);
+ compiler->size++;
+ *inst = *cpool_ptr++;
+ }
+ compiler->cpool_diff = CONST_POOL_EMPTY;
+ compiler->cpool_fill = 0;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_uw inst)
+{
+ sljit_uw* ptr;
+
+ if (SLJIT_UNLIKELY(compiler->cpool_diff != CONST_POOL_EMPTY && compiler->size - compiler->cpool_diff >= MAX_DIFFERENCE(4092)))
+ FAIL_IF(push_cpool(compiler));
+
+ ptr = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
+ FAIL_IF(!ptr);
+ compiler->size++;
+ *ptr = inst;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 push_inst_with_literal(struct sljit_compiler *compiler, sljit_uw inst, sljit_uw literal)
+{
+ sljit_uw* ptr;
+ sljit_uw cpool_index = CPOOL_SIZE;
+ sljit_uw* cpool_ptr;
+ sljit_uw* cpool_end;
+ sljit_u8* cpool_unique_ptr;
+
+ if (SLJIT_UNLIKELY(compiler->cpool_diff != CONST_POOL_EMPTY && compiler->size - compiler->cpool_diff >= MAX_DIFFERENCE(4092)))
+ FAIL_IF(push_cpool(compiler));
+ else if (compiler->cpool_fill > 0) {
+ cpool_ptr = compiler->cpool;
+ cpool_end = cpool_ptr + compiler->cpool_fill;
+ cpool_unique_ptr = compiler->cpool_unique;
+ do {
+ if ((*cpool_ptr == literal) && !(*cpool_unique_ptr)) {
+ cpool_index = (sljit_uw)(cpool_ptr - compiler->cpool);
+ break;
+ }
+ cpool_ptr++;
+ cpool_unique_ptr++;
+ } while (cpool_ptr < cpool_end);
+ }
+
+ if (cpool_index == CPOOL_SIZE) {
+ /* Must allocate a new entry in the literal pool. */
+ if (compiler->cpool_fill < CPOOL_SIZE) {
+ cpool_index = compiler->cpool_fill;
+ compiler->cpool_fill++;
+ }
+ else {
+ FAIL_IF(push_cpool(compiler));
+ cpool_index = 0;
+ compiler->cpool_fill = 1;
+ }
+ }
+
+ SLJIT_ASSERT((inst & 0xfff) == 0);
+ ptr = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
+ FAIL_IF(!ptr);
+ compiler->size++;
+ *ptr = inst | cpool_index;
+
+ compiler->cpool[cpool_index] = literal;
+ compiler->cpool_unique[cpool_index] = 0;
+ if (compiler->cpool_diff == CONST_POOL_EMPTY)
+ compiler->cpool_diff = compiler->size;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 push_inst_with_unique_literal(struct sljit_compiler *compiler, sljit_uw inst, sljit_uw literal)
+{
+ sljit_uw* ptr;
+ if (SLJIT_UNLIKELY((compiler->cpool_diff != CONST_POOL_EMPTY && compiler->size - compiler->cpool_diff >= MAX_DIFFERENCE(4092)) || compiler->cpool_fill >= CPOOL_SIZE))
+ FAIL_IF(push_cpool(compiler));
+
+ SLJIT_ASSERT(compiler->cpool_fill < CPOOL_SIZE && (inst & 0xfff) == 0);
+ ptr = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
+ FAIL_IF(!ptr);
+ compiler->size++;
+ *ptr = inst | compiler->cpool_fill;
+
+ compiler->cpool[compiler->cpool_fill] = literal;
+ compiler->cpool_unique[compiler->cpool_fill] = 1;
+ compiler->cpool_fill++;
+ if (compiler->cpool_diff == CONST_POOL_EMPTY)
+ compiler->cpool_diff = compiler->size;
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 prepare_blx(struct sljit_compiler *compiler)
+{
+ /* Place for at least two instruction (doesn't matter whether the first has a literal). */
+ if (SLJIT_UNLIKELY(compiler->cpool_diff != CONST_POOL_EMPTY && compiler->size - compiler->cpool_diff >= MAX_DIFFERENCE(4088)))
+ return push_cpool(compiler);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_blx(struct sljit_compiler *compiler)
+{
+ /* Must follow tightly the previous instruction (to be able to convert it to bl instruction). */
+ SLJIT_ASSERT(compiler->cpool_diff == CONST_POOL_EMPTY || compiler->size - compiler->cpool_diff < MAX_DIFFERENCE(4092));
+ SLJIT_ASSERT(reg_map[TMP_REG1] != 14);
+
+ return push_inst(compiler, BLX | RM(TMP_REG1));
+}
+
+static sljit_uw patch_pc_relative_loads(sljit_uw *last_pc_patch, sljit_uw *code_ptr, sljit_uw* const_pool, sljit_uw cpool_size)
+{
+ sljit_uw diff;
+ sljit_uw ind;
+ sljit_uw counter = 0;
+ sljit_uw* clear_const_pool = const_pool;
+ sljit_uw* clear_const_pool_end = const_pool + cpool_size;
+
+ SLJIT_ASSERT(const_pool - code_ptr <= CONST_POOL_ALIGNMENT);
+ /* Set unused flag for all literals in the constant pool.
+ I.e.: unused literals can belong to branches, which can be encoded as B or BL.
+ We can "compress" the constant pool by discarding these literals. */
+ while (clear_const_pool < clear_const_pool_end)
+ *clear_const_pool++ = (sljit_uw)(-1);
+
+ while (last_pc_patch < code_ptr) {
+ /* Data transfer instruction with Rn == r15. */
+ if ((*last_pc_patch & 0x0c0f0000) == 0x040f0000) {
+ diff = (sljit_uw)(const_pool - last_pc_patch);
+ ind = (*last_pc_patch) & 0xfff;
+
+ /* Must be a load instruction with immediate offset. */
+ SLJIT_ASSERT(ind < cpool_size && !(*last_pc_patch & (1 << 25)) && (*last_pc_patch & (1 << 20)));
+ if ((sljit_s32)const_pool[ind] < 0) {
+ const_pool[ind] = counter;
+ ind = counter;
+ counter++;
+ }
+ else
+ ind = const_pool[ind];
+
+ SLJIT_ASSERT(diff >= 1);
+ if (diff >= 2 || ind > 0) {
+ diff = (diff + (sljit_uw)ind - 2) << 2;
+ SLJIT_ASSERT(diff <= 0xfff);
+ *last_pc_patch = (*last_pc_patch & ~(sljit_uw)0xfff) | diff;
+ }
+ else
+ *last_pc_patch = (*last_pc_patch & ~(sljit_uw)(0xfff | (1 << 23))) | 0x004;
+ }
+ last_pc_patch++;
+ }
+ return counter;
+}
+
+/* In some rare ocasions we may need future patches. The probability is close to 0 in practice. */
+struct future_patch {
+ struct future_patch* next;
+ sljit_s32 index;
+ sljit_s32 value;
+};
+
+static sljit_s32 resolve_const_pool_index(struct sljit_compiler *compiler, struct future_patch **first_patch, sljit_uw cpool_current_index, sljit_uw *cpool_start_address, sljit_uw *buf_ptr)
+{
+ sljit_u32 value;
+ struct future_patch *curr_patch, *prev_patch;
+
+ SLJIT_UNUSED_ARG(compiler);
+
+ /* Using the values generated by patch_pc_relative_loads. */
+ if (!*first_patch)
+ value = cpool_start_address[cpool_current_index];
+ else {
+ curr_patch = *first_patch;
+ prev_patch = NULL;
+ while (1) {
+ if (!curr_patch) {
+ value = cpool_start_address[cpool_current_index];
+ break;
+ }
+ if ((sljit_uw)curr_patch->index == cpool_current_index) {
+ value = (sljit_uw)curr_patch->value;
+ if (prev_patch)
+ prev_patch->next = curr_patch->next;
+ else
+ *first_patch = curr_patch->next;
+ SLJIT_FREE(curr_patch, compiler->allocator_data);
+ break;
+ }
+ prev_patch = curr_patch;
+ curr_patch = curr_patch->next;
+ }
+ }
+
+ if ((sljit_sw)value >= 0) {
+ if (value > cpool_current_index) {
+ curr_patch = (struct future_patch*)SLJIT_MALLOC(sizeof(struct future_patch), compiler->allocator_data);
+ if (!curr_patch) {
+ while (*first_patch) {
+ curr_patch = *first_patch;
+ *first_patch = (*first_patch)->next;
+ SLJIT_FREE(curr_patch, compiler->allocator_data);
+ }
+ return SLJIT_ERR_ALLOC_FAILED;
+ }
+ curr_patch->next = *first_patch;
+ curr_patch->index = (sljit_sw)value;
+ curr_patch->value = (sljit_sw)cpool_start_address[value];
+ *first_patch = curr_patch;
+ }
+ cpool_start_address[value] = *buf_ptr;
+ }
+ return SLJIT_SUCCESS;
+}
+
+#else
+
+static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_uw inst)
+{
+ sljit_uw* ptr;
+
+ ptr = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
+ FAIL_IF(!ptr);
+ compiler->size++;
+ *ptr = inst;
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_imm(struct sljit_compiler *compiler, sljit_s32 reg, sljit_sw imm)
+{
+ FAIL_IF(push_inst(compiler, MOVW | RD(reg) | ((imm << 4) & 0xf0000) | ((sljit_u32)imm & 0xfff)));
+ return push_inst(compiler, MOVT | RD(reg) | ((imm >> 12) & 0xf0000) | (((sljit_u32)imm >> 16) & 0xfff));
+}
+
+#endif
+
+static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_uw *code_ptr, sljit_uw *code, sljit_sw executable_offset)
+{
+ sljit_sw diff;
+
+ if (jump->flags & SLJIT_REWRITABLE_JUMP)
+ return 0;
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (jump->flags & IS_BL)
+ code_ptr--;
+
+ if (jump->flags & JUMP_ADDR)
+ diff = ((sljit_sw)jump->u.target - (sljit_sw)(code_ptr + 2) - executable_offset);
+ else {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ diff = ((sljit_sw)(code + jump->u.label->size) - (sljit_sw)(code_ptr + 2));
+ }
+
+ /* Branch to Thumb code has not been optimized yet. */
+ if (diff & 0x3)
+ return 0;
+
+ if (jump->flags & IS_BL) {
+ if (diff <= 0x01ffffff && diff >= -0x02000000) {
+ *code_ptr = (BL - CONDITIONAL) | (*(code_ptr + 1) & COND_MASK);
+ jump->flags |= PATCH_B;
+ return 1;
+ }
+ }
+ else {
+ if (diff <= 0x01ffffff && diff >= -0x02000000) {
+ *code_ptr = (B - CONDITIONAL) | (*code_ptr & COND_MASK);
+ jump->flags |= PATCH_B;
+ }
+ }
+#else
+ if (jump->flags & JUMP_ADDR)
+ diff = ((sljit_sw)jump->u.target - (sljit_sw)code_ptr - executable_offset);
+ else {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ diff = ((sljit_sw)(code + jump->u.label->size) - (sljit_sw)code_ptr);
+ }
+
+ /* Branch to Thumb code has not been optimized yet. */
+ if (diff & 0x3)
+ return 0;
+
+ if (diff <= 0x01ffffff && diff >= -0x02000000) {
+ code_ptr -= 2;
+ *code_ptr = ((jump->flags & IS_BL) ? (BL - CONDITIONAL) : (B - CONDITIONAL)) | (code_ptr[2] & COND_MASK);
+ jump->flags |= PATCH_B;
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+static SLJIT_INLINE void inline_set_jump_addr(sljit_uw jump_ptr, sljit_sw executable_offset, sljit_uw new_addr, sljit_s32 flush_cache)
+{
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ sljit_uw *ptr = (sljit_uw *)jump_ptr;
+ sljit_uw *inst = (sljit_uw *)ptr[0];
+ sljit_uw mov_pc = ptr[1];
+ sljit_s32 bl = (mov_pc & 0x0000f000) != RD(TMP_PC);
+ sljit_sw diff = (sljit_sw)(((sljit_sw)new_addr - (sljit_sw)(inst + 2) - executable_offset) >> 2);
+
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ if (diff <= 0x7fffff && diff >= -0x800000) {
+ /* Turn to branch. */
+ if (!bl) {
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 0);
+ }
+ inst[0] = (mov_pc & COND_MASK) | (B - CONDITIONAL) | (diff & 0xffffff);
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 1);
+ }
+ } else {
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 0);
+ }
+ inst[0] = (mov_pc & COND_MASK) | (BL - CONDITIONAL) | (diff & 0xffffff);
+ inst[1] = NOP;
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 2);
+ }
+ }
+ } else {
+ /* Get the position of the constant. */
+ if (mov_pc & (1 << 23))
+ ptr = inst + ((mov_pc & 0xfff) >> 2) + 2;
+ else
+ ptr = inst + 1;
+
+ if (*inst != mov_pc) {
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + (!bl ? 1 : 2), 0);
+ }
+ inst[0] = mov_pc;
+ if (!bl) {
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 1);
+ }
+ } else {
+ inst[1] = BLX | RM(TMP_REG1);
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 2);
+ }
+ }
+ }
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(ptr, ptr + 1, 0);
+ }
+
+ *ptr = new_addr;
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(ptr, ptr + 1, 1);
+ }
+ }
+#else
+ sljit_uw *inst = (sljit_uw*)jump_ptr;
+
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_ASSERT((inst[0] & 0xfff00000) == MOVW && (inst[1] & 0xfff00000) == MOVT);
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 0);
+ }
+
+ inst[0] = MOVW | (inst[0] & 0xf000) | ((new_addr << 4) & 0xf0000) | (new_addr & 0xfff);
+ inst[1] = MOVT | (inst[1] & 0xf000) | ((new_addr >> 12) & 0xf0000) | ((new_addr >> 16) & 0xfff);
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 2);
+ }
+#endif
+}
+
+static sljit_uw get_imm(sljit_uw imm);
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, sljit_uw imm);
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg);
+
+static SLJIT_INLINE void inline_set_const(sljit_uw addr, sljit_sw executable_offset, sljit_uw new_constant, sljit_s32 flush_cache)
+{
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ sljit_uw *ptr = (sljit_uw*)addr;
+ sljit_uw *inst = (sljit_uw*)ptr[0];
+ sljit_uw ldr_literal = ptr[1];
+ sljit_uw src2;
+
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ src2 = get_imm(new_constant);
+ if (src2) {
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 0);
+ }
+
+ *inst = 0xe3a00000 | (ldr_literal & 0xf000) | src2;
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 1);
+ }
+ return;
+ }
+
+ src2 = get_imm(~new_constant);
+ if (src2) {
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 0);
+ }
+
+ *inst = 0xe3e00000 | (ldr_literal & 0xf000) | src2;
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 1);
+ }
+ return;
+ }
+
+ if (ldr_literal & (1 << 23))
+ ptr = inst + ((ldr_literal & 0xfff) >> 2) + 2;
+ else
+ ptr = inst + 1;
+
+ if (*inst != ldr_literal) {
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 0);
+ }
+
+ *inst = ldr_literal;
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 1);
+ }
+ }
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(ptr, ptr + 1, 0);
+ }
+
+ *ptr = new_constant;
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(ptr, ptr + 1, 1);
+ }
+#else
+ sljit_uw *inst = (sljit_uw*)addr;
+
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_ASSERT((inst[0] & 0xfff00000) == MOVW && (inst[1] & 0xfff00000) == MOVT);
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 0);
+ }
+
+ inst[0] = MOVW | (inst[0] & 0xf000) | ((new_constant << 4) & 0xf0000) | (new_constant & 0xfff);
+ inst[1] = MOVT | (inst[1] & 0xf000) | ((new_constant >> 12) & 0xf0000) | ((new_constant >> 16) & 0xfff);
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 2);
+ }
+#endif
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf;
+ sljit_uw *code;
+ sljit_uw *code_ptr;
+ sljit_uw *buf_ptr;
+ sljit_uw *buf_end;
+ sljit_uw size;
+ sljit_uw word_count;
+ sljit_uw next_addr;
+ sljit_sw executable_offset;
+ sljit_uw addr;
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ sljit_uw cpool_size;
+ sljit_uw cpool_skip_alignment;
+ sljit_uw cpool_current_index;
+ sljit_uw *cpool_start_address;
+ sljit_uw *last_pc_patch;
+ struct future_patch *first_patch;
+#endif
+
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ struct sljit_const *const_;
+ struct sljit_put_label *put_label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_generate_code(compiler));
+ reverse_buf(compiler);
+
+ /* Second code generation pass. */
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ size = compiler->size + (compiler->patches << 1);
+ if (compiler->cpool_fill > 0)
+ size += compiler->cpool_fill + CONST_POOL_ALIGNMENT - 1;
+#else
+ size = compiler->size;
+#endif
+ code = (sljit_uw*)SLJIT_MALLOC_EXEC(size * sizeof(sljit_uw), compiler->exec_allocator_data);
+ PTR_FAIL_WITH_EXEC_IF(code);
+ buf = compiler->buf;
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ cpool_size = 0;
+ cpool_skip_alignment = 0;
+ cpool_current_index = 0;
+ cpool_start_address = NULL;
+ first_patch = NULL;
+ last_pc_patch = code;
+#endif
+
+ code_ptr = code;
+ word_count = 0;
+ next_addr = 1;
+ executable_offset = SLJIT_EXEC_OFFSET(code);
+
+ label = compiler->labels;
+ jump = compiler->jumps;
+ const_ = compiler->consts;
+ put_label = compiler->put_labels;
+
+ if (label && label->size == 0) {
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+ label = label->next;
+ }
+
+ do {
+ buf_ptr = (sljit_uw*)buf->memory;
+ buf_end = buf_ptr + (buf->used_size >> 2);
+ do {
+ word_count++;
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (cpool_size > 0) {
+ if (cpool_skip_alignment > 0) {
+ buf_ptr++;
+ cpool_skip_alignment--;
+ }
+ else {
+ if (SLJIT_UNLIKELY(resolve_const_pool_index(compiler, &first_patch, cpool_current_index, cpool_start_address, buf_ptr))) {
+ SLJIT_FREE_EXEC(code, compiler->exec_allocator_data);
+ compiler->error = SLJIT_ERR_ALLOC_FAILED;
+ return NULL;
+ }
+ buf_ptr++;
+ if (++cpool_current_index >= cpool_size) {
+ SLJIT_ASSERT(!first_patch);
+ cpool_size = 0;
+ if (label && label->size == word_count) {
+ /* Points after the current instruction. */
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+
+ next_addr = compute_next_addr(label, jump, const_, put_label);
+ }
+ }
+ }
+ }
+ else if ((*buf_ptr & 0xff000000) != PUSH_POOL) {
+#endif
+ *code_ptr = *buf_ptr++;
+ if (next_addr == word_count) {
+ SLJIT_ASSERT(!label || label->size >= word_count);
+ SLJIT_ASSERT(!jump || jump->addr >= word_count);
+ SLJIT_ASSERT(!const_ || const_->addr >= word_count);
+ SLJIT_ASSERT(!put_label || put_label->addr >= word_count);
+
+ /* These structures are ordered by their address. */
+ if (jump && jump->addr == word_count) {
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (detect_jump_type(jump, code_ptr, code, executable_offset))
+ code_ptr--;
+ jump->addr = (sljit_uw)code_ptr;
+#else
+ jump->addr = (sljit_uw)(code_ptr - 2);
+ if (detect_jump_type(jump, code_ptr, code, executable_offset))
+ code_ptr -= 2;
+#endif
+ jump = jump->next;
+ }
+ if (label && label->size == word_count) {
+ /* code_ptr can be affected above. */
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr + 1, executable_offset);
+ label->size = (sljit_uw)((code_ptr + 1) - code);
+ label = label->next;
+ }
+ if (const_ && const_->addr == word_count) {
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ const_->addr = (sljit_uw)code_ptr;
+#else
+ const_->addr = (sljit_uw)(code_ptr - 1);
+#endif
+ const_ = const_->next;
+ }
+ if (put_label && put_label->addr == word_count) {
+ SLJIT_ASSERT(put_label->label);
+ put_label->addr = (sljit_uw)code_ptr;
+ put_label = put_label->next;
+ }
+ next_addr = compute_next_addr(label, jump, const_, put_label);
+ }
+ code_ptr++;
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ }
+ else {
+ /* Fortunately, no need to shift. */
+ cpool_size = *buf_ptr++ & ~PUSH_POOL;
+ SLJIT_ASSERT(cpool_size > 0);
+ cpool_start_address = ALIGN_INSTRUCTION(code_ptr + 1);
+ cpool_current_index = patch_pc_relative_loads(last_pc_patch, code_ptr, cpool_start_address, cpool_size);
+ if (cpool_current_index > 0) {
+ /* Unconditional branch. */
+ *code_ptr = B | (((sljit_uw)(cpool_start_address - code_ptr) + cpool_current_index - 2) & ~PUSH_POOL);
+ code_ptr = (sljit_uw*)(cpool_start_address + cpool_current_index);
+ }
+ cpool_skip_alignment = CONST_POOL_ALIGNMENT - 1;
+ cpool_current_index = 0;
+ last_pc_patch = code_ptr;
+ }
+#endif
+ } while (buf_ptr < buf_end);
+ buf = buf->next;
+ } while (buf);
+
+ SLJIT_ASSERT(!label);
+ SLJIT_ASSERT(!jump);
+ SLJIT_ASSERT(!const_);
+ SLJIT_ASSERT(!put_label);
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ SLJIT_ASSERT(cpool_size == 0);
+ if (compiler->cpool_fill > 0) {
+ cpool_start_address = ALIGN_INSTRUCTION(code_ptr);
+ cpool_current_index = patch_pc_relative_loads(last_pc_patch, code_ptr, cpool_start_address, compiler->cpool_fill);
+ if (cpool_current_index > 0)
+ code_ptr = (sljit_uw*)(cpool_start_address + cpool_current_index);
+
+ buf_ptr = compiler->cpool;
+ buf_end = buf_ptr + compiler->cpool_fill;
+ cpool_current_index = 0;
+ while (buf_ptr < buf_end) {
+ if (SLJIT_UNLIKELY(resolve_const_pool_index(compiler, &first_patch, cpool_current_index, cpool_start_address, buf_ptr))) {
+ SLJIT_FREE_EXEC(code, compiler->exec_allocator_data);
+ compiler->error = SLJIT_ERR_ALLOC_FAILED;
+ return NULL;
+ }
+ buf_ptr++;
+ cpool_current_index++;
+ }
+ SLJIT_ASSERT(!first_patch);
+ }
+#endif
+
+ jump = compiler->jumps;
+ while (jump) {
+ buf_ptr = (sljit_uw *)jump->addr;
+
+ if (jump->flags & PATCH_B) {
+ addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr + 2, executable_offset);
+ if (!(jump->flags & JUMP_ADDR)) {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ SLJIT_ASSERT((sljit_sw)(jump->u.label->addr - addr) <= 0x01ffffff && (sljit_sw)(jump->u.label->addr - addr) >= -0x02000000);
+ *buf_ptr |= ((jump->u.label->addr - addr) >> 2) & 0x00ffffff;
+ }
+ else {
+ SLJIT_ASSERT((sljit_sw)(jump->u.target - addr) <= 0x01ffffff && (sljit_sw)(jump->u.target - addr) >= -0x02000000);
+ *buf_ptr |= ((jump->u.target - addr) >> 2) & 0x00ffffff;
+ }
+ }
+ else if (jump->flags & SLJIT_REWRITABLE_JUMP) {
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ jump->addr = (sljit_uw)code_ptr;
+ code_ptr[0] = (sljit_uw)buf_ptr;
+ code_ptr[1] = *buf_ptr;
+ inline_set_jump_addr((sljit_uw)code_ptr, executable_offset, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0);
+ code_ptr += 2;
+#else
+ inline_set_jump_addr((sljit_uw)buf_ptr, executable_offset, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0);
+#endif
+ }
+ else {
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (jump->flags & IS_BL)
+ buf_ptr--;
+ if (*buf_ptr & (1 << 23))
+ buf_ptr += ((*buf_ptr & 0xfff) >> 2) + 2;
+ else
+ buf_ptr += 1;
+ *buf_ptr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target;
+#else
+ inline_set_jump_addr((sljit_uw)buf_ptr, executable_offset, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0);
+#endif
+ }
+ jump = jump->next;
+ }
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ const_ = compiler->consts;
+ while (const_) {
+ buf_ptr = (sljit_uw*)const_->addr;
+ const_->addr = (sljit_uw)code_ptr;
+
+ code_ptr[0] = (sljit_uw)buf_ptr;
+ code_ptr[1] = *buf_ptr;
+ if (*buf_ptr & (1 << 23))
+ buf_ptr += ((*buf_ptr & 0xfff) >> 2) + 2;
+ else
+ buf_ptr += 1;
+ /* Set the value again (can be a simple constant). */
+ inline_set_const((sljit_uw)code_ptr, executable_offset, *buf_ptr, 0);
+ code_ptr += 2;
+
+ const_ = const_->next;
+ }
+#endif
+
+ put_label = compiler->put_labels;
+ while (put_label) {
+ addr = put_label->label->addr;
+ buf_ptr = (sljit_uw*)put_label->addr;
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ SLJIT_ASSERT((buf_ptr[0] & 0xffff0000) == 0xe59f0000);
+ buf_ptr[((buf_ptr[0] & 0xfff) >> 2) + 2] = addr;
+#else
+ SLJIT_ASSERT((buf_ptr[-1] & 0xfff00000) == MOVW && (buf_ptr[0] & 0xfff00000) == MOVT);
+ buf_ptr[-1] |= ((addr << 4) & 0xf0000) | (addr & 0xfff);
+ buf_ptr[0] |= ((addr >> 12) & 0xf0000) | ((addr >> 16) & 0xfff);
+#endif
+ put_label = put_label->next;
+ }
+
+ SLJIT_ASSERT(code_ptr - code <= (sljit_s32)size);
+
+ compiler->error = SLJIT_ERR_COMPILED;
+ compiler->executable_offset = executable_offset;
+ compiler->executable_size = (sljit_uw)(code_ptr - code) * sizeof(sljit_uw);
+
+ code = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+ code_ptr = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+
+ SLJIT_CACHE_FLUSH(code, code_ptr);
+ SLJIT_UPDATE_WX_FLAGS(code, code_ptr, 1);
+ return code;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#else
+ /* Available by default. */
+ return 1;
+#endif
+
+ case SLJIT_HAS_CLZ:
+ case SLJIT_HAS_ROT:
+ case SLJIT_HAS_CMOV:
+#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+ case SLJIT_HAS_CTZ:
+ case SLJIT_HAS_PREFETCH:
+#endif
+ return 1;
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ case SLJIT_HAS_CTZ:
+ return 2;
+#endif
+
+ default:
+ return 0;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+/* Entry, exit */
+/* --------------------------------------------------------------------- */
+
+/* Creates an index in data_transfer_insts array. */
+#define WORD_SIZE 0x00
+#define BYTE_SIZE 0x01
+#define HALF_SIZE 0x02
+#define PRELOAD 0x03
+#define SIGNED 0x04
+#define LOAD_DATA 0x08
+
+/* Flag bits for emit_op. */
+#define ALLOW_IMM 0x10
+#define ALLOW_INV_IMM 0x20
+#define ALLOW_ANY_IMM (ALLOW_IMM | ALLOW_INV_IMM)
+#define ALLOW_NEG_IMM 0x40
+
+/* s/l - store/load (1 bit)
+ u/s - signed/unsigned (1 bit)
+ w/b/h/N - word/byte/half/NOT allowed (2 bit)
+ Storing signed and unsigned values are the same operations. */
+
+static const sljit_uw data_transfer_insts[16] = {
+/* s u w */ 0xe5000000 /* str */,
+/* s u b */ 0xe5400000 /* strb */,
+/* s u h */ 0xe10000b0 /* strh */,
+/* s u N */ 0x00000000 /* not allowed */,
+/* s s w */ 0xe5000000 /* str */,
+/* s s b */ 0xe5400000 /* strb */,
+/* s s h */ 0xe10000b0 /* strh */,
+/* s s N */ 0x00000000 /* not allowed */,
+
+/* l u w */ 0xe5100000 /* ldr */,
+/* l u b */ 0xe5500000 /* ldrb */,
+/* l u h */ 0xe11000b0 /* ldrh */,
+/* l u p */ 0xf5500000 /* preload */,
+/* l s w */ 0xe5100000 /* ldr */,
+/* l s b */ 0xe11000d0 /* ldrsb */,
+/* l s h */ 0xe11000f0 /* ldrsh */,
+/* l s N */ 0x00000000 /* not allowed */,
+};
+
+#define EMIT_DATA_TRANSFER(type, add, target_reg, base_reg, arg) \
+ (data_transfer_insts[(type) & 0xf] | ((add) << 23) | RD(target_reg) | RN(base_reg) | (sljit_uw)(arg))
+
+/* Normal ldr/str instruction.
+ Type2: ldrsb, ldrh, ldrsh */
+#define IS_TYPE1_TRANSFER(type) \
+ (data_transfer_insts[(type) & 0xf] & 0x04000000)
+#define TYPE2_TRANSFER_IMM(imm) \
+ (((imm) & 0xf) | (((imm) & 0xf0) << 4) | (1 << 22))
+
+#define EMIT_FPU_OPERATION(opcode, mode, dst, src1, src2) \
+ ((sljit_uw)(opcode) | (sljit_uw)(mode) | VD(dst) | VM(src1) | VN(src2))
+
+/* Flags for emit_op: */
+ /* Arguments are swapped. */
+#define ARGS_SWAPPED 0x01
+ /* Inverted immediate. */
+#define INV_IMM 0x02
+ /* Source and destination is register. */
+#define MOVE_REG_CONV 0x04
+ /* Unused return value. */
+#define UNUSED_RETURN 0x08
+/* SET_FLAGS must be (1 << 20) as it is also the value of S bit (can be used for optimization). */
+#define SET_FLAGS (1 << 20)
+/* dst: reg
+ src1: reg
+ src2: reg or imm (if allowed)
+ SRC2_IMM must be (1 << 25) as it is also the value of I bit (can be used for optimization). */
+#define SRC2_IMM (1 << 25)
+
+static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 inp_flags,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w);
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_uw imm, offset;
+ sljit_s32 i, tmp, size, word_arg_count;
+ sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+#ifdef __SOFTFP__
+ sljit_u32 float_arg_count;
+#else
+ sljit_u32 old_offset, f32_offset;
+ sljit_u32 remap[3];
+ sljit_u32 *remap_ptr = remap;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ imm = 0;
+
+ tmp = SLJIT_S0 - saveds;
+ for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--)
+ imm |= (sljit_uw)1 << reg_map[i];
+
+ for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--)
+ imm |= (sljit_uw)1 << reg_map[i];
+
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14);
+
+ /* Push saved and temporary registers
+ multiple registers: stmdb sp!, {..., lr}
+ single register: str reg, [sp, #-4]! */
+ if (imm != 0)
+ FAIL_IF(push_inst(compiler, PUSH | (1 << 14) | imm));
+ else
+ FAIL_IF(push_inst(compiler, 0xe52d0004 | RD(TMP_REG2)));
+
+ /* Stack must be aligned to 8 bytes: */
+ size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 1);
+
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if ((size & SSIZE_OF(sw)) != 0) {
+ FAIL_IF(push_inst(compiler, SUB | RD(SLJIT_SP) | RN(SLJIT_SP) | SRC2_IMM | sizeof(sljit_sw)));
+ size += SSIZE_OF(sw);
+ }
+
+ if (fsaveds + fscratches >= SLJIT_NUMBER_OF_FLOAT_REGISTERS) {
+ FAIL_IF(push_inst(compiler, VPUSH | VD(SLJIT_FS0) | ((sljit_uw)SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS << 1)));
+ } else {
+ if (fsaveds > 0)
+ FAIL_IF(push_inst(compiler, VPUSH | VD(SLJIT_FS0) | ((sljit_uw)fsaveds << 1)));
+ if (fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG)
+ FAIL_IF(push_inst(compiler, VPUSH | VD(fscratches) | ((sljit_uw)(fscratches - (SLJIT_FIRST_SAVED_FLOAT_REG - 1)) << 1)));
+ }
+ }
+
+ local_size = ((size + local_size + 0x7) & ~0x7) - size;
+ compiler->local_size = local_size;
+
+ if (options & SLJIT_ENTER_REG_ARG)
+ arg_types = 0;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ word_arg_count = 0;
+ saved_arg_count = 0;
+#ifdef __SOFTFP__
+ SLJIT_COMPILE_ASSERT(SLJIT_FR0 == 1, float_register_index_start);
+
+ offset = 0;
+ float_arg_count = 0;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset & 0x7)
+ offset += sizeof(sljit_sw);
+
+ if (offset < 4 * sizeof(sljit_sw))
+ FAIL_IF(push_inst(compiler, VMOV2 | (offset << 10) | ((offset + sizeof(sljit_sw)) << 14) | float_arg_count));
+ else
+ FAIL_IF(push_inst(compiler, VLDR_F32 | 0x800100 | RN(SLJIT_SP)
+ | (float_arg_count << 12) | ((offset + (sljit_uw)size - 4 * sizeof(sljit_sw)) >> 2)));
+ float_arg_count++;
+ offset += sizeof(sljit_f64) - sizeof(sljit_sw);
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (offset < 4 * sizeof(sljit_sw))
+ FAIL_IF(push_inst(compiler, VMOV | (float_arg_count << 16) | (offset << 10)));
+ else
+ FAIL_IF(push_inst(compiler, VLDR_F32 | 0x800000 | RN(SLJIT_SP)
+ | (float_arg_count << 12) | ((offset + (sljit_uw)size - 4 * sizeof(sljit_sw)) >> 2)));
+ float_arg_count++;
+ break;
+ default:
+ word_arg_count++;
+
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ tmp = SLJIT_S0 - saved_arg_count;
+ saved_arg_count++;
+ } else if (word_arg_count - 1 != (sljit_s32)(offset >> 2))
+ tmp = word_arg_count;
+ else
+ break;
+
+ if (offset < 4 * sizeof(sljit_sw))
+ FAIL_IF(push_inst(compiler, MOV | RD(tmp) | (offset >> 2)));
+ else
+ FAIL_IF(push_inst(compiler, LDR | 0x800000 | RN(SLJIT_SP) | RD(tmp) | (offset + (sljit_uw)size - 4 * sizeof(sljit_sw))));
+ break;
+ }
+
+ offset += sizeof(sljit_sw);
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ compiler->args_size = offset;
+#else
+ offset = SLJIT_FR0;
+ old_offset = SLJIT_FR0;
+ f32_offset = 0;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset != old_offset)
+ *remap_ptr++ = EMIT_FPU_OPERATION(VMOV_F32, SLJIT_32, offset, old_offset, 0);
+ old_offset++;
+ offset++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (f32_offset != 0) {
+ *remap_ptr++ = EMIT_FPU_OPERATION(VMOV_F32, 0x20, offset, f32_offset, 0);
+ f32_offset = 0;
+ } else {
+ if (offset != old_offset)
+ *remap_ptr++ = EMIT_FPU_OPERATION(VMOV_F32, 0, offset, old_offset, 0);
+ f32_offset = old_offset;
+ old_offset++;
+ }
+ offset++;
+ break;
+ default:
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ FAIL_IF(push_inst(compiler, MOV | RD(SLJIT_S0 - saved_arg_count) | RM(SLJIT_R0 + word_arg_count)));
+ saved_arg_count++;
+ }
+
+ word_arg_count++;
+ break;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ SLJIT_ASSERT((sljit_uw)(remap_ptr - remap) <= sizeof(remap));
+
+ while (remap_ptr > remap)
+ FAIL_IF(push_inst(compiler, *(--remap_ptr)));
+#endif
+
+ if (local_size > 0)
+ FAIL_IF(emit_op(compiler, SLJIT_SUB, ALLOW_IMM, SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, local_size));
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 size;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 1);
+
+ if ((size & SSIZE_OF(sw)) != 0 && (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG))
+ size += SSIZE_OF(sw);
+
+ compiler->local_size = ((size + local_size + 0x7) & ~0x7) - size;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_add_sp(struct sljit_compiler *compiler, sljit_uw imm)
+{
+ sljit_uw imm2 = get_imm(imm);
+
+ if (imm2 == 0) {
+ imm2 = (imm & ~(sljit_uw)0x3ff) >> 10;
+ imm = (imm & 0x3ff) >> 2;
+
+ FAIL_IF(push_inst(compiler, ADD | SRC2_IMM | RD(SLJIT_SP) | RN(SLJIT_SP) | 0xb00 | imm2));
+ return push_inst(compiler, ADD | SRC2_IMM | RD(SLJIT_SP) | RN(SLJIT_SP) | 0xf00 | (imm & 0xff));
+ }
+
+ return push_inst(compiler, ADD | RD(SLJIT_SP) | RN(SLJIT_SP) | imm2);
+}
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 frame_size)
+{
+ sljit_s32 local_size, fscratches, fsaveds, i, tmp;
+ sljit_s32 restored_reg = 0;
+ sljit_s32 lr_dst = TMP_PC;
+ sljit_uw reg_list = 0;
+
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14 && frame_size <= 128);
+
+ local_size = compiler->local_size;
+ fscratches = compiler->fscratches;
+ fsaveds = compiler->fsaveds;
+
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if (local_size > 0)
+ FAIL_IF(emit_add_sp(compiler, (sljit_uw)local_size));
+
+ if (fsaveds + fscratches >= SLJIT_NUMBER_OF_FLOAT_REGISTERS) {
+ FAIL_IF(push_inst(compiler, VPOP | VD(SLJIT_FS0) | ((sljit_uw)SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS << 1)));
+ } else {
+ if (fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG)
+ FAIL_IF(push_inst(compiler, VPOP | VD(fscratches) | ((sljit_uw)(fscratches - (SLJIT_FIRST_SAVED_FLOAT_REG - 1)) << 1)));
+ if (fsaveds > 0)
+ FAIL_IF(push_inst(compiler, VPOP | VD(SLJIT_FS0) | ((sljit_uw)fsaveds << 1)));
+ }
+
+ local_size = GET_SAVED_REGISTERS_SIZE(compiler->scratches, compiler->saveds, 1) & 0x7;
+ }
+
+ if (frame_size < 0) {
+ lr_dst = TMP_REG2;
+ frame_size = 0;
+ } else if (frame_size > 0) {
+ SLJIT_ASSERT(frame_size == 1 || (frame_size & 0x7) == 0);
+ lr_dst = 0;
+ frame_size &= ~0x7;
+ }
+
+ if (lr_dst != 0)
+ reg_list |= (sljit_uw)1 << reg_map[lr_dst];
+
+ tmp = SLJIT_S0 - compiler->saveds;
+ i = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options);
+ if (tmp < i) {
+ restored_reg = i;
+ do {
+ reg_list |= (sljit_uw)1 << reg_map[i];
+ } while (--i > tmp);
+ }
+
+ i = compiler->scratches;
+ if (i >= SLJIT_FIRST_SAVED_REG) {
+ restored_reg = i;
+ do {
+ reg_list |= (sljit_uw)1 << reg_map[i];
+ } while (--i >= SLJIT_FIRST_SAVED_REG);
+ }
+
+ if (lr_dst == TMP_REG2 && reg_list == 0) {
+ restored_reg = TMP_REG2;
+ lr_dst = 0;
+ }
+
+ if (lr_dst == 0 && (reg_list & (reg_list - 1)) == 0) {
+ /* The local_size does not include the saved registers. */
+ tmp = 0;
+ if (reg_list != 0) {
+ tmp = 2;
+ if (local_size <= 0xfff) {
+ if (local_size == 0) {
+ SLJIT_ASSERT(restored_reg != TMP_REG2);
+ if (frame_size == 0)
+ return push_inst(compiler, LDR_POST | RN(SLJIT_SP) | RD(restored_reg) | 0x800008);
+ if (frame_size > 2 * SSIZE_OF(sw))
+ return push_inst(compiler, LDR_POST | RN(SLJIT_SP) | RD(restored_reg) | (sljit_uw)(frame_size - (2 * SSIZE_OF(sw))));
+ }
+
+ FAIL_IF(push_inst(compiler, LDR | 0x800000 | RN(SLJIT_SP) | RD(restored_reg) | (sljit_uw)local_size));
+ tmp = 1;
+ } else if (frame_size == 0) {
+ frame_size = (restored_reg == TMP_REG2) ? SSIZE_OF(sw) : 2 * SSIZE_OF(sw);
+ tmp = 3;
+ }
+
+ /* Place for the saved register. */
+ if (restored_reg != TMP_REG2)
+ local_size += SSIZE_OF(sw);
+ }
+
+ /* Place for the lr register. */
+ local_size += SSIZE_OF(sw);
+
+ if (frame_size > local_size)
+ FAIL_IF(push_inst(compiler, SUB | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 25) | (sljit_uw)(frame_size - local_size)));
+ else if (frame_size < local_size)
+ FAIL_IF(emit_add_sp(compiler, (sljit_uw)(local_size - frame_size)));
+
+ if (tmp <= 1)
+ return SLJIT_SUCCESS;
+
+ if (tmp == 2) {
+ frame_size -= SSIZE_OF(sw);
+ if (restored_reg != TMP_REG2)
+ frame_size -= SSIZE_OF(sw);
+
+ return push_inst(compiler, LDR | 0x800000 | RN(SLJIT_SP) | RD(restored_reg) | (sljit_uw)frame_size);
+ }
+
+ tmp = (restored_reg == TMP_REG2) ? 0x800004 : 0x800008;
+ return push_inst(compiler, LDR_POST | RN(SLJIT_SP) | RD(restored_reg) | (sljit_uw)tmp);
+ }
+
+ if (local_size > 0)
+ FAIL_IF(emit_add_sp(compiler, (sljit_uw)local_size));
+
+ /* Pop saved and temporary registers
+ multiple registers: ldmia sp!, {...}
+ single register: ldr reg, [sp], #4 */
+ if ((reg_list & (reg_list - 1)) == 0) {
+ SLJIT_ASSERT(lr_dst != 0);
+ SLJIT_ASSERT(reg_list == (sljit_uw)1 << reg_map[lr_dst]);
+
+ return push_inst(compiler, LDR_POST | RN(SLJIT_SP) | RD(lr_dst) | 0x800004);
+ }
+
+ FAIL_IF(push_inst(compiler, POP | reg_list));
+
+ if (frame_size > 0)
+ return push_inst(compiler, SUB | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 25) | ((sljit_uw)frame_size - sizeof(sljit_sw)));
+
+ if (lr_dst != 0)
+ return SLJIT_SUCCESS;
+
+ return push_inst(compiler, ADD | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 25) | sizeof(sljit_sw));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ return emit_stack_frame_release(compiler, 0);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ srcw = 0;
+ } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | RM(src)));
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 1));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
+ sljit_uw dst, sljit_uw src1, sljit_uw src2)
+{
+ sljit_s32 is_masked;
+ sljit_uw shift_type;
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED));
+ if (dst != src2) {
+ if (src2 & SRC2_IMM) {
+ return push_inst(compiler, ((flags & INV_IMM) ? MVN : MOV) | RD(dst) | src2);
+ }
+ return push_inst(compiler, MOV | RD(dst) | RM(src2));
+ }
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U8:
+ case SLJIT_MOV_S8:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED));
+ if (flags & MOVE_REG_CONV) {
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (op == SLJIT_MOV_U8)
+ return push_inst(compiler, AND | RD(dst) | RN(src2) | SRC2_IMM | 0xff);
+ FAIL_IF(push_inst(compiler, MOV | RD(dst) | (24 << 7) | RM(src2)));
+ return push_inst(compiler, MOV | RD(dst) | (24 << 7) | (op == SLJIT_MOV_U8 ? 0x20 : 0x40) | RM(dst));
+#else
+ return push_inst(compiler, (op == SLJIT_MOV_U8 ? UXTB : SXTB) | RD(dst) | RM(src2));
+#endif
+ }
+ else if (dst != src2) {
+ SLJIT_ASSERT(src2 & SRC2_IMM);
+ return push_inst(compiler, ((flags & INV_IMM) ? MVN : MOV) | RD(dst) | src2);
+ }
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U16:
+ case SLJIT_MOV_S16:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED));
+ if (flags & MOVE_REG_CONV) {
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ FAIL_IF(push_inst(compiler, MOV | RD(dst) | (16 << 7) | RM(src2)));
+ return push_inst(compiler, MOV | RD(dst) | (16 << 7) | (op == SLJIT_MOV_U16 ? 0x20 : 0x40) | RM(dst));
+#else
+ return push_inst(compiler, (op == SLJIT_MOV_U16 ? UXTH : SXTH) | RD(dst) | RM(src2));
+#endif
+ }
+ else if (dst != src2) {
+ SLJIT_ASSERT(src2 & SRC2_IMM);
+ return push_inst(compiler, ((flags & INV_IMM) ? MVN : MOV) | RD(dst) | src2);
+ }
+ return SLJIT_SUCCESS;
+
+ case SLJIT_NOT:
+ if (src2 & SRC2_IMM)
+ return push_inst(compiler, ((flags & INV_IMM) ? MOV : MVN) | (flags & SET_FLAGS) | RD(dst) | src2);
+
+ return push_inst(compiler, MVN | (flags & SET_FLAGS) | RD(dst) | RM(src2));
+
+ case SLJIT_CLZ:
+ SLJIT_ASSERT(!(flags & INV_IMM) && !(src2 & SRC2_IMM));
+ FAIL_IF(push_inst(compiler, CLZ | RD(dst) | RM(src2)));
+ return SLJIT_SUCCESS;
+
+ case SLJIT_CTZ:
+ SLJIT_ASSERT(!(flags & INV_IMM) && !(src2 & SRC2_IMM));
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED));
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ FAIL_IF(push_inst(compiler, RSB | SRC2_IMM | RD(TMP_REG1) | RN(src2) | 0));
+ FAIL_IF(push_inst(compiler, AND | RD(TMP_REG2) | RN(src2) | RM(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, CLZ | RD(dst) | RM(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, CMP | SET_FLAGS | SRC2_IMM | RN(dst) | 32));
+ return push_inst(compiler, (EOR ^ 0xf0000000) | SRC2_IMM | RD(dst) | RN(dst) | 0x1f);
+#else /* !SLJIT_CONFIG_ARM_V5 */
+ FAIL_IF(push_inst(compiler, RBIT | RD(dst) | RM(src2)));
+ return push_inst(compiler, CLZ | RD(dst) | RM(dst));
+#endif /* SLJIT_CONFIG_ARM_V5 */
+
+ case SLJIT_ADD:
+ SLJIT_ASSERT(!(flags & INV_IMM));
+
+ if ((flags & (UNUSED_RETURN | ARGS_SWAPPED)) == UNUSED_RETURN)
+ return push_inst(compiler, CMN | SET_FLAGS | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+ return push_inst(compiler, ADD | (flags & SET_FLAGS) | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+
+ case SLJIT_ADDC:
+ SLJIT_ASSERT(!(flags & INV_IMM));
+ return push_inst(compiler, ADC | (flags & SET_FLAGS) | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+
+ case SLJIT_SUB:
+ SLJIT_ASSERT(!(flags & INV_IMM));
+
+ if ((flags & (UNUSED_RETURN | ARGS_SWAPPED)) == UNUSED_RETURN)
+ return push_inst(compiler, CMP | SET_FLAGS | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+
+ return push_inst(compiler, (!(flags & ARGS_SWAPPED) ? SUB : RSB) | (flags & SET_FLAGS)
+ | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+
+ case SLJIT_SUBC:
+ SLJIT_ASSERT(!(flags & INV_IMM));
+ return push_inst(compiler, (!(flags & ARGS_SWAPPED) ? SBC : RSC) | (flags & SET_FLAGS)
+ | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+
+ case SLJIT_MUL:
+ SLJIT_ASSERT(!(flags & INV_IMM));
+ SLJIT_ASSERT(!(src2 & SRC2_IMM));
+ compiler->status_flags_state = 0;
+
+ if (!HAS_FLAGS(op))
+ return push_inst(compiler, MUL | RN(dst) | RM8(src2) | RM(src1));
+
+ FAIL_IF(push_inst(compiler, SMULL | RN(TMP_REG1) | RD(dst) | RM8(src2) | RM(src1)));
+
+ /* cmp TMP_REG1, dst asr #31. */
+ return push_inst(compiler, CMP | SET_FLAGS | RN(TMP_REG1) | RM(dst) | 0xfc0);
+
+ case SLJIT_AND:
+ if ((flags & (UNUSED_RETURN | INV_IMM)) == UNUSED_RETURN)
+ return push_inst(compiler, TST | SET_FLAGS | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+ return push_inst(compiler, (!(flags & INV_IMM) ? AND : BIC) | (flags & SET_FLAGS)
+ | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+
+ case SLJIT_OR:
+ SLJIT_ASSERT(!(flags & INV_IMM));
+ return push_inst(compiler, ORR | (flags & SET_FLAGS) | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+
+ case SLJIT_XOR:
+ SLJIT_ASSERT(!(flags & INV_IMM));
+ return push_inst(compiler, EOR | (flags & SET_FLAGS) | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ shift_type = 0;
+ is_masked = GET_OPCODE(op) == SLJIT_MSHL;
+ break;
+
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ shift_type = 1;
+ is_masked = GET_OPCODE(op) == SLJIT_MLSHR;
+ break;
+
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ shift_type = 2;
+ is_masked = GET_OPCODE(op) == SLJIT_MASHR;
+ break;
+
+ case SLJIT_ROTL:
+ if (compiler->shift_imm == 0x20) {
+ FAIL_IF(push_inst(compiler, RSB | SRC2_IMM | RD(TMP_REG2) | RN(src2) | 0));
+ src2 = TMP_REG2;
+ } else
+ compiler->shift_imm = (sljit_uw)(-(sljit_sw)compiler->shift_imm) & 0x1f;
+ /* fallthrough */
+
+ case SLJIT_ROTR:
+ shift_type = 3;
+ is_masked = 0;
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+ }
+
+ SLJIT_ASSERT(!(flags & ARGS_SWAPPED) && !(flags & INV_IMM) && !(src2 & SRC2_IMM));
+
+ if (compiler->shift_imm != 0x20) {
+ SLJIT_ASSERT(src1 == TMP_REG1);
+
+ if (compiler->shift_imm != 0)
+ return push_inst(compiler, MOV | (flags & SET_FLAGS) |
+ RD(dst) | (compiler->shift_imm << 7) | (shift_type << 5) | RM(src2));
+ return push_inst(compiler, MOV | (flags & SET_FLAGS) | RD(dst) | RM(src2));
+ }
+
+ SLJIT_ASSERT(src1 != TMP_REG2);
+
+ if (is_masked) {
+ FAIL_IF(push_inst(compiler, AND | RD(TMP_REG2) | RN(src2) | SRC2_IMM | 0x1f));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, MOV | (flags & SET_FLAGS) | RD(dst)
+ | RM8(src2) | (sljit_uw)(shift_type << 5) | 0x10 | RM(src1));
+}
+
+#undef EMIT_SHIFT_INS_AND_RETURN
+
+/* Tests whether the immediate can be stored in the 12 bit imm field.
+ Returns with 0 if not possible. */
+static sljit_uw get_imm(sljit_uw imm)
+{
+ sljit_u32 rol;
+
+ if (imm <= 0xff)
+ return SRC2_IMM | imm;
+
+ if (!(imm & 0xff000000)) {
+ imm <<= 8;
+ rol = 8;
+ }
+ else {
+ imm = (imm << 24) | (imm >> 8);
+ rol = 0;
+ }
+
+ if (!(imm & 0xff000000)) {
+ imm <<= 8;
+ rol += 4;
+ }
+
+ if (!(imm & 0xf0000000)) {
+ imm <<= 4;
+ rol += 2;
+ }
+
+ if (!(imm & 0xc0000000)) {
+ imm <<= 2;
+ rol += 1;
+ }
+
+ if (!(imm & 0x00ffffff))
+ return SRC2_IMM | (imm >> 24) | (rol << 8);
+ else
+ return 0;
+}
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+static sljit_s32 generate_int(struct sljit_compiler *compiler, sljit_s32 reg, sljit_uw imm, sljit_s32 positive)
+{
+ sljit_uw mask;
+ sljit_uw imm1;
+ sljit_uw imm2;
+ sljit_uw rol;
+
+ /* Step1: Search a zero byte (8 continous zero bit). */
+ mask = 0xff000000;
+ rol = 8;
+ while(1) {
+ if (!(imm & mask)) {
+ /* Rol imm by rol. */
+ imm = (imm << rol) | (imm >> (32 - rol));
+ /* Calculate arm rol. */
+ rol = 4 + (rol >> 1);
+ break;
+ }
+ rol += 2;
+ mask >>= 2;
+ if (mask & 0x3) {
+ /* rol by 8. */
+ imm = (imm << 8) | (imm >> 24);
+ mask = 0xff00;
+ rol = 24;
+ while (1) {
+ if (!(imm & mask)) {
+ /* Rol imm by rol. */
+ imm = (imm << rol) | (imm >> (32 - rol));
+ /* Calculate arm rol. */
+ rol = (rol >> 1) - 8;
+ break;
+ }
+ rol += 2;
+ mask >>= 2;
+ if (mask & 0x3)
+ return 0;
+ }
+ break;
+ }
+ }
+
+ /* The low 8 bit must be zero. */
+ SLJIT_ASSERT(!(imm & 0xff));
+
+ if (!(imm & 0xff000000)) {
+ imm1 = SRC2_IMM | ((imm >> 16) & 0xff) | (((rol + 4) & 0xf) << 8);
+ imm2 = SRC2_IMM | ((imm >> 8) & 0xff) | (((rol + 8) & 0xf) << 8);
+ }
+ else if (imm & 0xc0000000) {
+ imm1 = SRC2_IMM | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8);
+ imm <<= 8;
+ rol += 4;
+
+ if (!(imm & 0xff000000)) {
+ imm <<= 8;
+ rol += 4;
+ }
+
+ if (!(imm & 0xf0000000)) {
+ imm <<= 4;
+ rol += 2;
+ }
+
+ if (!(imm & 0xc0000000)) {
+ imm <<= 2;
+ rol += 1;
+ }
+
+ if (!(imm & 0x00ffffff))
+ imm2 = SRC2_IMM | (imm >> 24) | ((rol & 0xf) << 8);
+ else
+ return 0;
+ }
+ else {
+ if (!(imm & 0xf0000000)) {
+ imm <<= 4;
+ rol += 2;
+ }
+
+ if (!(imm & 0xc0000000)) {
+ imm <<= 2;
+ rol += 1;
+ }
+
+ imm1 = SRC2_IMM | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8);
+ imm <<= 8;
+ rol += 4;
+
+ if (!(imm & 0xf0000000)) {
+ imm <<= 4;
+ rol += 2;
+ }
+
+ if (!(imm & 0xc0000000)) {
+ imm <<= 2;
+ rol += 1;
+ }
+
+ if (!(imm & 0x00ffffff))
+ imm2 = SRC2_IMM | (imm >> 24) | ((rol & 0xf) << 8);
+ else
+ return 0;
+ }
+
+ FAIL_IF(push_inst(compiler, (positive ? MOV : MVN) | RD(reg) | imm1));
+ FAIL_IF(push_inst(compiler, (positive ? ORR : BIC) | RD(reg) | RN(reg) | imm2));
+ return 1;
+}
+#endif
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, sljit_uw imm)
+{
+ sljit_uw tmp;
+
+#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+ if (!(imm & ~(sljit_uw)0xffff))
+ return push_inst(compiler, MOVW | RD(reg) | ((imm << 4) & 0xf0000) | (imm & 0xfff));
+#endif
+
+ /* Create imm by 1 inst. */
+ tmp = get_imm(imm);
+ if (tmp)
+ return push_inst(compiler, MOV | RD(reg) | tmp);
+
+ tmp = get_imm(~imm);
+ if (tmp)
+ return push_inst(compiler, MVN | RD(reg) | tmp);
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ /* Create imm by 2 inst. */
+ FAIL_IF(generate_int(compiler, reg, imm, 1));
+ FAIL_IF(generate_int(compiler, reg, ~imm, 0));
+
+ /* Load integer. */
+ return push_inst_with_literal(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, reg, TMP_PC, 0), imm);
+#else
+ FAIL_IF(push_inst(compiler, MOVW | RD(reg) | ((imm << 4) & 0xf0000) | (imm & 0xfff)));
+ if (imm <= 0xffff)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, MOVT | RD(reg) | ((imm >> 12) & 0xf0000) | ((imm >> 16) & 0xfff));
+#endif
+}
+
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg,
+ sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg)
+{
+ sljit_uw imm, offset_reg, tmp;
+ sljit_sw mask = IS_TYPE1_TRANSFER(flags) ? 0xfff : 0xff;
+ sljit_sw sign = IS_TYPE1_TRANSFER(flags) ? 0x1000 : 0x100;
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+ SLJIT_ASSERT((arg & REG_MASK) != tmp_reg || (arg == SLJIT_MEM1(tmp_reg) && argw >= -mask && argw <= mask));
+
+ if (SLJIT_UNLIKELY(!(arg & REG_MASK))) {
+ tmp = (sljit_uw)(argw & (sign | mask));
+ tmp = (sljit_uw)((argw + (tmp <= (sljit_uw)sign ? 0 : sign)) & ~mask);
+
+ FAIL_IF(load_immediate(compiler, tmp_reg, tmp));
+
+ argw -= (sljit_sw)tmp;
+ tmp = 1;
+
+ if (argw < 0) {
+ argw = -argw;
+ tmp = 0;
+ }
+
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, tmp, reg, tmp_reg,
+ (mask == 0xff) ? TYPE2_TRANSFER_IMM(argw) : argw));
+ }
+
+ if (arg & OFFS_REG_MASK) {
+ offset_reg = OFFS_REG(arg);
+ arg &= REG_MASK;
+ argw &= 0x3;
+
+ if (argw != 0 && (mask == 0xff)) {
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg) | RM(offset_reg) | ((sljit_uw)argw << 7)));
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, tmp_reg, TYPE2_TRANSFER_IMM(0)));
+ }
+
+ /* Bit 25: RM is offset. */
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg,
+ RM(offset_reg) | (mask == 0xff ? 0 : (1 << 25)) | ((sljit_uw)argw << 7)));
+ }
+
+ arg &= REG_MASK;
+
+ if (argw > mask) {
+ tmp = (sljit_uw)(argw & (sign | mask));
+ tmp = (sljit_uw)((argw + (tmp <= (sljit_uw)sign ? 0 : sign)) & ~mask);
+ imm = get_imm(tmp);
+
+ if (imm) {
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg) | imm));
+ argw -= (sljit_sw)tmp;
+ arg = tmp_reg;
+
+ SLJIT_ASSERT(argw >= -mask && argw <= mask);
+ }
+ } else if (argw < -mask) {
+ tmp = (sljit_uw)(-argw & (sign | mask));
+ tmp = (sljit_uw)((-argw + (tmp <= (sljit_uw)sign ? 0 : sign)) & ~mask);
+ imm = get_imm(tmp);
+
+ if (imm) {
+ FAIL_IF(push_inst(compiler, SUB | RD(tmp_reg) | RN(arg) | imm));
+ argw += (sljit_sw)tmp;
+ arg = tmp_reg;
+
+ SLJIT_ASSERT(argw >= -mask && argw <= mask);
+ }
+ }
+
+ if (argw <= mask && argw >= -mask) {
+ if (argw >= 0) {
+ if (mask == 0xff)
+ argw = TYPE2_TRANSFER_IMM(argw);
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg, argw));
+ }
+
+ argw = -argw;
+
+ if (mask == 0xff)
+ argw = TYPE2_TRANSFER_IMM(argw);
+
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 0, reg, arg, argw));
+ }
+
+ FAIL_IF(load_immediate(compiler, tmp_reg, (sljit_uw)argw));
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg,
+ RM(tmp_reg) | (mask == 0xff ? 0 : (1 << 25))));
+}
+
+static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 inp_flags,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ /* src1 is reg or TMP_REG1
+ src2 is reg, TMP_REG2, or imm
+ result goes to TMP_REG2, so put result can use TMP_REG1. */
+
+ /* We prefers register and simple consts. */
+ sljit_s32 dst_reg;
+ sljit_s32 src1_reg;
+ sljit_s32 src2_reg = 0;
+ sljit_s32 flags = HAS_FLAGS(op) ? SET_FLAGS : 0;
+ sljit_s32 neg_op = 0;
+
+ if (dst == TMP_REG2)
+ flags |= UNUSED_RETURN;
+
+ SLJIT_ASSERT(!(inp_flags & ALLOW_INV_IMM) || (inp_flags & ALLOW_IMM));
+
+ if (inp_flags & ALLOW_NEG_IMM) {
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ neg_op = SLJIT_SUB;
+ break;
+ case SLJIT_ADDC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ neg_op = SLJIT_SUBC;
+ break;
+ case SLJIT_SUB:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ neg_op = SLJIT_ADD;
+ break;
+ case SLJIT_SUBC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ neg_op = SLJIT_ADDC;
+ break;
+ }
+ }
+
+ do {
+ if (!(inp_flags & ALLOW_IMM))
+ break;
+
+ if (src2 & SLJIT_IMM) {
+ src2_reg = (sljit_s32)get_imm((sljit_uw)src2w);
+ if (src2_reg)
+ break;
+ if (inp_flags & ALLOW_INV_IMM) {
+ src2_reg = (sljit_s32)get_imm(~(sljit_uw)src2w);
+ if (src2_reg) {
+ flags |= INV_IMM;
+ break;
+ }
+ }
+ if (neg_op != 0) {
+ src2_reg = (sljit_s32)get_imm((sljit_uw)-src2w);
+ if (src2_reg) {
+ op = neg_op | GET_ALL_FLAGS(op);
+ break;
+ }
+ }
+ }
+
+ if (src1 & SLJIT_IMM) {
+ src2_reg = (sljit_s32)get_imm((sljit_uw)src1w);
+ if (src2_reg) {
+ flags |= ARGS_SWAPPED;
+ src1 = src2;
+ src1w = src2w;
+ break;
+ }
+ if (inp_flags & ALLOW_INV_IMM) {
+ src2_reg = (sljit_s32)get_imm(~(sljit_uw)src1w);
+ if (src2_reg) {
+ flags |= ARGS_SWAPPED | INV_IMM;
+ src1 = src2;
+ src1w = src2w;
+ break;
+ }
+ }
+ if (neg_op >= SLJIT_SUB) {
+ /* Note: additive operation (commutative). */
+ src2_reg = (sljit_s32)get_imm((sljit_uw)-src1w);
+ if (src2_reg) {
+ src1 = src2;
+ src1w = src2w;
+ op = neg_op | GET_ALL_FLAGS(op);
+ break;
+ }
+ }
+ }
+ } while(0);
+
+ /* Source 1. */
+ if (FAST_IS_REG(src1))
+ src1_reg = src1;
+ else if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, TMP_REG1));
+ src1_reg = TMP_REG1;
+ }
+ else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)src1w));
+ src1_reg = TMP_REG1;
+ }
+
+ /* Destination. */
+ dst_reg = FAST_IS_REG(dst) ? dst : TMP_REG2;
+
+ if (op <= SLJIT_MOV_P) {
+ if (dst & SLJIT_MEM) {
+ if (inp_flags & BYTE_SIZE)
+ inp_flags &= ~SIGNED;
+
+ if (FAST_IS_REG(src2))
+ return emit_op_mem(compiler, inp_flags, src2, dst, dstw, TMP_REG2);
+ }
+
+ if (FAST_IS_REG(src2) && dst_reg != TMP_REG2)
+ flags |= MOVE_REG_CONV;
+ }
+
+ /* Source 2. */
+ if (src2_reg == 0) {
+ src2_reg = (op <= SLJIT_MOV_P) ? dst_reg : TMP_REG2;
+
+ if (FAST_IS_REG(src2))
+ src2_reg = src2;
+ else if (src2 & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, inp_flags | LOAD_DATA, src2_reg, src2, src2w, TMP_REG2));
+ else
+ FAIL_IF(load_immediate(compiler, src2_reg, (sljit_uw)src2w));
+ }
+
+ FAIL_IF(emit_single_op(compiler, op, flags, (sljit_uw)dst_reg, (sljit_uw)src1_reg, (sljit_uw)src2_reg));
+
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+
+ return emit_op_mem(compiler, inp_flags, dst_reg, dst, dstw, TMP_REG1);
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__GNUC__)
+extern unsigned int __aeabi_uidivmod(unsigned int numerator, unsigned int denominator);
+extern int __aeabi_idivmod(int numerator, int denominator);
+#else
+#error "Software divmod functions are needed"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+ sljit_uw saved_reg_list[3];
+ sljit_sw saved_reg_count;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op0(compiler, op));
+
+ op = GET_OPCODE(op);
+ switch (op) {
+ case SLJIT_BREAKPOINT:
+ FAIL_IF(push_inst(compiler, BKPT));
+ break;
+ case SLJIT_NOP:
+ FAIL_IF(push_inst(compiler, NOP));
+ break;
+ case SLJIT_LMUL_UW:
+ case SLJIT_LMUL_SW:
+ return push_inst(compiler, (op == SLJIT_LMUL_UW ? UMULL : SMULL)
+ | RN(SLJIT_R1) | RD(SLJIT_R0) | RM8(SLJIT_R0) | RM(SLJIT_R1));
+ case SLJIT_DIVMOD_UW:
+ case SLJIT_DIVMOD_SW:
+ case SLJIT_DIV_UW:
+ case SLJIT_DIV_SW:
+ SLJIT_COMPILE_ASSERT((SLJIT_DIVMOD_UW & 0x2) == 0 && SLJIT_DIV_UW - 0x2 == SLJIT_DIVMOD_UW, bad_div_opcode_assignments);
+ SLJIT_ASSERT(reg_map[2] == 1 && reg_map[3] == 2 && reg_map[4] == 3);
+
+ saved_reg_count = 0;
+ if (compiler->scratches >= 4)
+ saved_reg_list[saved_reg_count++] = 3;
+ if (compiler->scratches >= 3)
+ saved_reg_list[saved_reg_count++] = 2;
+ if (op >= SLJIT_DIV_UW)
+ saved_reg_list[saved_reg_count++] = 1;
+
+ if (saved_reg_count > 0) {
+ FAIL_IF(push_inst(compiler, STR | 0x2d0000 | (saved_reg_count >= 3 ? 16 : 8)
+ | (saved_reg_list[0] << 12) /* str rX, [sp, #-8/-16]! */));
+ if (saved_reg_count >= 2) {
+ SLJIT_ASSERT(saved_reg_list[1] < 8);
+ FAIL_IF(push_inst(compiler, STR | 0x8d0004 | (saved_reg_list[1] << 12) /* str rX, [sp, #4] */));
+ }
+ if (saved_reg_count >= 3) {
+ SLJIT_ASSERT(saved_reg_list[2] < 8);
+ FAIL_IF(push_inst(compiler, STR | 0x8d0008 | (saved_reg_list[2] << 12) /* str rX, [sp, #8] */));
+ }
+ }
+
+#if defined(__GNUC__)
+ FAIL_IF(sljit_emit_ijump(compiler, SLJIT_FAST_CALL, SLJIT_IMM,
+ ((op | 0x2) == SLJIT_DIV_UW ? SLJIT_FUNC_ADDR(__aeabi_uidivmod) : SLJIT_FUNC_ADDR(__aeabi_idivmod))));
+#else
+#error "Software divmod functions are needed"
+#endif
+
+ if (saved_reg_count > 0) {
+ if (saved_reg_count >= 3) {
+ SLJIT_ASSERT(saved_reg_list[2] < 8);
+ FAIL_IF(push_inst(compiler, LDR | 0x8d0008 | (saved_reg_list[2] << 12) /* ldr rX, [sp, #8] */));
+ }
+ if (saved_reg_count >= 2) {
+ SLJIT_ASSERT(saved_reg_list[1] < 8);
+ FAIL_IF(push_inst(compiler, LDR | 0x8d0004 | (saved_reg_list[1] << 12) /* ldr rX, [sp, #4] */));
+ }
+ return push_inst(compiler, (LDR ^ (1 << 24)) | 0x8d0000 | (sljit_uw)(saved_reg_count >= 3 ? 16 : 8)
+ | (saved_reg_list[0] << 12) /* ldr rX, [sp], #8/16 */);
+ }
+ return SLJIT_SUCCESS;
+ case SLJIT_ENDBR:
+ case SLJIT_SKIP_FRAMES_BEFORE_RETURN:
+ return SLJIT_SUCCESS;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ case SLJIT_MOV_P:
+ return emit_op(compiler, SLJIT_MOV, ALLOW_ANY_IMM, dst, dstw, TMP_REG1, 0, src, srcw);
+
+ case SLJIT_MOV_U8:
+ return emit_op(compiler, SLJIT_MOV_U8, ALLOW_ANY_IMM | BYTE_SIZE, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw);
+
+ case SLJIT_MOV_S8:
+ return emit_op(compiler, SLJIT_MOV_S8, ALLOW_ANY_IMM | SIGNED | BYTE_SIZE, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw);
+
+ case SLJIT_MOV_U16:
+ return emit_op(compiler, SLJIT_MOV_U16, ALLOW_ANY_IMM | HALF_SIZE, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw);
+
+ case SLJIT_MOV_S16:
+ return emit_op(compiler, SLJIT_MOV_S16, ALLOW_ANY_IMM | SIGNED | HALF_SIZE, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw);
+
+ case SLJIT_NOT:
+ return emit_op(compiler, op, ALLOW_ANY_IMM, dst, dstw, TMP_REG1, 0, src, srcw);
+
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ return emit_op(compiler, op, 0, dst, dstw, TMP_REG1, 0, src, srcw);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD:
+ case SLJIT_ADDC:
+ case SLJIT_SUB:
+ case SLJIT_SUBC:
+ return emit_op(compiler, op, ALLOW_IMM | ALLOW_NEG_IMM, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ return emit_op(compiler, op, ALLOW_IMM, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_MUL:
+ return emit_op(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_AND:
+ return emit_op(compiler, op, ALLOW_ANY_IMM, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ if (src2 & SLJIT_IMM) {
+ compiler->shift_imm = src2w & 0x1f;
+ return emit_op(compiler, op, 0, dst, dstw, TMP_REG1, 0, src1, src1w);
+ } else {
+ compiler->shift_imm = 0x20;
+ return emit_op(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w);
+ }
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, TMP_REG2, 0, src1, src1w, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 is_left;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w));
+
+ op = GET_OPCODE(op);
+ is_left = (op == SLJIT_SHL || op == SLJIT_MSHL);
+
+ if (src_dst == src1) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, is_left ? SLJIT_ROTL : SLJIT_ROTR, src_dst, 0, src_dst, 0, src2, src2w);
+ }
+
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ /* Shift type of ROR is 3. */
+ if (src2 & SLJIT_IMM) {
+ src2w &= 0x1f;
+
+ if (src2w == 0)
+ return SLJIT_SUCCESS;
+ } else if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG2, src2, src2w, TMP_REG2));
+ src2 = TMP_REG2;
+ }
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, src1, src1w, TMP_REG1));
+ src1 = TMP_REG1;
+ } else if (src1 & SLJIT_IMM) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)src1w));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_IMM) {
+ FAIL_IF(push_inst(compiler, MOV | RD(src_dst) | RM(src_dst) | ((sljit_uw)(is_left ? 0 : 1) << 5) | ((sljit_uw)src2w << 7)));
+ src2w = (src2w ^ 0x1f) + 1;
+ return push_inst(compiler, ORR | RD(src_dst) | RN(src_dst) | RM(src1) | ((sljit_uw)(is_left ? 1 : 0) << 5) | ((sljit_uw)src2w << 7));
+ }
+
+ if (op == SLJIT_MSHL || op == SLJIT_MLSHR) {
+ FAIL_IF(push_inst(compiler, AND | SRC2_IMM | RD(TMP_REG2) | RN(src2) | 0x1f));
+ src2 = TMP_REG2;
+ }
+
+ FAIL_IF(push_inst(compiler, MOV | RD(src_dst) | RM8(src2) | ((sljit_uw)(is_left ? 0 : 1) << 5) | 0x10 | RM(src_dst)));
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | RM(src1) | ((sljit_uw)(is_left ? 1 : 0) << 5) | (1 << 7)));
+ FAIL_IF(push_inst(compiler, EOR | SRC2_IMM | RD(TMP_REG2) | RN(src2) | 0x1f));
+ return push_inst(compiler, ORR | RD(src_dst) | RN(src_dst) | RM(TMP_REG1) | ((sljit_uw)(is_left ? 1 : 0) << 5) | 0x10 | RM8(TMP_REG2));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_src(compiler, op, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ switch (op) {
+ case SLJIT_FAST_RETURN:
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14);
+
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG2) | RM(src)));
+ else
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG2, src, srcw, TMP_REG1));
+
+ return push_inst(compiler, BX | RM(TMP_REG2));
+ case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN:
+ return SLJIT_SUCCESS;
+ case SLJIT_PREFETCH_L1:
+ case SLJIT_PREFETCH_L2:
+ case SLJIT_PREFETCH_L3:
+ case SLJIT_PREFETCH_ONCE:
+#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+ SLJIT_ASSERT(src & SLJIT_MEM);
+ return emit_op_mem(compiler, PRELOAD | LOAD_DATA, TMP_PC, src, srcw, TMP_REG1);
+#else /* !SLJIT_CONFIG_ARM_V7 */
+ return SLJIT_SUCCESS;
+#endif /* SLJIT_CONFIG_ARM_V7 */
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_register_index(reg));
+ return reg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
+ return (freg_map[reg] << 1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ SLJIT_UNUSED_ARG(size);
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_custom(compiler, instruction, size));
+
+ return push_inst(compiler, *(sljit_uw*)instruction);
+}
+
+/* --------------------------------------------------------------------- */
+/* Floating point operators */
+/* --------------------------------------------------------------------- */
+
+#define FPU_LOAD (1 << 20)
+#define EMIT_FPU_DATA_TRANSFER(inst, add, base, freg, offs) \
+ ((inst) | (sljit_uw)((add) << 23) | RN(base) | VD(freg) | (sljit_uw)(offs))
+
+static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
+{
+ sljit_uw imm;
+ sljit_uw inst = VSTR_F32 | (flags & (SLJIT_32 | FPU_LOAD));
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+ arg &= ~SLJIT_MEM;
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG2) | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | (((sljit_uw)argw & 0x3) << 7)));
+ arg = TMP_REG2;
+ argw = 0;
+ }
+
+ /* Fast loads and stores. */
+ if (arg) {
+ if (!(argw & ~0x3fc))
+ return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, arg & REG_MASK, reg, argw >> 2));
+ if (!(-argw & ~0x3fc))
+ return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 0, arg & REG_MASK, reg, (-argw) >> 2));
+
+ imm = get_imm((sljit_uw)argw & ~(sljit_uw)0x3fc);
+ if (imm) {
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG2) | RN(arg & REG_MASK) | imm));
+ return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG2, reg, (argw & 0x3fc) >> 2));
+ }
+ imm = get_imm((sljit_uw)-argw & ~(sljit_uw)0x3fc);
+ if (imm) {
+ argw = -argw;
+ FAIL_IF(push_inst(compiler, SUB | RD(TMP_REG2) | RN(arg & REG_MASK) | imm));
+ return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 0, TMP_REG2, reg, (argw & 0x3fc) >> 2));
+ }
+ }
+
+ if (arg) {
+ FAIL_IF(load_immediate(compiler, TMP_REG2, (sljit_uw)argw));
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG2) | RN(arg & REG_MASK) | RM(TMP_REG2)));
+ }
+ else
+ FAIL_IF(load_immediate(compiler, TMP_REG2, (sljit_uw)argw));
+
+ return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG2, reg, 0));
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ op ^= SLJIT_32;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG1, src, srcw));
+ src = TMP_FREG1;
+ }
+
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VCVT_S32_F32, op & SLJIT_32, TMP_FREG1, src, 0)));
+
+ if (FAST_IS_REG(dst))
+ return push_inst(compiler, VMOV | (1 << 20) | RD(dst) | VN(TMP_FREG1));
+
+ /* Store the integer value from a VFP register. */
+ return emit_fop_mem(compiler, 0, TMP_FREG1, dst, dstw);
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ op ^= SLJIT_32;
+
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst(compiler, VMOV | RD(src) | VN(TMP_FREG1)));
+ else if (src & SLJIT_MEM) {
+ /* Load the integer value into a VFP register. */
+ FAIL_IF(emit_fop_mem(compiler, FPU_LOAD, TMP_FREG1, src, srcw));
+ }
+ else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)srcw));
+ FAIL_IF(push_inst(compiler, VMOV | RD(TMP_REG1) | VN(TMP_FREG1)));
+ }
+
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VCVT_F32_S32, op & SLJIT_32, dst_r, TMP_FREG1, 0)));
+
+ if (dst & SLJIT_MEM)
+ return emit_fop_mem(compiler, (op & SLJIT_32), TMP_FREG1, dst, dstw);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ op ^= SLJIT_32;
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG1, src1, src1w));
+ src1 = TMP_FREG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG2, src2, src2w));
+ src2 = TMP_FREG2;
+ }
+
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VCMP_F32, op & SLJIT_32, src1, src2, 0)));
+ return push_inst(compiler, VMRS);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+
+ SLJIT_COMPILE_ASSERT((SLJIT_32 == 0x100), float_transfer_bit_error);
+ SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (GET_OPCODE(op) != SLJIT_CONV_F64_FROM_F32)
+ op ^= SLJIT_32;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, dst_r, src, srcw));
+ src = dst_r;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV_F64:
+ if (src != dst_r) {
+ if (dst_r != TMP_FREG1)
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VMOV_F32, op & SLJIT_32, dst_r, src, 0)));
+ else
+ dst_r = src;
+ }
+ break;
+ case SLJIT_NEG_F64:
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VNEG_F32, op & SLJIT_32, dst_r, src, 0)));
+ break;
+ case SLJIT_ABS_F64:
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VABS_F32, op & SLJIT_32, dst_r, src, 0)));
+ break;
+ case SLJIT_CONV_F64_FROM_F32:
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VCVT_F64_F32, op & SLJIT_32, dst_r, src, 0)));
+ op ^= SLJIT_32;
+ break;
+ }
+
+ if (dst & SLJIT_MEM)
+ return emit_fop_mem(compiler, (op & SLJIT_32), dst_r, dst, dstw);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ op ^= SLJIT_32;
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG2, src2, src2w));
+ src2 = TMP_FREG2;
+ }
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG1, src1, src1w));
+ src1 = TMP_FREG1;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD_F64:
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VADD_F32, op & SLJIT_32, dst_r, src2, src1)));
+ break;
+
+ case SLJIT_SUB_F64:
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VSUB_F32, op & SLJIT_32, dst_r, src2, src1)));
+ break;
+
+ case SLJIT_MUL_F64:
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VMUL_F32, op & SLJIT_32, dst_r, src2, src1)));
+ break;
+
+ case SLJIT_DIV_F64:
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VDIV_F32, op & SLJIT_32, dst_r, src2, src1)));
+ break;
+ }
+
+ if (dst_r == TMP_FREG1)
+ FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_32), TMP_FREG1, dst, dstw));
+
+ return SLJIT_SUCCESS;
+}
+
+#undef EMIT_FPU_DATA_TRANSFER
+
+/* --------------------------------------------------------------------- */
+/* Other instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14);
+
+ if (FAST_IS_REG(dst))
+ return push_inst(compiler, MOV | RD(dst) | RM(TMP_REG2));
+
+ /* Memory. */
+ return emit_op_mem(compiler, WORD_SIZE, TMP_REG2, dst, dstw, TMP_REG1);
+}
+
+/* --------------------------------------------------------------------- */
+/* Conditional instructions */
+/* --------------------------------------------------------------------- */
+
+static sljit_uw get_cc(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ switch (type) {
+ case SLJIT_EQUAL:
+ case SLJIT_F_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */
+ return 0x00000000;
+
+ case SLJIT_NOT_EQUAL:
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL: /* Not supported. */
+ return 0x10000000;
+
+ case SLJIT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_ADD)
+ return 0x20000000;
+ /* fallthrough */
+
+ case SLJIT_LESS:
+ return 0x30000000;
+
+ case SLJIT_NOT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_ADD)
+ return 0x30000000;
+ /* fallthrough */
+
+ case SLJIT_GREATER_EQUAL:
+ return 0x20000000;
+
+ case SLJIT_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ return 0x80000000;
+
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ return 0x90000000;
+
+ case SLJIT_SIG_LESS:
+ case SLJIT_UNORDERED_OR_LESS:
+ return 0xb0000000;
+
+ case SLJIT_SIG_GREATER_EQUAL:
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ return 0xa0000000;
+
+ case SLJIT_SIG_GREATER:
+ case SLJIT_F_GREATER:
+ case SLJIT_ORDERED_GREATER:
+ return 0xc0000000;
+
+ case SLJIT_SIG_LESS_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ return 0xd0000000;
+
+ case SLJIT_OVERFLOW:
+ if (!(compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)))
+ return 0x10000000;
+ /* fallthrough */
+
+ case SLJIT_UNORDERED:
+ return 0x60000000;
+
+ case SLJIT_NOT_OVERFLOW:
+ if (!(compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)))
+ return 0x00000000;
+ /* fallthrough */
+
+ case SLJIT_ORDERED:
+ return 0x70000000;
+
+ case SLJIT_F_LESS:
+ case SLJIT_ORDERED_LESS:
+ return 0x40000000;
+
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ return 0x50000000;
+
+ default:
+ SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL_REG_ARG);
+ return 0xe0000000;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ struct sljit_label *label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_label(compiler));
+
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ return compiler->last_label;
+
+ label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
+ PTR_FAIL_IF(!label);
+ set_label(label, compiler);
+ return label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ struct sljit_jump *jump;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_jump(compiler, type));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ type &= 0xff;
+
+ SLJIT_ASSERT(reg_map[TMP_REG1] != 14);
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (type >= SLJIT_FAST_CALL)
+ PTR_FAIL_IF(prepare_blx(compiler));
+ PTR_FAIL_IF(push_inst_with_unique_literal(compiler, ((EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1,
+ type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, TMP_PC, 0)) & ~COND_MASK) | get_cc(compiler, type), 0));
+
+ if (jump->flags & SLJIT_REWRITABLE_JUMP) {
+ jump->addr = compiler->size;
+ compiler->patches++;
+ }
+
+ if (type >= SLJIT_FAST_CALL) {
+ jump->flags |= IS_BL;
+ PTR_FAIL_IF(emit_blx(compiler));
+ }
+
+ if (!(jump->flags & SLJIT_REWRITABLE_JUMP))
+ jump->addr = compiler->size;
+#else
+ if (type >= SLJIT_FAST_CALL)
+ jump->flags |= IS_BL;
+ PTR_FAIL_IF(emit_imm(compiler, TMP_REG1, 0));
+ PTR_FAIL_IF(push_inst(compiler, (((type <= SLJIT_JUMP ? BX : BLX) | RM(TMP_REG1)) & ~COND_MASK) | get_cc(compiler, type)));
+ jump->addr = compiler->size;
+#endif
+ return jump;
+}
+
+#ifdef __SOFTFP__
+
+static sljit_s32 softfloat_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src, sljit_u32 *extra_space)
+{
+ sljit_u32 is_tail_call = *extra_space & SLJIT_CALL_RETURN;
+ sljit_u32 offset = 0;
+ sljit_u32 word_arg_offset = 0;
+ sljit_u32 src_offset = 4 * sizeof(sljit_sw);
+ sljit_u32 float_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_u8 offsets[4];
+ sljit_u8 *offset_ptr = offsets;
+
+ if (src && FAST_IS_REG(*src))
+ src_offset = (sljit_uw)reg_map[*src] * sizeof(sljit_sw);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK);
+
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset & 0x7)
+ offset += sizeof(sljit_sw);
+ *offset_ptr++ = (sljit_u8)offset;
+ offset += sizeof(sljit_f64);
+ float_arg_count++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ *offset_ptr++ = (sljit_u8)offset;
+ offset += sizeof(sljit_f32);
+ float_arg_count++;
+ break;
+ default:
+ *offset_ptr++ = (sljit_u8)offset;
+ offset += sizeof(sljit_sw);
+ word_arg_offset += sizeof(sljit_sw);
+ break;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ if (offset > 4 * sizeof(sljit_sw) && (!is_tail_call || offset > compiler->args_size)) {
+ /* Keep lr register on the stack. */
+ if (is_tail_call)
+ offset += sizeof(sljit_sw);
+
+ offset = ((offset - 4 * sizeof(sljit_sw)) + 0x7) & ~(sljit_uw)0x7;
+
+ *extra_space = offset;
+
+ if (is_tail_call)
+ FAIL_IF(emit_stack_frame_release(compiler, (sljit_s32)offset));
+ else
+ FAIL_IF(push_inst(compiler, SUB | RD(SLJIT_SP) | RN(SLJIT_SP) | SRC2_IMM | offset));
+ } else {
+ if (is_tail_call)
+ FAIL_IF(emit_stack_frame_release(compiler, -1));
+ *extra_space = 0;
+ }
+
+ /* Process arguments in reversed direction. */
+ while (types) {
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ float_arg_count--;
+ offset = *(--offset_ptr);
+
+ SLJIT_ASSERT((offset & 0x7) == 0);
+
+ if (offset < 4 * sizeof(sljit_sw)) {
+ if (src_offset == offset || src_offset == offset + sizeof(sljit_sw)) {
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | (src_offset >> 2)));
+ *src = TMP_REG1;
+ }
+ FAIL_IF(push_inst(compiler, VMOV2 | 0x100000 | (offset << 10) | ((offset + sizeof(sljit_sw)) << 14) | float_arg_count));
+ } else
+ FAIL_IF(push_inst(compiler, VSTR_F32 | 0x800100 | RN(SLJIT_SP)
+ | (float_arg_count << 12) | ((offset - 4 * sizeof(sljit_sw)) >> 2)));
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ float_arg_count--;
+ offset = *(--offset_ptr);
+
+ if (offset < 4 * sizeof(sljit_sw)) {
+ if (src_offset == offset) {
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | (src_offset >> 2)));
+ *src = TMP_REG1;
+ }
+ FAIL_IF(push_inst(compiler, VMOV | 0x100000 | (float_arg_count << 16) | (offset << 10)));
+ } else
+ FAIL_IF(push_inst(compiler, VSTR_F32 | 0x800000 | RN(SLJIT_SP)
+ | (float_arg_count << 12) | ((offset - 4 * sizeof(sljit_sw)) >> 2)));
+ break;
+ default:
+ word_arg_offset -= sizeof(sljit_sw);
+ offset = *(--offset_ptr);
+
+ SLJIT_ASSERT(offset >= word_arg_offset);
+
+ if (offset != word_arg_offset) {
+ if (offset < 4 * sizeof(sljit_sw)) {
+ if (src_offset == offset) {
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | (src_offset >> 2)));
+ *src = TMP_REG1;
+ }
+ else if (src_offset == word_arg_offset) {
+ *src = (sljit_s32)(SLJIT_R0 + (offset >> 2));
+ src_offset = offset;
+ }
+ FAIL_IF(push_inst(compiler, MOV | (offset << 10) | (word_arg_offset >> 2)));
+ } else
+ FAIL_IF(push_inst(compiler, STR | 0x800000 | RN(SLJIT_SP) | (word_arg_offset << 10) | (offset - 4 * sizeof(sljit_sw))));
+ }
+ break;
+ }
+
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 softfloat_post_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types)
+{
+ if ((arg_types & SLJIT_ARG_MASK) == SLJIT_ARG_TYPE_F64)
+ FAIL_IF(push_inst(compiler, VMOV2 | (1 << 16) | (0 << 12) | 0));
+ if ((arg_types & SLJIT_ARG_MASK) == SLJIT_ARG_TYPE_F32)
+ FAIL_IF(push_inst(compiler, VMOV | (0 << 16) | (0 << 12)));
+
+ return SLJIT_SUCCESS;
+}
+
+#else /* !__SOFTFP__ */
+
+static sljit_s32 hardfloat_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types)
+{
+ sljit_u32 offset = SLJIT_FR0;
+ sljit_u32 new_offset = SLJIT_FR0;
+ sljit_u32 f32_offset = 0;
+
+ /* Remove return value. */
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset != new_offset)
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VMOV_F32,
+ SLJIT_32, new_offset, offset, 0)));
+
+ new_offset++;
+ offset++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (f32_offset != 0) {
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VMOV_F32,
+ 0x400000, f32_offset, offset, 0)));
+ f32_offset = 0;
+ } else {
+ if (offset != new_offset)
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VMOV_F32,
+ 0, new_offset, offset, 0)));
+ f32_offset = new_offset;
+ new_offset++;
+ }
+ offset++;
+ break;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#endif /* __SOFTFP__ */
+
+#undef EMIT_FPU_OPERATION
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+#ifdef __SOFTFP__
+ struct sljit_jump *jump;
+ sljit_u32 extra_space = (sljit_u32)type;
+#endif
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+#ifdef __SOFTFP__
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG) {
+ PTR_FAIL_IF(softfloat_call_with_args(compiler, arg_types, NULL, &extra_space));
+ SLJIT_ASSERT((extra_space & 0x7) == 0);
+
+ if ((type & SLJIT_CALL_RETURN) && extra_space == 0)
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+
+ SLJIT_SKIP_CHECKS(compiler);
+ jump = sljit_emit_jump(compiler, type);
+ PTR_FAIL_IF(jump == NULL);
+
+ if (extra_space > 0) {
+ if (type & SLJIT_CALL_RETURN)
+ PTR_FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1,
+ TMP_REG2, SLJIT_SP, extra_space - sizeof(sljit_sw))));
+
+ PTR_FAIL_IF(push_inst(compiler, ADD | RD(SLJIT_SP) | RN(SLJIT_SP) | SRC2_IMM | extra_space));
+
+ if (type & SLJIT_CALL_RETURN) {
+ PTR_FAIL_IF(push_inst(compiler, BX | RM(TMP_REG2)));
+ return jump;
+ }
+ }
+
+ SLJIT_ASSERT(!(type & SLJIT_CALL_RETURN));
+ PTR_FAIL_IF(softfloat_post_call_with_args(compiler, arg_types));
+ return jump;
+ }
+#endif /* __SOFTFP__ */
+
+ if (type & SLJIT_CALL_RETURN) {
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, -1));
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+ }
+
+#ifndef __SOFTFP__
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ PTR_FAIL_IF(hardfloat_call_with_args(compiler, arg_types));
+#endif /* !__SOFTFP__ */
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ struct sljit_jump *jump;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ SLJIT_ASSERT(reg_map[TMP_REG1] != 14);
+
+ if (!(src & SLJIT_IMM)) {
+ if (FAST_IS_REG(src)) {
+ SLJIT_ASSERT(reg_map[src] != 14);
+ return push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(src));
+ }
+
+ SLJIT_ASSERT(src & SLJIT_MEM);
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
+ return push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(TMP_REG1));
+ }
+
+ /* These jumps are converted to jump/call instructions when possible. */
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ FAIL_IF(!jump);
+ set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_BL : 0));
+ jump->u.target = (sljit_uw)srcw;
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (type >= SLJIT_FAST_CALL)
+ FAIL_IF(prepare_blx(compiler));
+ FAIL_IF(push_inst_with_unique_literal(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, TMP_PC, 0), 0));
+ if (type >= SLJIT_FAST_CALL)
+ FAIL_IF(emit_blx(compiler));
+#else
+ FAIL_IF(emit_imm(compiler, TMP_REG1, 0));
+ FAIL_IF(push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(TMP_REG1)));
+#endif
+ jump->addr = compiler->size;
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+#ifdef __SOFTFP__
+ sljit_u32 extra_space = (sljit_u32)type;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+
+ if ((type & SLJIT_CALL_RETURN) && (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options)))) {
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | RM(src)));
+ src = TMP_REG1;
+ }
+
+#ifdef __SOFTFP__
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG) {
+ FAIL_IF(softfloat_call_with_args(compiler, arg_types, &src, &extra_space));
+ SLJIT_ASSERT((extra_space & 0x7) == 0);
+
+ if ((type & SLJIT_CALL_RETURN) && extra_space == 0)
+ type = SLJIT_JUMP;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw));
+
+ if (extra_space > 0) {
+ if (type & SLJIT_CALL_RETURN)
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1,
+ TMP_REG2, SLJIT_SP, extra_space - sizeof(sljit_sw))));
+
+ FAIL_IF(push_inst(compiler, ADD | RD(SLJIT_SP) | RN(SLJIT_SP) | SRC2_IMM | extra_space));
+
+ if (type & SLJIT_CALL_RETURN)
+ return push_inst(compiler, BX | RM(TMP_REG2));
+ }
+
+ SLJIT_ASSERT(!(type & SLJIT_CALL_RETURN));
+ return softfloat_post_call_with_args(compiler, arg_types);
+ }
+#endif /* __SOFTFP__ */
+
+ if (type & SLJIT_CALL_RETURN) {
+ FAIL_IF(emit_stack_frame_release(compiler, -1));
+ type = SLJIT_JUMP;
+ }
+
+#ifndef __SOFTFP__
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ FAIL_IF(hardfloat_call_with_args(compiler, arg_types));
+#endif /* !__SOFTFP__ */
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
+
+#ifdef __SOFTFP__
+
+static SLJIT_INLINE sljit_s32 emit_fmov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+ if (compiler->options & SLJIT_ENTER_REG_ARG) {
+ if (src == SLJIT_FR0)
+ return SLJIT_SUCCESS;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_fop1(compiler, op, SLJIT_RETURN_FREG, 0, src, srcw);
+ }
+
+ if (FAST_IS_REG(src)) {
+ if (op & SLJIT_32)
+ return push_inst(compiler, VMOV | (1 << 20) | RD(SLJIT_R0) | VN(src));
+ return push_inst(compiler, VMOV2 | (1 << 20) | RD(SLJIT_R0) | RN(SLJIT_R1) | VM(src));
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+
+ if (op & SLJIT_32)
+ return sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, src, srcw);
+ return sljit_emit_mem(compiler, SLJIT_MOV, SLJIT_REG_PAIR(SLJIT_R0, SLJIT_R1), src, srcw);
+}
+
+#endif /* __SOFTFP__ */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ sljit_s32 dst_reg, flags = GET_ALL_FLAGS(op);
+ sljit_uw cc, ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ op = GET_OPCODE(op);
+ cc = get_cc(compiler, type);
+ dst_reg = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if (op < SLJIT_ADD) {
+ FAIL_IF(push_inst(compiler, MOV | RD(dst_reg) | SRC2_IMM | 0));
+ FAIL_IF(push_inst(compiler, ((MOV | RD(dst_reg) | SRC2_IMM | 1) & ~COND_MASK) | cc));
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, WORD_SIZE, TMP_REG1, dst, dstw, TMP_REG2);
+ return SLJIT_SUCCESS;
+ }
+
+ ins = (op == SLJIT_AND ? AND : (op == SLJIT_OR ? ORR : EOR));
+
+ if (dst & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, dst, dstw, TMP_REG2));
+
+ FAIL_IF(push_inst(compiler, ((ins | RD(dst_reg) | RN(dst_reg) | SRC2_IMM | 1) & ~COND_MASK) | cc));
+
+ if (op == SLJIT_AND)
+ FAIL_IF(push_inst(compiler, ((ins | RD(dst_reg) | RN(dst_reg) | SRC2_IMM | 0) & ~COND_MASK) | (cc ^ 0x10000000)));
+
+ if (dst & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, dst, dstw, TMP_REG2));
+
+ if (flags & SLJIT_SET_Z)
+ return push_inst(compiler, MOV | SET_FLAGS | RD(TMP_REG2) | RM(dst_reg));
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_uw cc, tmp;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+ cc = get_cc(compiler, type & ~SLJIT_32);
+
+ if (SLJIT_UNLIKELY(src & SLJIT_IMM)) {
+ tmp = get_imm((sljit_uw)srcw);
+ if (tmp)
+ return push_inst(compiler, ((MOV | RD(dst_reg) | tmp) & ~COND_MASK) | cc);
+
+ tmp = get_imm(~(sljit_uw)srcw);
+ if (tmp)
+ return push_inst(compiler, ((MVN | RD(dst_reg) | tmp) & ~COND_MASK) | cc);
+
+#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+ tmp = (sljit_uw)srcw;
+ FAIL_IF(push_inst(compiler, (MOVW & ~COND_MASK) | cc | RD(dst_reg) | ((tmp << 4) & 0xf0000) | (tmp & 0xfff)));
+ if (tmp <= 0xffff)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, (MOVT & ~COND_MASK) | cc | RD(dst_reg) | ((tmp >> 12) & 0xf0000) | ((tmp >> 16) & 0xfff));
+#else
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)srcw));
+ src = TMP_REG1;
+#endif
+ }
+
+ return push_inst(compiler, ((MOV | RD(dst_reg) | RM(src)) & ~COND_MASK) | cc);
+}
+
+static sljit_s32 update_mem_addr(struct sljit_compiler *compiler, sljit_s32 *mem, sljit_sw *memw, sljit_s32 max_offset)
+{
+ sljit_s32 arg = *mem;
+ sljit_sw argw = *memw;
+ sljit_uw imm, tmp;
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ sljit_sw mask = max_offset >= 0xf00 ? 0xfff : 0xff;
+ sljit_sw sign = max_offset >= 0xf00 ? 0x1000 : 0x100;
+#else /* !SLJIT_CONFIG_ARM_V5 */
+ sljit_sw mask = 0xfff;
+ sljit_sw sign = 0x1000;
+
+ SLJIT_ASSERT(max_offset >= 0xf00);
+#endif /* SLJIT_CONFIG_ARM_V5 */
+
+ *mem = TMP_REG1;
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ *memw = 0;
+ return push_inst(compiler, ADD | RD(TMP_REG1) | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | ((sljit_uw)(argw & 0x3) << 7));
+ }
+
+ arg &= REG_MASK;
+
+ if (arg) {
+ if (argw <= max_offset && argw >= -mask) {
+ *mem = arg;
+ return SLJIT_SUCCESS;
+ }
+
+ if (argw >= 0) {
+ tmp = (sljit_uw)(argw & (sign | mask));
+ tmp = (sljit_uw)((argw + ((tmp <= (sljit_uw)max_offset || tmp == (sljit_uw)sign) ? 0 : sign)) & ~mask);
+ imm = get_imm(tmp);
+
+ if (imm) {
+ *memw = argw - (sljit_sw)tmp;
+ SLJIT_ASSERT(*memw >= -mask && *memw <= max_offset);
+
+ return push_inst(compiler, ADD | RD(TMP_REG1) | RN(arg) | imm);
+ }
+ } else {
+ tmp = (sljit_uw)(-argw & (sign | mask));
+ tmp = (sljit_uw)((-argw + ((tmp <= (sljit_uw)((sign << 1) - max_offset - 1)) ? 0 : sign)) & ~mask);
+ imm = get_imm(tmp);
+
+ if (imm) {
+ *memw = argw + (sljit_sw)tmp;
+ SLJIT_ASSERT(*memw >= -mask && *memw <= max_offset);
+
+ return push_inst(compiler, SUB | RD(TMP_REG1) | RN(arg) | imm);
+ }
+ }
+ }
+
+ tmp = (sljit_uw)(argw & (sign | mask));
+ tmp = (sljit_uw)((argw + ((tmp <= (sljit_uw)max_offset || tmp == (sljit_uw)sign) ? 0 : sign)) & ~mask);
+ *memw = argw - (sljit_sw)tmp;
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, tmp));
+
+ if (arg == 0)
+ return SLJIT_SUCCESS;
+
+ return push_inst(compiler, ADD | RD(TMP_REG1) | RN(TMP_REG1) | RM(arg));
+}
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+
+static sljit_s32 sljit_emit_mem_unaligned(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 flags, steps, tmp_reg;
+ sljit_uw add, shift;
+
+ switch (type & 0xff) {
+ case SLJIT_MOV_U8:
+ case SLJIT_MOV_S8:
+ flags = BYTE_SIZE;
+ if (!(type & SLJIT_MEM_STORE))
+ flags |= LOAD_DATA;
+ if ((type & 0xff) == SLJIT_MOV_S8)
+ flags |= SIGNED;
+
+ return emit_op_mem(compiler, flags, reg, mem, memw, TMP_REG1);
+
+ case SLJIT_MOV_U16:
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 1));
+ flags = BYTE_SIZE;
+ steps = 1;
+ break;
+
+ case SLJIT_MOV_S16:
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xff - 1));
+ flags = BYTE_SIZE | SIGNED;
+ steps = 1;
+ break;
+
+ default:
+ if (type & SLJIT_MEM_UNALIGNED_32) {
+ flags = WORD_SIZE;
+ if (!(type & SLJIT_MEM_STORE))
+ flags |= LOAD_DATA;
+
+ return emit_op_mem(compiler, flags, reg, mem, memw, TMP_REG1);
+ }
+
+ if (!(type & SLJIT_MEM_UNALIGNED_16)) {
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 3));
+ flags = BYTE_SIZE;
+ steps = 3;
+ break;
+ }
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xff - 2));
+
+ add = 1;
+ if (memw < 0) {
+ add = 0;
+ memw = -memw;
+ }
+
+ tmp_reg = reg;
+
+ if (type & SLJIT_MEM_STORE) {
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(HALF_SIZE, add, reg, mem, TYPE2_TRANSFER_IMM(memw))));
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG2) | RM(reg) | (16 << 7) | (2 << 4)));
+ } else {
+ if (reg == mem) {
+ SLJIT_ASSERT(reg != TMP_REG1);
+ tmp_reg = TMP_REG1;
+ }
+
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(HALF_SIZE | LOAD_DATA, add, tmp_reg, mem, TYPE2_TRANSFER_IMM(memw))));
+ }
+
+ if (!add) {
+ memw -= 2;
+ if (memw <= 0) {
+ memw = -memw;
+ add = 1;
+ }
+ } else
+ memw += 2;
+
+ if (type & SLJIT_MEM_STORE)
+ return push_inst(compiler, EMIT_DATA_TRANSFER(HALF_SIZE, add, TMP_REG2, mem, TYPE2_TRANSFER_IMM(memw)));
+
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(HALF_SIZE | LOAD_DATA, add, TMP_REG2, mem, TYPE2_TRANSFER_IMM(memw))));
+ return push_inst(compiler, ORR | RD(reg) | RN(tmp_reg) | RM(TMP_REG2) | (16 << 7));
+ }
+
+ SLJIT_ASSERT(steps > 0);
+
+ add = 1;
+ if (memw < 0) {
+ add = 0;
+ memw = -memw;
+ }
+
+ if (type & SLJIT_MEM_STORE) {
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(BYTE_SIZE, add, reg, mem, memw)));
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG2) | RM(reg) | (8 << 7) | (2 << 4)));
+
+ while (1) {
+ if (!add) {
+ memw -= 1;
+ if (memw == 0)
+ add = 1;
+ } else
+ memw += 1;
+
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(BYTE_SIZE, add, TMP_REG2, mem, memw)));
+
+ if (--steps == 0)
+ return SLJIT_SUCCESS;
+
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG2) | RM(TMP_REG2) | (8 << 7) | (2 << 4)));
+ }
+ }
+
+ tmp_reg = reg;
+
+ if (reg == mem) {
+ SLJIT_ASSERT(reg != TMP_REG1);
+ tmp_reg = TMP_REG1;
+ }
+
+ shift = 8;
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(BYTE_SIZE | LOAD_DATA, add, tmp_reg, mem, memw)));
+
+ do {
+ if (!add) {
+ memw -= 1;
+ if (memw == 0)
+ add = 1;
+ } else
+ memw += 1;
+
+ if (steps > 1) {
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(BYTE_SIZE | LOAD_DATA, add, TMP_REG2, mem, memw)));
+ FAIL_IF(push_inst(compiler, ORR | RD(tmp_reg) | RN(tmp_reg) | RM(TMP_REG2) | (shift << 7)));
+ shift += 8;
+ }
+ } while (--steps != 0);
+
+ flags |= LOAD_DATA;
+
+ if (flags & SIGNED)
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(flags, add, TMP_REG2, mem, TYPE2_TRANSFER_IMM(memw))));
+ else
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(flags, add, TMP_REG2, mem, memw)));
+
+ return push_inst(compiler, ORR | RD(reg) | RN(tmp_reg) | RM(TMP_REG2) | (shift << 7));
+}
+
+#endif /* SLJIT_CONFIG_ARM_V5 */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 flags;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (!(reg & REG_PAIR_MASK)) {
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ ADJUST_LOCAL_OFFSET(mem, memw);
+#endif /* SLJIT_CONFIG_ARM_V5 */
+
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+ }
+
+ ADJUST_LOCAL_OFFSET(mem, memw);
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (type & (SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16)) {
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, (type & SLJIT_MEM_UNALIGNED_16) ? 0xfff - 6 : 0xfff - 7));
+
+ if (!(type & SLJIT_MEM_STORE) && REG_PAIR_FIRST(reg) == (mem & REG_MASK)) {
+ FAIL_IF(sljit_emit_mem_unaligned(compiler, type, REG_PAIR_SECOND(reg), SLJIT_MEM1(mem), memw + SSIZE_OF(sw)));
+ return sljit_emit_mem_unaligned(compiler, type, REG_PAIR_FIRST(reg), SLJIT_MEM1(mem), memw);
+ }
+
+ FAIL_IF(sljit_emit_mem_unaligned(compiler, type, REG_PAIR_FIRST(reg), SLJIT_MEM1(mem), memw));
+ return sljit_emit_mem_unaligned(compiler, type, REG_PAIR_SECOND(reg), SLJIT_MEM1(mem), memw + SSIZE_OF(sw));
+ }
+#endif /* SLJIT_CONFIG_ARM_V5 */
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 4));
+
+ flags = WORD_SIZE;
+
+ if (!(type & SLJIT_MEM_STORE)) {
+ if (REG_PAIR_FIRST(reg) == (mem & REG_MASK)) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, REG_PAIR_SECOND(reg), SLJIT_MEM1(mem), memw + SSIZE_OF(sw), TMP_REG1));
+ return emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, REG_PAIR_FIRST(reg), SLJIT_MEM1(mem), memw, TMP_REG1);
+ }
+
+ flags = WORD_SIZE | LOAD_DATA;
+ }
+
+ FAIL_IF(emit_op_mem(compiler, flags, REG_PAIR_FIRST(reg), SLJIT_MEM1(mem), memw, TMP_REG1));
+ return emit_op_mem(compiler, flags, REG_PAIR_SECOND(reg), SLJIT_MEM1(mem), memw + SSIZE_OF(sw), TMP_REG1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 flags;
+ sljit_uw is_type1_transfer, inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem_update(compiler, type, reg, mem, memw));
+
+ is_type1_transfer = 1;
+
+ switch (type & 0xff) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ case SLJIT_MOV_P:
+ flags = WORD_SIZE;
+ break;
+ case SLJIT_MOV_U8:
+ flags = BYTE_SIZE;
+ break;
+ case SLJIT_MOV_S8:
+ if (!(type & SLJIT_MEM_STORE))
+ is_type1_transfer = 0;
+ flags = BYTE_SIZE | SIGNED;
+ break;
+ case SLJIT_MOV_U16:
+ is_type1_transfer = 0;
+ flags = HALF_SIZE;
+ break;
+ case SLJIT_MOV_S16:
+ is_type1_transfer = 0;
+ flags = HALF_SIZE | SIGNED;
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ flags = WORD_SIZE;
+ break;
+ }
+
+ if (!(type & SLJIT_MEM_STORE))
+ flags |= LOAD_DATA;
+
+ SLJIT_ASSERT(is_type1_transfer == !!IS_TYPE1_TRANSFER(flags));
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ if (!is_type1_transfer && memw != 0)
+ return SLJIT_ERR_UNSUPPORTED;
+ } else {
+ if (is_type1_transfer) {
+ if (memw > 4095 || memw < -4095)
+ return SLJIT_ERR_UNSUPPORTED;
+ } else if (memw > 255 || memw < -255)
+ return SLJIT_ERR_UNSUPPORTED;
+ }
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ memw &= 0x3;
+
+ inst = EMIT_DATA_TRANSFER(flags, 1, reg, mem & REG_MASK, RM(OFFS_REG(mem)) | ((sljit_uw)memw << 7));
+
+ if (is_type1_transfer)
+ inst |= (1 << 25);
+
+ if (type & SLJIT_MEM_POST)
+ inst ^= (1 << 24);
+ else
+ inst |= (1 << 21);
+
+ return push_inst(compiler, inst);
+ }
+
+ inst = EMIT_DATA_TRANSFER(flags, 0, reg, mem & REG_MASK, 0);
+
+ if (type & SLJIT_MEM_POST)
+ inst ^= (1 << 24);
+ else
+ inst |= (1 << 21);
+
+ if (is_type1_transfer) {
+ if (memw >= 0)
+ inst |= (1 << 23);
+ else
+ memw = -memw;
+
+ return push_inst(compiler, inst | (sljit_uw)memw);
+ }
+
+ if (memw >= 0)
+ inst |= (1 << 23);
+ else
+ memw = -memw;
+
+ return push_inst(compiler, inst | TYPE2_TRANSFER_IMM((sljit_uw)memw));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ sljit_s32 max_offset;
+ sljit_s32 dst;
+#endif /* SLJIT_CONFIG_ARM_V5 */
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw));
+
+ if (type & SLJIT_MEM_UNALIGNED_32)
+ return emit_fop_mem(compiler, ((type ^ SLJIT_32) & SLJIT_32) | ((type & SLJIT_MEM_STORE) ? 0 : FPU_LOAD), freg, mem, memw);
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (type & SLJIT_MEM_STORE) {
+ FAIL_IF(push_inst(compiler, VMOV | (1 << 20) | VN(freg) | RD(TMP_REG2)));
+
+ if (type & SLJIT_32)
+ return sljit_emit_mem_unaligned(compiler, SLJIT_MOV | SLJIT_MEM_STORE | (type & SLJIT_MEM_UNALIGNED_16), TMP_REG2, mem, memw);
+
+ max_offset = 0xfff - 7;
+ if (type & SLJIT_MEM_UNALIGNED_16)
+ max_offset++;
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, max_offset));
+ mem |= SLJIT_MEM;
+
+ FAIL_IF(sljit_emit_mem_unaligned(compiler, SLJIT_MOV | SLJIT_MEM_STORE | (type & SLJIT_MEM_UNALIGNED_16), TMP_REG2, mem, memw));
+
+ FAIL_IF(push_inst(compiler, VMOV | (1 << 20) | VN(freg) | 0x80 | RD(TMP_REG2)));
+ return sljit_emit_mem_unaligned(compiler, SLJIT_MOV | SLJIT_MEM_STORE | (type & SLJIT_MEM_UNALIGNED_16), TMP_REG2, mem, memw + 4);
+ }
+
+ max_offset = (type & SLJIT_32) ? 0xfff - 3 : 0xfff - 7;
+ if (type & SLJIT_MEM_UNALIGNED_16)
+ max_offset++;
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, max_offset));
+
+ dst = TMP_REG1;
+
+ /* Stack offset adjustment is not needed because dst
+ is not stored on the stack when mem is SLJIT_SP. */
+
+ if (mem == TMP_REG1) {
+ dst = SLJIT_R3;
+
+ if (compiler->scratches >= 4)
+ FAIL_IF(push_inst(compiler, STR | (1 << 21) | RN(SLJIT_SP) | RD(SLJIT_R3) | 8));
+ }
+
+ mem |= SLJIT_MEM;
+
+ FAIL_IF(sljit_emit_mem_unaligned(compiler, SLJIT_MOV | (type & SLJIT_MEM_UNALIGNED_16), dst, mem, memw));
+ FAIL_IF(push_inst(compiler, VMOV | VN(freg) | RD(dst)));
+
+ if (!(type & SLJIT_32)) {
+ FAIL_IF(sljit_emit_mem_unaligned(compiler, SLJIT_MOV | (type & SLJIT_MEM_UNALIGNED_16), dst, mem, memw + 4));
+ FAIL_IF(push_inst(compiler, VMOV | VN(freg) | 0x80 | RD(dst)));
+ }
+
+ if (dst == SLJIT_R3 && compiler->scratches >= 4)
+ FAIL_IF(push_inst(compiler, (LDR ^ (0x1 << 24)) | (0x1 << 23) | RN(SLJIT_SP) | RD(SLJIT_R3) | 8));
+ return SLJIT_SUCCESS;
+#else /* !SLJIT_CONFIG_ARM_V5 */
+ if (type & SLJIT_MEM_STORE) {
+ FAIL_IF(push_inst(compiler, VMOV | (1 << 20) | VN(freg) | RD(TMP_REG2)));
+
+ if (type & SLJIT_32)
+ return emit_op_mem(compiler, WORD_SIZE, TMP_REG2, mem, memw, TMP_REG1);
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 4));
+ mem |= SLJIT_MEM;
+
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, mem, memw, TMP_REG1));
+ FAIL_IF(push_inst(compiler, VMOV | (1 << 20) | VN(freg) | 0x80 | RD(TMP_REG2)));
+ return emit_op_mem(compiler, WORD_SIZE, TMP_REG2, mem, memw + 4, TMP_REG1);
+ }
+
+ if (type & SLJIT_32) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG2, mem, memw, TMP_REG1));
+ return push_inst(compiler, VMOV | VN(freg) | RD(TMP_REG2));
+ }
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 4));
+ mem |= SLJIT_MEM;
+
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG2, mem, memw, TMP_REG1));
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, mem, memw + 4, TMP_REG1));
+ return push_inst(compiler, VMOV2 | VM(freg) | RD(TMP_REG2) | RN(TMP_REG1));
+#endif /* SLJIT_CONFIG_ARM_V5 */
+}
+
+#undef FPU_LOAD
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ struct sljit_const *const_;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ PTR_FAIL_IF(push_inst_with_unique_literal(compiler,
+ EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, dst_r, TMP_PC, 0), (sljit_uw)init_value));
+ compiler->patches++;
+#else
+ PTR_FAIL_IF(emit_imm(compiler, dst_r, init_value));
+#endif
+
+ const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const));
+ PTR_FAIL_IF(!const_);
+ set_const(const_, compiler);
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, dst, dstw, TMP_REG1));
+ return const_;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ struct sljit_put_label *put_label;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ PTR_FAIL_IF(push_inst_with_unique_literal(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, dst_r, TMP_PC, 0), 0));
+ compiler->patches++;
+#else
+ PTR_FAIL_IF(emit_imm(compiler, dst_r, 0));
+#endif
+
+ put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label));
+ PTR_FAIL_IF(!put_label);
+ set_put_label(put_label, compiler, 0);
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, dst, dstw, TMP_REG1));
+ return put_label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ inline_set_jump_addr(addr, executable_offset, new_target, 1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ inline_set_const(addr, executable_offset, (sljit_uw)new_constant, 1);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeARM_64.c b/contrib/libs/pcre2/src/sljit/sljitNativeARM_64.c
new file mode 100644
index 00000000000..89f747e7c89
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeARM_64.c
@@ -0,0 +1,2417 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+ return "ARM-64" SLJIT_CPUINFO;
+}
+
+/* Length of an instruction word */
+typedef sljit_u32 sljit_ins;
+
+#define TMP_ZERO (0)
+
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
+#define TMP_LR (SLJIT_NUMBER_OF_REGISTERS + 4)
+#define TMP_FP (SLJIT_NUMBER_OF_REGISTERS + 5)
+
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
+
+/* r18 - platform register, currently not used */
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 8] = {
+ 31, 0, 1, 2, 3, 4, 5, 6, 7, 11, 12, 13, 14, 15, 16, 17, 8, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 31, 9, 10, 30, 29
+};
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = {
+ 0, 0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 15, 14, 13, 12, 11, 10, 9, 8, 30, 31
+};
+
+#define W_OP ((sljit_ins)1 << 31)
+#define RD(rd) ((sljit_ins)reg_map[rd])
+#define RT(rt) ((sljit_ins)reg_map[rt])
+#define RN(rn) ((sljit_ins)reg_map[rn] << 5)
+#define RT2(rt2) ((sljit_ins)reg_map[rt2] << 10)
+#define RM(rm) ((sljit_ins)reg_map[rm] << 16)
+#define VD(vd) ((sljit_ins)freg_map[vd])
+#define VT(vt) ((sljit_ins)freg_map[vt])
+#define VT2(vt) ((sljit_ins)freg_map[vt] << 10)
+#define VN(vn) ((sljit_ins)freg_map[vn] << 5)
+#define VM(vm) ((sljit_ins)freg_map[vm] << 16)
+
+/* --------------------------------------------------------------------- */
+/* Instrucion forms */
+/* --------------------------------------------------------------------- */
+
+#define ADC 0x9a000000
+#define ADD 0x8b000000
+#define ADDE 0x8b200000
+#define ADDI 0x91000000
+#define AND 0x8a000000
+#define ANDI 0x92000000
+#define ASRV 0x9ac02800
+#define B 0x14000000
+#define B_CC 0x54000000
+#define BL 0x94000000
+#define BLR 0xd63f0000
+#define BR 0xd61f0000
+#define BRK 0xd4200000
+#define CBZ 0xb4000000
+#define CLZ 0xdac01000
+#define CSEL 0x9a800000
+#define CSINC 0x9a800400
+#define EOR 0xca000000
+#define EORI 0xd2000000
+#define EXTR 0x93c00000
+#define FABS 0x1e60c000
+#define FADD 0x1e602800
+#define FCMP 0x1e602000
+#define FCVT 0x1e224000
+#define FCVTZS 0x9e780000
+#define FDIV 0x1e601800
+#define FMOV 0x1e604000
+#define FMUL 0x1e600800
+#define FNEG 0x1e614000
+#define FSUB 0x1e603800
+#define LDRI 0xf9400000
+#define LDRI_F64 0xfd400000
+#define LDRI_POST 0xf8400400
+#define LDP 0xa9400000
+#define LDP_F64 0x6d400000
+#define LDP_POST 0xa8c00000
+#define LDR_PRE 0xf8400c00
+#define LSLV 0x9ac02000
+#define LSRV 0x9ac02400
+#define MADD 0x9b000000
+#define MOVK 0xf2800000
+#define MOVN 0x92800000
+#define MOVZ 0xd2800000
+#define NOP 0xd503201f
+#define ORN 0xaa200000
+#define ORR 0xaa000000
+#define ORRI 0xb2000000
+#define RBIT 0xdac00000
+#define RET 0xd65f0000
+#define RORV 0x9ac02c00
+#define SBC 0xda000000
+#define SBFM 0x93000000
+#define SCVTF 0x9e620000
+#define SDIV 0x9ac00c00
+#define SMADDL 0x9b200000
+#define SMULH 0x9b403c00
+#define STP 0xa9000000
+#define STP_F64 0x6d000000
+#define STP_PRE 0xa9800000
+#define STRB 0x38206800
+#define STRBI 0x39000000
+#define STRI 0xf9000000
+#define STRI_F64 0xfd000000
+#define STR_FI 0x3d000000
+#define STR_FR 0x3c206800
+#define STUR_FI 0x3c000000
+#define STURBI 0x38000000
+#define SUB 0xcb000000
+#define SUBI 0xd1000000
+#define SUBS 0xeb000000
+#define UBFM 0xd3000000
+#define UDIV 0x9ac00800
+#define UMULH 0x9bc03c00
+
+static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_ins ins)
+{
+ sljit_ins *ptr = (sljit_ins*)ensure_buf(compiler, sizeof(sljit_ins));
+ FAIL_IF(!ptr);
+ *ptr = ins;
+ compiler->size++;
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_imm64_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_uw imm)
+{
+ FAIL_IF(push_inst(compiler, MOVZ | RD(dst) | ((sljit_ins)(imm & 0xffff) << 5)));
+ FAIL_IF(push_inst(compiler, MOVK | RD(dst) | (((sljit_ins)(imm >> 16) & 0xffff) << 5) | (1 << 21)));
+ FAIL_IF(push_inst(compiler, MOVK | RD(dst) | (((sljit_ins)(imm >> 32) & 0xffff) << 5) | (2 << 21)));
+ return push_inst(compiler, MOVK | RD(dst) | ((sljit_ins)(imm >> 48) << 5) | (3 << 21));
+}
+
+static SLJIT_INLINE sljit_sw detect_jump_type(struct sljit_jump *jump, sljit_ins *code_ptr, sljit_ins *code, sljit_sw executable_offset)
+{
+ sljit_sw diff;
+ sljit_uw target_addr;
+
+ if (jump->flags & SLJIT_REWRITABLE_JUMP) {
+ jump->flags |= PATCH_ABS64;
+ return 0;
+ }
+
+ if (jump->flags & JUMP_ADDR)
+ target_addr = jump->u.target;
+ else {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ target_addr = (sljit_uw)(code + jump->u.label->size) + (sljit_uw)executable_offset;
+ }
+
+ diff = (sljit_sw)target_addr - (sljit_sw)(code_ptr + 4) - executable_offset;
+
+ if (jump->flags & IS_COND) {
+ diff += SSIZE_OF(ins);
+ if (diff <= 0xfffff && diff >= -0x100000) {
+ code_ptr[-5] ^= (jump->flags & IS_CBZ) ? (0x1 << 24) : 0x1;
+ jump->addr -= sizeof(sljit_ins);
+ jump->flags |= PATCH_COND;
+ return 5;
+ }
+ diff -= SSIZE_OF(ins);
+ }
+
+ if (diff <= 0x7ffffff && diff >= -0x8000000) {
+ jump->flags |= PATCH_B;
+ return 4;
+ }
+
+ if (target_addr < 0x100000000l) {
+ if (jump->flags & IS_COND)
+ code_ptr[-5] -= (2 << 5);
+ code_ptr[-2] = code_ptr[0];
+ return 2;
+ }
+
+ if (target_addr < 0x1000000000000l) {
+ if (jump->flags & IS_COND)
+ code_ptr[-5] -= (1 << 5);
+ jump->flags |= PATCH_ABS48;
+ code_ptr[-1] = code_ptr[0];
+ return 1;
+ }
+
+ jump->flags |= PATCH_ABS64;
+ return 0;
+}
+
+static SLJIT_INLINE sljit_sw put_label_get_length(struct sljit_put_label *put_label, sljit_uw max_label)
+{
+ if (max_label < 0x100000000l) {
+ put_label->flags = 0;
+ return 2;
+ }
+
+ if (max_label < 0x1000000000000l) {
+ put_label->flags = 1;
+ return 1;
+ }
+
+ put_label->flags = 2;
+ return 0;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf;
+ sljit_ins *code;
+ sljit_ins *code_ptr;
+ sljit_ins *buf_ptr;
+ sljit_ins *buf_end;
+ sljit_uw word_count;
+ sljit_uw next_addr;
+ sljit_sw executable_offset;
+ sljit_sw addr;
+ sljit_u32 dst;
+
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ struct sljit_const *const_;
+ struct sljit_put_label *put_label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_generate_code(compiler));
+ reverse_buf(compiler);
+
+ code = (sljit_ins*)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_ins), compiler->exec_allocator_data);
+ PTR_FAIL_WITH_EXEC_IF(code);
+ buf = compiler->buf;
+
+ code_ptr = code;
+ word_count = 0;
+ next_addr = 0;
+ executable_offset = SLJIT_EXEC_OFFSET(code);
+
+ label = compiler->labels;
+ jump = compiler->jumps;
+ const_ = compiler->consts;
+ put_label = compiler->put_labels;
+
+ do {
+ buf_ptr = (sljit_ins*)buf->memory;
+ buf_end = buf_ptr + (buf->used_size >> 2);
+ do {
+ *code_ptr = *buf_ptr++;
+ if (next_addr == word_count) {
+ SLJIT_ASSERT(!label || label->size >= word_count);
+ SLJIT_ASSERT(!jump || jump->addr >= word_count);
+ SLJIT_ASSERT(!const_ || const_->addr >= word_count);
+ SLJIT_ASSERT(!put_label || put_label->addr >= word_count);
+
+ /* These structures are ordered by their address. */
+ if (label && label->size == word_count) {
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+ if (jump && jump->addr == word_count) {
+ jump->addr = (sljit_uw)(code_ptr - 4);
+ code_ptr -= detect_jump_type(jump, code_ptr, code, executable_offset);
+ jump = jump->next;
+ }
+ if (const_ && const_->addr == word_count) {
+ const_->addr = (sljit_uw)code_ptr;
+ const_ = const_->next;
+ }
+ if (put_label && put_label->addr == word_count) {
+ SLJIT_ASSERT(put_label->label);
+ put_label->addr = (sljit_uw)(code_ptr - 3);
+ code_ptr -= put_label_get_length(put_label, (sljit_uw)(SLJIT_ADD_EXEC_OFFSET(code, executable_offset) + put_label->label->size));
+ put_label = put_label->next;
+ }
+ next_addr = compute_next_addr(label, jump, const_, put_label);
+ }
+ code_ptr++;
+ word_count++;
+ } while (buf_ptr < buf_end);
+
+ buf = buf->next;
+ } while (buf);
+
+ if (label && label->size == word_count) {
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+
+ SLJIT_ASSERT(!label);
+ SLJIT_ASSERT(!jump);
+ SLJIT_ASSERT(!const_);
+ SLJIT_ASSERT(!put_label);
+ SLJIT_ASSERT(code_ptr - code <= (sljit_sw)compiler->size);
+
+ jump = compiler->jumps;
+ while (jump) {
+ do {
+ addr = (sljit_sw)((jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target);
+ buf_ptr = (sljit_ins *)jump->addr;
+
+ if (jump->flags & PATCH_B) {
+ addr = (addr - (sljit_sw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset)) >> 2;
+ SLJIT_ASSERT(addr <= 0x1ffffff && addr >= -0x2000000);
+ buf_ptr[0] = ((jump->flags & IS_BL) ? BL : B) | (sljit_ins)(addr & 0x3ffffff);
+ if (jump->flags & IS_COND)
+ buf_ptr[-1] -= (4 << 5);
+ break;
+ }
+ if (jump->flags & PATCH_COND) {
+ addr = (addr - (sljit_sw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset)) >> 2;
+ SLJIT_ASSERT(addr <= 0x3ffff && addr >= -0x40000);
+ buf_ptr[0] = (buf_ptr[0] & ~(sljit_ins)0xffffe0) | (sljit_ins)((addr & 0x7ffff) << 5);
+ break;
+ }
+
+ SLJIT_ASSERT((jump->flags & (PATCH_ABS48 | PATCH_ABS64)) || (sljit_uw)addr <= (sljit_uw)0xffffffff);
+ SLJIT_ASSERT((jump->flags & PATCH_ABS64) || (sljit_uw)addr <= (sljit_uw)0xffffffffffff);
+
+ dst = buf_ptr[0] & 0x1f;
+ buf_ptr[0] = MOVZ | dst | (((sljit_ins)addr & 0xffff) << 5);
+ buf_ptr[1] = MOVK | dst | (((sljit_ins)(addr >> 16) & 0xffff) << 5) | (1 << 21);
+ if (jump->flags & (PATCH_ABS48 | PATCH_ABS64))
+ buf_ptr[2] = MOVK | dst | (((sljit_ins)(addr >> 32) & 0xffff) << 5) | (2 << 21);
+ if (jump->flags & PATCH_ABS64)
+ buf_ptr[3] = MOVK | dst | ((sljit_ins)(addr >> 48) << 5) | (3 << 21);
+ } while (0);
+ jump = jump->next;
+ }
+
+ put_label = compiler->put_labels;
+ while (put_label) {
+ addr = (sljit_sw)put_label->label->addr;
+ buf_ptr = (sljit_ins*)put_label->addr;
+
+ buf_ptr[0] |= ((sljit_ins)addr & 0xffff) << 5;
+ buf_ptr[1] |= ((sljit_ins)(addr >> 16) & 0xffff) << 5;
+
+ if (put_label->flags >= 1)
+ buf_ptr[2] |= ((sljit_ins)(addr >> 32) & 0xffff) << 5;
+
+ if (put_label->flags >= 2)
+ buf_ptr[3] |= (sljit_ins)(addr >> 48) << 5;
+
+ put_label = put_label->next;
+ }
+
+ compiler->error = SLJIT_ERR_COMPILED;
+ compiler->executable_offset = executable_offset;
+ compiler->executable_size = (sljit_uw)(code_ptr - code) * sizeof(sljit_ins);
+
+ code = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+ code_ptr = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+
+ SLJIT_CACHE_FLUSH(code, code_ptr);
+ SLJIT_UPDATE_WX_FLAGS(code, code_ptr, 1);
+ return code;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#else
+ /* Available by default. */
+ return 1;
+#endif
+
+ case SLJIT_HAS_CLZ:
+ case SLJIT_HAS_CTZ:
+ case SLJIT_HAS_ROT:
+ case SLJIT_HAS_CMOV:
+ case SLJIT_HAS_PREFETCH:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+/* Core code generator functions. */
+/* --------------------------------------------------------------------- */
+
+#define COUNT_TRAILING_ZERO(value, result) \
+ result = 0; \
+ if (!(value & 0xffffffff)) { \
+ result += 32; \
+ value >>= 32; \
+ } \
+ if (!(value & 0xffff)) { \
+ result += 16; \
+ value >>= 16; \
+ } \
+ if (!(value & 0xff)) { \
+ result += 8; \
+ value >>= 8; \
+ } \
+ if (!(value & 0xf)) { \
+ result += 4; \
+ value >>= 4; \
+ } \
+ if (!(value & 0x3)) { \
+ result += 2; \
+ value >>= 2; \
+ } \
+ if (!(value & 0x1)) { \
+ result += 1; \
+ value >>= 1; \
+ }
+
+#define LOGICAL_IMM_CHECK (sljit_ins)0x100
+
+static sljit_ins logical_imm(sljit_sw imm, sljit_u32 len)
+{
+ sljit_s32 negated;
+ sljit_u32 ones, right;
+ sljit_uw mask, uimm;
+ sljit_ins ins;
+
+ if (len & LOGICAL_IMM_CHECK) {
+ len &= ~LOGICAL_IMM_CHECK;
+ if (len == 32 && (imm == 0 || imm == -1))
+ return 0;
+ if (len == 16 && ((sljit_s32)imm == 0 || (sljit_s32)imm == -1))
+ return 0;
+ }
+
+ SLJIT_ASSERT((len == 32 && imm != 0 && imm != -1)
+ || (len == 16 && (sljit_s32)imm != 0 && (sljit_s32)imm != -1));
+
+ uimm = (sljit_uw)imm;
+ while (1) {
+ if (len <= 0) {
+ SLJIT_UNREACHABLE();
+ return 0;
+ }
+
+ mask = ((sljit_uw)1 << len) - 1;
+ if ((uimm & mask) != ((uimm >> len) & mask))
+ break;
+ len >>= 1;
+ }
+
+ len <<= 1;
+
+ negated = 0;
+ if (uimm & 0x1) {
+ negated = 1;
+ uimm = ~uimm;
+ }
+
+ if (len < 64)
+ uimm &= ((sljit_uw)1 << len) - 1;
+
+ /* Unsigned right shift. */
+ COUNT_TRAILING_ZERO(uimm, right);
+
+ /* Signed shift. We also know that the highest bit is set. */
+ imm = (sljit_sw)~uimm;
+ SLJIT_ASSERT(imm < 0);
+
+ COUNT_TRAILING_ZERO(imm, ones);
+
+ if (~imm)
+ return 0;
+
+ if (len == 64)
+ ins = 1 << 22;
+ else
+ ins = (0x3f - ((len << 1) - 1)) << 10;
+
+ if (negated)
+ return ins | ((len - ones - 1) << 10) | ((len - ones - right) << 16);
+
+ return ins | ((ones - 1) << 10) | ((len - right) << 16);
+}
+
+#undef COUNT_TRAILING_ZERO
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw simm)
+{
+ sljit_uw imm = (sljit_uw)simm;
+ sljit_u32 i, zeros, ones, first;
+ sljit_ins bitmask;
+
+ /* Handling simple immediates first. */
+ if (imm <= 0xffff)
+ return push_inst(compiler, MOVZ | RD(dst) | ((sljit_ins)imm << 5));
+
+ if (simm < 0 && simm >= -0x10000)
+ return push_inst(compiler, MOVN | RD(dst) | (((sljit_ins)~imm & 0xffff) << 5));
+
+ if (imm <= 0xffffffffl) {
+ if ((imm & 0xffff) == 0)
+ return push_inst(compiler, MOVZ | RD(dst) | ((sljit_ins)(imm >> 16) << 5) | (1 << 21));
+ if ((imm & 0xffff0000l) == 0xffff0000)
+ return push_inst(compiler, (MOVN ^ W_OP) | RD(dst) | (((sljit_ins)~imm & 0xffff) << 5));
+ if ((imm & 0xffff) == 0xffff)
+ return push_inst(compiler, (MOVN ^ W_OP) | RD(dst) | (((sljit_ins)~imm & 0xffff0000u) >> (16 - 5)) | (1 << 21));
+
+ bitmask = logical_imm(simm, 16);
+ if (bitmask != 0)
+ return push_inst(compiler, (ORRI ^ W_OP) | RD(dst) | RN(TMP_ZERO) | bitmask);
+
+ FAIL_IF(push_inst(compiler, MOVZ | RD(dst) | (((sljit_ins)imm & 0xffff) << 5)));
+ return push_inst(compiler, MOVK | RD(dst) | (((sljit_ins)imm & 0xffff0000u) >> (16 - 5)) | (1 << 21));
+ }
+
+ bitmask = logical_imm(simm, 32);
+ if (bitmask != 0)
+ return push_inst(compiler, ORRI | RD(dst) | RN(TMP_ZERO) | bitmask);
+
+ if (simm < 0 && simm >= -0x100000000l) {
+ if ((imm & 0xffff) == 0xffff)
+ return push_inst(compiler, MOVN | RD(dst) | (((sljit_ins)~imm & 0xffff0000u) >> (16 - 5)) | (1 << 21));
+
+ FAIL_IF(push_inst(compiler, MOVN | RD(dst) | (((sljit_ins)~imm & 0xffff) << 5)));
+ return push_inst(compiler, MOVK | RD(dst) | (((sljit_ins)imm & 0xffff0000u) >> (16 - 5)) | (1 << 21));
+ }
+
+ /* A large amount of number can be constructed from ORR and MOVx, but computing them is costly. */
+
+ zeros = 0;
+ ones = 0;
+ for (i = 4; i > 0; i--) {
+ if ((simm & 0xffff) == 0)
+ zeros++;
+ if ((simm & 0xffff) == 0xffff)
+ ones++;
+ simm >>= 16;
+ }
+
+ simm = (sljit_sw)imm;
+ first = 1;
+ if (ones > zeros) {
+ simm = ~simm;
+ for (i = 0; i < 4; i++) {
+ if (!(simm & 0xffff)) {
+ simm >>= 16;
+ continue;
+ }
+ if (first) {
+ first = 0;
+ FAIL_IF(push_inst(compiler, MOVN | RD(dst) | (((sljit_ins)simm & 0xffff) << 5) | (i << 21)));
+ }
+ else
+ FAIL_IF(push_inst(compiler, MOVK | RD(dst) | (((sljit_ins)~simm & 0xffff) << 5) | (i << 21)));
+ simm >>= 16;
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (!(simm & 0xffff)) {
+ simm >>= 16;
+ continue;
+ }
+ if (first) {
+ first = 0;
+ FAIL_IF(push_inst(compiler, MOVZ | RD(dst) | (((sljit_ins)simm & 0xffff) << 5) | (i << 21)));
+ }
+ else
+ FAIL_IF(push_inst(compiler, MOVK | RD(dst) | (((sljit_ins)simm & 0xffff) << 5) | (i << 21)));
+ simm >>= 16;
+ }
+ return SLJIT_SUCCESS;
+}
+
+#define ARG1_IMM 0x0010000
+#define ARG2_IMM 0x0020000
+#define INT_OP 0x0040000
+#define SET_FLAGS 0x0080000
+#define UNUSED_RETURN 0x0100000
+
+#define CHECK_FLAGS(flag_bits) \
+ if (flags & SET_FLAGS) { \
+ inv_bits |= flag_bits; \
+ if (flags & UNUSED_RETURN) \
+ dst = TMP_ZERO; \
+ }
+
+static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 dst, sljit_sw arg1, sljit_sw arg2)
+{
+ /* dst must be register, TMP_REG1
+ arg1 must be register, TMP_REG1, imm
+ arg2 must be register, TMP_REG2, imm */
+ sljit_ins inv_bits = (flags & INT_OP) ? W_OP : 0;
+ sljit_ins inst_bits;
+ sljit_s32 op = (flags & 0xffff);
+ sljit_s32 reg;
+ sljit_sw imm, nimm;
+
+ if (SLJIT_UNLIKELY((flags & (ARG1_IMM | ARG2_IMM)) == (ARG1_IMM | ARG2_IMM))) {
+ /* Both are immediates. */
+ flags &= ~ARG1_IMM;
+ if (arg1 == 0 && op != SLJIT_ADD && op != SLJIT_SUB)
+ arg1 = TMP_ZERO;
+ else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, arg1));
+ arg1 = TMP_REG1;
+ }
+ }
+
+ if (flags & (ARG1_IMM | ARG2_IMM)) {
+ reg = (sljit_s32)((flags & ARG2_IMM) ? arg1 : arg2);
+ imm = (flags & ARG2_IMM) ? arg2 : arg1;
+
+ switch (op) {
+ case SLJIT_MUL:
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ case SLJIT_ADDC:
+ case SLJIT_SUBC:
+ /* No form with immediate operand (except imm 0, which
+ is represented by a ZERO register). */
+ break;
+ case SLJIT_MOV:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && (flags & ARG2_IMM) && arg1 == TMP_REG1);
+ return load_immediate(compiler, dst, imm);
+ case SLJIT_NOT:
+ SLJIT_ASSERT(flags & ARG2_IMM);
+ FAIL_IF(load_immediate(compiler, dst, (flags & INT_OP) ? (~imm & 0xffffffff) : ~imm));
+ goto set_flags;
+ case SLJIT_SUB:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ if (flags & ARG1_IMM)
+ break;
+ imm = -imm;
+ /* Fall through. */
+ case SLJIT_ADD:
+ if (op != SLJIT_SUB)
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+
+ if (imm == 0) {
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, ((op == SLJIT_ADD ? ADDI : SUBI) ^ inv_bits) | RD(dst) | RN(reg));
+ }
+ if (imm > 0 && imm <= 0xfff) {
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, (ADDI ^ inv_bits) | RD(dst) | RN(reg) | ((sljit_ins)imm << 10));
+ }
+ nimm = -imm;
+ if (nimm > 0 && nimm <= 0xfff) {
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, (SUBI ^ inv_bits) | RD(dst) | RN(reg) | ((sljit_ins)nimm << 10));
+ }
+ if (imm > 0 && imm <= 0xffffff && !(imm & 0xfff)) {
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, (ADDI ^ inv_bits) | RD(dst) | RN(reg) | (((sljit_ins)imm >> 12) << 10) | (1 << 22));
+ }
+ if (nimm > 0 && nimm <= 0xffffff && !(nimm & 0xfff)) {
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, (SUBI ^ inv_bits) | RD(dst) | RN(reg) | (((sljit_ins)nimm >> 12) << 10) | (1 << 22));
+ }
+ if (imm > 0 && imm <= 0xffffff && !(flags & SET_FLAGS)) {
+ FAIL_IF(push_inst(compiler, (ADDI ^ inv_bits) | RD(dst) | RN(reg) | (((sljit_ins)imm >> 12) << 10) | (1 << 22)));
+ return push_inst(compiler, (ADDI ^ inv_bits) | RD(dst) | RN(dst) | (((sljit_ins)imm & 0xfff) << 10));
+ }
+ if (nimm > 0 && nimm <= 0xffffff && !(flags & SET_FLAGS)) {
+ FAIL_IF(push_inst(compiler, (SUBI ^ inv_bits) | RD(dst) | RN(reg) | (((sljit_ins)nimm >> 12) << 10) | (1 << 22)));
+ return push_inst(compiler, (SUBI ^ inv_bits) | RD(dst) | RN(dst) | (((sljit_ins)nimm & 0xfff) << 10));
+ }
+ break;
+ case SLJIT_AND:
+ inst_bits = logical_imm(imm, LOGICAL_IMM_CHECK | ((flags & INT_OP) ? 16 : 32));
+ if (!inst_bits)
+ break;
+ CHECK_FLAGS(3 << 29);
+ return push_inst(compiler, (ANDI ^ inv_bits) | RD(dst) | RN(reg) | inst_bits);
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ inst_bits = logical_imm(imm, LOGICAL_IMM_CHECK | ((flags & INT_OP) ? 16 : 32));
+ if (!inst_bits)
+ break;
+ if (op == SLJIT_OR)
+ inst_bits |= ORRI;
+ else
+ inst_bits |= EORI;
+ FAIL_IF(push_inst(compiler, (inst_bits ^ inv_bits) | RD(dst) | RN(reg)));
+ goto set_flags;
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ if (flags & ARG1_IMM)
+ break;
+
+ if (flags & INT_OP) {
+ imm &= 0x1f;
+ inst_bits = (((sljit_ins)-imm & 0x1f) << 16) | ((31 - (sljit_ins)imm) << 10);
+ } else {
+ imm &= 0x3f;
+ inst_bits = ((sljit_ins)1 << 22) | (((sljit_ins)-imm & 0x3f) << 16) | ((63 - (sljit_ins)imm) << 10);
+ }
+
+ FAIL_IF(push_inst(compiler, (UBFM ^ inv_bits) | RD(dst) | RN(arg1) | inst_bits));
+ goto set_flags;
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ if (flags & ARG1_IMM)
+ break;
+
+ if (op >= SLJIT_ASHR)
+ inv_bits |= 1 << 30;
+
+ if (flags & INT_OP) {
+ imm &= 0x1f;
+ inst_bits = ((sljit_ins)imm << 16) | (31 << 10);
+ } else {
+ imm &= 0x3f;
+ inst_bits = ((sljit_ins)1 << 22) | ((sljit_ins)imm << 16) | (63 << 10);
+ }
+
+ FAIL_IF(push_inst(compiler, (UBFM ^ inv_bits) | RD(dst) | RN(arg1) | inst_bits));
+ goto set_flags;
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ if (flags & ARG1_IMM)
+ break;
+
+ if (op == SLJIT_ROTL)
+ imm = -imm;
+
+ imm &= (flags & INT_OP) ? 0x1f : 0x3f;
+ return push_inst(compiler, (EXTR ^ (inv_bits | (inv_bits >> 9))) | RD(dst) | RN(arg1) | RM(arg1) | ((sljit_ins)imm << 10));
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+ if (flags & ARG2_IMM) {
+ if (arg2 == 0)
+ arg2 = TMP_ZERO;
+ else {
+ FAIL_IF(load_immediate(compiler, TMP_REG2, arg2));
+ arg2 = TMP_REG2;
+ }
+ }
+ else {
+ if (arg1 == 0)
+ arg1 = TMP_ZERO;
+ else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, arg1));
+ arg1 = TMP_REG1;
+ }
+ }
+ }
+
+ /* Both arguments are registers. */
+ switch (op) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ if (dst == arg2)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, ORR | RD(dst) | RN(TMP_ZERO) | RM(arg2));
+ case SLJIT_MOV_U8:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ return push_inst(compiler, (UBFM ^ W_OP) | RD(dst) | RN(arg2) | (7 << 10));
+ case SLJIT_MOV_S8:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ if (!(flags & INT_OP))
+ inv_bits |= 1 << 22;
+ return push_inst(compiler, (SBFM ^ inv_bits) | RD(dst) | RN(arg2) | (7 << 10));
+ case SLJIT_MOV_U16:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ return push_inst(compiler, (UBFM ^ W_OP) | RD(dst) | RN(arg2) | (15 << 10));
+ case SLJIT_MOV_S16:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ if (!(flags & INT_OP))
+ inv_bits |= 1 << 22;
+ return push_inst(compiler, (SBFM ^ inv_bits) | RD(dst) | RN(arg2) | (15 << 10));
+ case SLJIT_MOV32:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ if (dst == arg2)
+ return SLJIT_SUCCESS;
+ /* fallthrough */
+ case SLJIT_MOV_U32:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ return push_inst(compiler, (ORR ^ W_OP) | RD(dst) | RN(TMP_ZERO) | RM(arg2));
+ case SLJIT_MOV_S32:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ return push_inst(compiler, SBFM | (1 << 22) | RD(dst) | RN(arg2) | (31 << 10));
+ case SLJIT_NOT:
+ SLJIT_ASSERT(arg1 == TMP_REG1);
+ FAIL_IF(push_inst(compiler, (ORN ^ inv_bits) | RD(dst) | RN(TMP_ZERO) | RM(arg2)));
+ break; /* Set flags. */
+ case SLJIT_CLZ:
+ SLJIT_ASSERT(arg1 == TMP_REG1);
+ return push_inst(compiler, (CLZ ^ inv_bits) | RD(dst) | RN(arg2));
+ case SLJIT_CTZ:
+ SLJIT_ASSERT(arg1 == TMP_REG1);
+ FAIL_IF(push_inst(compiler, (RBIT ^ inv_bits) | RD(dst) | RN(arg2)));
+ return push_inst(compiler, (CLZ ^ inv_bits) | RD(dst) | RN(dst));
+ case SLJIT_ADD:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, (ADD ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2));
+ case SLJIT_ADDC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, (ADC ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2));
+ case SLJIT_SUB:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, (SUB ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2));
+ case SLJIT_SUBC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, (SBC ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2));
+ case SLJIT_MUL:
+ compiler->status_flags_state = 0;
+ if (!(flags & SET_FLAGS))
+ return push_inst(compiler, (MADD ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2) | RT2(TMP_ZERO));
+ if (flags & INT_OP) {
+ FAIL_IF(push_inst(compiler, SMADDL | RD(dst) | RN(arg1) | RM(arg2) | (31 << 10)));
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_LR) | RN(TMP_ZERO) | RM(dst) | (2 << 22) | (31 << 10)));
+ return push_inst(compiler, SUBS | RD(TMP_ZERO) | RN(TMP_LR) | RM(dst) | (2 << 22) | (63 << 10));
+ }
+ FAIL_IF(push_inst(compiler, SMULH | RD(TMP_LR) | RN(arg1) | RM(arg2)));
+ FAIL_IF(push_inst(compiler, MADD | RD(dst) | RN(arg1) | RM(arg2) | RT2(TMP_ZERO)));
+ return push_inst(compiler, SUBS | RD(TMP_ZERO) | RN(TMP_LR) | RM(dst) | (2 << 22) | (63 << 10));
+ case SLJIT_AND:
+ CHECK_FLAGS(3 << 29);
+ return push_inst(compiler, (AND ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2));
+ case SLJIT_OR:
+ FAIL_IF(push_inst(compiler, (ORR ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)));
+ break; /* Set flags. */
+ case SLJIT_XOR:
+ FAIL_IF(push_inst(compiler, (EOR ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)));
+ break; /* Set flags. */
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ FAIL_IF(push_inst(compiler, (LSLV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)));
+ break; /* Set flags. */
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ FAIL_IF(push_inst(compiler, (LSRV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)));
+ break; /* Set flags. */
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ FAIL_IF(push_inst(compiler, (ASRV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)));
+ break; /* Set flags. */
+ case SLJIT_ROTL:
+ FAIL_IF(push_inst(compiler, (SUB ^ inv_bits) | RD(TMP_REG2) | RN(TMP_ZERO) | RM(arg2)));
+ arg2 = TMP_REG2;
+ /* fallthrough */
+ case SLJIT_ROTR:
+ return push_inst(compiler, (RORV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2));
+ default:
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+ }
+
+set_flags:
+ if (flags & SET_FLAGS)
+ return push_inst(compiler, (SUBS ^ inv_bits) | RD(TMP_ZERO) | RN(dst) | RM(TMP_ZERO));
+ return SLJIT_SUCCESS;
+}
+
+#define STORE 0x10
+#define SIGNED 0x20
+
+#define BYTE_SIZE 0x0
+#define HALF_SIZE 0x1
+#define INT_SIZE 0x2
+#define WORD_SIZE 0x3
+
+#define MEM_SIZE_SHIFT(flags) ((sljit_ins)(flags) & 0x3)
+
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg,
+ sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg)
+{
+ sljit_u32 shift = MEM_SIZE_SHIFT(flags);
+ sljit_u32 type = (shift << 30);
+
+ if (!(flags & STORE))
+ type |= (flags & SIGNED) ? 0x00800000 : 0x00400000;
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ argw &= 0x3;
+
+ if (argw == 0 || argw == shift)
+ return push_inst(compiler, STRB | type | RT(reg)
+ | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | (argw ? (1 << 12) : 0));
+
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | ((sljit_ins)argw << 10)));
+ return push_inst(compiler, STRBI | type | RT(reg) | RN(tmp_reg));
+ }
+
+ arg &= REG_MASK;
+
+ if (!arg) {
+ FAIL_IF(load_immediate(compiler, tmp_reg, argw & ~(0xfff << shift)));
+
+ argw = (argw >> shift) & 0xfff;
+
+ return push_inst(compiler, STRBI | type | RT(reg) | RN(tmp_reg) | ((sljit_ins)argw << 10));
+ }
+
+ if ((argw & ((1 << shift) - 1)) == 0) {
+ if (argw >= 0) {
+ if ((argw >> shift) <= 0xfff)
+ return push_inst(compiler, STRBI | type | RT(reg) | RN(arg) | ((sljit_ins)argw << (10 - shift)));
+
+ if (argw <= 0xffffff) {
+ FAIL_IF(push_inst(compiler, ADDI | (1 << 22) | RD(tmp_reg) | RN(arg) | (((sljit_ins)argw >> 12) << 10)));
+
+ argw = ((argw & 0xfff) >> shift);
+ return push_inst(compiler, STRBI | type | RT(reg) | RN(tmp_reg) | ((sljit_ins)argw << 10));
+ }
+ } else if (argw < -256 && argw >= -0xfff000) {
+ FAIL_IF(push_inst(compiler, SUBI | (1 << 22) | RD(tmp_reg) | RN(arg) | (((sljit_ins)(-argw + 0xfff) >> 12) << 10)));
+ argw = ((0x1000 + argw) & 0xfff) >> shift;
+ return push_inst(compiler, STRBI | type | RT(reg) | RN(tmp_reg) | ((sljit_ins)argw << 10));
+ }
+ }
+
+ if (argw <= 0xff && argw >= -0x100)
+ return push_inst(compiler, STURBI | type | RT(reg) | RN(arg) | (((sljit_ins)argw & 0x1ff) << 12));
+
+ if (argw >= 0) {
+ if (argw <= 0xfff0ff && ((argw + 0x100) & 0xfff) <= 0x1ff) {
+ FAIL_IF(push_inst(compiler, ADDI | (1 << 22) | RD(tmp_reg) | RN(arg) | (((sljit_ins)argw >> 12) << 10)));
+ return push_inst(compiler, STURBI | type | RT(reg) | RN(tmp_reg) | (((sljit_ins)argw & 0x1ff) << 12));
+ }
+ } else if (argw >= -0xfff100 && ((-argw + 0xff) & 0xfff) <= 0x1ff) {
+ FAIL_IF(push_inst(compiler, SUBI | (1 << 22) | RD(tmp_reg) | RN(arg) | (((sljit_ins)-argw >> 12) << 10)));
+ return push_inst(compiler, STURBI | type | RT(reg) | RN(tmp_reg) | (((sljit_ins)argw & 0x1ff) << 12));
+ }
+
+ FAIL_IF(load_immediate(compiler, tmp_reg, argw));
+
+ return push_inst(compiler, STRB | type | RT(reg) | RN(arg) | RM(tmp_reg));
+}
+
+/* --------------------------------------------------------------------- */
+/* Entry, exit */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 prev, fprev, saved_regs_size, i, tmp;
+ sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+ sljit_ins offs;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 2);
+ saved_regs_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, SSIZE_OF(f64));
+
+ local_size = (local_size + saved_regs_size + 0xf) & ~0xf;
+ compiler->local_size = local_size;
+
+ if (local_size <= 512) {
+ FAIL_IF(push_inst(compiler, STP_PRE | RT(TMP_FP) | RT2(TMP_LR)
+ | RN(SLJIT_SP) | (sljit_ins)((-(local_size >> 3) & 0x7f) << 15)));
+ offs = (sljit_ins)(local_size - 2 * SSIZE_OF(sw)) << (15 - 3);
+ local_size = 0;
+ } else {
+ saved_regs_size = ((saved_regs_size - 2 * SSIZE_OF(sw)) + 0xf) & ~0xf;
+
+ FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | ((sljit_ins)saved_regs_size << 10)));
+ offs = (sljit_ins)(saved_regs_size - 2 * SSIZE_OF(sw)) << (15 - 3);
+ local_size -= saved_regs_size;
+ SLJIT_ASSERT(local_size > 0);
+ }
+
+ prev = -1;
+
+ tmp = SLJIT_S0 - saveds;
+ for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) {
+ if (prev == -1) {
+ prev = i;
+ continue;
+ }
+ FAIL_IF(push_inst(compiler, STP | RT(prev) | RT2(i) | RN(SLJIT_SP) | offs));
+ offs -= (sljit_ins)2 << 15;
+ prev = -1;
+ }
+
+ for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ if (prev == -1) {
+ prev = i;
+ continue;
+ }
+ FAIL_IF(push_inst(compiler, STP | RT(prev) | RT2(i) | RN(SLJIT_SP) | offs));
+ offs -= (sljit_ins)2 << 15;
+ prev = -1;
+ }
+
+ fprev = -1;
+
+ tmp = SLJIT_FS0 - fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ if (fprev == -1) {
+ fprev = i;
+ continue;
+ }
+ FAIL_IF(push_inst(compiler, STP_F64 | VT(fprev) | VT2(i) | RN(SLJIT_SP) | offs));
+ offs -= (sljit_ins)2 << 15;
+ fprev = -1;
+ }
+
+ for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ if (fprev == -1) {
+ fprev = i;
+ continue;
+ }
+ FAIL_IF(push_inst(compiler, STP_F64 | VT(fprev) | VT2(i) | RN(SLJIT_SP) | offs));
+ offs -= (sljit_ins)2 << 15;
+ fprev = -1;
+ }
+
+ if (fprev != -1)
+ FAIL_IF(push_inst(compiler, STRI_F64 | VT(fprev) | RN(SLJIT_SP) | (offs >> 5) | (1 << 10)));
+
+ if (prev != -1)
+ FAIL_IF(push_inst(compiler, STRI | RT(prev) | RN(SLJIT_SP) | (offs >> 5) | ((fprev == -1) ? (1 << 10) : 0)));
+
+
+#ifdef _WIN32
+ if (local_size > 4096)
+ FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 10) | (1 << 22)));
+#endif /* _WIN32 */
+
+ if (!(options & SLJIT_ENTER_REG_ARG)) {
+ arg_types >>= SLJIT_ARG_SHIFT;
+ saved_arg_count = 0;
+ tmp = SLJIT_R0;
+
+ while (arg_types) {
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) {
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ FAIL_IF(push_inst(compiler, ORR | RD(SLJIT_S0 - saved_arg_count) | RN(TMP_ZERO) | RM(tmp)));
+ saved_arg_count++;
+ }
+ tmp++;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+ }
+
+#ifdef _WIN32
+ if (local_size > 4096) {
+ if (local_size < 4 * 4096) {
+ /* No need for a loop. */
+
+ if (local_size >= 2 * 4096) {
+ if (local_size >= 3 * 4096) {
+ FAIL_IF(push_inst(compiler, LDRI | RT(TMP_ZERO) | RN(SLJIT_SP)));
+ FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 10) | (1 << 22)));
+ }
+
+ FAIL_IF(push_inst(compiler, LDRI | RT(TMP_ZERO) | RN(SLJIT_SP)));
+ FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 10) | (1 << 22)));
+ }
+ }
+ else {
+ FAIL_IF(push_inst(compiler, MOVZ | RD(TMP_REG1) | ((((sljit_ins)local_size >> 12) - 1) << 5)));
+ FAIL_IF(push_inst(compiler, LDRI | RT(TMP_ZERO) | RN(SLJIT_SP)));
+ FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 10) | (1 << 22)));
+ FAIL_IF(push_inst(compiler, SUBI | (1 << 29) | RD(TMP_REG1) | RN(TMP_REG1) | (1 << 10)));
+ FAIL_IF(push_inst(compiler, B_CC | ((((sljit_ins) -3) & 0x7ffff) << 5) | 0x1 /* not-equal */));
+ }
+
+ local_size &= 0xfff;
+
+ if (local_size > 0)
+ FAIL_IF(push_inst(compiler, LDRI | RT(TMP_ZERO) | RN(SLJIT_SP)));
+ else
+ FAIL_IF(push_inst(compiler, STP | RT(TMP_FP) | RT2(TMP_LR) | RN(SLJIT_SP)));
+ }
+
+ if (local_size > 0) {
+ if (local_size <= 512)
+ FAIL_IF(push_inst(compiler, STP_PRE | RT(TMP_FP) | RT2(TMP_LR)
+ | RN(SLJIT_SP) | (sljit_ins)((-(local_size >> 3) & 0x7f) << 15)));
+ else {
+ if (local_size >= 4096)
+ local_size = (1 << (22 - 10));
+
+ FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | ((sljit_ins)local_size << 10)));
+ FAIL_IF(push_inst(compiler, STP | RT(TMP_FP) | RT2(TMP_LR) | RN(SLJIT_SP)));
+ }
+ }
+
+#else /* !_WIN32 */
+
+ /* The local_size does not include saved registers size. */
+ if (local_size != 0) {
+ if (local_size > 0xfff) {
+ FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | (((sljit_ins)local_size >> 12) << 10) | (1 << 22)));
+ local_size &= 0xfff;
+ }
+
+ if (local_size > 512 || local_size == 0) {
+ if (local_size != 0)
+ FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | ((sljit_ins)local_size << 10)));
+
+ FAIL_IF(push_inst(compiler, STP | RT(TMP_FP) | RT2(TMP_LR) | RN(SLJIT_SP)));
+ } else
+ FAIL_IF(push_inst(compiler, STP_PRE | RT(TMP_FP) | RT2(TMP_LR)
+ | RN(SLJIT_SP) | (sljit_ins)((-(local_size >> 3) & 0x7f) << 15)));
+ }
+
+#endif /* _WIN32 */
+
+ return push_inst(compiler, ADDI | RD(TMP_FP) | RN(SLJIT_SP) | (0 << 10));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 saved_regs_size;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 2);
+ saved_regs_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, SSIZE_OF(f64));
+
+ compiler->local_size = (local_size + saved_regs_size + 0xf) & ~0xf;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 is_return_to)
+{
+ sljit_s32 local_size, prev, fprev, i, tmp;
+ sljit_ins offs;
+
+ local_size = compiler->local_size;
+
+ if (!is_return_to) {
+ if (local_size > 512 && local_size <= 512 + 496) {
+ FAIL_IF(push_inst(compiler, LDP_POST | RT(TMP_FP) | RT2(TMP_LR)
+ | RN(SLJIT_SP) | ((sljit_ins)(local_size - 512) << (15 - 3))));
+ local_size = 512;
+ } else
+ FAIL_IF(push_inst(compiler, LDP | RT(TMP_FP) | RT2(TMP_LR) | RN(SLJIT_SP)));
+ } else {
+ if (local_size > 512 && local_size <= 512 + 248) {
+ FAIL_IF(push_inst(compiler, LDRI_POST | RT(TMP_FP) | RN(SLJIT_SP) | ((sljit_ins)(local_size - 512) << 12)));
+ local_size = 512;
+ } else
+ FAIL_IF(push_inst(compiler, LDRI | RT(TMP_FP) | RN(SLJIT_SP) | 0));
+ }
+
+ if (local_size > 512) {
+ local_size -= 512;
+ if (local_size > 0xfff) {
+ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RN(SLJIT_SP)
+ | (((sljit_ins)local_size >> 12) << 10) | (1 << 22)));
+ local_size &= 0xfff;
+ }
+
+ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RN(SLJIT_SP) | ((sljit_ins)local_size << 10)));
+ local_size = 512;
+ }
+
+ offs = (sljit_ins)(local_size - 2 * SSIZE_OF(sw)) << (15 - 3);
+ prev = -1;
+
+ tmp = SLJIT_S0 - compiler->saveds;
+ for (i = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options); i > tmp; i--) {
+ if (prev == -1) {
+ prev = i;
+ continue;
+ }
+ FAIL_IF(push_inst(compiler, LDP | RT(prev) | RT2(i) | RN(SLJIT_SP) | offs));
+ offs -= (sljit_ins)2 << 15;
+ prev = -1;
+ }
+
+ for (i = compiler->scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ if (prev == -1) {
+ prev = i;
+ continue;
+ }
+ FAIL_IF(push_inst(compiler, LDP | RT(prev) | RT2(i) | RN(SLJIT_SP) | offs));
+ offs -= (sljit_ins)2 << 15;
+ prev = -1;
+ }
+
+ fprev = -1;
+
+ tmp = SLJIT_FS0 - compiler->fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ if (fprev == -1) {
+ fprev = i;
+ continue;
+ }
+ FAIL_IF(push_inst(compiler, LDP_F64 | VT(fprev) | VT2(i) | RN(SLJIT_SP) | offs));
+ offs -= (sljit_ins)2 << 15;
+ fprev = -1;
+ }
+
+ for (i = compiler->fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ if (fprev == -1) {
+ fprev = i;
+ continue;
+ }
+ FAIL_IF(push_inst(compiler, LDP_F64 | VT(fprev) | VT2(i) | RN(SLJIT_SP) | offs));
+ offs -= (sljit_ins)2 << 15;
+ fprev = -1;
+ }
+
+ if (fprev != -1)
+ FAIL_IF(push_inst(compiler, LDRI_F64 | VT(fprev) | RN(SLJIT_SP) | (offs >> 5) | (1 << 10)));
+
+ if (prev != -1)
+ FAIL_IF(push_inst(compiler, LDRI | RT(prev) | RN(SLJIT_SP) | (offs >> 5) | ((fprev == -1) ? (1 << 10) : 0)));
+
+ /* This and the next call/jump instruction can be executed parallelly. */
+ return push_inst(compiler, ADDI | RD(SLJIT_SP) | RN(SLJIT_SP) | (sljit_ins)(local_size << 10));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+
+ return push_inst(compiler, RET | RN(TMP_LR));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ srcw = 0;
+ } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, ORR | RD(TMP_REG1) | RN(TMP_ZERO) | RM(src)));
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 1));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+ sljit_ins inv_bits = (op & SLJIT_32) ? W_OP : 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op0(compiler, op));
+
+ op = GET_OPCODE(op);
+ switch (op) {
+ case SLJIT_BREAKPOINT:
+ return push_inst(compiler, BRK);
+ case SLJIT_NOP:
+ return push_inst(compiler, NOP);
+ case SLJIT_LMUL_UW:
+ case SLJIT_LMUL_SW:
+ FAIL_IF(push_inst(compiler, ORR | RD(TMP_REG1) | RN(TMP_ZERO) | RM(SLJIT_R0)));
+ FAIL_IF(push_inst(compiler, MADD | RD(SLJIT_R0) | RN(SLJIT_R0) | RM(SLJIT_R1) | RT2(TMP_ZERO)));
+ return push_inst(compiler, (op == SLJIT_LMUL_UW ? UMULH : SMULH) | RD(SLJIT_R1) | RN(TMP_REG1) | RM(SLJIT_R1));
+ case SLJIT_DIVMOD_UW:
+ case SLJIT_DIVMOD_SW:
+ FAIL_IF(push_inst(compiler, (ORR ^ inv_bits) | RD(TMP_REG1) | RN(TMP_ZERO) | RM(SLJIT_R0)));
+ FAIL_IF(push_inst(compiler, ((op == SLJIT_DIVMOD_UW ? UDIV : SDIV) ^ inv_bits) | RD(SLJIT_R0) | RN(SLJIT_R0) | RM(SLJIT_R1)));
+ FAIL_IF(push_inst(compiler, (MADD ^ inv_bits) | RD(SLJIT_R1) | RN(SLJIT_R0) | RM(SLJIT_R1) | RT2(TMP_ZERO)));
+ return push_inst(compiler, (SUB ^ inv_bits) | RD(SLJIT_R1) | RN(TMP_REG1) | RM(SLJIT_R1));
+ case SLJIT_DIV_UW:
+ case SLJIT_DIV_SW:
+ return push_inst(compiler, ((op == SLJIT_DIV_UW ? UDIV : SDIV) ^ inv_bits) | RD(SLJIT_R0) | RN(SLJIT_R0) | RM(SLJIT_R1));
+ case SLJIT_ENDBR:
+ case SLJIT_SKIP_FRAMES_BEFORE_RETURN:
+ return SLJIT_SUCCESS;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r, flags, mem_flags;
+ sljit_s32 op_flags = GET_ALL_FLAGS(op);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ op = GET_OPCODE(op);
+ if (op >= SLJIT_MOV && op <= SLJIT_MOV_P) {
+ /* Both operands are registers. */
+ if (dst_r != TMP_REG1 && FAST_IS_REG(src))
+ return emit_op_imm(compiler, op | ((op_flags & SLJIT_32) ? INT_OP : 0), dst_r, TMP_REG1, src);
+
+ switch (op) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+ mem_flags = WORD_SIZE;
+ break;
+ case SLJIT_MOV_U8:
+ mem_flags = BYTE_SIZE;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_u8)srcw;
+ break;
+ case SLJIT_MOV_S8:
+ mem_flags = BYTE_SIZE | SIGNED;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_s8)srcw;
+ break;
+ case SLJIT_MOV_U16:
+ mem_flags = HALF_SIZE;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_u16)srcw;
+ break;
+ case SLJIT_MOV_S16:
+ mem_flags = HALF_SIZE | SIGNED;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_s16)srcw;
+ break;
+ case SLJIT_MOV_U32:
+ mem_flags = INT_SIZE;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_u32)srcw;
+ break;
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ mem_flags = INT_SIZE | SIGNED;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_s32)srcw;
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ mem_flags = 0;
+ break;
+ }
+
+ if (src & SLJIT_IMM)
+ FAIL_IF(emit_op_imm(compiler, SLJIT_MOV | ARG2_IMM, dst_r, TMP_REG1, srcw));
+ else if (!(src & SLJIT_MEM))
+ dst_r = src;
+ else
+ FAIL_IF(emit_op_mem(compiler, mem_flags, dst_r, src, srcw, TMP_REG1));
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, mem_flags | STORE, dst_r, dst, dstw, TMP_REG2);
+ return SLJIT_SUCCESS;
+ }
+
+ flags = HAS_FLAGS(op_flags) ? SET_FLAGS : 0;
+ mem_flags = WORD_SIZE;
+
+ if (op_flags & SLJIT_32) {
+ flags |= INT_OP;
+ mem_flags = INT_SIZE;
+ }
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, mem_flags, TMP_REG2, src, srcw, TMP_REG2));
+ src = TMP_REG2;
+ }
+
+ emit_op_imm(compiler, flags | op, dst_r, TMP_REG1, src);
+
+ if (SLJIT_UNLIKELY(dst & SLJIT_MEM))
+ return emit_op_mem(compiler, mem_flags | STORE, dst_r, dst, dstw, TMP_REG2);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r, flags, mem_flags;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+ flags = HAS_FLAGS(op) ? SET_FLAGS : 0;
+ mem_flags = WORD_SIZE;
+
+ if (op & SLJIT_32) {
+ flags |= INT_OP;
+ mem_flags = INT_SIZE;
+ }
+
+ if (dst == TMP_REG1)
+ flags |= UNUSED_RETURN;
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, mem_flags, TMP_REG1, src1, src1w, TMP_REG1));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, mem_flags, TMP_REG2, src2, src2w, TMP_REG2));
+ src2 = TMP_REG2;
+ }
+
+ if (src1 & SLJIT_IMM)
+ flags |= ARG1_IMM;
+ else
+ src1w = src1;
+
+ if (src2 & SLJIT_IMM)
+ flags |= ARG2_IMM;
+ else
+ src2w = src2;
+
+ emit_op_imm(compiler, flags | GET_OPCODE(op), dst_r, src1w, src2w);
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, mem_flags | STORE, dst_r, dst, dstw, TMP_REG2);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, TMP_REG1, 0, src1, src1w, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_ins inv_bits, imm;
+ sljit_s32 is_left;
+ sljit_sw mask;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w));
+
+ is_left = (GET_OPCODE(op) == SLJIT_SHL || GET_OPCODE(op) == SLJIT_MSHL);
+
+ if (src_dst == src1) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, (is_left ? SLJIT_ROTL : SLJIT_ROTR) | (op & SLJIT_32), src_dst, 0, src_dst, 0, src2, src2w);
+ }
+
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ inv_bits = (op & SLJIT_32) ? W_OP : 0;
+ mask = inv_bits ? 0x1f : 0x3f;
+
+ if (src2 & SLJIT_IMM) {
+ src2w &= mask;
+
+ if (src2w == 0)
+ return SLJIT_SUCCESS;
+ } else if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inv_bits ? INT_SIZE : WORD_SIZE, TMP_REG2, src2, src2w, TMP_REG2));
+ src2 = TMP_REG2;
+ }
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inv_bits ? INT_SIZE : WORD_SIZE, TMP_REG1, src1, src1w, TMP_REG1));
+ src1 = TMP_REG1;
+ } else if (src1 & SLJIT_IMM) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, src1w));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_IMM) {
+ if (is_left)
+ src2w = (src2w ^ mask) + 1;
+
+ return push_inst(compiler, (EXTR ^ (inv_bits | (inv_bits >> 9))) | RD(src_dst)
+ | RN(is_left ? src_dst : src1) | RM(is_left ? src1 : src_dst) | ((sljit_ins)src2w << 10));
+ }
+
+ FAIL_IF(push_inst(compiler, ((is_left ? LSLV : LSRV) ^ inv_bits) | RD(src_dst) | RN(src_dst) | RM(src2)));
+
+ if (!(op & SLJIT_SHIFT_INTO_NON_ZERO)) {
+ /* Shift left/right by 1. */
+ if (is_left)
+ imm = (sljit_ins)(inv_bits ? ((1 << 16) | (31 << 10)) : ((1 << 16) | (63 << 10) | (1 << 22)));
+ else
+ imm = (sljit_ins)(inv_bits ? ((31 << 16) | (30 << 10)) : ((63 << 16) | (62 << 10) | (1 << 22)));
+
+ FAIL_IF(push_inst(compiler, (UBFM ^ inv_bits) | RD(TMP_REG1) | RN(src1) | imm));
+
+ /* Set imm to mask. */
+ imm = (sljit_ins)(inv_bits ? (4 << 10) : ((5 << 10) | (1 << 22)));
+ FAIL_IF(push_inst(compiler, (EORI ^ inv_bits) | RD(TMP_REG2) | RN(src2) | imm));
+
+ src1 = TMP_REG1;
+ } else
+ FAIL_IF(push_inst(compiler, (SUB ^ inv_bits) | RD(TMP_REG2) | RN(TMP_ZERO) | RM(src2)));
+
+ FAIL_IF(push_inst(compiler, ((is_left ? LSRV : LSLV) ^ inv_bits) | RD(TMP_REG1) | RN(src1) | RM(TMP_REG2)));
+ return push_inst(compiler, (ORR ^ inv_bits) | RD(src_dst) | RN(src_dst) | RM(TMP_REG1));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_src(compiler, op, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ switch (op) {
+ case SLJIT_FAST_RETURN:
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst(compiler, ORR | RD(TMP_LR) | RN(TMP_ZERO) | RM(src)));
+ else
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_LR, src, srcw, TMP_REG1));
+
+ return push_inst(compiler, RET | RN(TMP_LR));
+ case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN:
+ return SLJIT_SUCCESS;
+ case SLJIT_PREFETCH_L1:
+ case SLJIT_PREFETCH_L2:
+ case SLJIT_PREFETCH_L3:
+ case SLJIT_PREFETCH_ONCE:
+ SLJIT_ASSERT(reg_map[1] == 0 && reg_map[3] == 2 && reg_map[5] == 4);
+
+ /* The reg_map[op] should provide the appropriate constant. */
+ if (op == SLJIT_PREFETCH_L1)
+ op = 1;
+ else if (op == SLJIT_PREFETCH_L2)
+ op = 3;
+ else if (op == SLJIT_PREFETCH_L3)
+ op = 5;
+ else
+ op = 2;
+
+ /* Signed word sized load is the prefetch instruction. */
+ return emit_op_mem(compiler, WORD_SIZE | SIGNED, op, src, srcw, TMP_REG1);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_register_index(reg));
+ return reg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
+ return freg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ SLJIT_UNUSED_ARG(size);
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_custom(compiler, instruction, size));
+
+ return push_inst(compiler, *(sljit_ins*)instruction);
+}
+
+/* --------------------------------------------------------------------- */
+/* Floating point operators */
+/* --------------------------------------------------------------------- */
+
+static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
+{
+ sljit_u32 shift = MEM_SIZE_SHIFT(flags);
+ sljit_ins type = (shift << 30);
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+
+ if (!(flags & STORE))
+ type |= 0x00400000;
+
+ if (arg & OFFS_REG_MASK) {
+ argw &= 3;
+ if (argw == 0 || argw == shift)
+ return push_inst(compiler, STR_FR | type | VT(reg)
+ | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | (argw ? (1 << 12) : 0));
+
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | ((sljit_ins)argw << 10)));
+ return push_inst(compiler, STR_FI | type | VT(reg) | RN(TMP_REG1));
+ }
+
+ arg &= REG_MASK;
+
+ if (!arg) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, argw & ~(0xfff << shift)));
+
+ argw = (argw >> shift) & 0xfff;
+
+ return push_inst(compiler, STR_FI | type | VT(reg) | RN(TMP_REG1) | ((sljit_ins)argw << 10));
+ }
+
+ if (argw >= 0 && (argw & ((1 << shift) - 1)) == 0) {
+ if ((argw >> shift) <= 0xfff)
+ return push_inst(compiler, STR_FI | type | VT(reg) | RN(arg) | ((sljit_ins)argw << (10 - shift)));
+
+ if (argw <= 0xffffff) {
+ FAIL_IF(push_inst(compiler, ADDI | (1 << 22) | RD(TMP_REG1) | RN(arg) | (((sljit_ins)argw >> 12) << 10)));
+
+ argw = ((argw & 0xfff) >> shift);
+ return push_inst(compiler, STR_FI | type | VT(reg) | RN(TMP_REG1) | ((sljit_ins)argw << 10));
+ }
+ }
+
+ if (argw <= 255 && argw >= -256)
+ return push_inst(compiler, STUR_FI | type | VT(reg) | RN(arg) | (((sljit_ins)argw & 0x1ff) << 12));
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, argw));
+ return push_inst(compiler, STR_FR | type | VT(reg) | RN(arg) | RM(TMP_REG1));
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+ sljit_ins inv_bits = (op & SLJIT_32) ? (1 << 22) : 0;
+
+ if (GET_OPCODE(op) == SLJIT_CONV_S32_FROM_F64)
+ inv_bits |= W_OP;
+
+ if (src & SLJIT_MEM) {
+ emit_fop_mem(compiler, (op & SLJIT_32) ? INT_SIZE : WORD_SIZE, TMP_FREG1, src, srcw);
+ src = TMP_FREG1;
+ }
+
+ FAIL_IF(push_inst(compiler, (FCVTZS ^ inv_bits) | RD(dst_r) | VN(src)));
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, ((GET_OPCODE(op) == SLJIT_CONV_S32_FROM_F64) ? INT_SIZE : WORD_SIZE) | STORE, TMP_REG1, dst, dstw, TMP_REG2);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+ sljit_ins inv_bits = (op & SLJIT_32) ? (1 << 22) : 0;
+
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32)
+ inv_bits |= W_OP;
+
+ if (src & SLJIT_MEM) {
+ emit_op_mem(compiler, ((GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32) ? INT_SIZE : WORD_SIZE), TMP_REG1, src, srcw, TMP_REG1);
+ src = TMP_REG1;
+ } else if (src & SLJIT_IMM) {
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32)
+ srcw = (sljit_s32)srcw;
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
+ src = TMP_REG1;
+ }
+
+ FAIL_IF(push_inst(compiler, (SCVTF ^ inv_bits) | VD(dst_r) | RN(src)));
+
+ if (dst & SLJIT_MEM)
+ return emit_fop_mem(compiler, ((op & SLJIT_32) ? INT_SIZE : WORD_SIZE) | STORE, TMP_FREG1, dst, dstw);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 mem_flags = (op & SLJIT_32) ? INT_SIZE : WORD_SIZE;
+ sljit_ins inv_bits = (op & SLJIT_32) ? (1 << 22) : 0;
+
+ if (src1 & SLJIT_MEM) {
+ emit_fop_mem(compiler, mem_flags, TMP_FREG1, src1, src1w);
+ src1 = TMP_FREG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ emit_fop_mem(compiler, mem_flags, TMP_FREG2, src2, src2w);
+ src2 = TMP_FREG2;
+ }
+
+ return push_inst(compiler, (FCMP ^ inv_bits) | VN(src1) | VM(src2));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r, mem_flags = (op & SLJIT_32) ? INT_SIZE : WORD_SIZE;
+ sljit_ins inv_bits;
+
+ CHECK_ERROR();
+
+ SLJIT_COMPILE_ASSERT((INT_SIZE ^ 0x1) == WORD_SIZE, must_be_one_bit_difference);
+ SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
+
+ inv_bits = (op & SLJIT_32) ? (1 << 22) : 0;
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (src & SLJIT_MEM) {
+ emit_fop_mem(compiler, (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32) ? (mem_flags ^ 0x1) : mem_flags, dst_r, src, srcw);
+ src = dst_r;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV_F64:
+ if (src != dst_r) {
+ if (dst_r != TMP_FREG1)
+ FAIL_IF(push_inst(compiler, (FMOV ^ inv_bits) | VD(dst_r) | VN(src)));
+ else
+ dst_r = src;
+ }
+ break;
+ case SLJIT_NEG_F64:
+ FAIL_IF(push_inst(compiler, (FNEG ^ inv_bits) | VD(dst_r) | VN(src)));
+ break;
+ case SLJIT_ABS_F64:
+ FAIL_IF(push_inst(compiler, (FABS ^ inv_bits) | VD(dst_r) | VN(src)));
+ break;
+ case SLJIT_CONV_F64_FROM_F32:
+ FAIL_IF(push_inst(compiler, FCVT | (sljit_ins)((op & SLJIT_32) ? (1 << 22) : (1 << 15)) | VD(dst_r) | VN(src)));
+ break;
+ }
+
+ if (dst & SLJIT_MEM)
+ return emit_fop_mem(compiler, mem_flags | STORE, dst_r, dst, dstw);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r, mem_flags = (op & SLJIT_32) ? INT_SIZE : WORD_SIZE;
+ sljit_ins inv_bits = (op & SLJIT_32) ? (1 << 22) : 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+ if (src1 & SLJIT_MEM) {
+ emit_fop_mem(compiler, mem_flags, TMP_FREG1, src1, src1w);
+ src1 = TMP_FREG1;
+ }
+ if (src2 & SLJIT_MEM) {
+ emit_fop_mem(compiler, mem_flags, TMP_FREG2, src2, src2w);
+ src2 = TMP_FREG2;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD_F64:
+ FAIL_IF(push_inst(compiler, (FADD ^ inv_bits) | VD(dst_r) | VN(src1) | VM(src2)));
+ break;
+ case SLJIT_SUB_F64:
+ FAIL_IF(push_inst(compiler, (FSUB ^ inv_bits) | VD(dst_r) | VN(src1) | VM(src2)));
+ break;
+ case SLJIT_MUL_F64:
+ FAIL_IF(push_inst(compiler, (FMUL ^ inv_bits) | VD(dst_r) | VN(src1) | VM(src2)));
+ break;
+ case SLJIT_DIV_F64:
+ FAIL_IF(push_inst(compiler, (FDIV ^ inv_bits) | VD(dst_r) | VN(src1) | VM(src2)));
+ break;
+ }
+
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+ return emit_fop_mem(compiler, mem_flags | STORE, TMP_FREG1, dst, dstw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Other instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ if (FAST_IS_REG(dst))
+ return push_inst(compiler, ORR | RD(dst) | RN(TMP_ZERO) | RM(TMP_LR));
+
+ /* Memory. */
+ return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_LR, dst, dstw, TMP_REG1);
+}
+
+/* --------------------------------------------------------------------- */
+/* Conditional instructions */
+/* --------------------------------------------------------------------- */
+
+static sljit_ins get_cc(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ switch (type) {
+ case SLJIT_EQUAL:
+ case SLJIT_F_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */
+ return 0x1;
+
+ case SLJIT_NOT_EQUAL:
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL: /* Not supported. */
+ return 0x0;
+
+ case SLJIT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_ADD)
+ return 0x3;
+ /* fallthrough */
+
+ case SLJIT_LESS:
+ return 0x2;
+
+ case SLJIT_NOT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_ADD)
+ return 0x2;
+ /* fallthrough */
+
+ case SLJIT_GREATER_EQUAL:
+ return 0x3;
+
+ case SLJIT_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ return 0x9;
+
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ return 0x8;
+
+ case SLJIT_SIG_LESS:
+ case SLJIT_UNORDERED_OR_LESS:
+ return 0xa;
+
+ case SLJIT_SIG_GREATER_EQUAL:
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ return 0xb;
+
+ case SLJIT_SIG_GREATER:
+ case SLJIT_F_GREATER:
+ case SLJIT_ORDERED_GREATER:
+ return 0xd;
+
+ case SLJIT_SIG_LESS_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ return 0xc;
+
+ case SLJIT_OVERFLOW:
+ if (!(compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)))
+ return 0x0;
+ /* fallthrough */
+
+ case SLJIT_UNORDERED:
+ return 0x7;
+
+ case SLJIT_NOT_OVERFLOW:
+ if (!(compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)))
+ return 0x1;
+ /* fallthrough */
+
+ case SLJIT_ORDERED:
+ return 0x6;
+
+ case SLJIT_F_LESS:
+ case SLJIT_ORDERED_LESS:
+ return 0x5;
+
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ return 0x4;
+
+ default:
+ SLJIT_UNREACHABLE();
+ return 0xe;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ struct sljit_label *label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_label(compiler));
+
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ return compiler->last_label;
+
+ label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
+ PTR_FAIL_IF(!label);
+ set_label(label, compiler);
+ return label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ struct sljit_jump *jump;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_jump(compiler, type));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ type &= 0xff;
+
+ if (type < SLJIT_JUMP) {
+ jump->flags |= IS_COND;
+ PTR_FAIL_IF(push_inst(compiler, B_CC | (6 << 5) | get_cc(compiler, type)));
+ }
+ else if (type >= SLJIT_FAST_CALL)
+ jump->flags |= IS_BL;
+
+ PTR_FAIL_IF(emit_imm64_const(compiler, TMP_REG1, 0));
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, ((type >= SLJIT_FAST_CALL) ? BLR : BR) | RN(TMP_REG1)));
+
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ SLJIT_UNUSED_ARG(arg_types);
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ if (type & SLJIT_CALL_RETURN) {
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, 0));
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+}
+
+static SLJIT_INLINE struct sljit_jump* emit_cmp_to0(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src, sljit_sw srcw)
+{
+ struct sljit_jump *jump;
+ sljit_ins inv_bits = (type & SLJIT_32) ? W_OP : 0;
+
+ SLJIT_ASSERT((type & 0xff) == SLJIT_EQUAL || (type & 0xff) == SLJIT_NOT_EQUAL);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ jump->flags |= IS_CBZ | IS_COND;
+
+ if (src & SLJIT_MEM) {
+ PTR_FAIL_IF(emit_op_mem(compiler, inv_bits ? INT_SIZE : WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+ else if (src & SLJIT_IMM) {
+ PTR_FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
+ src = TMP_REG1;
+ }
+
+ SLJIT_ASSERT(FAST_IS_REG(src));
+
+ if ((type & 0xff) == SLJIT_EQUAL)
+ inv_bits |= 1 << 24;
+
+ PTR_FAIL_IF(push_inst(compiler, (CBZ ^ inv_bits) | (6 << 5) | RT(src)));
+ PTR_FAIL_IF(emit_imm64_const(compiler, TMP_REG1, 0));
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, BR | RN(TMP_REG1)));
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ struct sljit_jump *jump;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
+
+ if (!(src & SLJIT_IMM)) {
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+ return push_inst(compiler, ((type >= SLJIT_FAST_CALL) ? BLR : BR) | RN(src));
+ }
+
+ /* These jumps are converted to jump/call instructions when possible. */
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ FAIL_IF(!jump);
+ set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_BL : 0));
+ jump->u.target = (sljit_uw)srcw;
+
+ FAIL_IF(emit_imm64_const(compiler, TMP_REG1, 0));
+ jump->addr = compiler->size;
+ return push_inst(compiler, ((type >= SLJIT_FAST_CALL) ? BLR : BR) | RN(TMP_REG1));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(arg_types);
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+
+ if (type & SLJIT_CALL_RETURN) {
+ if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, ORR | RD(TMP_REG1) | RN(TMP_ZERO) | RM(src)));
+ src = TMP_REG1;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+ type = SLJIT_JUMP;
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ sljit_s32 dst_r, src_r, flags, mem_flags;
+ sljit_ins cc;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ cc = get_cc(compiler, type);
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if (GET_OPCODE(op) < SLJIT_ADD) {
+ FAIL_IF(push_inst(compiler, CSINC | (cc << 12) | RD(dst_r) | RN(TMP_ZERO) | RM(TMP_ZERO)));
+
+ if (dst_r == TMP_REG1) {
+ mem_flags = (GET_OPCODE(op) == SLJIT_MOV ? WORD_SIZE : INT_SIZE) | STORE;
+ return emit_op_mem(compiler, mem_flags, TMP_REG1, dst, dstw, TMP_REG2);
+ }
+
+ return SLJIT_SUCCESS;
+ }
+
+ flags = HAS_FLAGS(op) ? SET_FLAGS : 0;
+ mem_flags = WORD_SIZE;
+
+ if (op & SLJIT_32) {
+ flags |= INT_OP;
+ mem_flags = INT_SIZE;
+ }
+
+ src_r = dst;
+
+ if (dst & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, mem_flags, TMP_REG1, dst, dstw, TMP_REG1));
+ src_r = TMP_REG1;
+ }
+
+ FAIL_IF(push_inst(compiler, CSINC | (cc << 12) | RD(TMP_REG2) | RN(TMP_ZERO) | RM(TMP_ZERO)));
+ emit_op_imm(compiler, flags | GET_OPCODE(op), dst_r, src_r, TMP_REG2);
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, mem_flags | STORE, TMP_REG1, dst, dstw, TMP_REG2);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_ins inv_bits = (type & SLJIT_32) ? W_OP : 0;
+ sljit_ins cc;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+ if (SLJIT_UNLIKELY(src & SLJIT_IMM)) {
+ if (type & SLJIT_32)
+ srcw = (sljit_s32)srcw;
+ FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ cc = get_cc(compiler, type & ~SLJIT_32);
+
+ return push_inst(compiler, (CSEL ^ inv_bits) | (cc << 12) | RD(dst_reg) | RN(dst_reg) | RM(src));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_u32 inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (!(reg & REG_PAIR_MASK))
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+
+ ADJUST_LOCAL_OFFSET(mem, memw);
+
+ if (!(mem & REG_MASK)) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, memw & ~0x1f8));
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ memw &= 0x1f8;
+ } else if (mem & OFFS_REG_MASK) {
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RN(mem & REG_MASK) | RM(OFFS_REG(mem)) | ((sljit_ins)(memw & 0x3) << 10)));
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ memw = 0;
+ } else if ((memw & 0x7) != 0 || memw > 0x1f8 || memw < -0x200) {
+ inst = ADDI;
+
+ if (memw < 0) {
+ /* Remains negative for integer min. */
+ memw = -memw;
+ inst = SUBI;
+ } else if ((memw & 0x7) == 0 && memw <= 0x7ff0) {
+ if (!(type & SLJIT_MEM_STORE) && (mem & REG_MASK) == REG_PAIR_FIRST(reg)) {
+ FAIL_IF(push_inst(compiler, LDRI | RD(REG_PAIR_SECOND(reg)) | RN(mem & REG_MASK) | ((sljit_ins)memw << 7)));
+ return push_inst(compiler, LDRI | RD(REG_PAIR_FIRST(reg)) | RN(mem & REG_MASK) | ((sljit_ins)(memw + 0x8) << 7));
+ }
+
+ inst = (type & SLJIT_MEM_STORE) ? STRI : LDRI;
+
+ FAIL_IF(push_inst(compiler, inst | RD(REG_PAIR_FIRST(reg)) | RN(mem & REG_MASK) | ((sljit_ins)memw << 7)));
+ return push_inst(compiler, inst | RD(REG_PAIR_SECOND(reg)) | RN(mem & REG_MASK) | ((sljit_ins)(memw + 0x8) << 7));
+ }
+
+ if ((sljit_uw)memw <= 0xfff) {
+ FAIL_IF(push_inst(compiler, inst | RD(TMP_REG1) | RN(mem & REG_MASK) | ((sljit_ins)memw << 10)));
+ memw = 0;
+ } else if ((sljit_uw)memw <= 0xffffff) {
+ FAIL_IF(push_inst(compiler, inst | (1 << 22) | RD(TMP_REG1) | RN(mem & REG_MASK) | (((sljit_ins)memw >> 12) << 10)));
+
+ if ((memw & 0xe07) != 0) {
+ FAIL_IF(push_inst(compiler, inst | RD(TMP_REG1) | RN(TMP_REG1) | (((sljit_ins)memw & 0xfff) << 10)));
+ memw = 0;
+ } else {
+ memw &= 0xfff;
+ }
+ } else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, memw));
+ FAIL_IF(push_inst(compiler, (inst == ADDI ? ADD : SUB) | RD(TMP_REG1) | RN(mem & REG_MASK) | RM(TMP_REG1)));
+ memw = 0;
+ }
+
+ mem = SLJIT_MEM1(TMP_REG1);
+
+ if (inst == SUBI)
+ memw = -memw;
+ }
+
+ SLJIT_ASSERT((memw & 0x7) == 0 && memw <= 0x1f8 && memw >= -0x200);
+ return push_inst(compiler, ((type & SLJIT_MEM_STORE) ? STP : LDP) | RT(REG_PAIR_FIRST(reg)) | RT2(REG_PAIR_SECOND(reg)) | RN(mem & REG_MASK) | (sljit_ins)((memw & 0x3f8) << 12));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_u32 sign = 0, inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem_update(compiler, type, reg, mem, memw));
+
+ if ((mem & OFFS_REG_MASK) || (memw > 255 || memw < -256))
+ return SLJIT_ERR_UNSUPPORTED;
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ switch (type & 0xff) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+ inst = STURBI | (MEM_SIZE_SHIFT(WORD_SIZE) << 30) | 0x400;
+ break;
+ case SLJIT_MOV_S8:
+ sign = 1;
+ /* fallthrough */
+ case SLJIT_MOV_U8:
+ inst = STURBI | (MEM_SIZE_SHIFT(BYTE_SIZE) << 30) | 0x400;
+ break;
+ case SLJIT_MOV_S16:
+ sign = 1;
+ /* fallthrough */
+ case SLJIT_MOV_U16:
+ inst = STURBI | (MEM_SIZE_SHIFT(HALF_SIZE) << 30) | 0x400;
+ break;
+ case SLJIT_MOV_S32:
+ sign = 1;
+ /* fallthrough */
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV32:
+ inst = STURBI | (MEM_SIZE_SHIFT(INT_SIZE) << 30) | 0x400;
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ inst = STURBI | (MEM_SIZE_SHIFT(WORD_SIZE) << 30) | 0x400;
+ break;
+ }
+
+ if (!(type & SLJIT_MEM_STORE))
+ inst |= sign ? 0x00800000 : 0x00400000;
+
+ if (!(type & SLJIT_MEM_POST))
+ inst |= 0x800;
+
+ return push_inst(compiler, inst | RT(reg) | RN(mem & REG_MASK) | (sljit_ins)((memw & 0x1ff) << 12));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_u32 inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fmem_update(compiler, type, freg, mem, memw));
+
+ if ((mem & OFFS_REG_MASK) || (memw > 255 || memw < -256))
+ return SLJIT_ERR_UNSUPPORTED;
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ inst = STUR_FI | 0x80000400;
+
+ if (!(type & SLJIT_32))
+ inst |= 0x40000000;
+
+ if (!(type & SLJIT_MEM_STORE))
+ inst |= 0x00400000;
+
+ if (!(type & SLJIT_MEM_POST))
+ inst |= 0x800;
+
+ return push_inst(compiler, inst | VT(freg) | RN(mem & REG_MASK) | (sljit_ins)((memw & 0x1ff) << 12));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset)
+{
+ sljit_s32 dst_reg;
+ sljit_ins ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_get_local_base(compiler, dst, dstw, offset));
+ ADJUST_LOCAL_OFFSET(SLJIT_MEM1(SLJIT_SP), offset);
+
+ dst_reg = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ /* Not all instruction forms support accessing SP register. */
+ if (offset <= 0xffffff && offset >= -0xffffff) {
+ ins = ADDI;
+ if (offset < 0) {
+ offset = -offset;
+ ins = SUBI;
+ }
+
+ if (offset <= 0xfff)
+ FAIL_IF(push_inst(compiler, ins | RD(dst_reg) | RN(SLJIT_SP) | (sljit_ins)(offset << 10)));
+ else {
+ FAIL_IF(push_inst(compiler, ins | RD(dst_reg) | RN(SLJIT_SP) | (sljit_ins)((offset & 0xfff000) >> (12 - 10)) | (1 << 22)));
+
+ offset &= 0xfff;
+ if (offset != 0)
+ FAIL_IF(push_inst(compiler, ins | RD(dst_reg) | RN(dst_reg) | (sljit_ins)(offset << 10)));
+ }
+ }
+ else {
+ FAIL_IF(load_immediate (compiler, dst_reg, offset));
+ /* Add extended register form. */
+ FAIL_IF(push_inst(compiler, ADDE | (0x3 << 13) | RD(dst_reg) | RN(SLJIT_SP) | RM(dst_reg)));
+ }
+
+ if (SLJIT_UNLIKELY(dst & SLJIT_MEM))
+ return emit_op_mem(compiler, WORD_SIZE | STORE, dst_reg, dst, dstw, TMP_REG1);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ struct sljit_const *const_;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const));
+ PTR_FAIL_IF(!const_);
+ set_const(const_, compiler);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+ PTR_FAIL_IF(emit_imm64_const(compiler, dst_r, (sljit_uw)init_value));
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, dst_r, dst, dstw, TMP_REG2));
+ return const_;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ struct sljit_put_label *put_label;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+ PTR_FAIL_IF(emit_imm64_const(compiler, dst_r, 0));
+
+ put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label));
+ PTR_FAIL_IF(!put_label);
+ set_put_label(put_label, compiler, 1);
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, dst_r, dst, dstw, TMP_REG2));
+
+ return put_label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ sljit_ins* inst = (sljit_ins*)addr;
+ sljit_u32 dst;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 4, 0);
+
+ dst = inst[0] & 0x1f;
+ SLJIT_ASSERT((inst[0] & 0xffe00000) == MOVZ && (inst[1] & 0xffe00000) == (MOVK | (1 << 21)));
+ inst[0] = MOVZ | dst | (((sljit_u32)new_target & 0xffff) << 5);
+ inst[1] = MOVK | dst | (((sljit_u32)(new_target >> 16) & 0xffff) << 5) | (1 << 21);
+ inst[2] = MOVK | dst | (((sljit_u32)(new_target >> 32) & 0xffff) << 5) | (2 << 21);
+ inst[3] = MOVK | dst | ((sljit_u32)(new_target >> 48) << 5) | (3 << 21);
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 4, 1);
+ inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 4);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeARM_T2_32.c b/contrib/libs/pcre2/src/sljit/sljitNativeARM_T2_32.c
new file mode 100644
index 00000000000..7d6bac077e2
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeARM_T2_32.c
@@ -0,0 +1,3150 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+#ifdef __SOFTFP__
+ return "ARM-Thumb2" SLJIT_CPUINFO " ABI:softfp";
+#else
+ return "ARM-Thumb2" SLJIT_CPUINFO " ABI:hardfp";
+#endif
+}
+
+/* Length of an instruction word. */
+typedef sljit_u32 sljit_ins;
+
+/* Last register + 1. */
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
+#define TMP_PC (SLJIT_NUMBER_OF_REGISTERS + 4)
+
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
+
+/* See sljit_emit_enter and sljit_emit_op0 if you want to change them. */
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = {
+ 0, 0, 1, 2, 3, 11, 10, 9, 8, 7, 6, 5, 4, 13, 12, 14, 15
+};
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = {
+ 0, 0, 1, 2, 3, 4, 5, 15, 14, 13, 12, 11, 10, 9, 8, 6, 7
+};
+
+#define COPY_BITS(src, from, to, bits) \
+ ((from >= to ? ((sljit_ins)(src) >> (from - to)) : ((sljit_ins)(src) << (to - from))) & (((1 << bits) - 1) << to))
+
+#define NEGATE(uimm) ((sljit_uw)-(sljit_sw)(uimm))
+
+/* Thumb16 encodings. */
+#define RD3(rd) ((sljit_ins)reg_map[rd])
+#define RN3(rn) ((sljit_ins)reg_map[rn] << 3)
+#define RM3(rm) ((sljit_ins)reg_map[rm] << 6)
+#define RDN3(rdn) ((sljit_ins)reg_map[rdn] << 8)
+#define IMM3(imm) ((sljit_ins)imm << 6)
+#define IMM8(imm) ((sljit_ins)imm)
+
+/* Thumb16 helpers. */
+#define SET_REGS44(rd, rn) \
+ (((sljit_ins)reg_map[rn] << 3) | ((sljit_ins)reg_map[rd] & 0x7) | (((sljit_ins)reg_map[rd] & 0x8) << 4))
+#define IS_2_LO_REGS(reg1, reg2) \
+ (reg_map[reg1] <= 7 && reg_map[reg2] <= 7)
+#define IS_3_LO_REGS(reg1, reg2, reg3) \
+ (reg_map[reg1] <= 7 && reg_map[reg2] <= 7 && reg_map[reg3] <= 7)
+
+/* Thumb32 encodings. */
+#define RD4(rd) ((sljit_ins)reg_map[rd] << 8)
+#define RN4(rn) ((sljit_ins)reg_map[rn] << 16)
+#define RM4(rm) ((sljit_ins)reg_map[rm])
+#define RT4(rt) ((sljit_ins)reg_map[rt] << 12)
+#define DD4(dd) ((sljit_ins)freg_map[dd] << 12)
+#define DN4(dn) ((sljit_ins)freg_map[dn] << 16)
+#define DM4(dm) ((sljit_ins)freg_map[dm])
+#define IMM5(imm) \
+ (COPY_BITS(imm, 2, 12, 3) | (((sljit_ins)imm & 0x3) << 6))
+#define IMM12(imm) \
+ (COPY_BITS(imm, 11, 26, 1) | COPY_BITS(imm, 8, 12, 3) | ((sljit_ins)imm & 0xff))
+
+/* --------------------------------------------------------------------- */
+/* Instrucion forms */
+/* --------------------------------------------------------------------- */
+
+/* dot '.' changed to _
+ I immediate form (possibly followed by number of immediate bits). */
+#define ADCI 0xf1400000
+#define ADCS 0x4140
+#define ADC_W 0xeb400000
+#define ADD 0x4400
+#define ADDS 0x1800
+#define ADDSI3 0x1c00
+#define ADDSI8 0x3000
+#define ADDWI 0xf2000000
+#define ADD_SP 0x4485
+#define ADD_SP_I 0xb000
+#define ADD_W 0xeb000000
+#define ADD_WI 0xf1000000
+#define ANDI 0xf0000000
+#define ANDS 0x4000
+#define AND_W 0xea000000
+#define ASRS 0x4100
+#define ASRSI 0x1000
+#define ASR_W 0xfa40f000
+#define ASR_WI 0xea4f0020
+#define BCC 0xd000
+#define BICI 0xf0200000
+#define BKPT 0xbe00
+#define BLX 0x4780
+#define BX 0x4700
+#define CLZ 0xfab0f080
+#define CMNI_W 0xf1100f00
+#define CMP 0x4280
+#define CMPI 0x2800
+#define CMPI_W 0xf1b00f00
+#define CMP_X 0x4500
+#define CMP_W 0xebb00f00
+#define EORI 0xf0800000
+#define EORS 0x4040
+#define EOR_W 0xea800000
+#define IT 0xbf00
+#define LDR_SP 0x9800
+#define LDR 0xf8d00000
+#define LDRD 0xe9500000
+#define LDRI 0xf8500800
+#define LSLS 0x4080
+#define LSLSI 0x0000
+#define LSL_W 0xfa00f000
+#define LSL_WI 0xea4f0000
+#define LSRS 0x40c0
+#define LSRSI 0x0800
+#define LSR_W 0xfa20f000
+#define LSR_WI 0xea4f0010
+#define MOV 0x4600
+#define MOVS 0x0000
+#define MOVSI 0x2000
+#define MOVT 0xf2c00000
+#define MOVW 0xf2400000
+#define MOV_W 0xea4f0000
+#define MOV_WI 0xf04f0000
+#define MUL 0xfb00f000
+#define MVNS 0x43c0
+#define MVN_W 0xea6f0000
+#define MVN_WI 0xf06f0000
+#define NOP 0xbf00
+#define ORNI 0xf0600000
+#define ORRI 0xf0400000
+#define ORRS 0x4300
+#define ORR_W 0xea400000
+#define POP 0xbc00
+#define POP_W 0xe8bd0000
+#define PUSH 0xb400
+#define PUSH_W 0xe92d0000
+#define RBIT 0xfa90f0a0
+#define RORS 0x41c0
+#define ROR_W 0xfa60f000
+#define ROR_WI 0xea4f0030
+#define RSB_WI 0xf1c00000
+#define RSBSI 0x4240
+#define SBCI 0xf1600000
+#define SBCS 0x4180
+#define SBC_W 0xeb600000
+#define SDIV 0xfb90f0f0
+#define SMULL 0xfb800000
+#define STRD 0xe9400000
+#define STR_SP 0x9000
+#define SUBS 0x1a00
+#define SUBSI3 0x1e00
+#define SUBSI8 0x3800
+#define SUB_W 0xeba00000
+#define SUBWI 0xf2a00000
+#define SUB_SP_I 0xb080
+#define SUB_WI 0xf1a00000
+#define SXTB 0xb240
+#define SXTB_W 0xfa4ff080
+#define SXTH 0xb200
+#define SXTH_W 0xfa0ff080
+#define TST 0x4200
+#define TSTI 0xf0000f00
+#define TST_W 0xea000f00
+#define UDIV 0xfbb0f0f0
+#define UMULL 0xfba00000
+#define UXTB 0xb2c0
+#define UXTB_W 0xfa5ff080
+#define UXTH 0xb280
+#define UXTH_W 0xfa1ff080
+#define VABS_F32 0xeeb00ac0
+#define VADD_F32 0xee300a00
+#define VCMP_F32 0xeeb40a40
+#define VCVT_F32_S32 0xeeb80ac0
+#define VCVT_F64_F32 0xeeb70ac0
+#define VCVT_S32_F32 0xeebd0ac0
+#define VDIV_F32 0xee800a00
+#define VLDR_F32 0xed100a00
+#define VMOV_F32 0xeeb00a40
+#define VMOV 0xee000a10
+#define VMOV2 0xec400a10
+#define VMRS 0xeef1fa10
+#define VMUL_F32 0xee200a00
+#define VNEG_F32 0xeeb10a40
+#define VPOP 0xecbd0b00
+#define VPUSH 0xed2d0b00
+#define VSTR_F32 0xed000a00
+#define VSUB_F32 0xee300a40
+
+static sljit_s32 push_inst16(struct sljit_compiler *compiler, sljit_ins inst)
+{
+ sljit_u16 *ptr;
+ SLJIT_ASSERT(!(inst & 0xffff0000));
+
+ ptr = (sljit_u16*)ensure_buf(compiler, sizeof(sljit_u16));
+ FAIL_IF(!ptr);
+ *ptr = (sljit_u16)(inst);
+ compiler->size++;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 push_inst32(struct sljit_compiler *compiler, sljit_ins inst)
+{
+ sljit_u16 *ptr = (sljit_u16*)ensure_buf(compiler, sizeof(sljit_ins));
+ FAIL_IF(!ptr);
+ *ptr++ = (sljit_u16)(inst >> 16);
+ *ptr = (sljit_u16)(inst);
+ compiler->size += 2;
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_imm32_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_uw imm)
+{
+ FAIL_IF(push_inst32(compiler, MOVW | RD4(dst)
+ | COPY_BITS(imm, 12, 16, 4) | COPY_BITS(imm, 11, 26, 1) | COPY_BITS(imm, 8, 12, 3) | (imm & 0xff)));
+ return push_inst32(compiler, MOVT | RD4(dst)
+ | COPY_BITS(imm, 12 + 16, 16, 4) | COPY_BITS(imm, 11 + 16, 26, 1) | COPY_BITS(imm, 8 + 16, 12, 3) | ((imm & 0xff0000) >> 16));
+}
+
+static SLJIT_INLINE void modify_imm32_const(sljit_u16 *inst, sljit_uw new_imm)
+{
+ sljit_ins dst = inst[1] & 0x0f00;
+ SLJIT_ASSERT(((inst[0] & 0xfbf0) == (MOVW >> 16)) && ((inst[2] & 0xfbf0) == (MOVT >> 16)) && dst == (inst[3] & 0x0f00));
+ inst[0] = (sljit_u16)((MOVW >> 16) | COPY_BITS(new_imm, 12, 0, 4) | COPY_BITS(new_imm, 11, 10, 1));
+ inst[1] = (sljit_u16)(dst | COPY_BITS(new_imm, 8, 12, 3) | (new_imm & 0xff));
+ inst[2] = (sljit_u16)((MOVT >> 16) | COPY_BITS(new_imm, 12 + 16, 0, 4) | COPY_BITS(new_imm, 11 + 16, 10, 1));
+ inst[3] = (sljit_u16)(dst | COPY_BITS(new_imm, 8 + 16, 12, 3) | ((new_imm & 0xff0000) >> 16));
+}
+
+static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_u16 *code_ptr, sljit_u16 *code, sljit_sw executable_offset)
+{
+ sljit_sw diff;
+
+ if (jump->flags & SLJIT_REWRITABLE_JUMP)
+ return 0;
+
+ if (jump->flags & JUMP_ADDR) {
+ /* Branch to ARM code is not optimized yet. */
+ if (!(jump->u.target & 0x1))
+ return 0;
+ diff = ((sljit_sw)jump->u.target - (sljit_sw)(code_ptr + 2) - executable_offset) >> 1;
+ }
+ else {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ diff = ((sljit_sw)(code + jump->u.label->size) - (sljit_sw)(code_ptr + 2)) >> 1;
+ }
+
+ if (jump->flags & IS_COND) {
+ SLJIT_ASSERT(!(jump->flags & IS_BL));
+ if (diff <= 127 && diff >= -128) {
+ jump->flags |= PATCH_TYPE1;
+ return 5;
+ }
+ if (diff <= 524287 && diff >= -524288) {
+ jump->flags |= PATCH_TYPE2;
+ return 4;
+ }
+ /* +1 comes from the prefix IT instruction. */
+ diff--;
+ if (diff <= 8388607 && diff >= -8388608) {
+ jump->flags |= PATCH_TYPE3;
+ return 3;
+ }
+ }
+ else if (jump->flags & IS_BL) {
+ if (diff <= 8388607 && diff >= -8388608) {
+ jump->flags |= PATCH_BL;
+ return 3;
+ }
+ }
+ else {
+ if (diff <= 1023 && diff >= -1024) {
+ jump->flags |= PATCH_TYPE4;
+ return 4;
+ }
+ if (diff <= 8388607 && diff >= -8388608) {
+ jump->flags |= PATCH_TYPE5;
+ return 3;
+ }
+ }
+
+ return 0;
+}
+
+static SLJIT_INLINE void set_jump_instruction(struct sljit_jump *jump, sljit_sw executable_offset)
+{
+ sljit_s32 type = (jump->flags >> 4) & 0xf;
+ sljit_sw diff;
+ sljit_u16 *jump_inst;
+ sljit_s32 s, j1, j2;
+
+ if (SLJIT_UNLIKELY(type == 0)) {
+ modify_imm32_const((sljit_u16*)jump->addr, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target);
+ return;
+ }
+
+ if (jump->flags & JUMP_ADDR) {
+ SLJIT_ASSERT(jump->u.target & 0x1);
+ diff = ((sljit_sw)jump->u.target - (sljit_sw)(jump->addr + sizeof(sljit_u32)) - executable_offset) >> 1;
+ }
+ else {
+ SLJIT_ASSERT(jump->u.label->addr & 0x1);
+ diff = ((sljit_sw)(jump->u.label->addr) - (sljit_sw)(jump->addr + sizeof(sljit_u32)) - executable_offset) >> 1;
+ }
+ jump_inst = (sljit_u16*)jump->addr;
+
+ switch (type) {
+ case 1:
+ /* Encoding T1 of 'B' instruction */
+ SLJIT_ASSERT(diff <= 127 && diff >= -128 && (jump->flags & IS_COND));
+ jump_inst[0] = (sljit_u16)(0xd000 | (jump->flags & 0xf00) | ((sljit_ins)diff & 0xff));
+ return;
+ case 2:
+ /* Encoding T3 of 'B' instruction */
+ SLJIT_ASSERT(diff <= 524287 && diff >= -524288 && (jump->flags & IS_COND));
+ jump_inst[0] = (sljit_u16)(0xf000 | COPY_BITS(jump->flags, 8, 6, 4) | COPY_BITS(diff, 11, 0, 6) | COPY_BITS(diff, 19, 10, 1));
+ jump_inst[1] = (sljit_u16)(0x8000 | COPY_BITS(diff, 17, 13, 1) | COPY_BITS(diff, 18, 11, 1) | ((sljit_ins)diff & 0x7ff));
+ return;
+ case 3:
+ SLJIT_ASSERT(jump->flags & IS_COND);
+ *jump_inst++ = (sljit_u16)(IT | ((jump->flags >> 4) & 0xf0) | 0x8);
+ diff--;
+ type = 5;
+ break;
+ case 4:
+ /* Encoding T2 of 'B' instruction */
+ SLJIT_ASSERT(diff <= 1023 && diff >= -1024 && !(jump->flags & IS_COND));
+ jump_inst[0] = (sljit_u16)(0xe000 | (diff & 0x7ff));
+ return;
+ }
+
+ SLJIT_ASSERT(diff <= 8388607 && diff >= -8388608);
+
+ /* Really complex instruction form for branches. */
+ s = (diff >> 23) & 0x1;
+ j1 = (~(diff >> 22) ^ s) & 0x1;
+ j2 = (~(diff >> 21) ^ s) & 0x1;
+ jump_inst[0] = (sljit_u16)(0xf000 | ((sljit_ins)s << 10) | COPY_BITS(diff, 11, 0, 10));
+ jump_inst[1] = (sljit_u16)((j1 << 13) | (j2 << 11) | (diff & 0x7ff));
+
+ /* The others have a common form. */
+ if (type == 5) /* Encoding T4 of 'B' instruction */
+ jump_inst[1] |= 0x9000;
+ else if (type == 6) /* Encoding T1 of 'BL' instruction */
+ jump_inst[1] |= 0xd000;
+ else
+ SLJIT_UNREACHABLE();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf;
+ sljit_u16 *code;
+ sljit_u16 *code_ptr;
+ sljit_u16 *buf_ptr;
+ sljit_u16 *buf_end;
+ sljit_uw half_count;
+ sljit_uw next_addr;
+ sljit_sw executable_offset;
+
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ struct sljit_const *const_;
+ struct sljit_put_label *put_label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_generate_code(compiler));
+ reverse_buf(compiler);
+
+ code = (sljit_u16*)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_u16), compiler->exec_allocator_data);
+ PTR_FAIL_WITH_EXEC_IF(code);
+ buf = compiler->buf;
+
+ code_ptr = code;
+ half_count = 0;
+ next_addr = 0;
+ executable_offset = SLJIT_EXEC_OFFSET(code);
+
+ label = compiler->labels;
+ jump = compiler->jumps;
+ const_ = compiler->consts;
+ put_label = compiler->put_labels;
+
+ do {
+ buf_ptr = (sljit_u16*)buf->memory;
+ buf_end = buf_ptr + (buf->used_size >> 1);
+ do {
+ *code_ptr = *buf_ptr++;
+ if (next_addr == half_count) {
+ SLJIT_ASSERT(!label || label->size >= half_count);
+ SLJIT_ASSERT(!jump || jump->addr >= half_count);
+ SLJIT_ASSERT(!const_ || const_->addr >= half_count);
+ SLJIT_ASSERT(!put_label || put_label->addr >= half_count);
+
+ /* These structures are ordered by their address. */
+ if (label && label->size == half_count) {
+ label->addr = ((sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset)) | 0x1;
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+ if (jump && jump->addr == half_count) {
+ jump->addr = (sljit_uw)code_ptr - ((jump->flags & IS_COND) ? 10 : 8);
+ code_ptr -= detect_jump_type(jump, code_ptr, code, executable_offset);
+ jump = jump->next;
+ }
+ if (const_ && const_->addr == half_count) {
+ const_->addr = (sljit_uw)code_ptr;
+ const_ = const_->next;
+ }
+ if (put_label && put_label->addr == half_count) {
+ SLJIT_ASSERT(put_label->label);
+ put_label->addr = (sljit_uw)code_ptr;
+ put_label = put_label->next;
+ }
+ next_addr = compute_next_addr(label, jump, const_, put_label);
+ }
+ code_ptr++;
+ half_count++;
+ } while (buf_ptr < buf_end);
+
+ buf = buf->next;
+ } while (buf);
+
+ if (label && label->size == half_count) {
+ label->addr = ((sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset)) | 0x1;
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+
+ SLJIT_ASSERT(!label);
+ SLJIT_ASSERT(!jump);
+ SLJIT_ASSERT(!const_);
+ SLJIT_ASSERT(!put_label);
+ SLJIT_ASSERT(code_ptr - code <= (sljit_sw)compiler->size);
+
+ jump = compiler->jumps;
+ while (jump) {
+ set_jump_instruction(jump, executable_offset);
+ jump = jump->next;
+ }
+
+ put_label = compiler->put_labels;
+ while (put_label) {
+ modify_imm32_const((sljit_u16 *)put_label->addr, put_label->label->addr);
+ put_label = put_label->next;
+ }
+
+ compiler->error = SLJIT_ERR_COMPILED;
+ compiler->executable_offset = executable_offset;
+ compiler->executable_size = (sljit_uw)(code_ptr - code) * sizeof(sljit_u16);
+
+ code = (sljit_u16 *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+ code_ptr = (sljit_u16 *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+
+ SLJIT_CACHE_FLUSH(code, code_ptr);
+ SLJIT_UPDATE_WX_FLAGS(code, code_ptr, 1);
+
+ /* Set thumb mode flag. */
+ return (void*)((sljit_uw)code | 0x1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#else
+ /* Available by default. */
+ return 1;
+#endif
+
+ case SLJIT_HAS_CLZ:
+ case SLJIT_HAS_CTZ:
+ case SLJIT_HAS_ROT:
+ case SLJIT_HAS_CMOV:
+ case SLJIT_HAS_PREFETCH:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+/* Core code generator functions. */
+/* --------------------------------------------------------------------- */
+
+#define INVALID_IMM 0x80000000
+static sljit_uw get_imm(sljit_uw imm)
+{
+ /* Thumb immediate form. */
+ sljit_s32 counter;
+
+ if (imm <= 0xff)
+ return imm;
+
+ if ((imm & 0xffff) == (imm >> 16)) {
+ /* Some special cases. */
+ if (!(imm & 0xff00))
+ return (1 << 12) | (imm & 0xff);
+ if (!(imm & 0xff))
+ return (2 << 12) | ((imm >> 8) & 0xff);
+ if ((imm & 0xff00) == ((imm & 0xff) << 8))
+ return (3 << 12) | (imm & 0xff);
+ }
+
+ /* Assembly optimization: count leading zeroes? */
+ counter = 8;
+ if (!(imm & 0xffff0000)) {
+ counter += 16;
+ imm <<= 16;
+ }
+ if (!(imm & 0xff000000)) {
+ counter += 8;
+ imm <<= 8;
+ }
+ if (!(imm & 0xf0000000)) {
+ counter += 4;
+ imm <<= 4;
+ }
+ if (!(imm & 0xc0000000)) {
+ counter += 2;
+ imm <<= 2;
+ }
+ if (!(imm & 0x80000000)) {
+ counter += 1;
+ imm <<= 1;
+ }
+ /* Since imm >= 128, this must be true. */
+ SLJIT_ASSERT(counter <= 31);
+
+ if (imm & 0x00ffffff)
+ return INVALID_IMM; /* Cannot be encoded. */
+
+ return ((imm >> 24) & 0x7f) | COPY_BITS(counter, 4, 26, 1) | COPY_BITS(counter, 1, 12, 3) | COPY_BITS(counter, 0, 7, 1);
+}
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst, sljit_uw imm)
+{
+ sljit_uw tmp;
+
+ /* MOVS cannot be used since it destroy flags. */
+
+ if (imm >= 0x10000) {
+ tmp = get_imm(imm);
+ if (tmp != INVALID_IMM)
+ return push_inst32(compiler, MOV_WI | RD4(dst) | tmp);
+ tmp = get_imm(~imm);
+ if (tmp != INVALID_IMM)
+ return push_inst32(compiler, MVN_WI | RD4(dst) | tmp);
+ }
+
+ /* set low 16 bits, set hi 16 bits to 0. */
+ FAIL_IF(push_inst32(compiler, MOVW | RD4(dst)
+ | COPY_BITS(imm, 12, 16, 4) | COPY_BITS(imm, 11, 26, 1) | COPY_BITS(imm, 8, 12, 3) | (imm & 0xff)));
+
+ /* set hi 16 bit if needed. */
+ if (imm >= 0x10000)
+ return push_inst32(compiler, MOVT | RD4(dst)
+ | COPY_BITS(imm, 12 + 16, 16, 4) | COPY_BITS(imm, 11 + 16, 26, 1) | COPY_BITS(imm, 8 + 16, 12, 3) | ((imm & 0xff0000) >> 16));
+ return SLJIT_SUCCESS;
+}
+
+#define ARG1_IMM 0x0010000
+#define ARG2_IMM 0x0020000
+/* SET_FLAGS must be 0x100000 as it is also the value of S bit (can be used for optimization). */
+#define SET_FLAGS 0x0100000
+#define UNUSED_RETURN 0x0200000
+
+static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 dst, sljit_uw arg1, sljit_uw arg2)
+{
+ /* dst must be register, TMP_REG1
+ arg1 must be register, imm
+ arg2 must be register, imm */
+ sljit_s32 reg;
+ sljit_uw imm, imm2;
+
+ if (SLJIT_UNLIKELY((flags & (ARG1_IMM | ARG2_IMM)) == (ARG1_IMM | ARG2_IMM))) {
+ /* Both are immediates, no temporaries are used. */
+ flags &= ~ARG1_IMM;
+ FAIL_IF(load_immediate(compiler, TMP_REG1, arg1));
+ arg1 = TMP_REG1;
+ }
+
+ if (flags & (ARG1_IMM | ARG2_IMM)) {
+ reg = (sljit_s32)((flags & ARG2_IMM) ? arg1 : arg2);
+ imm = (flags & ARG2_IMM) ? arg2 : arg1;
+
+ switch (flags & 0xffff) {
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ case SLJIT_MUL:
+ /* No form with immediate operand. */
+ break;
+ case SLJIT_MOV:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && (flags & ARG2_IMM) && arg1 == TMP_REG2);
+ return load_immediate(compiler, dst, imm);
+ case SLJIT_NOT:
+ if (!(flags & SET_FLAGS))
+ return load_immediate(compiler, dst, ~imm);
+ /* Since the flags should be set, we just fallback to the register mode.
+ Although some clever things could be done here, "NOT IMM" does not worth the efforts. */
+ break;
+ case SLJIT_ADD:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ imm2 = NEGATE(imm);
+ if (IS_2_LO_REGS(reg, dst)) {
+ if (imm <= 0x7)
+ return push_inst16(compiler, ADDSI3 | IMM3(imm) | RD3(dst) | RN3(reg));
+ if (imm2 <= 0x7)
+ return push_inst16(compiler, SUBSI3 | IMM3(imm2) | RD3(dst) | RN3(reg));
+ if (reg == dst) {
+ if (imm <= 0xff)
+ return push_inst16(compiler, ADDSI8 | IMM8(imm) | RDN3(dst));
+ if (imm2 <= 0xff)
+ return push_inst16(compiler, SUBSI8 | IMM8(imm2) | RDN3(dst));
+ }
+ }
+ if (!(flags & SET_FLAGS)) {
+ if (imm <= 0xfff)
+ return push_inst32(compiler, ADDWI | RD4(dst) | RN4(reg) | IMM12(imm));
+ if (imm2 <= 0xfff)
+ return push_inst32(compiler, SUBWI | RD4(dst) | RN4(reg) | IMM12(imm2));
+ }
+ imm2 = get_imm(imm);
+ if (imm2 != INVALID_IMM)
+ return push_inst32(compiler, ADD_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm2);
+ imm = get_imm(NEGATE(imm));
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, SUB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ break;
+ case SLJIT_ADDC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ imm = get_imm(imm);
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, ADCI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ break;
+ case SLJIT_SUB:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ if (flags & ARG1_IMM) {
+ if (imm == 0 && IS_2_LO_REGS(reg, dst))
+ return push_inst16(compiler, RSBSI | RD3(dst) | RN3(reg));
+ imm = get_imm(imm);
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, RSB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ break;
+ }
+ if (flags & UNUSED_RETURN) {
+ if (imm <= 0xff && reg_map[reg] <= 7)
+ return push_inst16(compiler, CMPI | IMM8(imm) | RDN3(reg));
+ imm2 = get_imm(imm);
+ if (imm2 != INVALID_IMM)
+ return push_inst32(compiler, CMPI_W | RN4(reg) | imm2);
+ imm = get_imm(NEGATE(imm));
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, CMNI_W | RN4(reg) | imm);
+ break;
+ }
+ imm2 = NEGATE(imm);
+ if (IS_2_LO_REGS(reg, dst)) {
+ if (imm <= 0x7)
+ return push_inst16(compiler, SUBSI3 | IMM3(imm) | RD3(dst) | RN3(reg));
+ if (imm2 <= 0x7)
+ return push_inst16(compiler, ADDSI3 | IMM3(imm2) | RD3(dst) | RN3(reg));
+ if (reg == dst) {
+ if (imm <= 0xff)
+ return push_inst16(compiler, SUBSI8 | IMM8(imm) | RDN3(dst));
+ if (imm2 <= 0xff)
+ return push_inst16(compiler, ADDSI8 | IMM8(imm2) | RDN3(dst));
+ }
+ }
+ if (!(flags & SET_FLAGS)) {
+ if (imm <= 0xfff)
+ return push_inst32(compiler, SUBWI | RD4(dst) | RN4(reg) | IMM12(imm));
+ if (imm2 <= 0xfff)
+ return push_inst32(compiler, ADDWI | RD4(dst) | RN4(reg) | IMM12(imm2));
+ }
+ imm2 = get_imm(imm);
+ if (imm2 != INVALID_IMM)
+ return push_inst32(compiler, SUB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm2);
+ imm = get_imm(NEGATE(imm));
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, ADD_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ break;
+ case SLJIT_SUBC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ if (flags & ARG1_IMM)
+ break;
+ imm = get_imm(imm);
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, SBCI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ break;
+ case SLJIT_AND:
+ imm2 = get_imm(imm);
+ if (imm2 != INVALID_IMM)
+ return push_inst32(compiler, ((flags & UNUSED_RETURN) ? TSTI : ANDI) | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm2);
+ imm = get_imm(~imm);
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, BICI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ break;
+ case SLJIT_OR:
+ imm2 = get_imm(imm);
+ if (imm2 != INVALID_IMM)
+ return push_inst32(compiler, ORRI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm2);
+ imm = get_imm(~imm);
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, ORNI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ break;
+ case SLJIT_XOR:
+ imm = get_imm(imm);
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, EORI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ break;
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ if (flags & ARG1_IMM)
+ break;
+ imm &= 0x1f;
+
+ if (imm == 0) {
+ if (!(flags & SET_FLAGS))
+ return push_inst16(compiler, MOV | SET_REGS44(dst, reg));
+ if (IS_2_LO_REGS(dst, reg))
+ return push_inst16(compiler, MOVS | RD3(dst) | RN3(reg));
+ return push_inst32(compiler, MOV_W | SET_FLAGS | RD4(dst) | RM4(reg));
+ }
+
+ switch (flags & 0xffff) {
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ if (IS_2_LO_REGS(dst, reg))
+ return push_inst16(compiler, LSLSI | RD3(dst) | RN3(reg) | (imm << 6));
+ return push_inst32(compiler, LSL_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm));
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ if (IS_2_LO_REGS(dst, reg))
+ return push_inst16(compiler, LSRSI | RD3(dst) | RN3(reg) | (imm << 6));
+ return push_inst32(compiler, LSR_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm));
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ if (IS_2_LO_REGS(dst, reg))
+ return push_inst16(compiler, ASRSI | RD3(dst) | RN3(reg) | (imm << 6));
+ return push_inst32(compiler, ASR_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm));
+ case SLJIT_ROTL:
+ imm = (imm ^ 0x1f) + 1;
+ /* fallthrough */
+ default: /* SLJIT_ROTR */
+ return push_inst32(compiler, ROR_WI | RD4(dst) | RM4(reg) | IMM5(imm));
+ }
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+ if (flags & ARG2_IMM) {
+ imm = arg2;
+ arg2 = (arg1 == TMP_REG1) ? TMP_REG2 : TMP_REG1;
+ FAIL_IF(load_immediate(compiler, (sljit_s32)arg2, imm));
+ }
+ else {
+ imm = arg1;
+ arg1 = (arg2 == TMP_REG1) ? TMP_REG2 : TMP_REG1;
+ FAIL_IF(load_immediate(compiler, (sljit_s32)arg1, imm));
+ }
+
+ SLJIT_ASSERT(arg1 != arg2);
+ }
+
+ /* Both arguments are registers. */
+ switch (flags & 0xffff) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ case SLJIT_MOV_P:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2);
+ if (dst == (sljit_s32)arg2)
+ return SLJIT_SUCCESS;
+ return push_inst16(compiler, MOV | SET_REGS44(dst, arg2));
+ case SLJIT_MOV_U8:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2);
+ if (IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, UXTB | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, UXTB_W | RD4(dst) | RM4(arg2));
+ case SLJIT_MOV_S8:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2);
+ if (IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, SXTB | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, SXTB_W | RD4(dst) | RM4(arg2));
+ case SLJIT_MOV_U16:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2);
+ if (IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, UXTH | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, UXTH_W | RD4(dst) | RM4(arg2));
+ case SLJIT_MOV_S16:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2);
+ if (IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, SXTH | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, SXTH_W | RD4(dst) | RM4(arg2));
+ case SLJIT_NOT:
+ SLJIT_ASSERT(arg1 == TMP_REG2);
+ if (IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, MVNS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, MVN_W | (flags & SET_FLAGS) | RD4(dst) | RM4(arg2));
+ case SLJIT_CLZ:
+ SLJIT_ASSERT(arg1 == TMP_REG2);
+ return push_inst32(compiler, CLZ | RN4(arg2) | RD4(dst) | RM4(arg2));
+ case SLJIT_CTZ:
+ SLJIT_ASSERT(arg1 == TMP_REG2);
+ FAIL_IF(push_inst32(compiler, RBIT | RN4(arg2) | RD4(dst) | RM4(arg2)));
+ return push_inst32(compiler, CLZ | RN4(dst) | RD4(dst) | RM4(dst));
+ case SLJIT_ADD:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ if (IS_3_LO_REGS(dst, arg1, arg2))
+ return push_inst16(compiler, ADDS | RD3(dst) | RN3(arg1) | RM3(arg2));
+ if (dst == (sljit_s32)arg1 && !(flags & SET_FLAGS))
+ return push_inst16(compiler, ADD | SET_REGS44(dst, arg2));
+ return push_inst32(compiler, ADD_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_ADDC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, ADCS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, ADC_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_SUB:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ if (flags & UNUSED_RETURN) {
+ if (IS_2_LO_REGS(arg1, arg2))
+ return push_inst16(compiler, CMP | RD3(arg1) | RN3(arg2));
+ return push_inst16(compiler, CMP_X | SET_REGS44(arg1, arg2));
+ }
+ if (IS_3_LO_REGS(dst, arg1, arg2))
+ return push_inst16(compiler, SUBS | RD3(dst) | RN3(arg1) | RM3(arg2));
+ return push_inst32(compiler, SUB_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_SUBC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, SBCS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, SBC_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_MUL:
+ compiler->status_flags_state = 0;
+ if (!(flags & SET_FLAGS))
+ return push_inst32(compiler, MUL | RD4(dst) | RN4(arg1) | RM4(arg2));
+ SLJIT_ASSERT(dst != TMP_REG2);
+ FAIL_IF(push_inst32(compiler, SMULL | RT4(dst) | RD4(TMP_REG2) | RN4(arg1) | RM4(arg2)));
+ /* cmp TMP_REG2, dst asr #31. */
+ return push_inst32(compiler, CMP_W | RN4(TMP_REG2) | 0x70e0 | RM4(dst));
+ case SLJIT_AND:
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, ANDS | RD3(dst) | RN3(arg2));
+ if ((flags & UNUSED_RETURN) && IS_2_LO_REGS(arg1, arg2))
+ return push_inst16(compiler, TST | RD3(arg1) | RN3(arg2));
+ return push_inst32(compiler, ((flags & UNUSED_RETURN) ? TST_W : AND_W) | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_OR:
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, ORRS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, ORR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_XOR:
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, EORS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, EOR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_MSHL:
+ FAIL_IF(push_inst32(compiler, ANDI | RD4(TMP_REG2) | RN4(arg2) | 0x1f));
+ arg2 = TMP_REG2;
+ /* fallthrough */
+ case SLJIT_SHL:
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, LSLS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, LSL_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_MLSHR:
+ FAIL_IF(push_inst32(compiler, ANDI | RD4(TMP_REG2) | RN4(arg2) | 0x1f));
+ arg2 = TMP_REG2;
+ /* fallthrough */
+ case SLJIT_LSHR:
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, LSRS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, LSR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_MASHR:
+ FAIL_IF(push_inst32(compiler, ANDI | RD4(TMP_REG2) | RN4(arg2) | 0x1f));
+ arg2 = TMP_REG2;
+ /* fallthrough */
+ case SLJIT_ASHR:
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, ASRS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, ASR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_ROTL:
+ FAIL_IF(push_inst32(compiler, RSB_WI | RD4(TMP_REG2) | RN4(arg2) | 0));
+ arg2 = TMP_REG2;
+ /* fallthrough */
+ case SLJIT_ROTR:
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, RORS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, ROR_W | RD4(dst) | RN4(arg1) | RM4(arg2));
+ }
+
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+}
+
+#define STORE 0x01
+#define SIGNED 0x02
+
+#define WORD_SIZE 0x00
+#define BYTE_SIZE 0x04
+#define HALF_SIZE 0x08
+#define PRELOAD 0x0c
+
+#define IS_WORD_SIZE(flags) (!((flags) & (BYTE_SIZE | HALF_SIZE)))
+#define ALIGN_CHECK(argw, imm, shift) (!((argw) & ~((imm) << (shift))))
+
+/*
+ 1st letter:
+ w = word
+ b = byte
+ h = half
+
+ 2nd letter:
+ s = signed
+ u = unsigned
+
+ 3rd letter:
+ l = load
+ s = store
+*/
+
+static const sljit_ins sljit_mem16[12] = {
+/* w u l */ 0x5800 /* ldr */,
+/* w u s */ 0x5000 /* str */,
+/* w s l */ 0x5800 /* ldr */,
+/* w s s */ 0x5000 /* str */,
+
+/* b u l */ 0x5c00 /* ldrb */,
+/* b u s */ 0x5400 /* strb */,
+/* b s l */ 0x5600 /* ldrsb */,
+/* b s s */ 0x5400 /* strb */,
+
+/* h u l */ 0x5a00 /* ldrh */,
+/* h u s */ 0x5200 /* strh */,
+/* h s l */ 0x5e00 /* ldrsh */,
+/* h s s */ 0x5200 /* strh */,
+};
+
+static const sljit_ins sljit_mem16_imm5[12] = {
+/* w u l */ 0x6800 /* ldr imm5 */,
+/* w u s */ 0x6000 /* str imm5 */,
+/* w s l */ 0x6800 /* ldr imm5 */,
+/* w s s */ 0x6000 /* str imm5 */,
+
+/* b u l */ 0x7800 /* ldrb imm5 */,
+/* b u s */ 0x7000 /* strb imm5 */,
+/* b s l */ 0x0000 /* not allowed */,
+/* b s s */ 0x7000 /* strb imm5 */,
+
+/* h u l */ 0x8800 /* ldrh imm5 */,
+/* h u s */ 0x8000 /* strh imm5 */,
+/* h s l */ 0x0000 /* not allowed */,
+/* h s s */ 0x8000 /* strh imm5 */,
+};
+
+#define MEM_IMM8 0xc00
+#define MEM_IMM12 0x800000
+static const sljit_ins sljit_mem32[13] = {
+/* w u l */ 0xf8500000 /* ldr.w */,
+/* w u s */ 0xf8400000 /* str.w */,
+/* w s l */ 0xf8500000 /* ldr.w */,
+/* w s s */ 0xf8400000 /* str.w */,
+
+/* b u l */ 0xf8100000 /* ldrb.w */,
+/* b u s */ 0xf8000000 /* strb.w */,
+/* b s l */ 0xf9100000 /* ldrsb.w */,
+/* b s s */ 0xf8000000 /* strb.w */,
+
+/* h u l */ 0xf8300000 /* ldrh.w */,
+/* h u s */ 0xf8200000 /* strsh.w */,
+/* h s l */ 0xf9300000 /* ldrsh.w */,
+/* h s s */ 0xf8200000 /* strsh.w */,
+
+/* p u l */ 0xf8100000 /* pld */,
+};
+
+/* Helper function. Dst should be reg + value, using at most 1 instruction, flags does not set. */
+static sljit_s32 emit_set_delta(struct sljit_compiler *compiler, sljit_s32 dst, sljit_s32 reg, sljit_sw value)
+{
+ sljit_uw imm;
+
+ if (value >= 0) {
+ if (value <= 0xfff)
+ return push_inst32(compiler, ADDWI | RD4(dst) | RN4(reg) | IMM12(value));
+ imm = get_imm((sljit_uw)value);
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, ADD_WI | RD4(dst) | RN4(reg) | imm);
+ }
+ else {
+ value = -value;
+ if (value <= 0xfff)
+ return push_inst32(compiler, SUBWI | RD4(dst) | RN4(reg) | IMM12(value));
+ imm = get_imm((sljit_uw)value);
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, SUB_WI | RD4(dst) | RN4(reg) | imm);
+ }
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg,
+ sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg)
+{
+ sljit_s32 other_r;
+ sljit_uw imm, tmp;
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+ SLJIT_ASSERT((arg & REG_MASK) != tmp_reg || (arg == SLJIT_MEM1(tmp_reg) && argw >= -0xff && argw <= 0xfff));
+
+ if (SLJIT_UNLIKELY(!(arg & REG_MASK))) {
+ imm = get_imm((sljit_uw)argw & ~(sljit_uw)0xfff);
+ if (imm != INVALID_IMM) {
+ FAIL_IF(push_inst32(compiler, MOV_WI | RD4(tmp_reg) | imm));
+ return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(tmp_reg) | (argw & 0xfff));
+ }
+
+ FAIL_IF(load_immediate(compiler, tmp_reg, (sljit_uw)argw));
+ if (IS_2_LO_REGS(reg, tmp_reg) && sljit_mem16_imm5[flags])
+ return push_inst16(compiler, sljit_mem16_imm5[flags] | RD3(reg) | RN3(tmp_reg));
+ return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(tmp_reg));
+ }
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ argw &= 0x3;
+ other_r = OFFS_REG(arg);
+ arg &= REG_MASK;
+
+ if (!argw && IS_3_LO_REGS(reg, arg, other_r))
+ return push_inst16(compiler, sljit_mem16[flags] | RD3(reg) | RN3(arg) | RM3(other_r));
+ return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(other_r) | ((sljit_ins)argw << 4));
+ }
+
+ arg &= REG_MASK;
+
+ if (argw > 0xfff) {
+ imm = get_imm((sljit_uw)(argw & ~0xfff));
+ if (imm != INVALID_IMM) {
+ push_inst32(compiler, ADD_WI | RD4(tmp_reg) | RN4(arg) | imm);
+ arg = tmp_reg;
+ argw = argw & 0xfff;
+ }
+ }
+ else if (argw < -0xff) {
+ tmp = (sljit_uw)((-argw + 0xfff) & ~0xfff);
+ SLJIT_ASSERT(tmp >= (sljit_uw)-argw);
+ imm = get_imm(tmp);
+
+ if (imm != INVALID_IMM) {
+ push_inst32(compiler, SUB_WI | RD4(tmp_reg) | RN4(arg) | imm);
+ arg = tmp_reg;
+ argw += (sljit_sw)tmp;
+
+ SLJIT_ASSERT(argw >= 0 && argw <= 0xfff);
+ }
+ }
+
+ /* 16 bit instruction forms. */
+ if (IS_2_LO_REGS(reg, arg) && sljit_mem16_imm5[flags]) {
+ tmp = 3;
+ if (IS_WORD_SIZE(flags)) {
+ if (ALIGN_CHECK(argw, 0x1f, 2))
+ tmp = 2;
+ }
+ else if (flags & BYTE_SIZE)
+ {
+ if (ALIGN_CHECK(argw, 0x1f, 0))
+ tmp = 0;
+ }
+ else {
+ SLJIT_ASSERT(flags & HALF_SIZE);
+ if (ALIGN_CHECK(argw, 0x1f, 1))
+ tmp = 1;
+ }
+
+ if (tmp < 3)
+ return push_inst16(compiler, sljit_mem16_imm5[flags] | RD3(reg) | RN3(arg) | ((sljit_ins)argw << (6 - tmp)));
+ }
+ else if (SLJIT_UNLIKELY(arg == SLJIT_SP) && IS_WORD_SIZE(flags) && ALIGN_CHECK(argw, 0xff, 2) && reg_map[reg] <= 7) {
+ /* SP based immediate. */
+ return push_inst16(compiler, STR_SP | (sljit_ins)((flags & STORE) ? 0 : 0x800) | RDN3(reg) | ((sljit_ins)argw >> 2));
+ }
+
+ if (argw >= 0 && argw <= 0xfff)
+ return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(arg) | (sljit_ins)argw);
+ else if (argw < 0 && argw >= -0xff)
+ return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM8 | RT4(reg) | RN4(arg) | (sljit_ins)-argw);
+
+ SLJIT_ASSERT(arg != tmp_reg);
+
+ FAIL_IF(load_immediate(compiler, tmp_reg, (sljit_uw)argw));
+ if (IS_3_LO_REGS(reg, arg, tmp_reg))
+ return push_inst16(compiler, sljit_mem16[flags] | RD3(reg) | RN3(arg) | RM3(tmp_reg));
+ return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(tmp_reg));
+}
+
+#undef ALIGN_CHECK
+#undef IS_WORD_SIZE
+
+/* --------------------------------------------------------------------- */
+/* Entry, exit */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 size, i, tmp, word_arg_count;
+ sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+ sljit_uw offset;
+ sljit_uw imm = 0;
+#ifdef __SOFTFP__
+ sljit_u32 float_arg_count;
+#else
+ sljit_u32 old_offset, f32_offset;
+ sljit_u32 remap[3];
+ sljit_u32 *remap_ptr = remap;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ tmp = SLJIT_S0 - saveds;
+ for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--)
+ imm |= (sljit_uw)1 << reg_map[i];
+
+ for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--)
+ imm |= (sljit_uw)1 << reg_map[i];
+
+ /* At least two registers must be set for PUSH_W and one for PUSH instruction. */
+ FAIL_IF((imm & 0xff00)
+ ? push_inst32(compiler, PUSH_W | (1 << 14) | imm)
+ : push_inst16(compiler, PUSH | (1 << 8) | imm));
+
+ /* Stack must be aligned to 8 bytes: (LR, R4) */
+ size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 1);
+
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if ((size & SSIZE_OF(sw)) != 0) {
+ FAIL_IF(push_inst16(compiler, SUB_SP_I | (sizeof(sljit_sw) >> 2)));
+ size += SSIZE_OF(sw);
+ }
+
+ if (fsaveds + fscratches >= SLJIT_NUMBER_OF_FLOAT_REGISTERS) {
+ FAIL_IF(push_inst32(compiler, VPUSH | DD4(SLJIT_FS0) | ((sljit_uw)SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS << 1)));
+ } else {
+ if (fsaveds > 0)
+ FAIL_IF(push_inst32(compiler, VPUSH | DD4(SLJIT_FS0) | ((sljit_uw)fsaveds << 1)));
+ if (fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG)
+ FAIL_IF(push_inst32(compiler, VPUSH | DD4(fscratches) | ((sljit_uw)(fscratches - (SLJIT_FIRST_SAVED_FLOAT_REG - 1)) << 1)));
+ }
+ }
+
+ local_size = ((size + local_size + 0x7) & ~0x7) - size;
+ compiler->local_size = local_size;
+
+ if (options & SLJIT_ENTER_REG_ARG)
+ arg_types = 0;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ word_arg_count = 0;
+ saved_arg_count = 0;
+#ifdef __SOFTFP__
+ SLJIT_COMPILE_ASSERT(SLJIT_FR0 == 1, float_register_index_start);
+
+ offset = 0;
+ float_arg_count = 0;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset & 0x7)
+ offset += sizeof(sljit_sw);
+
+ if (offset < 4 * sizeof(sljit_sw))
+ FAIL_IF(push_inst32(compiler, VMOV2 | (offset << 10) | ((offset + sizeof(sljit_sw)) << 14) | float_arg_count));
+ else
+ FAIL_IF(push_inst32(compiler, VLDR_F32 | 0x800100 | RN4(SLJIT_SP)
+ | (float_arg_count << 12) | ((offset + (sljit_uw)size - 4 * sizeof(sljit_sw)) >> 2)));
+ float_arg_count++;
+ offset += sizeof(sljit_f64) - sizeof(sljit_sw);
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (offset < 4 * sizeof(sljit_sw))
+ FAIL_IF(push_inst32(compiler, VMOV | (float_arg_count << 16) | (offset << 10)));
+ else
+ FAIL_IF(push_inst32(compiler, VLDR_F32 | 0x800000 | RN4(SLJIT_SP)
+ | (float_arg_count << 12) | ((offset + (sljit_uw)size - 4 * sizeof(sljit_sw)) >> 2)));
+ float_arg_count++;
+ break;
+ default:
+ word_arg_count++;
+
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ tmp = SLJIT_S0 - saved_arg_count;
+ saved_arg_count++;
+ } else if (word_arg_count - 1 != (sljit_s32)(offset >> 2))
+ tmp = word_arg_count;
+ else
+ break;
+
+ if (offset < 4 * sizeof(sljit_sw))
+ FAIL_IF(push_inst16(compiler, MOV | ((sljit_ins)reg_map[tmp] & 0x7) | (((sljit_ins)reg_map[tmp] & 0x8) << 4) | (offset << 1)));
+ else if (reg_map[tmp] <= 7)
+ FAIL_IF(push_inst16(compiler, LDR_SP | RDN3(tmp)
+ | ((offset + (sljit_uw)size - 4 * sizeof(sljit_sw)) >> 2)));
+ else
+ FAIL_IF(push_inst32(compiler, LDR | RT4(tmp) | RN4(SLJIT_SP)
+ | ((offset + (sljit_uw)size - 4 * sizeof(sljit_sw)))));
+ break;
+ }
+
+ offset += sizeof(sljit_sw);
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ compiler->args_size = offset;
+#else
+ offset = SLJIT_FR0;
+ old_offset = SLJIT_FR0;
+ f32_offset = 0;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset != old_offset)
+ *remap_ptr++ = VMOV_F32 | SLJIT_32 | DD4(offset) | DM4(old_offset);
+ old_offset++;
+ offset++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (f32_offset != 0) {
+ *remap_ptr++ = VMOV_F32 | 0x20 | DD4(offset) | DM4(f32_offset);
+ f32_offset = 0;
+ } else {
+ if (offset != old_offset)
+ *remap_ptr++ = VMOV_F32 | DD4(offset) | DM4(old_offset);
+ f32_offset = old_offset;
+ old_offset++;
+ }
+ offset++;
+ break;
+ default:
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(SLJIT_S0 - saved_arg_count, SLJIT_R0 + word_arg_count)));
+ saved_arg_count++;
+ }
+
+ word_arg_count++;
+ break;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ SLJIT_ASSERT((sljit_uw)(remap_ptr - remap) <= sizeof(remap));
+
+ while (remap_ptr > remap)
+ FAIL_IF(push_inst32(compiler, *(--remap_ptr)));
+#endif
+
+#ifdef _WIN32
+ if (local_size >= 4096) {
+ imm = get_imm(4096);
+ SLJIT_ASSERT(imm != INVALID_IMM);
+
+ FAIL_IF(push_inst32(compiler, SUB_WI | RD4(SLJIT_SP) | RN4(SLJIT_SP) | imm));
+
+ if (local_size < 4 * 4096) {
+ if (local_size > 2 * 4096) {
+ if (local_size > 3 * 4096) {
+ FAIL_IF(push_inst32(compiler, LDRI | 0x400 | RT4(TMP_REG1) | RN4(SLJIT_SP)));
+ FAIL_IF(push_inst32(compiler, SUB_WI | RD4(SLJIT_SP) | RN4(SLJIT_SP) | imm));
+ }
+
+ FAIL_IF(push_inst32(compiler, LDRI | 0x400 | RT4(TMP_REG1) | RN4(SLJIT_SP)));
+ FAIL_IF(push_inst32(compiler, SUB_WI | RD4(SLJIT_SP) | RN4(SLJIT_SP) | imm));
+ }
+ } else {
+ FAIL_IF(load_immediate(compiler, TMP_REG2, ((sljit_uw)local_size >> 12) - 1));
+ FAIL_IF(push_inst32(compiler, LDRI | 0x400 | RT4(TMP_REG1) | RN4(SLJIT_SP)));
+ FAIL_IF(push_inst32(compiler, SUB_WI | RD4(SLJIT_SP) | RN4(SLJIT_SP) | imm));
+ FAIL_IF(push_inst32(compiler, SUB_WI | SET_FLAGS | RD4(TMP_REG2) | RN4(TMP_REG2) | 1));
+ FAIL_IF(push_inst16(compiler, BCC | (0x1 << 8) /* not-equal */ | (-8 & 0xff)));
+ }
+
+ FAIL_IF(push_inst32(compiler, LDRI | 0x400 | RT4(TMP_REG1) | RN4(SLJIT_SP)));
+ local_size &= 0xfff;
+ }
+
+ if (local_size >= 256) {
+ SLJIT_ASSERT(local_size < 4096);
+
+ if (local_size <= (127 << 2))
+ FAIL_IF(push_inst16(compiler, SUB_SP_I | ((sljit_uw)local_size >> 2)));
+ else
+ FAIL_IF(emit_op_imm(compiler, SLJIT_SUB | ARG2_IMM, SLJIT_SP, SLJIT_SP, (sljit_uw)local_size));
+
+ FAIL_IF(push_inst32(compiler, LDRI | 0x400 | RT4(TMP_REG1) | RN4(SLJIT_SP)));
+ } else if (local_size > 0)
+ FAIL_IF(push_inst32(compiler, LDRI | 0x500 | RT4(TMP_REG1) | RN4(SLJIT_SP) | (sljit_uw)local_size));
+#else /* !_WIN32 */
+ if (local_size > 0) {
+ if (local_size <= (127 << 2))
+ FAIL_IF(push_inst16(compiler, SUB_SP_I | ((sljit_uw)local_size >> 2)));
+ else
+ FAIL_IF(emit_op_imm(compiler, SLJIT_SUB | ARG2_IMM, SLJIT_SP, SLJIT_SP, (sljit_uw)local_size));
+ }
+#endif /* _WIN32 */
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 size;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 1);
+
+ if ((size & SSIZE_OF(sw)) != 0 && (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG))
+ size += SSIZE_OF(sw);
+
+ compiler->local_size = ((size + local_size + 0x7) & ~0x7) - size;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_add_sp(struct sljit_compiler *compiler, sljit_uw imm)
+{
+ sljit_uw imm2;
+
+ /* The TMP_REG1 register must keep its value. */
+ if (imm <= (127u << 2))
+ return push_inst16(compiler, ADD_SP_I | (imm >> 2));
+
+ if (imm <= 0xfff)
+ return push_inst32(compiler, ADDWI | RD4(SLJIT_SP) | RN4(SLJIT_SP) | IMM12(imm));
+
+ imm2 = get_imm(imm);
+
+ if (imm2 != INVALID_IMM)
+ return push_inst32(compiler, ADD_WI | RD4(SLJIT_SP) | RN4(SLJIT_SP) | imm2);
+
+ FAIL_IF(load_immediate(compiler, TMP_REG2, imm));
+ return push_inst16(compiler, ADD_SP | RN3(TMP_REG2));
+}
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 frame_size)
+{
+ sljit_s32 local_size, fscratches, fsaveds, i, tmp;
+ sljit_s32 restored_reg = 0;
+ sljit_s32 lr_dst = TMP_PC;
+ sljit_uw reg_list = 0;
+
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14 && frame_size <= 128);
+
+ local_size = compiler->local_size;
+ fscratches = compiler->fscratches;
+ fsaveds = compiler->fsaveds;
+
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if (local_size > 0)
+ FAIL_IF(emit_add_sp(compiler, (sljit_uw)local_size));
+
+ if (fsaveds + fscratches >= SLJIT_NUMBER_OF_FLOAT_REGISTERS) {
+ FAIL_IF(push_inst32(compiler, VPOP | DD4(SLJIT_FS0) | ((sljit_uw)SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS << 1)));
+ } else {
+ if (fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG)
+ FAIL_IF(push_inst32(compiler, VPOP | DD4(fscratches) | ((sljit_uw)(fscratches - (SLJIT_FIRST_SAVED_FLOAT_REG - 1)) << 1)));
+ if (fsaveds > 0)
+ FAIL_IF(push_inst32(compiler, VPOP | DD4(SLJIT_FS0) | ((sljit_uw)fsaveds << 1)));
+ }
+
+ local_size = GET_SAVED_REGISTERS_SIZE(compiler->scratches, compiler->saveds, 1) & 0x7;
+ }
+
+ if (frame_size < 0) {
+ lr_dst = TMP_REG2;
+ frame_size = 0;
+ } else if (frame_size > 0) {
+ SLJIT_ASSERT(frame_size == 1 || (frame_size & 0x7) == 0);
+ lr_dst = 0;
+ frame_size &= ~0x7;
+ }
+
+ tmp = SLJIT_S0 - compiler->saveds;
+ i = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options);
+ if (tmp < i) {
+ restored_reg = i;
+ do {
+ reg_list |= (sljit_uw)1 << reg_map[i];
+ } while (--i > tmp);
+ }
+
+ i = compiler->scratches;
+ if (i >= SLJIT_FIRST_SAVED_REG) {
+ restored_reg = i;
+ do {
+ reg_list |= (sljit_uw)1 << reg_map[i];
+ } while (--i >= SLJIT_FIRST_SAVED_REG);
+ }
+
+ if (lr_dst == TMP_REG2 && reg_list == 0) {
+ reg_list |= (sljit_uw)1 << reg_map[TMP_REG2];
+ restored_reg = TMP_REG2;
+ lr_dst = 0;
+ }
+
+ if (lr_dst == 0 && (reg_list & (reg_list - 1)) == 0) {
+ /* The local_size does not include the saved registers. */
+ tmp = 0;
+ if (reg_list != 0) {
+ tmp = 2;
+ if (local_size <= 0xfff) {
+ if (local_size == 0) {
+ SLJIT_ASSERT(restored_reg != TMP_REG2);
+ if (frame_size == 0)
+ return push_inst32(compiler, LDRI | RT4(restored_reg) | RN4(SLJIT_SP) | 0x308);
+ if (frame_size > 2 * SSIZE_OF(sw))
+ return push_inst32(compiler, LDRI | RT4(restored_reg) | RN4(SLJIT_SP) | 0x100 | (sljit_ins)(frame_size - (2 * SSIZE_OF(sw))));
+ }
+
+ if (reg_map[restored_reg] <= 7 && local_size <= 0x3fc)
+ FAIL_IF(push_inst16(compiler, STR_SP | 0x800 | RDN3(restored_reg) | (sljit_ins)(local_size >> 2)));
+ else
+ FAIL_IF(push_inst32(compiler, LDR | RT4(restored_reg) | RN4(SLJIT_SP) | (sljit_ins)local_size));
+ tmp = 1;
+ } else if (frame_size == 0) {
+ frame_size = (restored_reg == TMP_REG2) ? SSIZE_OF(sw) : 2 * SSIZE_OF(sw);
+ tmp = 3;
+ }
+
+ /* Place for the saved register. */
+ if (restored_reg != TMP_REG2)
+ local_size += SSIZE_OF(sw);
+ }
+
+ /* Place for the lr register. */
+ local_size += SSIZE_OF(sw);
+
+ if (frame_size > local_size)
+ FAIL_IF(push_inst16(compiler, SUB_SP_I | ((sljit_ins)(frame_size - local_size) >> 2)));
+ else if (frame_size < local_size)
+ FAIL_IF(emit_add_sp(compiler, (sljit_uw)(local_size - frame_size)));
+
+ if (tmp <= 1)
+ return SLJIT_SUCCESS;
+
+ if (tmp == 2) {
+ frame_size -= SSIZE_OF(sw);
+ if (restored_reg != TMP_REG2)
+ frame_size -= SSIZE_OF(sw);
+
+ if (reg_map[restored_reg] <= 7)
+ return push_inst16(compiler, STR_SP | 0x800 | RDN3(restored_reg) | (sljit_ins)(frame_size >> 2));
+
+ return push_inst32(compiler, LDR | RT4(restored_reg) | RN4(SLJIT_SP) | (sljit_ins)frame_size);
+ }
+
+ tmp = (restored_reg == TMP_REG2) ? 0x304 : 0x308;
+ return push_inst32(compiler, LDRI | RT4(restored_reg) | RN4(SLJIT_SP) | (sljit_ins)tmp);
+ }
+
+ if (local_size > 0)
+ FAIL_IF(emit_add_sp(compiler, (sljit_uw)local_size));
+
+ if (!(reg_list & 0xff00) && lr_dst != TMP_REG2) {
+ if (lr_dst == TMP_PC)
+ reg_list |= 1u << 8;
+
+ /* At least one register must be set for POP instruction. */
+ SLJIT_ASSERT(reg_list != 0);
+
+ FAIL_IF(push_inst16(compiler, POP | reg_list));
+ } else {
+ if (lr_dst != 0)
+ reg_list |= (sljit_uw)1 << reg_map[lr_dst];
+
+ /* At least two registers must be set for POP_W instruction. */
+ SLJIT_ASSERT((reg_list & (reg_list - 1)) != 0);
+
+ FAIL_IF(push_inst32(compiler, POP_W | reg_list));
+ }
+
+ if (frame_size > 0)
+ return push_inst16(compiler, SUB_SP_I | (((sljit_ins)frame_size - sizeof(sljit_sw)) >> 2));
+
+ if (lr_dst != 0)
+ return SLJIT_SUCCESS;
+
+ return push_inst16(compiler, ADD_SP_I | 1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ return emit_stack_frame_release(compiler, 0);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ srcw = 0;
+ } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG1, src)));
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 1));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+#if !(defined __ARM_FEATURE_IDIV) && !(defined __ARM_ARCH_EXT_IDIV__)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _WIN32
+extern unsigned long long __rt_udiv(unsigned int denominator, unsigned int numerator);
+extern long long __rt_sdiv(int denominator, int numerator);
+#elif defined(__GNUC__)
+extern unsigned int __aeabi_uidivmod(unsigned int numerator, int unsigned denominator);
+extern int __aeabi_idivmod(int numerator, int denominator);
+#else
+#error "Software divmod functions are needed"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__ARM_FEATURE_IDIV && !__ARM_ARCH_EXT_IDIV__ */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+#if !(defined __ARM_FEATURE_IDIV) && !(defined __ARM_ARCH_EXT_IDIV__)
+ sljit_uw saved_reg_list[3];
+ sljit_uw saved_reg_count;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op0(compiler, op));
+
+ op = GET_OPCODE(op);
+ switch (op) {
+ case SLJIT_BREAKPOINT:
+ return push_inst16(compiler, BKPT);
+ case SLJIT_NOP:
+ return push_inst16(compiler, NOP);
+ case SLJIT_LMUL_UW:
+ case SLJIT_LMUL_SW:
+ return push_inst32(compiler, (op == SLJIT_LMUL_UW ? UMULL : SMULL)
+ | RD4(SLJIT_R1) | RT4(SLJIT_R0) | RN4(SLJIT_R0) | RM4(SLJIT_R1));
+#if (defined __ARM_FEATURE_IDIV) || (defined __ARM_ARCH_EXT_IDIV__)
+ case SLJIT_DIVMOD_UW:
+ case SLJIT_DIVMOD_SW:
+ FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG1, SLJIT_R0)));
+ FAIL_IF(push_inst32(compiler, (op == SLJIT_DIVMOD_UW ? UDIV : SDIV) | RD4(SLJIT_R0) | RN4(SLJIT_R0) | RM4(SLJIT_R1)));
+ FAIL_IF(push_inst32(compiler, MUL | RD4(SLJIT_R1) | RN4(SLJIT_R0) | RM4(SLJIT_R1)));
+ return push_inst32(compiler, SUB_W | RD4(SLJIT_R1) | RN4(TMP_REG1) | RM4(SLJIT_R1));
+ case SLJIT_DIV_UW:
+ case SLJIT_DIV_SW:
+ return push_inst32(compiler, (op == SLJIT_DIV_UW ? UDIV : SDIV) | RD4(SLJIT_R0) | RN4(SLJIT_R0) | RM4(SLJIT_R1));
+#else /* !__ARM_FEATURE_IDIV && !__ARM_ARCH_EXT_IDIV__ */
+ case SLJIT_DIVMOD_UW:
+ case SLJIT_DIVMOD_SW:
+ case SLJIT_DIV_UW:
+ case SLJIT_DIV_SW:
+ SLJIT_COMPILE_ASSERT((SLJIT_DIVMOD_UW & 0x2) == 0 && SLJIT_DIV_UW - 0x2 == SLJIT_DIVMOD_UW, bad_div_opcode_assignments);
+ SLJIT_ASSERT(reg_map[2] == 1 && reg_map[3] == 2 && reg_map[4] == 3);
+
+ saved_reg_count = 0;
+ if (compiler->scratches >= 4)
+ saved_reg_list[saved_reg_count++] = 3;
+ if (compiler->scratches >= 3)
+ saved_reg_list[saved_reg_count++] = 2;
+ if (op >= SLJIT_DIV_UW)
+ saved_reg_list[saved_reg_count++] = 1;
+
+ if (saved_reg_count > 0) {
+ FAIL_IF(push_inst32(compiler, 0xf84d0d00 | (saved_reg_count >= 3 ? 16 : 8)
+ | (saved_reg_list[0] << 12) /* str rX, [sp, #-8/-16]! */));
+ if (saved_reg_count >= 2) {
+ SLJIT_ASSERT(saved_reg_list[1] < 8);
+ FAIL_IF(push_inst16(compiler, 0x9001 | (saved_reg_list[1] << 8) /* str rX, [sp, #4] */));
+ }
+ if (saved_reg_count >= 3) {
+ SLJIT_ASSERT(saved_reg_list[2] < 8);
+ FAIL_IF(push_inst16(compiler, 0x9002 | (saved_reg_list[2] << 8) /* str rX, [sp, #8] */));
+ }
+ }
+
+#ifdef _WIN32
+ FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG1, SLJIT_R0)));
+ FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(SLJIT_R0, SLJIT_R1)));
+ FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(SLJIT_R1, TMP_REG1)));
+ FAIL_IF(sljit_emit_ijump(compiler, SLJIT_FAST_CALL, SLJIT_IMM,
+ ((op | 0x2) == SLJIT_DIV_UW ? SLJIT_FUNC_ADDR(__rt_udiv) : SLJIT_FUNC_ADDR(__rt_sdiv))));
+#elif defined(__GNUC__)
+ FAIL_IF(sljit_emit_ijump(compiler, SLJIT_FAST_CALL, SLJIT_IMM,
+ ((op | 0x2) == SLJIT_DIV_UW ? SLJIT_FUNC_ADDR(__aeabi_uidivmod) : SLJIT_FUNC_ADDR(__aeabi_idivmod))));
+#else
+#error "Software divmod functions are needed"
+#endif
+
+ if (saved_reg_count > 0) {
+ if (saved_reg_count >= 3) {
+ SLJIT_ASSERT(saved_reg_list[2] < 8);
+ FAIL_IF(push_inst16(compiler, 0x9802 | (saved_reg_list[2] << 8) /* ldr rX, [sp, #8] */));
+ }
+ if (saved_reg_count >= 2) {
+ SLJIT_ASSERT(saved_reg_list[1] < 8);
+ FAIL_IF(push_inst16(compiler, 0x9801 | (saved_reg_list[1] << 8) /* ldr rX, [sp, #4] */));
+ }
+ return push_inst32(compiler, 0xf85d0b00 | (saved_reg_count >= 3 ? 16 : 8)
+ | (saved_reg_list[0] << 12) /* ldr rX, [sp], #8/16 */);
+ }
+ return SLJIT_SUCCESS;
+#endif /* __ARM_FEATURE_IDIV || __ARM_ARCH_EXT_IDIV__ */
+ case SLJIT_ENDBR:
+ case SLJIT_SKIP_FRAMES_BEFORE_RETURN:
+ return SLJIT_SUCCESS;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r, flags;
+ sljit_s32 op_flags = GET_ALL_FLAGS(op);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ op = GET_OPCODE(op);
+ if (op >= SLJIT_MOV && op <= SLJIT_MOV_P) {
+ switch (op) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ case SLJIT_MOV_P:
+ flags = WORD_SIZE;
+ break;
+ case SLJIT_MOV_U8:
+ flags = BYTE_SIZE;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_u8)srcw;
+ break;
+ case SLJIT_MOV_S8:
+ flags = BYTE_SIZE | SIGNED;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_s8)srcw;
+ break;
+ case SLJIT_MOV_U16:
+ flags = HALF_SIZE;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_u16)srcw;
+ break;
+ case SLJIT_MOV_S16:
+ flags = HALF_SIZE | SIGNED;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_s16)srcw;
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ flags = 0;
+ break;
+ }
+
+ if (src & SLJIT_IMM)
+ FAIL_IF(emit_op_imm(compiler, SLJIT_MOV | ARG2_IMM, dst_r, TMP_REG2, (sljit_uw)srcw));
+ else if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, flags, dst_r, src, srcw, TMP_REG1));
+ } else {
+ if (dst_r != TMP_REG1)
+ return emit_op_imm(compiler, op, dst_r, TMP_REG2, (sljit_uw)src);
+ dst_r = src;
+ }
+
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+
+ return emit_op_mem(compiler, flags | STORE, dst_r, dst, dstw, TMP_REG2);
+ }
+
+ flags = HAS_FLAGS(op_flags) ? SET_FLAGS : 0;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+
+ emit_op_imm(compiler, flags | op, dst_r, TMP_REG2, (sljit_uw)src);
+
+ if (SLJIT_UNLIKELY(dst & SLJIT_MEM))
+ return emit_op_mem(compiler, flags | STORE, dst_r, dst, dstw, TMP_REG2);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_reg, flags, src2_reg;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ dst_reg = FAST_IS_REG(dst) ? dst : TMP_REG1;
+ flags = HAS_FLAGS(op) ? SET_FLAGS : 0;
+
+ if (dst == TMP_REG1)
+ flags |= UNUSED_RETURN;
+
+ if (src1 & SLJIT_IMM)
+ flags |= ARG1_IMM;
+ else if (src1 & SLJIT_MEM) {
+ emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src1, src1w, TMP_REG1);
+ src1w = TMP_REG1;
+ }
+ else
+ src1w = src1;
+
+ if (src2 & SLJIT_IMM)
+ flags |= ARG2_IMM;
+ else if (src2 & SLJIT_MEM) {
+ src2_reg = (!(flags & ARG1_IMM) && (src1w == TMP_REG1)) ? TMP_REG2 : TMP_REG1;
+ emit_op_mem(compiler, WORD_SIZE, src2_reg, src2, src2w, src2_reg);
+ src2w = src2_reg;
+ }
+ else
+ src2w = src2;
+
+ emit_op_imm(compiler, flags | GET_OPCODE(op), dst_reg, (sljit_uw)src1w, (sljit_uw)src2w);
+
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+ return emit_op_mem(compiler, WORD_SIZE | STORE, dst_reg, dst, dstw, TMP_REG2);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, TMP_REG1, 0, src1, src1w, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 is_left;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w));
+
+ op = GET_OPCODE(op);
+ is_left = (op == SLJIT_SHL || op == SLJIT_MSHL);
+
+ if (src_dst == src1) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, is_left ? SLJIT_ROTL : SLJIT_ROTR, src_dst, 0, src_dst, 0, src2, src2w);
+ }
+
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ if (src2 & SLJIT_IMM) {
+ src2w &= 0x1f;
+
+ if (src2w == 0)
+ return SLJIT_SUCCESS;
+ } else if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, src2, src2w, TMP_REG2));
+ src2 = TMP_REG2;
+ }
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src1, src1w, TMP_REG1));
+ src1 = TMP_REG1;
+ } else if (src1 & SLJIT_IMM) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)src1w));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_IMM) {
+ if (reg_map[src_dst] <= 7)
+ FAIL_IF(push_inst16(compiler, (is_left ? LSLSI : LSRSI) | RD3(src_dst) | RN3(src_dst) | ((sljit_ins)src2w << 6)));
+ else
+ FAIL_IF(push_inst32(compiler, (is_left ? LSL_WI : LSR_WI) | RD4(src_dst) | RM4(src_dst) | IMM5(src2w)));
+
+ src2w = (src2w ^ 0x1f) + 1;
+ return push_inst32(compiler, ORR_W | RD4(src_dst) | RN4(src_dst) | RM4(src1) | (is_left ? 0x10 : 0x0) | IMM5(src2w));
+ }
+
+ if (op == SLJIT_MSHL || op == SLJIT_MLSHR) {
+ FAIL_IF(push_inst32(compiler, ANDI | RD4(TMP_REG2) | RN4(src2) | 0x1f));
+ src2 = TMP_REG2;
+ }
+
+ if (IS_2_LO_REGS(src_dst, src2))
+ FAIL_IF(push_inst16(compiler, (is_left ? LSLS : LSRS) | RD3(src_dst) | RN3(src2)));
+ else
+ FAIL_IF(push_inst32(compiler, (is_left ? LSL_W : LSR_W) | RD4(src_dst) | RN4(src_dst) | RM4(src2)));
+
+ FAIL_IF(push_inst32(compiler, (is_left ? LSR_WI : LSL_WI) | RD4(TMP_REG1) | RM4(src1) | (1 << 6)));
+ FAIL_IF(push_inst32(compiler, EORI | RD4(TMP_REG2) | RN4(src2) | 0x1f));
+ FAIL_IF(push_inst32(compiler, (is_left ? LSR_W : LSL_W) | RD4(TMP_REG1) | RN4(TMP_REG1) | RM4(TMP_REG2)));
+ return push_inst32(compiler, ORR_W | RD4(src_dst) | RN4(src_dst) | RM4(TMP_REG1));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_src(compiler, op, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ switch (op) {
+ case SLJIT_FAST_RETURN:
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14);
+
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG2, src)));
+ else
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, src, srcw, TMP_REG2));
+
+ return push_inst16(compiler, BX | RN3(TMP_REG2));
+ case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN:
+ return SLJIT_SUCCESS;
+ case SLJIT_PREFETCH_L1:
+ case SLJIT_PREFETCH_L2:
+ case SLJIT_PREFETCH_L3:
+ case SLJIT_PREFETCH_ONCE:
+ return emit_op_mem(compiler, PRELOAD, TMP_PC, src, srcw, TMP_REG1);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_register_index(reg));
+ return reg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
+ return (freg_map[reg] << 1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_custom(compiler, instruction, size));
+
+ if (size == 2)
+ return push_inst16(compiler, *(sljit_u16*)instruction);
+ return push_inst32(compiler, *(sljit_ins*)instruction);
+}
+
+/* --------------------------------------------------------------------- */
+/* Floating point operators */
+/* --------------------------------------------------------------------- */
+
+#define FPU_LOAD (1 << 20)
+
+static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
+{
+ sljit_uw imm;
+ sljit_ins inst = VSTR_F32 | (flags & (SLJIT_32 | FPU_LOAD));
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+
+ /* Fast loads and stores. */
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ FAIL_IF(push_inst32(compiler, ADD_W | RD4(TMP_REG1) | RN4(arg & REG_MASK) | RM4(OFFS_REG(arg)) | (((sljit_uw)argw & 0x3) << 6)));
+ arg = SLJIT_MEM | TMP_REG1;
+ argw = 0;
+ }
+
+ if ((arg & REG_MASK) && (argw & 0x3) == 0) {
+ if (!(argw & ~0x3fc))
+ return push_inst32(compiler, inst | 0x800000 | RN4(arg & REG_MASK) | DD4(reg) | ((sljit_uw)argw >> 2));
+ if (!(-argw & ~0x3fc))
+ return push_inst32(compiler, inst | RN4(arg & REG_MASK) | DD4(reg) | ((sljit_uw)-argw >> 2));
+ }
+
+ if (arg & REG_MASK) {
+ if (emit_set_delta(compiler, TMP_REG1, arg & REG_MASK, argw) != SLJIT_ERR_UNSUPPORTED) {
+ FAIL_IF(compiler->error);
+ return push_inst32(compiler, inst | 0x800000 | RN4(TMP_REG1) | DD4(reg));
+ }
+
+ imm = get_imm((sljit_uw)argw & ~(sljit_uw)0x3fc);
+ if (imm != INVALID_IMM) {
+ FAIL_IF(push_inst32(compiler, ADD_WI | RD4(TMP_REG1) | RN4(arg & REG_MASK) | imm));
+ return push_inst32(compiler, inst | 0x800000 | RN4(TMP_REG1) | DD4(reg) | (((sljit_uw)argw & 0x3fc) >> 2));
+ }
+
+ imm = get_imm((sljit_uw)-argw & ~(sljit_uw)0x3fc);
+ if (imm != INVALID_IMM) {
+ argw = -argw;
+ FAIL_IF(push_inst32(compiler, SUB_WI | RD4(TMP_REG1) | RN4(arg & REG_MASK) | imm));
+ return push_inst32(compiler, inst | RN4(TMP_REG1) | DD4(reg) | (((sljit_uw)argw & 0x3fc) >> 2));
+ }
+ }
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)argw));
+ if (arg & REG_MASK)
+ FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, (arg & REG_MASK))));
+ return push_inst32(compiler, inst | 0x800000 | RN4(TMP_REG1) | DD4(reg));
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ op ^= SLJIT_32;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG1, src, srcw));
+ src = TMP_FREG1;
+ }
+
+ FAIL_IF(push_inst32(compiler, VCVT_S32_F32 | (op & SLJIT_32) | DD4(TMP_FREG1) | DM4(src)));
+
+ if (FAST_IS_REG(dst))
+ return push_inst32(compiler, VMOV | (1 << 20) | RT4(dst) | DN4(TMP_FREG1));
+
+ /* Store the integer value from a VFP register. */
+ return emit_fop_mem(compiler, 0, TMP_FREG1, dst, dstw);
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ op ^= SLJIT_32;
+
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst32(compiler, VMOV | RT4(src) | DN4(TMP_FREG1)));
+ else if (src & SLJIT_MEM) {
+ /* Load the integer value into a VFP register. */
+ FAIL_IF(emit_fop_mem(compiler, FPU_LOAD, TMP_FREG1, src, srcw));
+ }
+ else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)srcw));
+ FAIL_IF(push_inst32(compiler, VMOV | RT4(TMP_REG1) | DN4(TMP_FREG1)));
+ }
+
+ FAIL_IF(push_inst32(compiler, VCVT_F32_S32 | (op & SLJIT_32) | DD4(dst_r) | DM4(TMP_FREG1)));
+
+ if (dst & SLJIT_MEM)
+ return emit_fop_mem(compiler, (op & SLJIT_32), TMP_FREG1, dst, dstw);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ op ^= SLJIT_32;
+
+ if (src1 & SLJIT_MEM) {
+ emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG1, src1, src1w);
+ src1 = TMP_FREG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG2, src2, src2w);
+ src2 = TMP_FREG2;
+ }
+
+ FAIL_IF(push_inst32(compiler, VCMP_F32 | (op & SLJIT_32) | DD4(src1) | DM4(src2)));
+ return push_inst32(compiler, VMRS);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+
+ SLJIT_COMPILE_ASSERT((SLJIT_32 == 0x100), float_transfer_bit_error);
+ SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (GET_OPCODE(op) != SLJIT_CONV_F64_FROM_F32)
+ op ^= SLJIT_32;
+
+ if (src & SLJIT_MEM) {
+ emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, dst_r, src, srcw);
+ src = dst_r;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV_F64:
+ if (src != dst_r) {
+ if (dst_r != TMP_FREG1)
+ FAIL_IF(push_inst32(compiler, VMOV_F32 | (op & SLJIT_32) | DD4(dst_r) | DM4(src)));
+ else
+ dst_r = src;
+ }
+ break;
+ case SLJIT_NEG_F64:
+ FAIL_IF(push_inst32(compiler, VNEG_F32 | (op & SLJIT_32) | DD4(dst_r) | DM4(src)));
+ break;
+ case SLJIT_ABS_F64:
+ FAIL_IF(push_inst32(compiler, VABS_F32 | (op & SLJIT_32) | DD4(dst_r) | DM4(src)));
+ break;
+ case SLJIT_CONV_F64_FROM_F32:
+ FAIL_IF(push_inst32(compiler, VCVT_F64_F32 | (op & SLJIT_32) | DD4(dst_r) | DM4(src)));
+ op ^= SLJIT_32;
+ break;
+ }
+
+ if (dst & SLJIT_MEM)
+ return emit_fop_mem(compiler, (op & SLJIT_32), dst_r, dst, dstw);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ op ^= SLJIT_32;
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+ if (src1 & SLJIT_MEM) {
+ emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG1, src1, src1w);
+ src1 = TMP_FREG1;
+ }
+ if (src2 & SLJIT_MEM) {
+ emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG2, src2, src2w);
+ src2 = TMP_FREG2;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD_F64:
+ FAIL_IF(push_inst32(compiler, VADD_F32 | (op & SLJIT_32) | DD4(dst_r) | DN4(src1) | DM4(src2)));
+ break;
+ case SLJIT_SUB_F64:
+ FAIL_IF(push_inst32(compiler, VSUB_F32 | (op & SLJIT_32) | DD4(dst_r) | DN4(src1) | DM4(src2)));
+ break;
+ case SLJIT_MUL_F64:
+ FAIL_IF(push_inst32(compiler, VMUL_F32 | (op & SLJIT_32) | DD4(dst_r) | DN4(src1) | DM4(src2)));
+ break;
+ case SLJIT_DIV_F64:
+ FAIL_IF(push_inst32(compiler, VDIV_F32 | (op & SLJIT_32) | DD4(dst_r) | DN4(src1) | DM4(src2)));
+ break;
+ }
+
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+ return emit_fop_mem(compiler, (op & SLJIT_32), TMP_FREG1, dst, dstw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Other instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14);
+
+ if (FAST_IS_REG(dst))
+ return push_inst16(compiler, MOV | SET_REGS44(dst, TMP_REG2));
+
+ /* Memory. */
+ return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG2, dst, dstw, TMP_REG1);
+}
+
+/* --------------------------------------------------------------------- */
+/* Conditional instructions */
+/* --------------------------------------------------------------------- */
+
+static sljit_uw get_cc(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ switch (type) {
+ case SLJIT_EQUAL:
+ case SLJIT_F_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */
+ return 0x0;
+
+ case SLJIT_NOT_EQUAL:
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL: /* Not supported. */
+ return 0x1;
+
+ case SLJIT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_ADD)
+ return 0x2;
+ /* fallthrough */
+
+ case SLJIT_LESS:
+ return 0x3;
+
+ case SLJIT_NOT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_ADD)
+ return 0x3;
+ /* fallthrough */
+
+ case SLJIT_GREATER_EQUAL:
+ return 0x2;
+
+ case SLJIT_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ return 0x8;
+
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ return 0x9;
+
+ case SLJIT_SIG_LESS:
+ case SLJIT_UNORDERED_OR_LESS:
+ return 0xb;
+
+ case SLJIT_SIG_GREATER_EQUAL:
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ return 0xa;
+
+ case SLJIT_SIG_GREATER:
+ case SLJIT_F_GREATER:
+ case SLJIT_ORDERED_GREATER:
+ return 0xc;
+
+ case SLJIT_SIG_LESS_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ return 0xd;
+
+ case SLJIT_OVERFLOW:
+ if (!(compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)))
+ return 0x1;
+ /* fallthrough */
+
+ case SLJIT_UNORDERED:
+ return 0x6;
+
+ case SLJIT_NOT_OVERFLOW:
+ if (!(compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)))
+ return 0x0;
+ /* fallthrough */
+
+ case SLJIT_ORDERED:
+ return 0x7;
+
+ case SLJIT_F_LESS:
+ case SLJIT_ORDERED_LESS:
+ return 0x4;
+
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ return 0x5;
+
+ default: /* SLJIT_JUMP */
+ SLJIT_UNREACHABLE();
+ return 0xe;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ struct sljit_label *label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_label(compiler));
+
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ return compiler->last_label;
+
+ label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
+ PTR_FAIL_IF(!label);
+ set_label(label, compiler);
+ return label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ struct sljit_jump *jump;
+ sljit_ins cc;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_jump(compiler, type));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ type &= 0xff;
+
+ PTR_FAIL_IF(emit_imm32_const(compiler, TMP_REG1, 0));
+ if (type < SLJIT_JUMP) {
+ jump->flags |= IS_COND;
+ cc = get_cc(compiler, type);
+ jump->flags |= cc << 8;
+ PTR_FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8));
+ }
+
+ jump->addr = compiler->size;
+ if (type <= SLJIT_JUMP)
+ PTR_FAIL_IF(push_inst16(compiler, BX | RN3(TMP_REG1)));
+ else {
+ jump->flags |= IS_BL;
+ PTR_FAIL_IF(push_inst16(compiler, BLX | RN3(TMP_REG1)));
+ }
+
+ return jump;
+}
+
+#ifdef __SOFTFP__
+
+static sljit_s32 softfloat_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src, sljit_u32 *extra_space)
+{
+ sljit_u32 is_tail_call = *extra_space & SLJIT_CALL_RETURN;
+ sljit_u32 offset = 0;
+ sljit_u32 word_arg_offset = 0;
+ sljit_u32 float_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_u32 src_offset = 4 * sizeof(sljit_sw);
+ sljit_u8 offsets[4];
+ sljit_u8 *offset_ptr = offsets;
+
+ if (src && FAST_IS_REG(*src))
+ src_offset = (sljit_u32)reg_map[*src] * sizeof(sljit_sw);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK);
+
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset & 0x7)
+ offset += sizeof(sljit_sw);
+ *offset_ptr++ = (sljit_u8)offset;
+ offset += sizeof(sljit_f64);
+ float_arg_count++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ *offset_ptr++ = (sljit_u8)offset;
+ offset += sizeof(sljit_f32);
+ float_arg_count++;
+ break;
+ default:
+ *offset_ptr++ = (sljit_u8)offset;
+ offset += sizeof(sljit_sw);
+ word_arg_offset += sizeof(sljit_sw);
+ break;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ if (offset > 4 * sizeof(sljit_sw) && (!is_tail_call || offset > compiler->args_size)) {
+ /* Keep lr register on the stack. */
+ if (is_tail_call)
+ offset += sizeof(sljit_sw);
+
+ offset = ((offset - 4 * sizeof(sljit_sw)) + 0x7) & ~(sljit_uw)0x7;
+
+ *extra_space = offset;
+
+ if (is_tail_call)
+ FAIL_IF(emit_stack_frame_release(compiler, (sljit_s32)offset));
+ else
+ FAIL_IF(push_inst16(compiler, SUB_SP_I | (offset >> 2)));
+ } else {
+ if (is_tail_call)
+ FAIL_IF(emit_stack_frame_release(compiler, -1));
+ *extra_space = 0;
+ }
+
+ SLJIT_ASSERT(reg_map[TMP_REG1] == 12);
+
+ /* Process arguments in reversed direction. */
+ while (types) {
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ float_arg_count--;
+ offset = *(--offset_ptr);
+
+ SLJIT_ASSERT((offset & 0x7) == 0);
+
+ if (offset < 4 * sizeof(sljit_sw)) {
+ if (src_offset == offset || src_offset == offset + sizeof(sljit_sw)) {
+ FAIL_IF(push_inst16(compiler, MOV | (src_offset << 1) | 4 | (1 << 7)));
+ *src = TMP_REG1;
+ }
+ FAIL_IF(push_inst32(compiler, VMOV2 | 0x100000 | (offset << 10) | ((offset + sizeof(sljit_sw)) << 14) | float_arg_count));
+ } else
+ FAIL_IF(push_inst32(compiler, VSTR_F32 | 0x800100 | RN4(SLJIT_SP)
+ | (float_arg_count << 12) | ((offset - 4 * sizeof(sljit_sw)) >> 2)));
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ float_arg_count--;
+ offset = *(--offset_ptr);
+
+ if (offset < 4 * sizeof(sljit_sw)) {
+ if (src_offset == offset) {
+ FAIL_IF(push_inst16(compiler, MOV | (src_offset << 1) | 4 | (1 << 7)));
+ *src = TMP_REG1;
+ }
+ FAIL_IF(push_inst32(compiler, VMOV | 0x100000 | (float_arg_count << 16) | (offset << 10)));
+ } else
+ FAIL_IF(push_inst32(compiler, VSTR_F32 | 0x800000 | RN4(SLJIT_SP)
+ | (float_arg_count << 12) | ((offset - 4 * sizeof(sljit_sw)) >> 2)));
+ break;
+ default:
+ word_arg_offset -= sizeof(sljit_sw);
+ offset = *(--offset_ptr);
+
+ SLJIT_ASSERT(offset >= word_arg_offset);
+
+ if (offset != word_arg_offset) {
+ if (offset < 4 * sizeof(sljit_sw)) {
+ if (src_offset == offset) {
+ FAIL_IF(push_inst16(compiler, MOV | (src_offset << 1) | 4 | (1 << 7)));
+ *src = TMP_REG1;
+ }
+ else if (src_offset == word_arg_offset) {
+ *src = (sljit_s32)(1 + (offset >> 2));
+ src_offset = offset;
+ }
+ FAIL_IF(push_inst16(compiler, MOV | (offset >> 2) | (word_arg_offset << 1)));
+ } else
+ FAIL_IF(push_inst16(compiler, STR_SP | (word_arg_offset << 6) | ((offset - 4 * sizeof(sljit_sw)) >> 2)));
+ }
+ break;
+ }
+
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 softfloat_post_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types)
+{
+ if ((arg_types & SLJIT_ARG_MASK) == SLJIT_ARG_TYPE_F64)
+ FAIL_IF(push_inst32(compiler, VMOV2 | (1 << 16) | (0 << 12) | 0));
+ if ((arg_types & SLJIT_ARG_MASK) == SLJIT_ARG_TYPE_F32)
+ FAIL_IF(push_inst32(compiler, VMOV | (0 << 16) | (0 << 12)));
+
+ return SLJIT_SUCCESS;
+}
+
+#else
+
+static sljit_s32 hardfloat_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types)
+{
+ sljit_u32 offset = SLJIT_FR0;
+ sljit_u32 new_offset = SLJIT_FR0;
+ sljit_u32 f32_offset = 0;
+
+ /* Remove return value. */
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset != new_offset)
+ FAIL_IF(push_inst32(compiler, VMOV_F32 | SLJIT_32 | DD4(new_offset) | DM4(offset)));
+
+ new_offset++;
+ offset++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (f32_offset != 0) {
+ FAIL_IF(push_inst32(compiler, VMOV_F32 | 0x400000 | DD4(f32_offset) | DM4(offset)));
+ f32_offset = 0;
+ } else {
+ if (offset != new_offset)
+ FAIL_IF(push_inst32(compiler, VMOV_F32 | 0x400000 | DD4(new_offset) | DM4(offset)));
+ f32_offset = new_offset;
+ new_offset++;
+ }
+ offset++;
+ break;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+#ifdef __SOFTFP__
+ struct sljit_jump *jump;
+ sljit_u32 extra_space = (sljit_u32)type;
+#endif
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+#ifdef __SOFTFP__
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG) {
+ PTR_FAIL_IF(softfloat_call_with_args(compiler, arg_types, NULL, &extra_space));
+ SLJIT_ASSERT((extra_space & 0x7) == 0);
+
+ if ((type & SLJIT_CALL_RETURN) && extra_space == 0)
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+
+ SLJIT_SKIP_CHECKS(compiler);
+ jump = sljit_emit_jump(compiler, type);
+ PTR_FAIL_IF(jump == NULL);
+
+ if (extra_space > 0) {
+ if (type & SLJIT_CALL_RETURN)
+ PTR_FAIL_IF(push_inst32(compiler, LDR | RT4(TMP_REG2)
+ | RN4(SLJIT_SP) | (extra_space - sizeof(sljit_sw))));
+
+ PTR_FAIL_IF(push_inst16(compiler, ADD_SP_I | (extra_space >> 2)));
+
+ if (type & SLJIT_CALL_RETURN) {
+ PTR_FAIL_IF(push_inst16(compiler, BX | RN3(TMP_REG2)));
+ return jump;
+ }
+ }
+
+ SLJIT_ASSERT(!(type & SLJIT_CALL_RETURN));
+ PTR_FAIL_IF(softfloat_post_call_with_args(compiler, arg_types));
+ return jump;
+ }
+#endif /* __SOFTFP__ */
+
+ if (type & SLJIT_CALL_RETURN) {
+ /* ldmia sp!, {..., lr} */
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, -1));
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+ }
+
+#ifndef __SOFTFP__
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ PTR_FAIL_IF(hardfloat_call_with_args(compiler, arg_types));
+#endif /* !__SOFTFP__ */
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ struct sljit_jump *jump;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ SLJIT_ASSERT(reg_map[TMP_REG1] != 14);
+
+ if (!(src & SLJIT_IMM)) {
+ if (FAST_IS_REG(src)) {
+ SLJIT_ASSERT(reg_map[src] != 14);
+ return push_inst16(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RN3(src));
+ }
+
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, src, srcw, TMP_REG1));
+ if (type >= SLJIT_FAST_CALL)
+ return push_inst16(compiler, BLX | RN3(TMP_REG1));
+ }
+
+ /* These jumps are converted to jump/call instructions when possible. */
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ FAIL_IF(!jump);
+ set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_BL : 0));
+ jump->u.target = (sljit_uw)srcw;
+
+ FAIL_IF(emit_imm32_const(compiler, TMP_REG1, 0));
+ jump->addr = compiler->size;
+ return push_inst16(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RN3(TMP_REG1));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+#ifdef __SOFTFP__
+ sljit_u32 extra_space = (sljit_u32)type;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+
+ if ((type & SLJIT_CALL_RETURN) && (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options)))) {
+ FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG1, src)));
+ src = TMP_REG1;
+ }
+
+#ifdef __SOFTFP__
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG) {
+ FAIL_IF(softfloat_call_with_args(compiler, arg_types, &src, &extra_space));
+ SLJIT_ASSERT((extra_space & 0x7) == 0);
+
+ if ((type & SLJIT_CALL_RETURN) && extra_space == 0)
+ type = SLJIT_JUMP;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw));
+
+ if (extra_space > 0) {
+ if (type & SLJIT_CALL_RETURN)
+ FAIL_IF(push_inst32(compiler, LDR | RT4(TMP_REG2)
+ | RN4(SLJIT_SP) | (extra_space - sizeof(sljit_sw))));
+
+ FAIL_IF(push_inst16(compiler, ADD_SP_I | (extra_space >> 2)));
+
+ if (type & SLJIT_CALL_RETURN)
+ return push_inst16(compiler, BX | RN3(TMP_REG2));
+ }
+
+ SLJIT_ASSERT(!(type & SLJIT_CALL_RETURN));
+ return softfloat_post_call_with_args(compiler, arg_types);
+ }
+#endif /* __SOFTFP__ */
+
+ if (type & SLJIT_CALL_RETURN) {
+ /* ldmia sp!, {..., lr} */
+ FAIL_IF(emit_stack_frame_release(compiler, -1));
+ type = SLJIT_JUMP;
+ }
+
+#ifndef __SOFTFP__
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ FAIL_IF(hardfloat_call_with_args(compiler, arg_types));
+#endif /* !__SOFTFP__ */
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
+
+#ifdef __SOFTFP__
+
+static SLJIT_INLINE sljit_s32 emit_fmov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+ if (compiler->options & SLJIT_ENTER_REG_ARG) {
+ if (src == SLJIT_FR0)
+ return SLJIT_SUCCESS;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_fop1(compiler, op, SLJIT_RETURN_FREG, 0, src, srcw);
+ }
+
+ if (FAST_IS_REG(src)) {
+ if (op & SLJIT_32)
+ return push_inst32(compiler, VMOV | (1 << 20) | DN4(src) | RT4(SLJIT_R0));
+ return push_inst32(compiler, VMOV2 | (1 << 20) | DM4(src) | RT4(SLJIT_R0) | RN4(SLJIT_R1));
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+
+ if (op & SLJIT_32)
+ return sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, src, srcw);
+ return sljit_emit_mem(compiler, SLJIT_MOV, SLJIT_REG_PAIR(SLJIT_R0, SLJIT_R1), src, srcw);
+}
+
+#endif /* __SOFTFP__ */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ sljit_s32 dst_r, flags = GET_ALL_FLAGS(op);
+ sljit_ins cc;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ op = GET_OPCODE(op);
+ cc = get_cc(compiler, type);
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if (op < SLJIT_ADD) {
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | (((cc & 0x1) ^ 0x1) << 3) | 0x4));
+ if (reg_map[dst_r] > 7) {
+ FAIL_IF(push_inst32(compiler, MOV_WI | RD4(dst_r) | 1));
+ FAIL_IF(push_inst32(compiler, MOV_WI | RD4(dst_r) | 0));
+ } else {
+ /* The movsi (immediate) instruction does not set flags in IT block. */
+ FAIL_IF(push_inst16(compiler, MOVSI | RDN3(dst_r) | 1));
+ FAIL_IF(push_inst16(compiler, MOVSI | RDN3(dst_r) | 0));
+ }
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+ return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG1, dst, dstw, TMP_REG2);
+ }
+
+ if (dst & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, dst, dstw, TMP_REG2));
+
+ if (op == SLJIT_AND) {
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | (((cc & 0x1) ^ 0x1) << 3) | 0x4));
+ FAIL_IF(push_inst32(compiler, ANDI | RN4(dst_r) | RD4(dst_r) | 1));
+ FAIL_IF(push_inst32(compiler, ANDI | RN4(dst_r) | RD4(dst_r) | 0));
+ }
+ else {
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8));
+ FAIL_IF(push_inst32(compiler, ((op == SLJIT_OR) ? ORRI : EORI) | RN4(dst_r) | RD4(dst_r) | 1));
+ }
+
+ if (dst & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG1, dst, dstw, TMP_REG2));
+
+ if (!(flags & SLJIT_SET_Z))
+ return SLJIT_SUCCESS;
+
+ /* The condition must always be set, even if the ORR/EORI is not executed above. */
+ return push_inst32(compiler, MOV_W | SET_FLAGS | RD4(TMP_REG1) | RM4(dst_r));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_uw cc, tmp;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+ cc = get_cc(compiler, type & ~SLJIT_32);
+
+ if (!(src & SLJIT_IMM)) {
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8));
+ return push_inst16(compiler, MOV | SET_REGS44(dst_reg, src));
+ }
+
+ tmp = (sljit_uw) srcw;
+
+ if (tmp < 0x10000) {
+ /* set low 16 bits, set hi 16 bits to 0. */
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8));
+ return push_inst32(compiler, MOVW | RD4(dst_reg)
+ | COPY_BITS(tmp, 12, 16, 4) | COPY_BITS(tmp, 11, 26, 1) | COPY_BITS(tmp, 8, 12, 3) | (tmp & 0xff));
+ }
+
+ tmp = get_imm((sljit_uw)srcw);
+ if (tmp != INVALID_IMM) {
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8));
+ return push_inst32(compiler, MOV_WI | RD4(dst_reg) | tmp);
+ }
+
+ tmp = get_imm(~(sljit_uw)srcw);
+ if (tmp != INVALID_IMM) {
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8));
+ return push_inst32(compiler, MVN_WI | RD4(dst_reg) | tmp);
+ }
+
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | ((cc & 0x1) << 3) | 0x4));
+
+ tmp = (sljit_uw) srcw;
+ FAIL_IF(push_inst32(compiler, MOVW | RD4(dst_reg)
+ | COPY_BITS(tmp, 12, 16, 4) | COPY_BITS(tmp, 11, 26, 1) | COPY_BITS(tmp, 8, 12, 3) | (tmp & 0xff)));
+ return push_inst32(compiler, MOVT | RD4(dst_reg)
+ | COPY_BITS(tmp, 12 + 16, 16, 4) | COPY_BITS(tmp, 11 + 16, 26, 1) | COPY_BITS(tmp, 8 + 16, 12, 3) | ((tmp & 0xff0000) >> 16));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 flags;
+ sljit_uw imm, tmp;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (!(reg & REG_PAIR_MASK))
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+
+ if (type & (SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32)) {
+ if ((mem & REG_MASK) == 0) {
+ if ((memw & 0xfff) >= (0x1000 - SSIZE_OF(sw))) {
+ imm = get_imm((sljit_uw)((memw + 0x1000) & ~0xfff));
+
+ if (imm != INVALID_IMM)
+ memw = (memw & 0xfff) - 0x1000;
+ } else {
+ imm = get_imm((sljit_uw)(memw & ~0xfff));
+
+ if (imm != INVALID_IMM)
+ memw &= 0xff;
+ }
+
+ if (imm == INVALID_IMM) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw));
+ memw = 0;
+ } else
+ FAIL_IF(push_inst32(compiler, MOV_WI | RD4(TMP_REG1) | imm));
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ } else if (mem & OFFS_REG_MASK) {
+ FAIL_IF(push_inst32(compiler, ADD_W | RD4(TMP_REG1) | RN4(mem & REG_MASK) | RM4(OFFS_REG(mem)) | ((sljit_uw)(memw & 0x3) << 6)));
+ memw = 0;
+ mem = SLJIT_MEM1(TMP_REG1);
+ } else if (memw < -0xff) {
+ /* Zero value can be included in the first case. */
+ if ((-memw & 0xfff) <= SSIZE_OF(sw))
+ tmp = (sljit_uw)((-memw + 0x7ff) & ~0x7ff);
+ else
+ tmp = (sljit_uw)((-memw + 0xfff) & ~0xfff);
+
+ SLJIT_ASSERT(tmp >= (sljit_uw)-memw);
+ imm = get_imm(tmp);
+
+ if (imm != INVALID_IMM) {
+ FAIL_IF(push_inst32(compiler, SUB_WI | RD4(TMP_REG1) | RN4(mem & REG_MASK) | imm));
+ memw += (sljit_sw)tmp;
+ SLJIT_ASSERT(memw >= 0 && memw <= 0xfff - SSIZE_OF(sw));
+ } else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw));
+ FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, mem & REG_MASK)));
+ memw = 0;
+ }
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ } else if (memw >= (0x1000 - SSIZE_OF(sw))) {
+ if ((memw & 0xfff) >= (0x1000 - SSIZE_OF(sw))) {
+ imm = get_imm((sljit_uw)((memw + 0x1000) & ~0xfff));
+
+ if (imm != INVALID_IMM)
+ memw = (memw & 0xfff) - 0x1000;
+ } else {
+ imm = get_imm((sljit_uw)(memw & ~0xfff));
+
+ if (imm != INVALID_IMM)
+ memw &= 0xfff;
+ }
+
+ if (imm != INVALID_IMM) {
+ SLJIT_ASSERT(memw >= -0xff && memw <= 0xfff);
+ FAIL_IF(push_inst32(compiler, ADD_WI | RD4(TMP_REG1) | RN4(mem & REG_MASK) | imm));
+ } else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw));
+ FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, mem & REG_MASK)));
+ memw = 0;
+ }
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ }
+
+ flags = WORD_SIZE;
+
+ SLJIT_ASSERT(memw <= 0xfff - SSIZE_OF(sw) && memw >= -0xff);
+
+ if (type & SLJIT_MEM_STORE) {
+ flags |= STORE;
+ } else if (REG_PAIR_FIRST(reg) == (mem & REG_MASK)) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, REG_PAIR_SECOND(reg), mem, memw + SSIZE_OF(sw), TMP_REG2));
+ return emit_op_mem(compiler, WORD_SIZE, REG_PAIR_FIRST(reg), mem, memw, TMP_REG2);
+ }
+
+ FAIL_IF(emit_op_mem(compiler, flags, REG_PAIR_FIRST(reg), mem, memw, TMP_REG2));
+ return emit_op_mem(compiler, flags, REG_PAIR_SECOND(reg), mem, memw + SSIZE_OF(sw), TMP_REG2);
+ }
+
+ flags = 1 << 23;
+
+ if ((mem & REG_MASK) == 0) {
+ tmp = (sljit_uw)(memw & 0x7fc);
+ imm = get_imm((sljit_uw)((memw + (tmp <= 0x400 ? 0 : 0x400)) & ~0x3fc));
+
+ if (imm == INVALID_IMM) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw));
+ memw = 0;
+ } else {
+ FAIL_IF(push_inst32(compiler, MOV_WI | RD4(TMP_REG1) | imm));
+ memw = (memw & 0x3fc) >> 2;
+
+ if (tmp > 0x400) {
+ memw = 0x100 - memw;
+ flags = 0;
+ }
+
+ SLJIT_ASSERT(memw >= 0 && memw <= 0xff);
+ }
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ } else if (mem & OFFS_REG_MASK) {
+ FAIL_IF(push_inst32(compiler, ADD_W | RD4(TMP_REG1) | RN4(mem & REG_MASK) | RM4(OFFS_REG(mem)) | ((sljit_uw)(memw & 0x3) << 6)));
+ memw = 0;
+ mem = SLJIT_MEM1(TMP_REG1);
+ } else if (memw < 0) {
+ if ((-memw & ~0x3fc) == 0) {
+ flags = 0;
+ memw = -memw >> 2;
+ } else {
+ tmp = (sljit_uw)(-memw & 0x7fc);
+ imm = get_imm((sljit_uw)((-memw + (tmp <= 0x400 ? 0 : 0x400)) & ~0x3fc));
+
+ if (imm != INVALID_IMM) {
+ FAIL_IF(push_inst32(compiler, SUB_WI | RD4(TMP_REG1) | RN4(mem & REG_MASK) | imm));
+ memw = (-memw & 0x3fc) >> 2;
+
+ if (tmp <= 0x400)
+ flags = 0;
+ else
+ memw = 0x100 - memw;
+ } else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw));
+ FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, mem & REG_MASK)));
+ memw = 0;
+ }
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ }
+ } else if ((memw & ~0x3fc) != 0) {
+ tmp = (sljit_uw)(memw & 0x7fc);
+ imm = get_imm((sljit_uw)((memw + (tmp <= 0x400 ? 0 : 0x400)) & ~0x3fc));
+
+ if (imm != INVALID_IMM) {
+ FAIL_IF(push_inst32(compiler, ADD_WI | RD4(TMP_REG1) | RN4(mem & REG_MASK) | imm));
+ memw = (memw & 0x3fc) >> 2;
+
+ if (tmp > 0x400) {
+ memw = 0x100 - memw;
+ flags = 0;
+ }
+ } else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw));
+ FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, mem & REG_MASK)));
+ memw = 0;
+ }
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ } else
+ memw >>= 2;
+
+ SLJIT_ASSERT(memw >= 0 && memw <= 0xff);
+ return push_inst32(compiler, ((type & SLJIT_MEM_STORE) ? STRD : LDRD) | (sljit_ins)flags | RN4(mem & REG_MASK) | RT4(REG_PAIR_FIRST(reg)) | RD4(REG_PAIR_SECOND(reg)) | (sljit_ins)memw);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 flags;
+ sljit_ins inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem_update(compiler, type, reg, mem, memw));
+
+ if ((mem & OFFS_REG_MASK) || (memw > 255 || memw < -255))
+ return SLJIT_ERR_UNSUPPORTED;
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ switch (type & 0xff) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ case SLJIT_MOV_P:
+ flags = WORD_SIZE;
+ break;
+ case SLJIT_MOV_U8:
+ flags = BYTE_SIZE;
+ break;
+ case SLJIT_MOV_S8:
+ flags = BYTE_SIZE | SIGNED;
+ break;
+ case SLJIT_MOV_U16:
+ flags = HALF_SIZE;
+ break;
+ case SLJIT_MOV_S16:
+ flags = HALF_SIZE | SIGNED;
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ flags = WORD_SIZE;
+ break;
+ }
+
+ if (type & SLJIT_MEM_STORE)
+ flags |= STORE;
+
+ inst = sljit_mem32[flags] | 0x900;
+
+ if (!(type & SLJIT_MEM_POST))
+ inst |= 0x400;
+
+ if (memw >= 0)
+ inst |= 0x200;
+ else
+ memw = -memw;
+
+ return push_inst32(compiler, inst | RT4(reg) | RN4(mem & REG_MASK) | (sljit_ins)memw);
+}
+
+static sljit_s32 update_mem_addr(struct sljit_compiler *compiler, sljit_s32 *mem, sljit_sw *memw, sljit_s32 max_offset)
+{
+ sljit_s32 arg = *mem;
+ sljit_sw argw = *memw;
+ sljit_uw imm;
+
+ *mem = TMP_REG1;
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ *memw = 0;
+ return push_inst32(compiler, ADD_W | RD4(TMP_REG1) | RN4(arg & REG_MASK) | RM4(OFFS_REG(arg)) | ((sljit_uw)(argw & 0x3) << 6));
+ }
+
+ arg &= REG_MASK;
+
+ if (arg) {
+ if (argw <= max_offset && argw >= -0xff) {
+ *mem = arg;
+ return SLJIT_SUCCESS;
+ }
+
+ if (argw < 0) {
+ imm = get_imm((sljit_uw)(-argw & ~0xff));
+
+ if (imm) {
+ *memw = -(-argw & 0xff);
+ return push_inst32(compiler, SUB_WI | RD4(TMP_REG1) | RN4(arg) | imm);
+ }
+ } else if ((argw & 0xfff) <= max_offset) {
+ imm = get_imm((sljit_uw)(argw & ~0xfff));
+
+ if (imm) {
+ *memw = argw & 0xfff;
+ return push_inst32(compiler, ADD_WI | RD4(TMP_REG1) | RN4(arg) | imm);
+ }
+ } else {
+ imm = get_imm((sljit_uw)((argw | 0xfff) + 1));
+
+ if (imm) {
+ *memw = (argw & 0xfff) - 0x1000;
+ return push_inst32(compiler, ADD_WI | RD4(TMP_REG1) | RN4(arg) | imm);
+ }
+ }
+ }
+
+ imm = (sljit_uw)(argw & ~0xfff);
+
+ if ((argw & 0xfff) > max_offset) {
+ imm += 0x1000;
+ *memw = (argw & 0xfff) - 0x1000;
+ } else
+ *memw = argw & 0xfff;
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, imm));
+
+ if (arg == 0)
+ return SLJIT_SUCCESS;
+
+ return push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, arg));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw));
+
+ if (type & SLJIT_MEM_UNALIGNED_32)
+ return emit_fop_mem(compiler, ((type ^ SLJIT_32) & SLJIT_32) | ((type & SLJIT_MEM_STORE) ? 0 : FPU_LOAD), freg, mem, memw);
+
+ if (type & SLJIT_MEM_STORE) {
+ FAIL_IF(push_inst32(compiler, VMOV | (1 << 20) | DN4(freg) | RT4(TMP_REG2)));
+
+ if (type & SLJIT_32)
+ return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG2, mem, memw, TMP_REG1);
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 4));
+ mem |= SLJIT_MEM;
+
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG2, mem, memw, TMP_REG1));
+ FAIL_IF(push_inst32(compiler, VMOV | (1 << 20) | DN4(freg) | 0x80 | RT4(TMP_REG2)));
+ return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG2, mem, memw + 4, TMP_REG1);
+ }
+
+ if (type & SLJIT_32) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, mem, memw, TMP_REG1));
+ return push_inst32(compiler, VMOV | DN4(freg) | RT4(TMP_REG2));
+ }
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 4));
+ mem |= SLJIT_MEM;
+
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, mem, memw, TMP_REG1));
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, mem, memw + 4, TMP_REG1));
+ return push_inst32(compiler, VMOV2 | DM4(freg) | RT4(TMP_REG2) | RN4(TMP_REG1));
+}
+
+#undef FPU_LOAD
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ struct sljit_const *const_;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const));
+ PTR_FAIL_IF(!const_);
+ set_const(const_, compiler);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+ PTR_FAIL_IF(emit_imm32_const(compiler, dst_r, (sljit_uw)init_value));
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, dst_r, dst, dstw, TMP_REG2));
+ return const_;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ struct sljit_put_label *put_label;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label));
+ PTR_FAIL_IF(!put_label);
+ set_put_label(put_label, compiler, 0);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+ PTR_FAIL_IF(emit_imm32_const(compiler, dst_r, 0));
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, dst_r, dst, dstw, TMP_REG2));
+ return put_label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ sljit_u16 *inst = (sljit_u16*)addr;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 4, 0);
+ modify_imm32_const(inst, new_target);
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 4, 1);
+ inst = (sljit_u16 *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 4);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_32.c b/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_32.c
new file mode 100644
index 00000000000..e6853c98f67
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_32.c
@@ -0,0 +1,314 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* mips 32-bit arch dependent functions. */
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_ar, sljit_sw imm)
+{
+ if (!(imm & ~0xffff))
+ return push_inst(compiler, ORI | SA(0) | TA(dst_ar) | IMM(imm), dst_ar);
+
+ if (imm < 0 && imm >= SIMM_MIN)
+ return push_inst(compiler, ADDIU | SA(0) | TA(dst_ar) | IMM(imm), dst_ar);
+
+ FAIL_IF(push_inst(compiler, LUI | TA(dst_ar) | IMM(imm >> 16), dst_ar));
+ return (imm & 0xffff) ? push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar) : SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value)
+{
+ FAIL_IF(push_inst(compiler, LUI | T(dst) | IMM(init_value >> 16), DR(dst)));
+ return push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value), DR(dst));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ sljit_ins *inst = (sljit_ins *)addr;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 0);
+ SLJIT_ASSERT((inst[0] & 0xffe00000) == LUI && (inst[1] & 0xfc000000) == ORI);
+ inst[0] = (inst[0] & 0xffff0000) | ((new_target >> 16) & 0xffff);
+ inst[1] = (inst[1] & 0xffff0000) | (new_target & 0xffff);
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 1);
+ inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 2);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset);
+}
+
+static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_ins *ins_ptr, sljit_u32 *extra_space)
+{
+ sljit_u32 is_tail_call = *extra_space & SLJIT_CALL_RETURN;
+ sljit_u32 offset = 0;
+ sljit_s32 float_arg_count = 0;
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_ins prev_ins = NOP;
+ sljit_ins ins = NOP;
+ sljit_u8 offsets[4];
+ sljit_u8 *offsets_ptr = offsets;
+
+ SLJIT_ASSERT(reg_map[TMP_REG1] == 4 && freg_map[TMP_FREG1] == 12);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ /* See ABI description in sljit_emit_enter. */
+
+ while (arg_types) {
+ types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK);
+ *offsets_ptr = (sljit_u8)offset;
+
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset & 0x7) {
+ offset += sizeof(sljit_sw);
+ *offsets_ptr = (sljit_u8)offset;
+ }
+
+ if (word_arg_count == 0 && float_arg_count <= 1)
+ *offsets_ptr = (sljit_u8)(254 + float_arg_count);
+
+ offset += sizeof(sljit_f64);
+ float_arg_count++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (word_arg_count == 0 && float_arg_count <= 1)
+ *offsets_ptr = (sljit_u8)(254 + float_arg_count);
+
+ offset += sizeof(sljit_f32);
+ float_arg_count++;
+ break;
+ default:
+ offset += sizeof(sljit_sw);
+ word_arg_count++;
+ break;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ offsets_ptr++;
+ }
+
+ /* Stack is aligned to 16 bytes. */
+ SLJIT_ASSERT(offset <= 8 * sizeof(sljit_sw));
+
+ if (offset > 4 * sizeof(sljit_sw) && (!is_tail_call || offset > compiler->args_size)) {
+ if (is_tail_call) {
+ offset = (offset + sizeof(sljit_sw) + 15) & ~(sljit_uw)0xf;
+ FAIL_IF(emit_stack_frame_release(compiler, (sljit_s32)offset, &prev_ins));
+ *extra_space = offset;
+ } else {
+ FAIL_IF(push_inst(compiler, ADDIU | S(SLJIT_SP) | T(SLJIT_SP) | IMM(-16), DR(SLJIT_SP)));
+ *extra_space = 16;
+ }
+ } else {
+ if (is_tail_call)
+ FAIL_IF(emit_stack_frame_release(compiler, 0, &prev_ins));
+ *extra_space = 0;
+ }
+
+ while (types) {
+ --offsets_ptr;
+
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (*offsets_ptr < 4 * sizeof (sljit_sw)) {
+ if (prev_ins != NOP)
+ FAIL_IF(push_inst(compiler, prev_ins, MOVABLE_INS));
+
+ /* Must be preceded by at least one other argument,
+ * and its starting offset must be 8 because of alignment. */
+ SLJIT_ASSERT((*offsets_ptr >> 2) == 2);
+
+ prev_ins = MFC1 | TA(6) | FS(float_arg_count) | (1 << 11);
+ ins = MFC1 | TA(7) | FS(float_arg_count);
+ } else if (*offsets_ptr < 254)
+ ins = SDC1 | S(SLJIT_SP) | FT(float_arg_count) | IMM(*offsets_ptr);
+ else if (*offsets_ptr == 254)
+ ins = MOV_S | FMT_D | FS(SLJIT_FR0) | FD(TMP_FREG1);
+
+ float_arg_count--;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (*offsets_ptr < 4 * sizeof (sljit_sw))
+ ins = MFC1 | TA(4 + (*offsets_ptr >> 2)) | FS(float_arg_count);
+ else if (*offsets_ptr < 254)
+ ins = SWC1 | S(SLJIT_SP) | FT(float_arg_count) | IMM(*offsets_ptr);
+ else if (*offsets_ptr == 254)
+ ins = MOV_S | FMT_S | FS(SLJIT_FR0) | FD(TMP_FREG1);
+
+ float_arg_count--;
+ break;
+ default:
+ if (*offsets_ptr >= 4 * sizeof (sljit_sw))
+ ins = SW | S(SLJIT_SP) | T(word_arg_count) | IMM(*offsets_ptr);
+ else if ((*offsets_ptr >> 2) != word_arg_count - 1)
+ ins = ADDU | S(word_arg_count) | TA(0) | DA(4 + (*offsets_ptr >> 2));
+ else if (*offsets_ptr == 0)
+ ins = ADDU | S(SLJIT_R0) | TA(0) | DA(4);
+
+ word_arg_count--;
+ break;
+ }
+
+ if (ins != NOP) {
+ if (prev_ins != NOP)
+ FAIL_IF(push_inst(compiler, prev_ins, MOVABLE_INS));
+ prev_ins = ins;
+ ins = NOP;
+ }
+
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ *ins_ptr = prev_ins;
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ struct sljit_jump *jump;
+ sljit_u32 extra_space = 0;
+ sljit_ins ins = NOP;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG) {
+ extra_space = (sljit_u32)type;
+ PTR_FAIL_IF(call_with_args(compiler, arg_types, &ins, &extra_space));
+ } else if (type & SLJIT_CALL_RETURN)
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, 0, &ins));
+
+ SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2);
+
+ if (ins == NOP && compiler->delay_slot != UNMOVABLE_INS)
+ jump->flags |= IS_MOVABLE;
+
+ if (!(type & SLJIT_CALL_RETURN) || extra_space > 0) {
+ jump->flags |= IS_JAL;
+
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ jump->flags |= IS_CALL;
+
+ PTR_FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ } else
+ PTR_FAIL_IF(push_inst(compiler, JR | S(PIC_ADDR_REG), UNMOVABLE_INS));
+
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, ins, UNMOVABLE_INS));
+
+ /* Maximum number of instructions required for generating a constant. */
+ compiler->size += 2;
+
+ if (extra_space == 0)
+ return jump;
+
+ if (type & SLJIT_CALL_RETURN)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, RETURN_ADDR_REG,
+ SLJIT_MEM1(SLJIT_SP), (sljit_sw)(extra_space - sizeof(sljit_sw))));
+
+ if (type & SLJIT_CALL_RETURN)
+ PTR_FAIL_IF(push_inst(compiler, JR | SA(RETURN_ADDR_REG), UNMOVABLE_INS));
+
+ PTR_FAIL_IF(push_inst(compiler, ADDIU | S(SLJIT_SP) | T(SLJIT_SP) | IMM(extra_space),
+ (type & SLJIT_CALL_RETURN) ? UNMOVABLE_INS : DR(SLJIT_SP)));
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u32 extra_space = (sljit_u32)type;
+ sljit_ins ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(PIC_ADDR_REG), src, srcw));
+ src = PIC_ADDR_REG;
+ srcw = 0;
+ }
+
+ if ((type & 0xff) == SLJIT_CALL_REG_ARG) {
+ if (type & SLJIT_CALL_RETURN) {
+ if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, ADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG)));
+ src = PIC_ADDR_REG;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0, &ins));
+
+ if (ins != NOP)
+ FAIL_IF(push_inst(compiler, ins, MOVABLE_INS));
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+ }
+
+ SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2);
+
+ if (src & SLJIT_IMM)
+ FAIL_IF(load_immediate(compiler, DR(PIC_ADDR_REG), srcw));
+ else if (src != PIC_ADDR_REG)
+ FAIL_IF(push_inst(compiler, ADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG)));
+
+ FAIL_IF(call_with_args(compiler, arg_types, &ins, &extra_space));
+
+ /* Register input. */
+ if (!(type & SLJIT_CALL_RETURN) || extra_space > 0)
+ FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ else
+ FAIL_IF(push_inst(compiler, JR | S(PIC_ADDR_REG), UNMOVABLE_INS));
+ FAIL_IF(push_inst(compiler, ins, UNMOVABLE_INS));
+
+ if (extra_space == 0)
+ return SLJIT_SUCCESS;
+
+ if (type & SLJIT_CALL_RETURN)
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, RETURN_ADDR_REG,
+ SLJIT_MEM1(SLJIT_SP), (sljit_sw)(extra_space - sizeof(sljit_sw))));
+
+ if (type & SLJIT_CALL_RETURN)
+ FAIL_IF(push_inst(compiler, JR | SA(RETURN_ADDR_REG), UNMOVABLE_INS));
+
+ return push_inst(compiler, ADDIU | S(SLJIT_SP) | T(SLJIT_SP) | IMM(extra_space),
+ (type & SLJIT_CALL_RETURN) ? UNMOVABLE_INS : DR(SLJIT_SP));
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_64.c b/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_64.c
new file mode 100644
index 00000000000..d2a5924f8e9
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_64.c
@@ -0,0 +1,319 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* mips 64-bit arch dependent functions. */
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_ar, sljit_sw imm)
+{
+ sljit_s32 shift = 32;
+ sljit_s32 shift2;
+ sljit_s32 inv = 0;
+ sljit_ins ins;
+ sljit_uw uimm;
+
+ if (!(imm & ~0xffff))
+ return push_inst(compiler, ORI | SA(0) | TA(dst_ar) | IMM(imm), dst_ar);
+
+ if (imm < 0 && imm >= SIMM_MIN)
+ return push_inst(compiler, ADDIU | SA(0) | TA(dst_ar) | IMM(imm), dst_ar);
+
+ if (imm <= 0x7fffffffl && imm >= -0x80000000l) {
+ FAIL_IF(push_inst(compiler, LUI | TA(dst_ar) | IMM(imm >> 16), dst_ar));
+ return (imm & 0xffff) ? push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar) : SLJIT_SUCCESS;
+ }
+
+ /* Zero extended number. */
+ uimm = (sljit_uw)imm;
+ if (imm < 0) {
+ uimm = ~(sljit_uw)imm;
+ inv = 1;
+ }
+
+ while (!(uimm & 0xff00000000000000l)) {
+ shift -= 8;
+ uimm <<= 8;
+ }
+
+ if (!(uimm & 0xf000000000000000l)) {
+ shift -= 4;
+ uimm <<= 4;
+ }
+
+ if (!(uimm & 0xc000000000000000l)) {
+ shift -= 2;
+ uimm <<= 2;
+ }
+
+ if ((sljit_sw)uimm < 0) {
+ uimm >>= 1;
+ shift += 1;
+ }
+ SLJIT_ASSERT(((uimm & 0xc000000000000000l) == 0x4000000000000000l) && (shift > 0) && (shift <= 32));
+
+ if (inv)
+ uimm = ~uimm;
+
+ FAIL_IF(push_inst(compiler, LUI | TA(dst_ar) | IMM(uimm >> 48), dst_ar));
+ if (uimm & 0x0000ffff00000000l)
+ FAIL_IF(push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(uimm >> 32), dst_ar));
+
+ imm &= (1l << shift) - 1;
+ if (!(imm & ~0xffff)) {
+ ins = (shift == 32) ? DSLL32 : DSLL;
+ if (shift < 32)
+ ins |= SH_IMM(shift);
+ FAIL_IF(push_inst(compiler, ins | TA(dst_ar) | DA(dst_ar), dst_ar));
+ return !(imm & 0xffff) ? SLJIT_SUCCESS : push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar);
+ }
+
+ /* Double shifts needs to be performed. */
+ uimm <<= 32;
+ shift2 = shift - 16;
+
+ while (!(uimm & 0xf000000000000000l)) {
+ shift2 -= 4;
+ uimm <<= 4;
+ }
+
+ if (!(uimm & 0xc000000000000000l)) {
+ shift2 -= 2;
+ uimm <<= 2;
+ }
+
+ if (!(uimm & 0x8000000000000000l)) {
+ shift2--;
+ uimm <<= 1;
+ }
+
+ SLJIT_ASSERT((uimm & 0x8000000000000000l) && (shift2 > 0) && (shift2 <= 16));
+
+ FAIL_IF(push_inst(compiler, DSLL | TA(dst_ar) | DA(dst_ar) | SH_IMM(shift - shift2), dst_ar));
+ FAIL_IF(push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(uimm >> 48), dst_ar));
+ FAIL_IF(push_inst(compiler, DSLL | TA(dst_ar) | DA(dst_ar) | SH_IMM(shift2), dst_ar));
+
+ imm &= (1l << shift2) - 1;
+ return !(imm & 0xffff) ? SLJIT_SUCCESS : push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar);
+}
+
+static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value)
+{
+ FAIL_IF(push_inst(compiler, LUI | T(dst) | IMM(init_value >> 48), DR(dst)));
+ FAIL_IF(push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value >> 32), DR(dst)));
+ FAIL_IF(push_inst(compiler, DSLL | T(dst) | D(dst) | SH_IMM(16), DR(dst)));
+ FAIL_IF(push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value >> 16), DR(dst)));
+ FAIL_IF(push_inst(compiler, DSLL | T(dst) | D(dst) | SH_IMM(16), DR(dst)));
+ return push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value), DR(dst));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ sljit_ins *inst = (sljit_ins *)addr;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 6, 0);
+ inst[0] = (inst[0] & 0xffff0000) | ((sljit_ins)(new_target >> 48) & 0xffff);
+ inst[1] = (inst[1] & 0xffff0000) | ((sljit_ins)(new_target >> 32) & 0xffff);
+ inst[3] = (inst[3] & 0xffff0000) | ((sljit_ins)(new_target >> 16) & 0xffff);
+ inst[5] = (inst[5] & 0xffff0000) | ((sljit_ins)new_target & 0xffff);
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 6, 1);
+ inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 6);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset);
+}
+
+static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_ins *ins_ptr)
+{
+ sljit_s32 arg_count = 0;
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 float_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_ins prev_ins = *ins_ptr;
+ sljit_ins ins = NOP;
+
+ SLJIT_ASSERT(reg_map[TMP_REG1] == 4 && freg_map[TMP_FREG1] == 12);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK);
+
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ case SLJIT_ARG_TYPE_F32:
+ arg_count++;
+ float_arg_count++;
+ break;
+ default:
+ arg_count++;
+ word_arg_count++;
+ break;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ while (types) {
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (arg_count != float_arg_count)
+ ins = MOV_S | FMT_D | FS(float_arg_count) | FD(arg_count);
+ else if (arg_count == 1)
+ ins = MOV_S | FMT_D | FS(SLJIT_FR0) | FD(TMP_FREG1);
+ arg_count--;
+ float_arg_count--;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (arg_count != float_arg_count)
+ ins = MOV_S | FMT_S | FS(float_arg_count) | FD(arg_count);
+ else if (arg_count == 1)
+ ins = MOV_S | FMT_S | FS(SLJIT_FR0) | FD(TMP_FREG1);
+ arg_count--;
+ float_arg_count--;
+ break;
+ default:
+ if (arg_count != word_arg_count)
+ ins = DADDU | S(word_arg_count) | TA(0) | D(arg_count);
+ else if (arg_count == 1)
+ ins = DADDU | S(SLJIT_R0) | TA(0) | DA(4);
+ arg_count--;
+ word_arg_count--;
+ break;
+ }
+
+ if (ins != NOP) {
+ if (prev_ins != NOP)
+ FAIL_IF(push_inst(compiler, prev_ins, MOVABLE_INS));
+ prev_ins = ins;
+ ins = NOP;
+ }
+
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ *ins_ptr = prev_ins;
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ struct sljit_jump *jump;
+ sljit_ins ins = NOP;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+
+ if (type & SLJIT_CALL_RETURN)
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, 0, &ins));
+
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ PTR_FAIL_IF(call_with_args(compiler, arg_types, &ins));
+
+ SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2);
+
+ if (ins == NOP && compiler->delay_slot != UNMOVABLE_INS)
+ jump->flags |= IS_MOVABLE;
+
+ if (!(type & SLJIT_CALL_RETURN)) {
+ jump->flags |= IS_JAL;
+
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ jump->flags |= IS_CALL;
+
+ PTR_FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ } else
+ PTR_FAIL_IF(push_inst(compiler, JR | S(PIC_ADDR_REG), UNMOVABLE_INS));
+
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, ins, UNMOVABLE_INS));
+
+ /* Maximum number of instructions required for generating a constant. */
+ compiler->size += 6;
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_ins ins = NOP;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(PIC_ADDR_REG), src, srcw));
+ src = PIC_ADDR_REG;
+ srcw = 0;
+ }
+
+ if ((type & 0xff) == SLJIT_CALL_REG_ARG) {
+ if (type & SLJIT_CALL_RETURN) {
+ if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, DADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG)));
+ src = PIC_ADDR_REG;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0, &ins));
+
+ if (ins != NOP)
+ FAIL_IF(push_inst(compiler, ins, MOVABLE_INS));
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+ }
+
+ SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2);
+
+ if (src & SLJIT_IMM)
+ FAIL_IF(load_immediate(compiler, DR(PIC_ADDR_REG), srcw));
+ else if (src != PIC_ADDR_REG)
+ FAIL_IF(push_inst(compiler, DADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG)));
+
+ if (type & SLJIT_CALL_RETURN)
+ FAIL_IF(emit_stack_frame_release(compiler, 0, &ins));
+
+ FAIL_IF(call_with_args(compiler, arg_types, &ins));
+
+ /* Register input. */
+ if (!(type & SLJIT_CALL_RETURN))
+ FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ else
+ FAIL_IF(push_inst(compiler, JR | S(PIC_ADDR_REG), UNMOVABLE_INS));
+ return push_inst(compiler, ins, UNMOVABLE_INS);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_common.c b/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_common.c
new file mode 100644
index 00000000000..9afe901c382
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_common.c
@@ -0,0 +1,3720 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Latest MIPS architecture. */
+
+#ifndef __mips_hard_float
+/* Disable automatic detection, covers both -msoft-float and -mno-float */
+#undef SLJIT_IS_FPU_AVAILABLE
+#define SLJIT_IS_FPU_AVAILABLE 0
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ return "MIPS32-R6" SLJIT_CPUINFO;
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ return "MIPS64-R6" SLJIT_CPUINFO;
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+#elif (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 2)
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ return "MIPS32-R2" SLJIT_CPUINFO;
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ return "MIPS64-R2" SLJIT_CPUINFO;
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+#elif (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ return "MIPS32-R1" SLJIT_CPUINFO;
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ return "MIPS64-R1" SLJIT_CPUINFO;
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+#else /* SLJIT_MIPS_REV < 1 */
+ return "MIPS III" SLJIT_CPUINFO;
+#endif /* SLJIT_MIPS_REV >= 6 */
+}
+
+/* Length of an instruction word
+ Both for mips-32 and mips-64 */
+typedef sljit_u32 sljit_ins;
+
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
+#define TMP_REG3 (SLJIT_NUMBER_OF_REGISTERS + 4)
+
+/* For position independent code, t9 must contain the function address. */
+#define PIC_ADDR_REG TMP_REG2
+
+/* Floating point status register. */
+#define FCSR_REG 31
+/* Return address register. */
+#define RETURN_ADDR_REG 31
+
+/* Flags are kept in volatile registers. */
+#define EQUAL_FLAG 3
+#define OTHER_FLAG 1
+
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
+#define TMP_FREG3 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3)
+
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = {
+ 0, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 24, 23, 22, 21, 20, 19, 18, 17, 16, 29, 4, 25, 31
+};
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 4] = {
+ 0, 0, 14, 2, 4, 6, 8, 18, 30, 28, 26, 24, 22, 20, 12, 10, 16
+};
+
+#else
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 4] = {
+ 0, 0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 1, 2, 3, 4, 5, 6, 7, 8, 9, 31, 30, 29, 28, 27, 26, 25, 24, 12, 11, 10
+};
+
+#endif
+
+/* --------------------------------------------------------------------- */
+/* Instrucion forms */
+/* --------------------------------------------------------------------- */
+
+#define S(s) ((sljit_ins)reg_map[s] << 21)
+#define T(t) ((sljit_ins)reg_map[t] << 16)
+#define D(d) ((sljit_ins)reg_map[d] << 11)
+#define FT(t) ((sljit_ins)freg_map[t] << 16)
+#define FS(s) ((sljit_ins)freg_map[s] << 11)
+#define FD(d) ((sljit_ins)freg_map[d] << 6)
+/* Absolute registers. */
+#define SA(s) ((sljit_ins)(s) << 21)
+#define TA(t) ((sljit_ins)(t) << 16)
+#define DA(d) ((sljit_ins)(d) << 11)
+#define IMM(imm) ((sljit_ins)(imm) & 0xffff)
+#define SH_IMM(imm) ((sljit_ins)(imm) << 6)
+
+#define DR(dr) (reg_map[dr])
+#define FR(dr) (freg_map[dr])
+#define HI(opcode) ((sljit_ins)(opcode) << 26)
+#define LO(opcode) ((sljit_ins)(opcode))
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+/* CMP.cond.fmt */
+/* S = (20 << 21) D = (21 << 21) */
+#define CMP_FMT_S (20 << 21)
+#endif /* SLJIT_MIPS_REV >= 6 */
+/* S = (16 << 21) D = (17 << 21) */
+#define FMT_S (16 << 21)
+#define FMT_D (17 << 21)
+
+#define ABS_S (HI(17) | FMT_S | LO(5))
+#define ADD_S (HI(17) | FMT_S | LO(0))
+#define ADDIU (HI(9))
+#define ADDU (HI(0) | LO(33))
+#define AND (HI(0) | LO(36))
+#define ANDI (HI(12))
+#define B (HI(4))
+#define BAL (HI(1) | (17 << 16))
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#define BC1EQZ (HI(17) | (9 << 21) | FT(TMP_FREG3))
+#define BC1NEZ (HI(17) | (13 << 21) | FT(TMP_FREG3))
+#else /* SLJIT_MIPS_REV < 6 */
+#define BC1F (HI(17) | (8 << 21))
+#define BC1T (HI(17) | (8 << 21) | (1 << 16))
+#endif /* SLJIT_MIPS_REV >= 6 */
+#define BEQ (HI(4))
+#define BGEZ (HI(1) | (1 << 16))
+#define BGTZ (HI(7))
+#define BLEZ (HI(6))
+#define BLTZ (HI(1) | (0 << 16))
+#define BNE (HI(5))
+#define BREAK (HI(0) | LO(13))
+#define CFC1 (HI(17) | (2 << 21))
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#define C_EQ_S (HI(17) | CMP_FMT_S | LO(2))
+#define C_OLE_S (HI(17) | CMP_FMT_S | LO(6))
+#define C_OLT_S (HI(17) | CMP_FMT_S | LO(4))
+#define C_UEQ_S (HI(17) | CMP_FMT_S | LO(3))
+#define C_ULE_S (HI(17) | CMP_FMT_S | LO(7))
+#define C_ULT_S (HI(17) | CMP_FMT_S | LO(5))
+#define C_UN_S (HI(17) | CMP_FMT_S | LO(1))
+#define C_FD (FD(TMP_FREG3))
+#else /* SLJIT_MIPS_REV < 6 */
+#define C_EQ_S (HI(17) | FMT_S | LO(50))
+#define C_OLE_S (HI(17) | FMT_S | LO(54))
+#define C_OLT_S (HI(17) | FMT_S | LO(52))
+#define C_UEQ_S (HI(17) | FMT_S | LO(51))
+#define C_ULE_S (HI(17) | FMT_S | LO(55))
+#define C_ULT_S (HI(17) | FMT_S | LO(53))
+#define C_UN_S (HI(17) | FMT_S | LO(49))
+#define C_FD (0)
+#endif /* SLJIT_MIPS_REV >= 6 */
+#define CVT_S_S (HI(17) | FMT_S | LO(32))
+#define DADDIU (HI(25))
+#define DADDU (HI(0) | LO(45))
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#define DDIV (HI(0) | (2 << 6) | LO(30))
+#define DDIVU (HI(0) | (2 << 6) | LO(31))
+#define DMOD (HI(0) | (3 << 6) | LO(30))
+#define DMODU (HI(0) | (3 << 6) | LO(31))
+#define DIV (HI(0) | (2 << 6) | LO(26))
+#define DIVU (HI(0) | (2 << 6) | LO(27))
+#define DMUH (HI(0) | (3 << 6) | LO(28))
+#define DMUHU (HI(0) | (3 << 6) | LO(29))
+#define DMUL (HI(0) | (2 << 6) | LO(28))
+#define DMULU (HI(0) | (2 << 6) | LO(29))
+#else /* SLJIT_MIPS_REV < 6 */
+#define DDIV (HI(0) | LO(30))
+#define DDIVU (HI(0) | LO(31))
+#define DIV (HI(0) | LO(26))
+#define DIVU (HI(0) | LO(27))
+#define DMULT (HI(0) | LO(28))
+#define DMULTU (HI(0) | LO(29))
+#endif /* SLJIT_MIPS_REV >= 6 */
+#define DIV_S (HI(17) | FMT_S | LO(3))
+#define DINSU (HI(31) | LO(6))
+#define DROTR (HI(0) | (1 << 21) | LO(58))
+#define DROTR32 (HI(0) | (1 << 21) | LO(62))
+#define DROTRV (HI(0) | (1 << 6) | LO(22))
+#define DSLL (HI(0) | LO(56))
+#define DSLL32 (HI(0) | LO(60))
+#define DSLLV (HI(0) | LO(20))
+#define DSRA (HI(0) | LO(59))
+#define DSRA32 (HI(0) | LO(63))
+#define DSRAV (HI(0) | LO(23))
+#define DSRL (HI(0) | LO(58))
+#define DSRL32 (HI(0) | LO(62))
+#define DSRLV (HI(0) | LO(22))
+#define DSUBU (HI(0) | LO(47))
+#define J (HI(2))
+#define JAL (HI(3))
+#define JALR (HI(0) | LO(9))
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#define JR (HI(0) | LO(9))
+#else /* SLJIT_MIPS_REV < 6 */
+#define JR (HI(0) | LO(8))
+#endif /* SLJIT_MIPS_REV >= 6 */
+#define LD (HI(55))
+#define LDL (HI(26))
+#define LDR (HI(27))
+#define LDC1 (HI(53))
+#define LUI (HI(15))
+#define LW (HI(35))
+#define LWL (HI(34))
+#define LWR (HI(38))
+#define LWC1 (HI(49))
+#define MFC1 (HI(17))
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#define MOD (HI(0) | (3 << 6) | LO(26))
+#define MODU (HI(0) | (3 << 6) | LO(27))
+#else /* SLJIT_MIPS_REV < 6 */
+#define MFHI (HI(0) | LO(16))
+#define MFLO (HI(0) | LO(18))
+#endif /* SLJIT_MIPS_REV >= 6 */
+#define MOV_S (HI(17) | FMT_S | LO(6))
+#define MTC1 (HI(17) | (4 << 21))
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#define MUH (HI(0) | (3 << 6) | LO(24))
+#define MUHU (HI(0) | (3 << 6) | LO(25))
+#define MUL (HI(0) | (2 << 6) | LO(24))
+#define MULU (HI(0) | (2 << 6) | LO(25))
+#else /* SLJIT_MIPS_REV < 6 */
+#define MULT (HI(0) | LO(24))
+#define MULTU (HI(0) | LO(25))
+#endif /* SLJIT_MIPS_REV >= 6 */
+#define MUL_S (HI(17) | FMT_S | LO(2))
+#define NEG_S (HI(17) | FMT_S | LO(7))
+#define NOP (HI(0) | LO(0))
+#define NOR (HI(0) | LO(39))
+#define OR (HI(0) | LO(37))
+#define ORI (HI(13))
+#define ROTR (HI(0) | (1 << 21) | LO(2))
+#define ROTRV (HI(0) | (1 << 6) | LO(6))
+#define SD (HI(63))
+#define SDL (HI(44))
+#define SDR (HI(45))
+#define SDC1 (HI(61))
+#define SLT (HI(0) | LO(42))
+#define SLTI (HI(10))
+#define SLTIU (HI(11))
+#define SLTU (HI(0) | LO(43))
+#define SLL (HI(0) | LO(0))
+#define SLLV (HI(0) | LO(4))
+#define SRL (HI(0) | LO(2))
+#define SRLV (HI(0) | LO(6))
+#define SRA (HI(0) | LO(3))
+#define SRAV (HI(0) | LO(7))
+#define SUB_S (HI(17) | FMT_S | LO(1))
+#define SUBU (HI(0) | LO(35))
+#define SW (HI(43))
+#define SWL (HI(42))
+#define SWR (HI(46))
+#define SWC1 (HI(57))
+#define TRUNC_W_S (HI(17) | FMT_S | LO(13))
+#define XOR (HI(0) | LO(38))
+#define XORI (HI(14))
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+#define CLZ (HI(28) | LO(32))
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#define DCLZ (LO(18))
+#else /* SLJIT_MIPS_REV < 6 */
+#define DCLZ (HI(28) | LO(36))
+#define MOVF (HI(0) | (0 << 16) | LO(1))
+#define MOVN (HI(0) | LO(11))
+#define MOVT (HI(0) | (1 << 16) | LO(1))
+#define MOVZ (HI(0) | LO(10))
+#define MUL (HI(28) | LO(2))
+#endif /* SLJIT_MIPS_REV >= 6 */
+#define PREF (HI(51))
+#define PREFX (HI(19) | LO(15))
+#define SEB (HI(31) | (16 << 6) | LO(32))
+#define SEH (HI(31) | (24 << 6) | LO(32))
+#endif /* SLJIT_MIPS_REV >= 1 */
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#define ADDU_W ADDU
+#define ADDIU_W ADDIU
+#define SLL_W SLL
+#define SRA_W SRA
+#define SUBU_W SUBU
+#define STORE_W SW
+#define LOAD_W LW
+#else
+#define ADDU_W DADDU
+#define ADDIU_W DADDIU
+#define SLL_W DSLL
+#define SRA_W DSRA
+#define SUBU_W DSUBU
+#define STORE_W SD
+#define LOAD_W LD
+#endif
+
+#define SIMM_MAX (0x7fff)
+#define SIMM_MIN (-0x8000)
+#define UIMM_MAX (0xffff)
+
+/* dest_reg is the absolute name of the register
+ Useful for reordering instructions in the delay slot. */
+static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_ins ins, sljit_s32 delay_slot)
+{
+ sljit_ins *ptr = (sljit_ins*)ensure_buf(compiler, sizeof(sljit_ins));
+ SLJIT_ASSERT(delay_slot == MOVABLE_INS || delay_slot >= UNMOVABLE_INS
+ || (sljit_ins)delay_slot == ((ins >> 11) & 0x1f)
+ || (sljit_ins)delay_slot == ((ins >> 16) & 0x1f));
+ FAIL_IF(!ptr);
+ *ptr = ins;
+ compiler->size++;
+ compiler->delay_slot = delay_slot;
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_ins invert_branch(sljit_uw flags)
+{
+ if (flags & IS_BIT26_COND)
+ return (1 << 26);
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ if (flags & IS_BIT23_COND)
+ return (1 << 23);
+#endif /* SLJIT_MIPS_REV >= 6 */
+ return (1 << 16);
+}
+
+static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_ins *code, sljit_sw executable_offset)
+{
+ sljit_sw diff;
+ sljit_uw target_addr;
+ sljit_ins *inst;
+ sljit_ins saved_inst;
+
+ inst = (sljit_ins *)jump->addr;
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ if (jump->flags & (SLJIT_REWRITABLE_JUMP | IS_CALL))
+ goto exit;
+#else
+ if (jump->flags & SLJIT_REWRITABLE_JUMP)
+ goto exit;
+#endif
+
+ if (jump->flags & JUMP_ADDR)
+ target_addr = jump->u.target;
+ else {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ target_addr = (sljit_uw)(code + jump->u.label->size) + (sljit_uw)executable_offset;
+ }
+
+ if (jump->flags & IS_COND)
+ inst--;
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (jump->flags & IS_CALL)
+ goto preserve_addr;
+#endif
+
+ /* B instructions. */
+ if (jump->flags & IS_MOVABLE) {
+ diff = ((sljit_sw)target_addr - (sljit_sw)inst - executable_offset) >> 2;
+ if (diff <= SIMM_MAX && diff >= SIMM_MIN) {
+ jump->flags |= PATCH_B;
+
+ if (!(jump->flags & IS_COND)) {
+ inst[0] = inst[-1];
+ inst[-1] = (jump->flags & IS_JAL) ? BAL : B;
+ jump->addr -= sizeof(sljit_ins);
+ return inst;
+ }
+ saved_inst = inst[0];
+ inst[0] = inst[-1];
+ inst[-1] = saved_inst ^ invert_branch(jump->flags);
+ jump->addr -= 2 * sizeof(sljit_ins);
+ return inst;
+ }
+ } else {
+ diff = ((sljit_sw)target_addr - (sljit_sw)(inst + 1) - executable_offset) >> 2;
+ if (diff <= SIMM_MAX && diff >= SIMM_MIN) {
+ jump->flags |= PATCH_B;
+
+ if (!(jump->flags & IS_COND)) {
+ inst[0] = (jump->flags & IS_JAL) ? BAL : B;
+ /* Keep inst[1] */
+ return inst + 1;
+ }
+ inst[0] ^= invert_branch(jump->flags);
+ inst[1] = NOP;
+ jump->addr -= sizeof(sljit_ins);
+ return inst + 1;
+ }
+ }
+
+ if (jump->flags & IS_COND) {
+ if ((jump->flags & IS_MOVABLE) && (target_addr & ~(sljit_uw)0xfffffff) == ((jump->addr + 2 * sizeof(sljit_ins)) & ~(sljit_uw)0xfffffff)) {
+ jump->flags |= PATCH_J;
+ saved_inst = inst[0];
+ inst[0] = inst[-1];
+ inst[-1] = (saved_inst & 0xffff0000) | 3;
+ inst[1] = J;
+ inst[2] = NOP;
+ return inst + 2;
+ }
+ else if ((target_addr & ~(sljit_uw)0xfffffff) == ((jump->addr + 3 * sizeof(sljit_ins)) & ~(sljit_uw)0xfffffff)) {
+ jump->flags |= PATCH_J;
+ inst[0] = (inst[0] & 0xffff0000) | 3;
+ inst[1] = NOP;
+ inst[2] = J;
+ inst[3] = NOP;
+ jump->addr += sizeof(sljit_ins);
+ return inst + 3;
+ }
+ }
+ else {
+ /* J instuctions. */
+ if ((jump->flags & IS_MOVABLE) && (target_addr & ~(sljit_uw)0xfffffff) == (jump->addr & ~(sljit_uw)0xfffffff)) {
+ jump->flags |= PATCH_J;
+ inst[0] = inst[-1];
+ inst[-1] = (jump->flags & IS_JAL) ? JAL : J;
+ jump->addr -= sizeof(sljit_ins);
+ return inst;
+ }
+
+ if ((target_addr & ~(sljit_uw)0xfffffff) == ((jump->addr + sizeof(sljit_ins)) & ~(sljit_uw)0xfffffff)) {
+ jump->flags |= PATCH_J;
+ inst[0] = (jump->flags & IS_JAL) ? JAL : J;
+ /* Keep inst[1] */
+ return inst + 1;
+ }
+ }
+
+ if (jump->flags & IS_COND)
+ inst++;
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+preserve_addr:
+ if (target_addr <= 0x7fffffff) {
+ jump->flags |= PATCH_ABS32;
+ if (jump->flags & IS_COND)
+ inst[-1] -= 4;
+
+ inst[2] = inst[0];
+ inst[3] = inst[1];
+ return inst + 3;
+ }
+ if (target_addr <= 0x7fffffffffffl) {
+ jump->flags |= PATCH_ABS48;
+ if (jump->flags & IS_COND)
+ inst[-1] -= 2;
+
+ inst[4] = inst[0];
+ inst[5] = inst[1];
+ return inst + 5;
+ }
+#endif
+
+exit:
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ inst[2] = inst[0];
+ inst[3] = inst[1];
+ return inst + 3;
+#else
+ inst[6] = inst[0];
+ inst[7] = inst[1];
+ return inst + 7;
+#endif
+}
+
+#ifdef __GNUC__
+static __attribute__ ((noinline)) void sljit_cache_flush(void* code, void* code_ptr)
+{
+ SLJIT_CACHE_FLUSH(code, code_ptr);
+}
+#endif
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+
+static SLJIT_INLINE sljit_sw put_label_get_length(struct sljit_put_label *put_label, sljit_uw max_label)
+{
+ if (max_label < 0x80000000l) {
+ put_label->flags = PATCH_ABS32;
+ return 1;
+ }
+
+ if (max_label < 0x800000000000l) {
+ put_label->flags = PATCH_ABS48;
+ return 3;
+ }
+
+ put_label->flags = 0;
+ return 5;
+}
+
+#endif /* SLJIT_CONFIG_MIPS_64 */
+
+static SLJIT_INLINE void load_addr_to_reg(void *dst, sljit_u32 reg)
+{
+ struct sljit_jump *jump;
+ struct sljit_put_label *put_label;
+ sljit_uw flags;
+ sljit_ins *inst;
+ sljit_uw addr;
+
+ if (reg != 0) {
+ jump = (struct sljit_jump*)dst;
+ flags = jump->flags;
+ inst = (sljit_ins*)jump->addr;
+ addr = (flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target;
+ } else {
+ put_label = (struct sljit_put_label*)dst;
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ flags = put_label->flags;
+#endif
+ inst = (sljit_ins*)put_label->addr;
+ addr = put_label->label->addr;
+ reg = *inst;
+ }
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ inst[0] = LUI | T(reg) | IMM(addr >> 16);
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ if (flags & PATCH_ABS32) {
+ SLJIT_ASSERT(addr < 0x80000000l);
+ inst[0] = LUI | T(reg) | IMM(addr >> 16);
+ }
+ else if (flags & PATCH_ABS48) {
+ SLJIT_ASSERT(addr < 0x800000000000l);
+ inst[0] = LUI | T(reg) | IMM(addr >> 32);
+ inst[1] = ORI | S(reg) | T(reg) | IMM((addr >> 16) & 0xffff);
+ inst[2] = DSLL | T(reg) | D(reg) | SH_IMM(16);
+ inst += 2;
+ }
+ else {
+ inst[0] = LUI | T(reg) | IMM(addr >> 48);
+ inst[1] = ORI | S(reg) | T(reg) | IMM((addr >> 32) & 0xffff);
+ inst[2] = DSLL | T(reg) | D(reg) | SH_IMM(16);
+ inst[3] = ORI | S(reg) | T(reg) | IMM((addr >> 16) & 0xffff);
+ inst[4] = DSLL | T(reg) | D(reg) | SH_IMM(16);
+ inst += 4;
+ }
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+ inst[1] = ORI | S(reg) | T(reg) | IMM(addr & 0xffff);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf;
+ sljit_ins *code;
+ sljit_ins *code_ptr;
+ sljit_ins *buf_ptr;
+ sljit_ins *buf_end;
+ sljit_uw word_count;
+ sljit_uw next_addr;
+ sljit_sw executable_offset;
+ sljit_uw addr;
+
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ struct sljit_const *const_;
+ struct sljit_put_label *put_label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_generate_code(compiler));
+ reverse_buf(compiler);
+
+ code = (sljit_ins*)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_ins), compiler->exec_allocator_data);
+ PTR_FAIL_WITH_EXEC_IF(code);
+ buf = compiler->buf;
+
+ code_ptr = code;
+ word_count = 0;
+ next_addr = 0;
+ executable_offset = SLJIT_EXEC_OFFSET(code);
+
+ label = compiler->labels;
+ jump = compiler->jumps;
+ const_ = compiler->consts;
+ put_label = compiler->put_labels;
+
+ do {
+ buf_ptr = (sljit_ins*)buf->memory;
+ buf_end = buf_ptr + (buf->used_size >> 2);
+ do {
+ *code_ptr = *buf_ptr++;
+ if (next_addr == word_count) {
+ SLJIT_ASSERT(!label || label->size >= word_count);
+ SLJIT_ASSERT(!jump || jump->addr >= word_count);
+ SLJIT_ASSERT(!const_ || const_->addr >= word_count);
+ SLJIT_ASSERT(!put_label || put_label->addr >= word_count);
+
+ /* These structures are ordered by their address. */
+ if (label && label->size == word_count) {
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+ if (jump && jump->addr == word_count) {
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ word_count += 2;
+#else
+ word_count += 6;
+#endif
+ jump->addr = (sljit_uw)(code_ptr - 1);
+ code_ptr = detect_jump_type(jump, code, executable_offset);
+ jump = jump->next;
+ }
+ if (const_ && const_->addr == word_count) {
+ const_->addr = (sljit_uw)code_ptr;
+ const_ = const_->next;
+ }
+ if (put_label && put_label->addr == word_count) {
+ SLJIT_ASSERT(put_label->label);
+ put_label->addr = (sljit_uw)code_ptr;
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ code_ptr += 1;
+ word_count += 1;
+#else
+ code_ptr += put_label_get_length(put_label, (sljit_uw)(SLJIT_ADD_EXEC_OFFSET(code, executable_offset) + put_label->label->size));
+ word_count += 5;
+#endif
+ put_label = put_label->next;
+ }
+ next_addr = compute_next_addr(label, jump, const_, put_label);
+ }
+ code_ptr++;
+ word_count++;
+ } while (buf_ptr < buf_end);
+
+ buf = buf->next;
+ } while (buf);
+
+ if (label && label->size == word_count) {
+ label->addr = (sljit_uw)code_ptr;
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+
+ SLJIT_ASSERT(!label);
+ SLJIT_ASSERT(!jump);
+ SLJIT_ASSERT(!const_);
+ SLJIT_ASSERT(!put_label);
+ SLJIT_ASSERT(code_ptr - code <= (sljit_sw)compiler->size);
+
+ jump = compiler->jumps;
+ while (jump) {
+ do {
+ addr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target;
+ buf_ptr = (sljit_ins *)jump->addr;
+
+ if (jump->flags & PATCH_B) {
+ addr = (sljit_uw)((sljit_sw)(addr - (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset) - sizeof(sljit_ins)) >> 2);
+ SLJIT_ASSERT((sljit_sw)addr <= SIMM_MAX && (sljit_sw)addr >= SIMM_MIN);
+ buf_ptr[0] = (buf_ptr[0] & 0xffff0000) | ((sljit_ins)addr & 0xffff);
+ break;
+ }
+ if (jump->flags & PATCH_J) {
+ SLJIT_ASSERT((addr & ~(sljit_uw)0xfffffff)
+ == (((sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset) + sizeof(sljit_ins)) & ~(sljit_uw)0xfffffff));
+ buf_ptr[0] |= (sljit_ins)(addr >> 2) & 0x03ffffff;
+ break;
+ }
+
+ load_addr_to_reg(jump, PIC_ADDR_REG);
+ } while (0);
+ jump = jump->next;
+ }
+
+ put_label = compiler->put_labels;
+ while (put_label) {
+ load_addr_to_reg(put_label, 0);
+ put_label = put_label->next;
+ }
+
+ compiler->error = SLJIT_ERR_COMPILED;
+ compiler->executable_offset = executable_offset;
+ compiler->executable_size = (sljit_uw)(code_ptr - code) * sizeof(sljit_ins);
+
+ code = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+ code_ptr = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+
+#ifndef __GNUC__
+ SLJIT_CACHE_FLUSH(code, code_ptr);
+#else
+ /* GCC workaround for invalid code generation with -O2. */
+ sljit_cache_flush(code, code_ptr);
+#endif
+ SLJIT_UPDATE_WX_FLAGS(code, code_ptr, 1);
+ return code;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+#if defined(__GNUC__) && !defined(SLJIT_IS_FPU_AVAILABLE)
+ sljit_sw fir = 0;
+#endif /* __GNUC__ && !SLJIT_IS_FPU_AVAILABLE */
+
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#elif defined(__GNUC__)
+ __asm__ ("cfc1 %0, $0" : "=r"(fir));
+ return (fir >> 22) & 0x1;
+#else
+#error "FIR check is not implemented for this architecture"
+#endif
+ case SLJIT_HAS_ZERO_REGISTER:
+ return 1;
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+ case SLJIT_HAS_CLZ:
+ case SLJIT_HAS_CMOV:
+ case SLJIT_HAS_PREFETCH:
+ return 1;
+
+ case SLJIT_HAS_CTZ:
+ return 2;
+#endif /* SLJIT_MIPS_REV >= 1 */
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 2)
+ case SLJIT_HAS_ROT:
+ return 1;
+#endif /* SLJIT_MIPS_REV >= 2 */
+ default:
+ return 0;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type)
+{
+ return (type >= SLJIT_ORDERED_EQUAL && type <= SLJIT_ORDERED_LESS_EQUAL);
+}
+
+/* --------------------------------------------------------------------- */
+/* Entry, exit */
+/* --------------------------------------------------------------------- */
+
+/* Creates an index in data_transfer_insts array. */
+#define LOAD_DATA 0x01
+#define WORD_DATA 0x00
+#define BYTE_DATA 0x02
+#define HALF_DATA 0x04
+#define INT_DATA 0x06
+#define SIGNED_DATA 0x08
+/* Separates integer and floating point registers */
+#define GPR_REG 0x0f
+#define DOUBLE_DATA 0x10
+#define SINGLE_DATA 0x12
+
+#define MEM_MASK 0x1f
+
+#define ARG_TEST 0x00020
+#define ALT_KEEP_CACHE 0x00040
+#define CUMULATIVE_OP 0x00080
+#define LOGICAL_OP 0x00100
+#define IMM_OP 0x00200
+#define MOVE_OP 0x00400
+#define SRC2_IMM 0x00800
+
+#define UNUSED_DEST 0x01000
+#define REG_DEST 0x02000
+#define REG1_SOURCE 0x04000
+#define REG2_SOURCE 0x08000
+#define SLOW_SRC1 0x10000
+#define SLOW_SRC2 0x20000
+#define SLOW_DEST 0x40000
+
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg_ar, sljit_s32 arg, sljit_sw argw);
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 frame_size, sljit_ins *ins_ptr);
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#include "sljitNativeMIPS_32.c"
+#else
+#include "sljitNativeMIPS_64.c"
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_ins base;
+ sljit_s32 i, tmp, offset;
+ sljit_s32 arg_count, word_arg_count, float_arg_count;
+ sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 1);
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if ((local_size & SSIZE_OF(sw)) != 0)
+ local_size += SSIZE_OF(sw);
+ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+ }
+
+ local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf;
+#else
+ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+ local_size = (local_size + SLJIT_LOCALS_OFFSET + 31) & ~0x1f;
+#endif
+ compiler->local_size = local_size;
+
+ offset = 0;
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ if (!(options & SLJIT_ENTER_REG_ARG)) {
+ tmp = arg_types >> SLJIT_ARG_SHIFT;
+ arg_count = 0;
+
+ while (tmp) {
+ offset = arg_count;
+ if ((tmp & SLJIT_ARG_MASK) == SLJIT_ARG_TYPE_F64) {
+ if ((arg_count & 0x1) != 0)
+ arg_count++;
+ arg_count++;
+ }
+
+ arg_count++;
+ tmp >>= SLJIT_ARG_SHIFT;
+ }
+
+ compiler->args_size = (sljit_uw)arg_count << 2;
+ offset = (offset >= 4) ? (offset << 2) : 0;
+ }
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+ if (local_size + offset <= -SIMM_MIN) {
+ /* Frequent case. */
+ FAIL_IF(push_inst(compiler, ADDIU_W | S(SLJIT_SP) | T(SLJIT_SP) | IMM(-local_size), DR(SLJIT_SP)));
+ base = S(SLJIT_SP);
+ offset = local_size - SSIZE_OF(sw);
+ } else {
+ FAIL_IF(load_immediate(compiler, OTHER_FLAG, local_size));
+ FAIL_IF(push_inst(compiler, ADDU_W | S(SLJIT_SP) | TA(0) | D(TMP_REG2), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, SUBU_W | S(SLJIT_SP) | TA(OTHER_FLAG) | D(SLJIT_SP), DR(SLJIT_SP)));
+ base = S(TMP_REG2);
+ offset = -SSIZE_OF(sw);
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ local_size = 0;
+#endif
+ }
+
+ FAIL_IF(push_inst(compiler, STORE_W | base | TA(RETURN_ADDR_REG) | IMM(offset), UNMOVABLE_INS));
+
+ tmp = SLJIT_S0 - saveds;
+ for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STORE_W | base | T(i) | IMM(offset), MOVABLE_INS));
+ }
+
+ for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STORE_W | base | T(i) | IMM(offset), MOVABLE_INS));
+ }
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ /* This alignment is valid because offset is not used after storing FPU regs. */
+ if ((offset & SSIZE_OF(sw)) != 0)
+ offset -= SSIZE_OF(sw);
+#endif
+
+ tmp = SLJIT_FS0 - fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, SDC1 | base | FT(i) | IMM(offset), MOVABLE_INS));
+ }
+
+ for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, SDC1 | base | FT(i) | IMM(offset), MOVABLE_INS));
+ }
+
+ if (options & SLJIT_ENTER_REG_ARG)
+ return SLJIT_SUCCESS;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ arg_count = 0;
+ word_arg_count = 0;
+ float_arg_count = 0;
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ /* The first maximum two floating point arguments are passed in floating point
+ registers if no integer argument precedes them. The first 16 byte data is
+ passed in four integer registers, the rest is placed onto the stack.
+ The floating point registers are also part of the first 16 byte data, so
+ their corresponding integer registers are not used when they are present. */
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ float_arg_count++;
+ if ((arg_count & 0x1) != 0)
+ arg_count++;
+
+ if (word_arg_count == 0 && float_arg_count <= 2) {
+ if (float_arg_count == 1)
+ FAIL_IF(push_inst(compiler, MOV_S | FMT_D | FS(TMP_FREG1) | FD(SLJIT_FR0), MOVABLE_INS));
+ } else if (arg_count < 4) {
+ FAIL_IF(push_inst(compiler, MTC1 | TA(4 + arg_count) | FS(float_arg_count), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, MTC1 | TA(5 + arg_count) | FS(float_arg_count) | (1 << 11), MOVABLE_INS));
+ } else
+ FAIL_IF(push_inst(compiler, LDC1 | base | FT(float_arg_count) | IMM(local_size + (arg_count << 2)), MOVABLE_INS));
+ arg_count++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ float_arg_count++;
+
+ if (word_arg_count == 0 && float_arg_count <= 2) {
+ if (float_arg_count == 1)
+ FAIL_IF(push_inst(compiler, MOV_S | FMT_S | FS(TMP_FREG1) | FD(SLJIT_FR0), MOVABLE_INS));
+ } else if (arg_count < 4)
+ FAIL_IF(push_inst(compiler, MTC1 | TA(4 + arg_count) | FS(float_arg_count), MOVABLE_INS));
+ else
+ FAIL_IF(push_inst(compiler, LWC1 | base | FT(float_arg_count) | IMM(local_size + (arg_count << 2)), MOVABLE_INS));
+ break;
+ default:
+ word_arg_count++;
+
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ tmp = SLJIT_S0 - saved_arg_count;
+ saved_arg_count++;
+ } else if (word_arg_count != arg_count + 1 || arg_count == 0)
+ tmp = word_arg_count;
+ else
+ break;
+
+ if (arg_count < 4)
+ FAIL_IF(push_inst(compiler, ADDU_W | SA(4 + arg_count) | TA(0) | D(tmp), DR(tmp)));
+ else
+ FAIL_IF(push_inst(compiler, LW | base | T(tmp) | IMM(local_size + (arg_count << 2)), DR(tmp)));
+ break;
+ }
+ arg_count++;
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ SLJIT_ASSERT(compiler->args_size == (sljit_uw)arg_count << 2);
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ while (arg_types) {
+ arg_count++;
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ float_arg_count++;
+ if (arg_count != float_arg_count)
+ FAIL_IF(push_inst(compiler, MOV_S | FMT_D | FS(arg_count) | FD(float_arg_count), MOVABLE_INS));
+ else if (arg_count == 1)
+ FAIL_IF(push_inst(compiler, MOV_S | FMT_D | FS(TMP_FREG1) | FD(SLJIT_FR0), MOVABLE_INS));
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ float_arg_count++;
+ if (arg_count != float_arg_count)
+ FAIL_IF(push_inst(compiler, MOV_S | FMT_S | FS(arg_count) | FD(float_arg_count), MOVABLE_INS));
+ else if (arg_count == 1)
+ FAIL_IF(push_inst(compiler, MOV_S | FMT_S | FS(TMP_FREG1) | FD(SLJIT_FR0), MOVABLE_INS));
+ break;
+ default:
+ word_arg_count++;
+
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ tmp = SLJIT_S0 - saved_arg_count;
+ saved_arg_count++;
+ } else if (word_arg_count != arg_count || word_arg_count <= 1)
+ tmp = word_arg_count;
+ else
+ break;
+
+ FAIL_IF(push_inst(compiler, ADDU_W | SA(3 + arg_count) | TA(0) | D(tmp), DR(tmp)));
+ break;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 1);
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if ((local_size & SSIZE_OF(sw)) != 0)
+ local_size += SSIZE_OF(sw);
+ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+ }
+
+ compiler->local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf;
+#else
+ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+ compiler->local_size = (local_size + SLJIT_LOCALS_OFFSET + 31) & ~0x1f;
+#endif
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 frame_size, sljit_ins *ins_ptr)
+{
+ sljit_s32 local_size, i, tmp, offset;
+ sljit_s32 load_return_addr = (frame_size == 0);
+ sljit_s32 scratches = compiler->scratches;
+ sljit_s32 saveds = compiler->saveds;
+ sljit_s32 fsaveds = compiler->fsaveds;
+ sljit_s32 fscratches = compiler->fscratches;
+ sljit_s32 kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(compiler->options);
+
+ SLJIT_ASSERT(frame_size == 1 || (frame_size & 0xf) == 0);
+ frame_size &= ~0xf;
+
+ local_size = compiler->local_size;
+
+ tmp = GET_SAVED_REGISTERS_SIZE(scratches, saveds - kept_saveds_count, 1);
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if ((tmp & SSIZE_OF(sw)) != 0)
+ tmp += SSIZE_OF(sw);
+ tmp += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+ }
+#else
+ tmp += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+#endif
+
+ if (local_size <= SIMM_MAX) {
+ if (local_size < frame_size) {
+ FAIL_IF(push_inst(compiler, ADDIU_W | S(SLJIT_SP) | T(SLJIT_SP) | IMM(local_size - frame_size), DR(SLJIT_SP)));
+ local_size = frame_size;
+ }
+ } else {
+ if (tmp < frame_size)
+ tmp = frame_size;
+
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), local_size - tmp));
+ FAIL_IF(push_inst(compiler, ADDU_W | S(SLJIT_SP) | T(TMP_REG1) | D(SLJIT_SP), DR(SLJIT_SP)));
+ local_size = tmp;
+ }
+
+ SLJIT_ASSERT(local_size >= frame_size);
+
+ offset = local_size - SSIZE_OF(sw);
+ if (load_return_addr)
+ FAIL_IF(push_inst(compiler, LOAD_W | S(SLJIT_SP) | TA(RETURN_ADDR_REG) | IMM(offset), RETURN_ADDR_REG));
+
+ tmp = SLJIT_S0 - saveds;
+ for (i = SLJIT_S0 - kept_saveds_count; i > tmp; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, LOAD_W | S(SLJIT_SP) | T(i) | IMM(offset), MOVABLE_INS));
+ }
+
+ for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, LOAD_W | S(SLJIT_SP) | T(i) | IMM(offset), MOVABLE_INS));
+ }
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ /* This alignment is valid because offset is not used after storing FPU regs. */
+ if ((offset & SSIZE_OF(sw)) != 0)
+ offset -= SSIZE_OF(sw);
+#endif
+
+ tmp = SLJIT_FS0 - fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, LDC1 | S(SLJIT_SP) | FT(i) | IMM(offset), MOVABLE_INS));
+ }
+
+ for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, LDC1 | S(SLJIT_SP) | FT(i) | IMM(offset), MOVABLE_INS));
+ }
+
+ if (local_size > frame_size)
+ *ins_ptr = ADDIU_W | S(SLJIT_SP) | T(SLJIT_SP) | IMM(local_size - frame_size);
+ else
+ *ins_ptr = NOP;
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ sljit_ins ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ emit_stack_frame_release(compiler, 0, &ins);
+
+ FAIL_IF(push_inst(compiler, JR | SA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ return push_inst(compiler, ins, UNMOVABLE_INS);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_ins ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(PIC_ADDR_REG), src, srcw));
+ src = PIC_ADDR_REG;
+ srcw = 0;
+ } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, ADDU_W | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG)));
+ src = PIC_ADDR_REG;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 1, &ins));
+
+ if (!(src & SLJIT_IMM)) {
+ FAIL_IF(push_inst(compiler, JR | S(src), UNMOVABLE_INS));
+ return push_inst(compiler, ins, UNMOVABLE_INS);
+ }
+
+ if (ins != NOP)
+ FAIL_IF(push_inst(compiler, ins, MOVABLE_INS));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#define ARCH_32_64(a, b) a
+#else
+#define ARCH_32_64(a, b) b
+#endif
+
+static const sljit_ins data_transfer_insts[16 + 4] = {
+/* u w s */ ARCH_32_64(HI(43) /* sw */, HI(63) /* sd */),
+/* u w l */ ARCH_32_64(HI(35) /* lw */, HI(55) /* ld */),
+/* u b s */ HI(40) /* sb */,
+/* u b l */ HI(36) /* lbu */,
+/* u h s */ HI(41) /* sh */,
+/* u h l */ HI(37) /* lhu */,
+/* u i s */ HI(43) /* sw */,
+/* u i l */ ARCH_32_64(HI(35) /* lw */, HI(39) /* lwu */),
+
+/* s w s */ ARCH_32_64(HI(43) /* sw */, HI(63) /* sd */),
+/* s w l */ ARCH_32_64(HI(35) /* lw */, HI(55) /* ld */),
+/* s b s */ HI(40) /* sb */,
+/* s b l */ HI(32) /* lb */,
+/* s h s */ HI(41) /* sh */,
+/* s h l */ HI(33) /* lh */,
+/* s i s */ HI(43) /* sw */,
+/* s i l */ HI(35) /* lw */,
+
+/* d s */ HI(61) /* sdc1 */,
+/* d l */ HI(53) /* ldc1 */,
+/* s s */ HI(57) /* swc1 */,
+/* s l */ HI(49) /* lwc1 */,
+};
+
+#undef ARCH_32_64
+
+/* reg_ar is an absoulute register! */
+
+/* Can perform an operation using at most 1 instruction. */
+static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg_ar, sljit_s32 arg, sljit_sw argw)
+{
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+
+ if (!(arg & OFFS_REG_MASK) && argw <= SIMM_MAX && argw >= SIMM_MIN) {
+ /* Works for both absoulte and relative addresses. */
+ if (SLJIT_UNLIKELY(flags & ARG_TEST))
+ return 1;
+ FAIL_IF(push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(arg & REG_MASK)
+ | TA(reg_ar) | IMM(argw), ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) ? reg_ar : MOVABLE_INS));
+ return -1;
+ }
+ return 0;
+}
+
+#define TO_ARGW_HI(argw) (((argw) & ~0xffff) + (((argw) & 0x8000) ? 0x10000 : 0))
+
+/* See getput_arg below.
+ Note: can_cache is called only for binary operators. */
+static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw)
+{
+ SLJIT_ASSERT((arg & SLJIT_MEM) && (next_arg & SLJIT_MEM));
+
+ /* Simple operation except for updates. */
+ if (arg & OFFS_REG_MASK) {
+ argw &= 0x3;
+ next_argw &= 0x3;
+ if (argw && argw == next_argw && (arg == next_arg || (arg & OFFS_REG_MASK) == (next_arg & OFFS_REG_MASK)))
+ return 1;
+ return 0;
+ }
+
+ if (arg == next_arg) {
+ if (((next_argw - argw) <= SIMM_MAX && (next_argw - argw) >= SIMM_MIN)
+ || TO_ARGW_HI(argw) == TO_ARGW_HI(next_argw))
+ return 1;
+ return 0;
+ }
+
+ return 0;
+}
+
+/* Emit the necessary instructions. See can_cache above. */
+static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg_ar, sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw)
+{
+ sljit_s32 tmp_ar, base, delay_slot;
+ sljit_sw offset, argw_hi;
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+ if (!(next_arg & SLJIT_MEM)) {
+ next_arg = 0;
+ next_argw = 0;
+ }
+
+ /* Since tmp can be the same as base or offset registers,
+ * these might be unavailable after modifying tmp. */
+ if ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) {
+ tmp_ar = reg_ar;
+ delay_slot = reg_ar;
+ }
+ else {
+ tmp_ar = DR(TMP_REG1);
+ delay_slot = MOVABLE_INS;
+ }
+ base = arg & REG_MASK;
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ argw &= 0x3;
+
+ /* Using the cache. */
+ if (argw == compiler->cache_argw) {
+ if (arg == compiler->cache_arg)
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), delay_slot);
+
+ if ((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg) {
+ if (arg == next_arg && argw == (next_argw & 0x3)) {
+ compiler->cache_arg = arg;
+ compiler->cache_argw = argw;
+ FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | D(TMP_REG3), DR(TMP_REG3)));
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), delay_slot);
+ }
+ FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | DA(tmp_ar), tmp_ar));
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot);
+ }
+ }
+
+ if (SLJIT_UNLIKELY(argw)) {
+ compiler->cache_arg = SLJIT_MEM | (arg & OFFS_REG_MASK);
+ compiler->cache_argw = argw;
+ FAIL_IF(push_inst(compiler, SLL_W | T(OFFS_REG(arg)) | D(TMP_REG3) | SH_IMM(argw), DR(TMP_REG3)));
+ }
+
+ if (arg == next_arg && argw == (next_argw & 0x3)) {
+ compiler->cache_arg = arg;
+ compiler->cache_argw = argw;
+ FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(!argw ? OFFS_REG(arg) : TMP_REG3) | D(TMP_REG3), DR(TMP_REG3)));
+ tmp_ar = DR(TMP_REG3);
+ }
+ else
+ FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(!argw ? OFFS_REG(arg) : TMP_REG3) | DA(tmp_ar), tmp_ar));
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot);
+ }
+
+ if (compiler->cache_arg == arg && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN)
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar) | IMM(argw - compiler->cache_argw), delay_slot);
+
+ if (compiler->cache_arg == SLJIT_MEM && (argw - compiler->cache_argw) <= SIMM_MAX && (argw - compiler->cache_argw) >= SIMM_MIN) {
+ offset = argw - compiler->cache_argw;
+ } else {
+ compiler->cache_arg = SLJIT_MEM;
+
+ argw_hi = TO_ARGW_HI(argw);
+
+ if (next_arg && next_argw - argw <= SIMM_MAX && next_argw - argw >= SIMM_MIN && argw_hi != TO_ARGW_HI(next_argw)) {
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG3), argw));
+ compiler->cache_argw = argw;
+ offset = 0;
+ } else {
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG3), argw_hi));
+ compiler->cache_argw = argw_hi;
+ offset = argw & 0xffff;
+ argw = argw_hi;
+ }
+ }
+
+ if (!base)
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar) | IMM(offset), delay_slot);
+
+ if (arg == next_arg && next_argw - argw <= SIMM_MAX && next_argw - argw >= SIMM_MIN) {
+ compiler->cache_arg = arg;
+ FAIL_IF(push_inst(compiler, ADDU_W | S(TMP_REG3) | T(base) | D(TMP_REG3), DR(TMP_REG3)));
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar) | IMM(offset), delay_slot);
+ }
+
+ FAIL_IF(push_inst(compiler, ADDU_W | S(TMP_REG3) | T(base) | DA(tmp_ar), tmp_ar));
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar) | IMM(offset), delay_slot);
+}
+
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg_ar, sljit_s32 arg, sljit_sw argw)
+{
+ sljit_s32 tmp_ar, base, delay_slot;
+
+ if (getput_arg_fast(compiler, flags, reg_ar, arg, argw))
+ return compiler->error;
+
+ if ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) {
+ tmp_ar = reg_ar;
+ delay_slot = reg_ar;
+ }
+ else {
+ tmp_ar = DR(TMP_REG1);
+ delay_slot = MOVABLE_INS;
+ }
+ base = arg & REG_MASK;
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ argw &= 0x3;
+
+ if (SLJIT_UNLIKELY(argw)) {
+ FAIL_IF(push_inst(compiler, SLL_W | T(OFFS_REG(arg)) | DA(tmp_ar) | SH_IMM(argw), tmp_ar));
+ FAIL_IF(push_inst(compiler, ADDU_W | SA(tmp_ar) | T(base) | DA(tmp_ar), tmp_ar));
+ }
+ else
+ FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(OFFS_REG(arg)) | DA(tmp_ar), tmp_ar));
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot);
+ }
+
+ FAIL_IF(load_immediate(compiler, tmp_ar, TO_ARGW_HI(argw)));
+
+ if (base != 0)
+ FAIL_IF(push_inst(compiler, ADDU_W | SA(tmp_ar) | T(base) | DA(tmp_ar), tmp_ar));
+
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar) | IMM(argw), delay_slot);
+}
+
+static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg1, sljit_sw arg1w, sljit_s32 arg2, sljit_sw arg2w)
+{
+ if (getput_arg_fast(compiler, flags, reg, arg1, arg1w))
+ return compiler->error;
+ return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w);
+}
+
+#define EMIT_LOGICAL(op_imm, op_reg) \
+ if (flags & SRC2_IMM) { \
+ if (op & SLJIT_SET_Z) \
+ FAIL_IF(push_inst(compiler, op_imm | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); \
+ if (!(flags & UNUSED_DEST)) \
+ FAIL_IF(push_inst(compiler, op_imm | S(src1) | T(dst) | IMM(src2), DR(dst))); \
+ } \
+ else { \
+ if (op & SLJIT_SET_Z) \
+ FAIL_IF(push_inst(compiler, op_reg | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); \
+ if (!(flags & UNUSED_DEST)) \
+ FAIL_IF(push_inst(compiler, op_reg | S(src1) | T(src2) | D(dst), DR(dst))); \
+ }
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+
+#define SELECT_OP(a, b) (b)
+
+#define EMIT_SHIFT(dimm, dimm32, imm, dv, v) \
+ op_imm = (imm); \
+ op_v = (v);
+
+#else /* !SLJIT_CONFIG_MIPS_32 */
+
+#define SELECT_OP(a, b) \
+ (!(op & SLJIT_32) ? a : b)
+
+#define EMIT_SHIFT(dimm, dimm32, imm, dv, v) \
+ op_dimm = (dimm); \
+ op_dimm32 = (dimm32); \
+ op_imm = (imm); \
+ op_dv = (dv); \
+ op_v = (v);
+
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV < 1)
+
+static sljit_s32 emit_clz_ctz(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw src)
+{
+ sljit_s32 is_clz = (GET_OPCODE(op) == SLJIT_CLZ);
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ sljit_ins max = (op & SLJIT_32) ? 32 : 64;
+#else /* !SLJIT_CONFIG_RISCV_64 */
+ sljit_ins max = 32;
+#endif /* SLJIT_CONFIG_RISCV_64 */
+
+ /* The TMP_REG2 is the next value. */
+ if (src != TMP_REG2)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src) | TA(0) | D(TMP_REG2), DR(TMP_REG2)));
+
+ FAIL_IF(push_inst(compiler, BEQ | S(TMP_REG2) | TA(0) | IMM(is_clz ? 13 : 14), UNMOVABLE_INS));
+ /* The OTHER_FLAG is the counter. Delay slot. */
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(OTHER_FLAG) | IMM(max), OTHER_FLAG));
+
+ if (!is_clz) {
+ FAIL_IF(push_inst(compiler, ANDI | S(TMP_REG2) | T(TMP_REG1) | IMM(1), DR(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, BNE | S(TMP_REG1) | TA(0) | IMM(11), UNMOVABLE_INS));
+ } else
+ FAIL_IF(push_inst(compiler, BLTZ | S(TMP_REG2) | TA(0) | IMM(11), UNMOVABLE_INS));
+
+ /* Delay slot. */
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(OTHER_FLAG) | IMM(0), OTHER_FLAG));
+
+ /* The TMP_REG1 is the next shift. */
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | T(TMP_REG1) | IMM(max), DR(TMP_REG1)));
+
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(TMP_REG2) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSRL, SRL) | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(1), DR(TMP_REG1)));
+
+ FAIL_IF(push_inst(compiler, (is_clz ? SELECT_OP(DSRLV, SRLV) : SELECT_OP(DSLLV, SLLV)) | S(TMP_REG1) | TA(EQUAL_FLAG) | D(TMP_REG2), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, BNE | S(TMP_REG2) | TA(0) | IMM(-4), UNMOVABLE_INS));
+ /* Delay slot. */
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(TMP_REG1) | T(TMP_REG2) | IMM(-1), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, (is_clz ? SELECT_OP(DSRLV, SRLV) : SELECT_OP(DSLLV, SLLV)) | S(TMP_REG2) | TA(EQUAL_FLAG) | D(TMP_REG2), DR(TMP_REG2)));
+
+ FAIL_IF(push_inst(compiler, BEQ | S(TMP_REG2) | TA(0) | IMM(-7), UNMOVABLE_INS));
+ /* Delay slot. */
+ FAIL_IF(push_inst(compiler, OR | SA(OTHER_FLAG) | T(TMP_REG1) | DA(OTHER_FLAG), OTHER_FLAG));
+
+ return push_inst(compiler, SELECT_OP(DADDU, ADDU) | SA(OTHER_FLAG) | TA(0) | D(dst), DR(dst));
+}
+
+#endif /* SLJIT_MIPS_REV < 1 */
+
+static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
+ sljit_s32 dst, sljit_s32 src1, sljit_sw src2)
+{
+ sljit_s32 is_overflow, is_carry, carry_src_ar, is_handled;
+ sljit_ins op_imm, op_v;
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ sljit_ins ins, op_dimm, op_dimm32, op_dv;
+#endif
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if (dst != src2)
+ return push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src2) | TA(0) | D(dst), DR(dst));
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U8:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE))
+ return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xff), DR(dst));
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_S8:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+ return push_inst(compiler, SEB | T(src2) | D(dst), DR(dst));
+#else /* SLJIT_MIPS_REV < 1 */
+ FAIL_IF(push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(24), DR(dst)));
+ return push_inst(compiler, SRA | T(dst) | D(dst) | SH_IMM(24), DR(dst));
+#endif /* SLJIT_MIPS_REV >= 1 */
+#else /* !SLJIT_CONFIG_MIPS_32 */
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+ if (op & SLJIT_32)
+ return push_inst(compiler, SEB | T(src2) | D(dst), DR(dst));
+#endif /* SLJIT_MIPS_REV >= 1 */
+ FAIL_IF(push_inst(compiler, DSLL32 | T(src2) | D(dst) | SH_IMM(24), DR(dst)));
+ return push_inst(compiler, DSRA32 | T(dst) | D(dst) | SH_IMM(24), DR(dst));
+#endif /* SLJIT_CONFIG_MIPS_32 */
+ }
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U16:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE))
+ return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xffff), DR(dst));
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_S16:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+ return push_inst(compiler, SEH | T(src2) | D(dst), DR(dst));
+#else /* SLJIT_MIPS_REV < 1 */
+ FAIL_IF(push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(16), DR(dst)));
+ return push_inst(compiler, SRA | T(dst) | D(dst) | SH_IMM(16), DR(dst));
+#endif /* SLJIT_MIPS_REV >= 1 */
+#else /* !SLJIT_CONFIG_MIPS_32 */
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+ if (op & SLJIT_32)
+ return push_inst(compiler, SEH | T(src2) | D(dst), DR(dst));
+#endif /* SLJIT_MIPS_REV >= 1 */
+ FAIL_IF(push_inst(compiler, DSLL32 | T(src2) | D(dst) | SH_IMM(16), DR(dst)));
+ return push_inst(compiler, DSRA32 | T(dst) | D(dst) | SH_IMM(16), DR(dst));
+#endif /* SLJIT_CONFIG_MIPS_32 */
+ }
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ case SLJIT_MOV_U32:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM) && !(op & SLJIT_32));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 2)
+ if (dst == src2)
+ return push_inst(compiler, DINSU | T(src2) | SA(0) | (31 << 11) | (0 << 11), DR(dst));
+#endif /* SLJIT_MIPS_REV >= 2 */
+ FAIL_IF(push_inst(compiler, DSLL32 | T(src2) | D(dst) | SH_IMM(0), DR(dst)));
+ return push_inst(compiler, DSRL32 | T(dst) | D(dst) | SH_IMM(0), DR(dst));
+ }
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_S32:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM) && !(op & SLJIT_32));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ return push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(0), DR(dst));
+ }
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+#endif /* SLJIT_CONFIG_MIPS_64 */
+
+ case SLJIT_NOT:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ if (!(flags & UNUSED_DEST))
+ FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | D(dst), DR(dst)));
+ return SLJIT_SUCCESS;
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+ case SLJIT_CLZ:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ return push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(src2) | D(dst), DR(dst));
+#else /* SLJIT_MIPS_REV < 6 */
+ return push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(src2) | T(dst) | D(dst), DR(dst));
+#endif /* SLJIT_MIPS_REV >= 6 */
+ case SLJIT_CTZ:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(0) | T(src2) | D(TMP_REG1), DR(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, AND | S(src2) | T(TMP_REG1) | D(dst), DR(dst)));
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(dst) | D(dst), DR(dst)));
+#else /* SLJIT_MIPS_REV < 6 */
+ FAIL_IF(push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(dst) | T(dst) | D(dst), DR(dst)));
+#endif /* SLJIT_MIPS_REV >= 6 */
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(dst) | T(TMP_REG1) | IMM(SELECT_OP(-64, -32)), DR(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSRL32, SRL) | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(SELECT_OP(26, 27)), DR(TMP_REG1)));
+ return push_inst(compiler, XOR | S(dst) | T(TMP_REG1) | D(dst), DR(dst));
+#else /* SLJIT_MIPS_REV < 1 */
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ return emit_clz_ctz(compiler, op, dst, src2);
+#endif /* SLJIT_MIPS_REV >= 1 */
+
+ case SLJIT_ADD:
+ /* Overflow computation (both add and sub): overflow = src1_sign ^ src2_sign ^ result_sign ^ carry_flag */
+ is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW;
+ carry_src_ar = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
+ if (flags & SRC2_IMM) {
+ if (is_overflow) {
+ if (src2 >= 0)
+ FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ else
+ FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ }
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG));
+
+ /* Only the zero flag is needed. */
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(src2), DR(dst)));
+ }
+ else {
+ if (is_overflow)
+ FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+
+ if (is_overflow || carry_src_ar != 0) {
+ if (src1 != dst)
+ carry_src_ar = DR(src1);
+ else if (src2 != dst)
+ carry_src_ar = DR(src2);
+ else {
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | TA(0) | DA(OTHER_FLAG), OTHER_FLAG));
+ carry_src_ar = OTHER_FLAG;
+ }
+ }
+
+ /* Only the zero flag is needed. */
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | D(dst), DR(dst)));
+ }
+
+ /* Carry is zero if a + b >= a or a + b >= b, otherwise it is 1. */
+ if (is_overflow || carry_src_ar != 0) {
+ if (flags & SRC2_IMM)
+ FAIL_IF(push_inst(compiler, SLTIU | S(dst) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
+ else
+ FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(carry_src_ar) | DA(OTHER_FLAG), OTHER_FLAG));
+ }
+
+ if (!is_overflow)
+ return SLJIT_SUCCESS;
+
+ FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | D(TMP_REG1), DR(TMP_REG1)));
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSRL32, SRL) | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1)));
+ return push_inst(compiler, XOR | S(TMP_REG1) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG);
+
+ case SLJIT_ADDC:
+ carry_src_ar = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
+ if (flags & SRC2_IMM) {
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(src2), DR(dst)));
+ } else {
+ if (carry_src_ar != 0) {
+ if (src1 != dst)
+ carry_src_ar = DR(src1);
+ else if (src2 != dst)
+ carry_src_ar = DR(src2);
+ else {
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ carry_src_ar = EQUAL_FLAG;
+ }
+ }
+
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | D(dst), DR(dst)));
+ }
+
+ /* Carry is zero if a + b >= a or a + b >= b, otherwise it is 1. */
+ if (carry_src_ar != 0) {
+ if (flags & SRC2_IMM)
+ FAIL_IF(push_inst(compiler, SLTIU | S(dst) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG));
+ else
+ FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(carry_src_ar) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ }
+
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst)));
+
+ if (carry_src_ar == 0)
+ return SLJIT_SUCCESS;
+
+ /* Set ULESS_FLAG (dst == 0) && (OTHER_FLAG == 1). */
+ FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG));
+ /* Set carry flag. */
+ return push_inst(compiler, OR | SA(OTHER_FLAG) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG);
+
+ case SLJIT_SUB:
+ if ((flags & SRC2_IMM) && src2 == SIMM_MIN) {
+ FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2)));
+ src2 = TMP_REG2;
+ flags &= ~SRC2_IMM;
+ }
+
+ is_handled = 0;
+
+ if (flags & SRC2_IMM) {
+ if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) {
+ FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
+ is_handled = 1;
+ }
+ else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) {
+ FAIL_IF(push_inst(compiler, SLTI | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
+ is_handled = 1;
+ }
+ }
+
+ if (!is_handled && GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL) {
+ is_handled = 1;
+
+ if (flags & SRC2_IMM) {
+ FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2)));
+ src2 = TMP_REG2;
+ flags &= ~SRC2_IMM;
+ }
+
+ switch (GET_FLAG_TYPE(op)) {
+ case SLJIT_LESS:
+ case SLJIT_GREATER_EQUAL:
+ FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG));
+ break;
+ case SLJIT_GREATER:
+ case SLJIT_LESS_EQUAL:
+ FAIL_IF(push_inst(compiler, SLTU | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG));
+ break;
+ case SLJIT_SIG_LESS:
+ case SLJIT_SIG_GREATER_EQUAL:
+ FAIL_IF(push_inst(compiler, SLT | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG));
+ break;
+ case SLJIT_SIG_GREATER:
+ case SLJIT_SIG_LESS_EQUAL:
+ FAIL_IF(push_inst(compiler, SLT | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG));
+ break;
+ }
+ }
+
+ if (is_handled) {
+ if (flags & SRC2_IMM) {
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG));
+ if (!(flags & UNUSED_DEST))
+ return push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst));
+ }
+ else {
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ if (!(flags & UNUSED_DEST))
+ return push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst));
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW;
+ is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
+ if (flags & SRC2_IMM) {
+ if (is_overflow) {
+ if (src2 >= 0)
+ FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ else
+ FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ }
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG));
+
+ if (is_overflow || is_carry)
+ FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
+
+ /* Only the zero flag is needed. */
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst)));
+ }
+ else {
+ if (is_overflow)
+ FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+
+ if (is_overflow || is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG));
+
+ /* Only the zero flag is needed. */
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst)));
+ }
+
+ if (!is_overflow)
+ return SLJIT_SUCCESS;
+
+ FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | D(TMP_REG1), DR(TMP_REG1)));
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSRL32, SRL) | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1)));
+ return push_inst(compiler, XOR | S(TMP_REG1) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG);
+
+ case SLJIT_SUBC:
+ if ((flags & SRC2_IMM) && src2 == SIMM_MIN) {
+ FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2)));
+ src2 = TMP_REG2;
+ flags &= ~SRC2_IMM;
+ }
+
+ is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
+ if (flags & SRC2_IMM) {
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG));
+
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst)));
+ }
+ else {
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst)));
+ }
+
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | D(TMP_REG1), DR(TMP_REG1)));
+
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst)));
+
+ if (!is_carry)
+ return SLJIT_SUCCESS;
+
+ return push_inst(compiler, OR | SA(EQUAL_FLAG) | T(TMP_REG1) | DA(OTHER_FLAG), OTHER_FLAG);
+
+ case SLJIT_MUL:
+ SLJIT_ASSERT(!(flags & SRC2_IMM));
+
+ if (GET_FLAG_TYPE(op) != SLJIT_OVERFLOW) {
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ return push_inst(compiler, SELECT_OP(DMUL, MUL) | S(src1) | T(src2) | D(dst), DR(dst));
+#elif (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ return push_inst(compiler, MUL | S(src1) | T(src2) | D(dst), DR(dst));
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ if (op & SLJIT_32)
+ return push_inst(compiler, MUL | S(src1) | T(src2) | D(dst), DR(dst));
+ FAIL_IF(push_inst(compiler, DMULT | S(src1) | T(src2), MOVABLE_INS));
+ return push_inst(compiler, MFLO | D(dst), DR(dst));
+#endif /* SLJIT_CONFIG_MIPS_32 */
+#else /* SLJIT_MIPS_REV < 1 */
+ FAIL_IF(push_inst(compiler, SELECT_OP(DMULT, MULT) | S(src1) | T(src2), MOVABLE_INS));
+ return push_inst(compiler, MFLO | D(dst), DR(dst));
+#endif /* SLJIT_MIPS_REV >= 6 */
+ }
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DMUL, MUL) | S(src1) | T(src2) | D(dst), DR(dst)));
+ FAIL_IF(push_inst(compiler, SELECT_OP(DMUH, MUH) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+#else /* SLJIT_MIPS_REV < 6 */
+ FAIL_IF(push_inst(compiler, SELECT_OP(DMULT, MULT) | S(src1) | T(src2), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, MFHI | DA(EQUAL_FLAG), EQUAL_FLAG));
+ FAIL_IF(push_inst(compiler, MFLO | D(dst), DR(dst)));
+#endif /* SLJIT_MIPS_REV >= 6 */
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSRA32, SRA) | T(dst) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG));
+ return push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(EQUAL_FLAG) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG);
+
+ case SLJIT_AND:
+ EMIT_LOGICAL(ANDI, AND);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_OR:
+ EMIT_LOGICAL(ORI, OR);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_XOR:
+ EMIT_LOGICAL(XORI, XOR);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ EMIT_SHIFT(DSLL, DSLL32, SLL, DSLLV, SLLV);
+ break;
+
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ EMIT_SHIFT(DSRL, DSRL32, SRL, DSRLV, SRLV);
+ break;
+
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ EMIT_SHIFT(DSRA, DSRA32, SRA, DSRAV, SRAV);
+ break;
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 2)
+ case SLJIT_ROTL:
+ if ((flags & SRC2_IMM) || src2 == 0) {
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ src2 = -src2 & 0x1f;
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ src2 = -src2 & ((op & SLJIT_32) ? 0x1f : 0x3f);
+#endif /* SLJIT_CONFIG_MIPS_32 */
+ } else {
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(0) | T(src2) | D(TMP_REG2), DR(TMP_REG2)));
+ src2 = TMP_REG2;
+ }
+ /* fallthrough */
+
+ case SLJIT_ROTR:
+ EMIT_SHIFT(DROTR, DROTR32, ROTR, DROTRV, ROTRV);
+ break;
+#else /* SLJIT_MIPS_REV < 1 */
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ if (flags & SRC2_IMM) {
+ SLJIT_ASSERT(src2 != 0);
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (!(op & SLJIT_32)) {
+ if (GET_OPCODE(op) == SLJIT_ROTL)
+ op_imm = ((src2 < 32) ? DSLL : DSLL32);
+ else
+ op_imm = ((src2 < 32) ? DSRL : DSRL32);
+
+ FAIL_IF(push_inst(compiler, op_imm | T(src1) | DA(OTHER_FLAG) | (((sljit_ins)src2 & 0x1f) << 6), OTHER_FLAG));
+
+ src2 = 64 - src2;
+ if (GET_OPCODE(op) == SLJIT_ROTL)
+ op_imm = ((src2 < 32) ? DSRL : DSRL32);
+ else
+ op_imm = ((src2 < 32) ? DSLL : DSLL32);
+
+ FAIL_IF(push_inst(compiler, op_imm | T(src1) | D(dst) | (((sljit_ins)src2 & 0x1f) << 6), DR(dst)));
+ return push_inst(compiler, OR | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst));
+ }
+#endif /* SLJIT_CONFIG_MIPS_64 */
+
+ op_imm = (GET_OPCODE(op) == SLJIT_ROTL) ? SLL : SRL;
+ FAIL_IF(push_inst(compiler, op_imm | T(src1) | DA(OTHER_FLAG) | ((sljit_ins)src2 << 6), OTHER_FLAG));
+
+ src2 = 32 - src2;
+ op_imm = (GET_OPCODE(op) == SLJIT_ROTL) ? SRL : SLL;
+ FAIL_IF(push_inst(compiler, op_imm | T(src1) | D(dst) | (((sljit_ins)src2 & 0x1f) << 6), DR(dst)));
+ return push_inst(compiler, OR | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst));
+ }
+
+ if (src2 == 0) {
+ if (dst != src1)
+ return push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | TA(0) | D(dst), DR(dst));
+ return SLJIT_SUCCESS;
+ }
+
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(0) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (!(op & SLJIT_32)) {
+ op_v = (GET_OPCODE(op) == SLJIT_ROTL) ? DSLLV : DSRLV;
+ FAIL_IF(push_inst(compiler, op_v | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG));
+ op_v = (GET_OPCODE(op) == SLJIT_ROTL) ? DSRLV : DSLLV;
+ FAIL_IF(push_inst(compiler, op_v | SA(EQUAL_FLAG) | T(src1) | D(dst), DR(dst)));
+ return push_inst(compiler, OR | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst));
+ }
+#endif /* SLJIT_CONFIG_MIPS_64 */
+
+ op_v = (GET_OPCODE(op) == SLJIT_ROTL) ? SLLV : SRLV;
+ FAIL_IF(push_inst(compiler, op_v | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG));
+ op_v = (GET_OPCODE(op) == SLJIT_ROTL) ? SRLV : SLLV;
+ FAIL_IF(push_inst(compiler, op_v | SA(EQUAL_FLAG) | T(src1) | D(dst), DR(dst)));
+ return push_inst(compiler, OR | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst));
+#endif /* SLJIT_MIPS_REV >= 2 */
+
+ default:
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+ }
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ if ((flags & SRC2_IMM) || src2 == 0) {
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, op_imm | T(src1) | DA(EQUAL_FLAG) | SH_IMM(src2), EQUAL_FLAG));
+
+ if (flags & UNUSED_DEST)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, op_imm | T(src1) | D(dst) | SH_IMM(src2), DR(dst));
+ }
+
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, op_v | S(src2) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
+
+ if (flags & UNUSED_DEST)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, op_v | S(src2) | T(src1) | D(dst), DR(dst));
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ if ((flags & SRC2_IMM) || src2 == 0) {
+ if (src2 >= 32) {
+ SLJIT_ASSERT(!(op & SLJIT_32));
+ ins = op_dimm32;
+ src2 -= 32;
+ }
+ else
+ ins = (op & SLJIT_32) ? op_imm : op_dimm;
+
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ins | T(src1) | DA(EQUAL_FLAG) | SH_IMM(src2), EQUAL_FLAG));
+
+ if (flags & UNUSED_DEST)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, ins | T(src1) | D(dst) | SH_IMM(src2), DR(dst));
+ }
+
+ ins = (op & SLJIT_32) ? op_v : op_dv;
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ins | S(src2) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
+
+ if (flags & UNUSED_DEST)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, ins | S(src2) | T(src1) | D(dst), DR(dst));
+#endif /* SLJIT_CONFIG_MIPS_32 */
+}
+
+#define CHECK_IMM(flags, srcw) \
+ ((!((flags) & LOGICAL_OP) && ((srcw) <= SIMM_MAX && (srcw) >= SIMM_MIN)) \
+ || (((flags) & LOGICAL_OP) && !((srcw) & ~UIMM_MAX)))
+
+static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ /* arg1 goes to TMP_REG1 or src reg
+ arg2 goes to TMP_REG2, imm or src reg
+ TMP_REG3 can be used for caching
+ result goes to TMP_REG2, so put result can use TMP_REG1 and TMP_REG3. */
+ sljit_s32 dst_r = TMP_REG2;
+ sljit_s32 src1_r;
+ sljit_sw src2_r = 0;
+ sljit_s32 sugg_src2_r = TMP_REG2;
+
+ if (!(flags & ALT_KEEP_CACHE)) {
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+ }
+
+ if (dst == TMP_REG2) {
+ SLJIT_ASSERT(HAS_FLAGS(op));
+ flags |= UNUSED_DEST;
+ }
+ else if (FAST_IS_REG(dst)) {
+ dst_r = dst;
+ flags |= REG_DEST;
+ if (flags & MOVE_OP)
+ sugg_src2_r = dst_r;
+ }
+ else if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, flags | ARG_TEST, DR(TMP_REG1), dst, dstw))
+ flags |= SLOW_DEST;
+
+ if (flags & IMM_OP) {
+ if ((src2 & SLJIT_IMM) && src2w != 0 && CHECK_IMM(flags, src2w)) {
+ flags |= SRC2_IMM;
+ src2_r = src2w;
+ } else if ((flags & CUMULATIVE_OP) && (src1 & SLJIT_IMM) && src1w != 0 && CHECK_IMM(flags, src1w)) {
+ flags |= SRC2_IMM;
+ src2_r = src1w;
+
+ /* And swap arguments. */
+ src1 = src2;
+ src1w = src2w;
+ src2 = SLJIT_IMM;
+ /* src2w = src2_r unneeded. */
+ }
+ }
+
+ /* Source 1. */
+ if (FAST_IS_REG(src1)) {
+ src1_r = src1;
+ flags |= REG1_SOURCE;
+ }
+ else if (src1 & SLJIT_IMM) {
+ if (src1w) {
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), src1w));
+ src1_r = TMP_REG1;
+ }
+ else
+ src1_r = 0;
+ }
+ else {
+ if (getput_arg_fast(compiler, flags | LOAD_DATA, DR(TMP_REG1), src1, src1w))
+ FAIL_IF(compiler->error);
+ else
+ flags |= SLOW_SRC1;
+ src1_r = TMP_REG1;
+ }
+
+ /* Source 2. */
+ if (FAST_IS_REG(src2)) {
+ src2_r = src2;
+ flags |= REG2_SOURCE;
+ if ((flags & (REG_DEST | MOVE_OP)) == MOVE_OP)
+ dst_r = (sljit_s32)src2_r;
+ }
+ else if (src2 & SLJIT_IMM) {
+ if (!(flags & SRC2_IMM)) {
+ if (src2w) {
+ FAIL_IF(load_immediate(compiler, DR(sugg_src2_r), src2w));
+ src2_r = sugg_src2_r;
+ }
+ else {
+ src2_r = 0;
+ if (flags & MOVE_OP) {
+ if (dst & SLJIT_MEM)
+ dst_r = 0;
+ else
+ op = SLJIT_MOV;
+ }
+ }
+ }
+ }
+ else {
+ if (getput_arg_fast(compiler, flags | LOAD_DATA, DR(sugg_src2_r), src2, src2w))
+ FAIL_IF(compiler->error);
+ else
+ flags |= SLOW_SRC2;
+ src2_r = sugg_src2_r;
+ }
+
+ if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) {
+ SLJIT_ASSERT(src2_r == TMP_REG2);
+ if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(TMP_REG2), src2, src2w, src1, src1w));
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(TMP_REG1), src1, src1w, dst, dstw));
+ }
+ else {
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(TMP_REG1), src1, src1w, src2, src2w));
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(TMP_REG2), src2, src2w, dst, dstw));
+ }
+ }
+ else if (flags & SLOW_SRC1)
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(TMP_REG1), src1, src1w, dst, dstw));
+ else if (flags & SLOW_SRC2)
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(sugg_src2_r), src2, src2w, dst, dstw));
+
+ FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r));
+
+ if (dst & SLJIT_MEM) {
+ if (!(flags & SLOW_DEST)) {
+ getput_arg_fast(compiler, flags, DR(dst_r), dst, dstw);
+ return compiler->error;
+ }
+ return getput_arg(compiler, flags, DR(dst_r), dst, dstw, 0, 0);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#undef CHECK_IMM
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ sljit_s32 int_op = op & SLJIT_32;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op0(compiler, op));
+
+ op = GET_OPCODE(op);
+ switch (op) {
+ case SLJIT_BREAKPOINT:
+ return push_inst(compiler, BREAK, UNMOVABLE_INS);
+ case SLJIT_NOP:
+ return push_inst(compiler, NOP, UNMOVABLE_INS);
+ case SLJIT_LMUL_UW:
+ case SLJIT_LMUL_SW:
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ FAIL_IF(push_inst(compiler, (op == SLJIT_LMUL_UW ? DMULU : DMUL) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG3), DR(TMP_REG3)));
+ FAIL_IF(push_inst(compiler, (op == SLJIT_LMUL_UW ? DMUHU : DMUH) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG1), DR(TMP_REG1)));
+#else /* !SLJIT_CONFIG_MIPS_64 */
+ FAIL_IF(push_inst(compiler, (op == SLJIT_LMUL_UW ? MULU : MUL) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG3), DR(TMP_REG3)));
+ FAIL_IF(push_inst(compiler, (op == SLJIT_LMUL_UW ? MUHU : MUH) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG1), DR(TMP_REG1)));
+#endif /* SLJIT_CONFIG_MIPS_64 */
+ FAIL_IF(push_inst(compiler, ADDU_W | S(TMP_REG3) | TA(0) | D(SLJIT_R0), DR(SLJIT_R0)));
+ return push_inst(compiler, ADDU_W | S(TMP_REG1) | TA(0) | D(SLJIT_R1), DR(SLJIT_R1));
+#else /* SLJIT_MIPS_REV < 6 */
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ FAIL_IF(push_inst(compiler, (op == SLJIT_LMUL_UW ? DMULTU : DMULT) | S(SLJIT_R0) | T(SLJIT_R1), MOVABLE_INS));
+#else /* !SLJIT_CONFIG_MIPS_64 */
+ FAIL_IF(push_inst(compiler, (op == SLJIT_LMUL_UW ? MULTU : MULT) | S(SLJIT_R0) | T(SLJIT_R1), MOVABLE_INS));
+#endif /* SLJIT_CONFIG_MIPS_64 */
+ FAIL_IF(push_inst(compiler, MFLO | D(SLJIT_R0), DR(SLJIT_R0)));
+ return push_inst(compiler, MFHI | D(SLJIT_R1), DR(SLJIT_R1));
+#endif /* SLJIT_MIPS_REV >= 6 */
+ case SLJIT_DIVMOD_UW:
+ case SLJIT_DIVMOD_SW:
+ case SLJIT_DIV_UW:
+ case SLJIT_DIV_SW:
+ SLJIT_COMPILE_ASSERT((SLJIT_DIVMOD_UW & 0x2) == 0 && SLJIT_DIV_UW - 0x2 == SLJIT_DIVMOD_UW, bad_div_opcode_assignments);
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (int_op) {
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? DIVU : DIV) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG3), DR(TMP_REG3)));
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? MODU : MOD) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG1), DR(TMP_REG1)));
+ }
+ else {
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? DDIVU : DDIV) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG3), DR(TMP_REG3)));
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? DMODU : DMOD) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG1), DR(TMP_REG1)));
+ }
+#else /* !SLJIT_CONFIG_MIPS_64 */
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? DIVU : DIV) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG3), DR(TMP_REG3)));
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? MODU : MOD) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG1), DR(TMP_REG1)));
+#endif /* SLJIT_CONFIG_MIPS_64 */
+ FAIL_IF(push_inst(compiler, ADDU_W | S(TMP_REG3) | TA(0) | D(SLJIT_R0), DR(SLJIT_R0)));
+ return (op >= SLJIT_DIV_UW) ? SLJIT_SUCCESS : push_inst(compiler, ADDU_W | S(TMP_REG1) | TA(0) | D(SLJIT_R1), DR(SLJIT_R1));
+#else /* SLJIT_MIPS_REV < 6 */
+#if !(defined SLJIT_MIPS_REV)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif /* !SLJIT_MIPS_REV */
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (int_op)
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? DIVU : DIV) | S(SLJIT_R0) | T(SLJIT_R1), MOVABLE_INS));
+ else
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? DDIVU : DDIV) | S(SLJIT_R0) | T(SLJIT_R1), MOVABLE_INS));
+#else /* !SLJIT_CONFIG_MIPS_64 */
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? DIVU : DIV) | S(SLJIT_R0) | T(SLJIT_R1), MOVABLE_INS));
+#endif /* SLJIT_CONFIG_MIPS_64 */
+ FAIL_IF(push_inst(compiler, MFLO | D(SLJIT_R0), DR(SLJIT_R0)));
+ return (op >= SLJIT_DIV_UW) ? SLJIT_SUCCESS : push_inst(compiler, MFHI | D(SLJIT_R1), DR(SLJIT_R1));
+#endif /* SLJIT_MIPS_REV >= 6 */
+ case SLJIT_ENDBR:
+ case SLJIT_SKIP_FRAMES_BEFORE_RETURN:
+ return SLJIT_SUCCESS;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+static sljit_s32 emit_prefetch(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (!(src & OFFS_REG_MASK)) {
+ if (srcw <= SIMM_MAX && srcw >= SIMM_MIN)
+ return push_inst(compiler, PREF | S(src & REG_MASK) | IMM(srcw), MOVABLE_INS);
+
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), srcw));
+ return push_inst(compiler, PREFX | S(src & REG_MASK) | T(TMP_REG1), MOVABLE_INS);
+ }
+
+ srcw &= 0x3;
+
+ if (SLJIT_UNLIKELY(srcw != 0)) {
+ FAIL_IF(push_inst(compiler, SLL_W | T(OFFS_REG(src)) | D(TMP_REG1) | SH_IMM(srcw), DR(TMP_REG1)));
+ return push_inst(compiler, PREFX | S(src & REG_MASK) | T(TMP_REG1), MOVABLE_INS);
+ }
+
+ return push_inst(compiler, PREFX | S(src & REG_MASK) | T(OFFS_REG(src)), MOVABLE_INS);
+}
+#endif /* SLJIT_MIPS_REV >= 1 */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 flags = 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (op & SLJIT_32)
+ flags = INT_DATA | SIGNED_DATA;
+#endif
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV:
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+#endif
+ case SLJIT_MOV_P:
+ return emit_op(compiler, SLJIT_MOV, WORD_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, srcw);
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ case SLJIT_MOV_U32:
+ return emit_op(compiler, SLJIT_MOV_U32, INT_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u32)srcw : srcw);
+
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ return emit_op(compiler, SLJIT_MOV_S32, INT_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s32)srcw : srcw);
+#endif
+
+ case SLJIT_MOV_U8:
+ return emit_op(compiler, op, BYTE_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw);
+
+ case SLJIT_MOV_S8:
+ return emit_op(compiler, op, BYTE_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw);
+
+ case SLJIT_MOV_U16:
+ return emit_op(compiler, op, HALF_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw);
+
+ case SLJIT_MOV_S16:
+ return emit_op(compiler, op, HALF_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw);
+
+ case SLJIT_NOT:
+ return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw);
+
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw);
+ }
+
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 flags = 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (op & SLJIT_32) {
+ flags |= INT_DATA | SIGNED_DATA;
+ if (src1 & SLJIT_IMM)
+ src1w = (sljit_s32)src1w;
+ if (src2 & SLJIT_IMM)
+ src2w = (sljit_s32)src2w;
+ }
+#endif
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD:
+ case SLJIT_ADDC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ return emit_op(compiler, op, flags | CUMULATIVE_OP | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_SUB:
+ case SLJIT_SUBC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_MUL:
+ compiler->status_flags_state = 0;
+ return emit_op(compiler, op, flags | CUMULATIVE_OP, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_AND:
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ return emit_op(compiler, op, flags | CUMULATIVE_OP | LOGICAL_OP | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ if (src2 & SLJIT_IMM)
+ src2w &= 0x1f;
+#else
+ if (src2 & SLJIT_IMM) {
+ if (op & SLJIT_32)
+ src2w &= 0x1f;
+ else
+ src2w &= 0x3f;
+ }
+#endif
+ return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, TMP_REG2, 0, src1, src1w, src2, src2w);
+}
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+#define SELECT_OP3(op, src2w, D, D32, W) (((op & SLJIT_32) ? (W) : ((src2w) < 32) ? (D) : (D32)) | (((sljit_ins)src2w & 0x1f) << 6))
+#define SELECT_OP2(op, D, W) ((op & SLJIT_32) ? (W) : (D))
+#else /* !SLJIT_CONFIG_MIPS_64 */
+#define SELECT_OP3(op, src2w, D, D32, W) ((W) | ((sljit_ins)(src2w) << 6))
+#define SELECT_OP2(op, D, W) (W)
+#endif /* SLJIT_CONFIG_MIPS_64 */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 is_left;
+ sljit_ins ins1, ins2, ins3;
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ sljit_s32 inp_flags = ((op & SLJIT_32) ? INT_DATA : WORD_DATA) | LOAD_DATA;
+ sljit_sw bit_length = (op & SLJIT_32) ? 32 : 64;
+#else /* !SLJIT_CONFIG_MIPS_64 */
+ sljit_s32 inp_flags = WORD_DATA | LOAD_DATA;
+ sljit_sw bit_length = 32;
+#endif /* SLJIT_CONFIG_MIPS_64 */
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w));
+
+ is_left = (GET_OPCODE(op) == SLJIT_SHL || GET_OPCODE(op) == SLJIT_MSHL);
+
+ if (src_dst == src1) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, (is_left ? SLJIT_ROTL : SLJIT_ROTR) | (op & SLJIT_32), src_dst, 0, src_dst, 0, src2, src2w);
+ }
+
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ if (src2 & SLJIT_IMM) {
+ src2w &= bit_length - 1;
+
+ if (src2w == 0)
+ return SLJIT_SUCCESS;
+ } else if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inp_flags, DR(TMP_REG2), src2, src2w));
+ src2 = TMP_REG2;
+ }
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inp_flags, DR(TMP_REG1), src1, src1w));
+ src1 = TMP_REG1;
+ } else if (src1 & SLJIT_IMM) {
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), src1w));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_IMM) {
+ if (is_left) {
+ ins1 = SELECT_OP3(op, src2w, DSLL, DSLL32, SLL);
+ src2w = bit_length - src2w;
+ ins2 = SELECT_OP3(op, src2w, DSRL, DSRL32, SRL);
+ } else {
+ ins1 = SELECT_OP3(op, src2w, DSRL, DSRL32, SRL);
+ src2w = bit_length - src2w;
+ ins2 = SELECT_OP3(op, src2w, DSLL, DSLL32, SLL);
+ }
+
+ FAIL_IF(push_inst(compiler, ins1 | T(src_dst) | D(src_dst), DR(src_dst)));
+ FAIL_IF(push_inst(compiler, ins2 | T(src1) | D(TMP_REG1), DR(TMP_REG1)));
+ return push_inst(compiler, OR | S(src_dst) | T(TMP_REG1) | D(src_dst), DR(src_dst));
+ }
+
+ if (is_left) {
+ ins1 = SELECT_OP2(op, DSRL, SRL);
+ ins2 = SELECT_OP2(op, DSLLV, SLLV);
+ ins3 = SELECT_OP2(op, DSRLV, SRLV);
+ } else {
+ ins1 = SELECT_OP2(op, DSLL, SLL);
+ ins2 = SELECT_OP2(op, DSRLV, SRLV);
+ ins3 = SELECT_OP2(op, DSLLV, SLLV);
+ }
+
+ FAIL_IF(push_inst(compiler, ins2 | S(src2) | T(src_dst) | D(src_dst), DR(src_dst)));
+
+ if (!(op & SLJIT_SHIFT_INTO_NON_ZERO)) {
+ FAIL_IF(push_inst(compiler, ins1 | T(src1) | D(TMP_REG1) | (1 << 6), DR(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, XORI | S(src2) | T(TMP_REG2) | ((sljit_ins)bit_length - 1), DR(TMP_REG2)));
+ src1 = TMP_REG1;
+ } else
+ FAIL_IF(push_inst(compiler, SELECT_OP2(op, DSUBU, SUBU) | SA(0) | T(src2) | D(TMP_REG2), DR(TMP_REG2)));
+
+ FAIL_IF(push_inst(compiler, ins3 | S(TMP_REG2) | T(src1) | D(TMP_REG1), DR(TMP_REG1)));
+ return push_inst(compiler, OR | S(src_dst) | T(TMP_REG1) | D(src_dst), DR(src_dst));
+}
+
+#undef SELECT_OP3
+#undef SELECT_OP2
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_src(compiler, op, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ switch (op) {
+ case SLJIT_FAST_RETURN:
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst(compiler, ADDU_W | S(src) | TA(0) | DA(RETURN_ADDR_REG), RETURN_ADDR_REG));
+ else
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, RETURN_ADDR_REG, src, srcw));
+
+ FAIL_IF(push_inst(compiler, JR | SA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ return push_inst(compiler, NOP, UNMOVABLE_INS);
+ case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN:
+ return SLJIT_SUCCESS;
+ case SLJIT_PREFETCH_L1:
+ case SLJIT_PREFETCH_L2:
+ case SLJIT_PREFETCH_L3:
+ case SLJIT_PREFETCH_ONCE:
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+ return emit_prefetch(compiler, src, srcw);
+#else /* SLJIT_MIPS_REV < 1 */
+ return SLJIT_SUCCESS;
+#endif /* SLJIT_MIPS_REV >= 1 */
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_register_index(reg));
+ return reg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
+ return FR(reg);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_custom(compiler, instruction, size));
+
+ return push_inst(compiler, *(sljit_ins*)instruction, UNMOVABLE_INS);
+}
+
+/* --------------------------------------------------------------------- */
+/* Floating point operators */
+/* --------------------------------------------------------------------- */
+
+#define FLOAT_DATA(op) (DOUBLE_DATA | ((op & SLJIT_32) >> 7))
+#define FMT(op) ((((sljit_ins)op & SLJIT_32) ^ SLJIT_32) << (21 - 8))
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+# define flags (sljit_u32)0
+#else
+ sljit_u32 flags = ((sljit_u32)(GET_OPCODE(op) == SLJIT_CONV_SW_FROM_F64)) << 21;
+#endif
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src, srcw, dst, dstw));
+ src = TMP_FREG1;
+ }
+
+ FAIL_IF(push_inst(compiler, (TRUNC_W_S ^ (flags >> 19)) | FMT(op) | FS(src) | FD(TMP_FREG1), MOVABLE_INS));
+
+ if (FAST_IS_REG(dst)) {
+ FAIL_IF(push_inst(compiler, MFC1 | flags | T(dst) | FS(TMP_FREG1), MOVABLE_INS));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+ return SLJIT_SUCCESS;
+ }
+
+ /* Store the integer value from a VFP register. */
+ return emit_op_mem2(compiler, flags ? DOUBLE_DATA : SINGLE_DATA, FR(TMP_FREG1), dst, dstw, 0, 0);
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+# undef flags
+#endif
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+# define flags (sljit_u32)0
+#else
+ sljit_u32 flags = ((sljit_u32)(GET_OPCODE(op) == SLJIT_CONV_F64_FROM_SW)) << 21;
+#endif
+
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (FAST_IS_REG(src)) {
+ FAIL_IF(push_inst(compiler, MTC1 | flags | T(src) | FS(TMP_FREG1), MOVABLE_INS));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+ } else if (src & SLJIT_MEM) {
+ /* Load the integer value into a VFP register. */
+ FAIL_IF(emit_op_mem2(compiler, (flags ? DOUBLE_DATA : SINGLE_DATA) | LOAD_DATA, FR(TMP_FREG1), src, srcw, dst, dstw));
+ }
+ else {
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32)
+ srcw = (sljit_s32)srcw;
+#endif
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), srcw));
+ FAIL_IF(push_inst(compiler, MTC1 | flags | T(TMP_REG1) | FS(TMP_FREG1), MOVABLE_INS));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+ }
+
+ FAIL_IF(push_inst(compiler, CVT_S_S | flags | (4 << 21) | ((((sljit_ins)op & SLJIT_32) ^ SLJIT_32) >> 8) | FS(TMP_FREG1) | FD(dst_r), MOVABLE_INS));
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem2(compiler, FLOAT_DATA(op), FR(TMP_FREG1), dst, dstw, 0, 0);
+ return SLJIT_SUCCESS;
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+# undef flags
+#endif
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_ins inst;
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w, src2, src2w));
+ src1 = TMP_FREG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w, 0, 0));
+ src2 = TMP_FREG2;
+ }
+
+ switch (GET_FLAG_TYPE(op)) {
+ case SLJIT_F_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ inst = C_EQ_S;
+ break;
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ inst = C_UEQ_S;
+ break;
+ case SLJIT_F_LESS:
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ inst = C_OLT_S;
+ break;
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ inst = C_ULT_S;
+ break;
+ case SLJIT_F_GREATER:
+ case SLJIT_ORDERED_GREATER:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ inst = C_ULE_S;
+ break;
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ inst = C_OLE_S;
+ break;
+ default:
+ SLJIT_ASSERT(GET_FLAG_TYPE(op) == SLJIT_UNORDERED || GET_FLAG_TYPE(op) == SLJIT_ORDERED);
+ inst = C_UN_S;
+ break;
+ }
+ return push_inst(compiler, inst | FMT(op) | FT(src2) | FS(src1) | C_FD, UNMOVABLE_INS);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+
+ SLJIT_COMPILE_ASSERT((SLJIT_32 == 0x100) && !(DOUBLE_DATA & 0x2), float_transfer_bit_error);
+ SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
+
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32)
+ op ^= SLJIT_32;
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(dst_r), src, srcw, dst, dstw));
+ src = dst_r;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV_F64:
+ if (src != dst_r) {
+ if (dst_r != TMP_FREG1)
+ FAIL_IF(push_inst(compiler, MOV_S | FMT(op) | FS(src) | FD(dst_r), MOVABLE_INS));
+ else
+ dst_r = src;
+ }
+ break;
+ case SLJIT_NEG_F64:
+ FAIL_IF(push_inst(compiler, NEG_S | FMT(op) | FS(src) | FD(dst_r), MOVABLE_INS));
+ break;
+ case SLJIT_ABS_F64:
+ FAIL_IF(push_inst(compiler, ABS_S | FMT(op) | FS(src) | FD(dst_r), MOVABLE_INS));
+ break;
+ case SLJIT_CONV_F64_FROM_F32:
+ /* The SLJIT_32 bit is inverted because sljit_f32 needs to be loaded from the memory. */
+ FAIL_IF(push_inst(compiler, CVT_S_S | (sljit_ins)((op & SLJIT_32) ? 1 : (1 << 21)) | FS(src) | FD(dst_r), MOVABLE_INS));
+ op ^= SLJIT_32;
+ break;
+ }
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem2(compiler, FLOAT_DATA(op), FR(dst_r), dst, dstw, 0, 0);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r, flags = 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG2;
+
+ if (src1 & SLJIT_MEM) {
+ if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w)) {
+ FAIL_IF(compiler->error);
+ src1 = TMP_FREG1;
+ } else
+ flags |= SLOW_SRC1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w)) {
+ FAIL_IF(compiler->error);
+ src2 = TMP_FREG2;
+ } else
+ flags |= SLOW_SRC2;
+ }
+
+ if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) {
+ if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w, src1, src1w));
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w, dst, dstw));
+ }
+ else {
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w, src2, src2w));
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w, dst, dstw));
+ }
+ }
+ else if (flags & SLOW_SRC1)
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w, dst, dstw));
+ else if (flags & SLOW_SRC2)
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w, dst, dstw));
+
+ if (flags & SLOW_SRC1)
+ src1 = TMP_FREG1;
+ if (flags & SLOW_SRC2)
+ src2 = TMP_FREG2;
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD_F64:
+ FAIL_IF(push_inst(compiler, ADD_S | FMT(op) | FT(src2) | FS(src1) | FD(dst_r), MOVABLE_INS));
+ break;
+
+ case SLJIT_SUB_F64:
+ FAIL_IF(push_inst(compiler, SUB_S | FMT(op) | FT(src2) | FS(src1) | FD(dst_r), MOVABLE_INS));
+ break;
+
+ case SLJIT_MUL_F64:
+ FAIL_IF(push_inst(compiler, MUL_S | FMT(op) | FT(src2) | FS(src1) | FD(dst_r), MOVABLE_INS));
+ break;
+
+ case SLJIT_DIV_F64:
+ FAIL_IF(push_inst(compiler, DIV_S | FMT(op) | FT(src2) | FS(src1) | FD(dst_r), MOVABLE_INS));
+ break;
+ }
+
+ if (dst_r == TMP_FREG2)
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), FR(TMP_FREG2), dst, dstw, 0, 0));
+
+ return SLJIT_SUCCESS;
+}
+
+#undef FLOAT_DATA
+#undef FMT
+
+/* --------------------------------------------------------------------- */
+/* Other instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ if (FAST_IS_REG(dst))
+ return push_inst(compiler, ADDU_W | SA(RETURN_ADDR_REG) | TA(0) | D(dst), UNMOVABLE_INS);
+
+ /* Memory. */
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA, RETURN_ADDR_REG, dst, dstw));
+ compiler->delay_slot = UNMOVABLE_INS;
+ return SLJIT_SUCCESS;
+}
+
+/* --------------------------------------------------------------------- */
+/* Conditional instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ struct sljit_label *label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_label(compiler));
+
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ return compiler->last_label;
+
+ label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
+ PTR_FAIL_IF(!label);
+ set_label(label, compiler);
+ compiler->delay_slot = UNMOVABLE_INS;
+ return label;
+}
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#define BRANCH_LENGTH 4
+#else
+#define BRANCH_LENGTH 8
+#endif
+
+#define BR_Z(src) \
+ inst = BEQ | SA(src) | TA(0) | BRANCH_LENGTH; \
+ flags = IS_BIT26_COND; \
+ delay_check = src;
+
+#define BR_NZ(src) \
+ inst = BNE | SA(src) | TA(0) | BRANCH_LENGTH; \
+ flags = IS_BIT26_COND; \
+ delay_check = src;
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+
+#define BR_T() \
+ inst = BC1NEZ; \
+ flags = IS_BIT23_COND; \
+ delay_check = FCSR_FCC;
+#define BR_F() \
+ inst = BC1EQZ; \
+ flags = IS_BIT23_COND; \
+ delay_check = FCSR_FCC;
+
+#else /* SLJIT_MIPS_REV < 6 */
+
+#define BR_T() \
+ inst = BC1T | BRANCH_LENGTH; \
+ flags = IS_BIT16_COND; \
+ delay_check = FCSR_FCC;
+#define BR_F() \
+ inst = BC1F | BRANCH_LENGTH; \
+ flags = IS_BIT16_COND; \
+ delay_check = FCSR_FCC;
+
+#endif /* SLJIT_MIPS_REV >= 6 */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ struct sljit_jump *jump;
+ sljit_ins inst;
+ sljit_u32 flags = 0;
+ sljit_s32 delay_check = UNMOVABLE_INS;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_jump(compiler, type));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ type &= 0xff;
+
+ switch (type) {
+ case SLJIT_EQUAL:
+ BR_NZ(EQUAL_FLAG);
+ break;
+ case SLJIT_NOT_EQUAL:
+ BR_Z(EQUAL_FLAG);
+ break;
+ case SLJIT_LESS:
+ case SLJIT_GREATER:
+ case SLJIT_SIG_LESS:
+ case SLJIT_SIG_GREATER:
+ case SLJIT_OVERFLOW:
+ case SLJIT_CARRY:
+ BR_Z(OTHER_FLAG);
+ break;
+ case SLJIT_GREATER_EQUAL:
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_SIG_GREATER_EQUAL:
+ case SLJIT_SIG_LESS_EQUAL:
+ case SLJIT_NOT_OVERFLOW:
+ case SLJIT_NOT_CARRY:
+ BR_NZ(OTHER_FLAG);
+ break;
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_F_GREATER:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_ORDERED:
+ BR_T();
+ break;
+ case SLJIT_F_EQUAL:
+ case SLJIT_F_LESS:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL:
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_UNORDERED_OR_LESS:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ case SLJIT_UNORDERED:
+ BR_F();
+ break;
+ default:
+ /* Not conditional branch. */
+ inst = 0;
+ break;
+ }
+
+ jump->flags |= flags;
+ if (compiler->delay_slot == MOVABLE_INS || (compiler->delay_slot != UNMOVABLE_INS && compiler->delay_slot != delay_check))
+ jump->flags |= IS_MOVABLE;
+
+ if (inst)
+ PTR_FAIL_IF(push_inst(compiler, inst, UNMOVABLE_INS));
+
+ if (type <= SLJIT_JUMP)
+ PTR_FAIL_IF(push_inst(compiler, JR | S(TMP_REG2), UNMOVABLE_INS));
+ else {
+ jump->flags |= IS_JAL;
+ PTR_FAIL_IF(push_inst(compiler, JALR | S(TMP_REG2) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ }
+
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+
+ /* Maximum number of instructions required for generating a constant. */
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ compiler->size += 2;
+#else
+ compiler->size += 6;
+#endif
+ return jump;
+}
+
+#define RESOLVE_IMM1() \
+ if (src1 & SLJIT_IMM) { \
+ if (src1w) { \
+ PTR_FAIL_IF(load_immediate(compiler, DR(TMP_REG1), src1w)); \
+ src1 = TMP_REG1; \
+ } \
+ else \
+ src1 = 0; \
+ }
+
+#define RESOLVE_IMM2() \
+ if (src2 & SLJIT_IMM) { \
+ if (src2w) { \
+ PTR_FAIL_IF(load_immediate(compiler, DR(TMP_REG2), src2w)); \
+ src2 = TMP_REG2; \
+ } \
+ else \
+ src2 = 0; \
+ }
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ struct sljit_jump *jump;
+ sljit_s32 flags;
+ sljit_ins inst;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_cmp(compiler, type, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ flags = WORD_DATA | LOAD_DATA;
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ flags = ((type & SLJIT_32) ? INT_DATA : WORD_DATA) | LOAD_DATA;
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+ if (src1 & SLJIT_MEM) {
+ PTR_FAIL_IF(emit_op_mem2(compiler, flags, DR(TMP_REG1), src1, src1w, src2, src2w));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ PTR_FAIL_IF(emit_op_mem2(compiler, flags, DR(TMP_REG2), src2, src2w, 0, 0));
+ src2 = TMP_REG2;
+ }
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ type &= 0xff;
+
+ if (type <= SLJIT_NOT_EQUAL) {
+ RESOLVE_IMM1();
+ RESOLVE_IMM2();
+ jump->flags |= IS_BIT26_COND;
+ if (compiler->delay_slot == MOVABLE_INS || (compiler->delay_slot != UNMOVABLE_INS && compiler->delay_slot != DR(src1) && compiler->delay_slot != DR(src2)))
+ jump->flags |= IS_MOVABLE;
+ PTR_FAIL_IF(push_inst(compiler, (type == SLJIT_EQUAL ? BNE : BEQ) | S(src1) | T(src2) | BRANCH_LENGTH, UNMOVABLE_INS));
+ }
+ else if (type >= SLJIT_SIG_LESS && (((src1 & SLJIT_IMM) && (src1w == 0)) || ((src2 & SLJIT_IMM) && (src2w == 0)))) {
+ inst = NOP;
+ if ((src1 & SLJIT_IMM) && (src1w == 0)) {
+ RESOLVE_IMM2();
+ switch (type) {
+ case SLJIT_SIG_LESS:
+ inst = BLEZ;
+ jump->flags |= IS_BIT26_COND;
+ break;
+ case SLJIT_SIG_GREATER_EQUAL:
+ inst = BGTZ;
+ jump->flags |= IS_BIT26_COND;
+ break;
+ case SLJIT_SIG_GREATER:
+ inst = BGEZ;
+ jump->flags |= IS_BIT16_COND;
+ break;
+ case SLJIT_SIG_LESS_EQUAL:
+ inst = BLTZ;
+ jump->flags |= IS_BIT16_COND;
+ break;
+ }
+ src1 = src2;
+ }
+ else {
+ RESOLVE_IMM1();
+ switch (type) {
+ case SLJIT_SIG_LESS:
+ inst = BGEZ;
+ jump->flags |= IS_BIT16_COND;
+ break;
+ case SLJIT_SIG_GREATER_EQUAL:
+ inst = BLTZ;
+ jump->flags |= IS_BIT16_COND;
+ break;
+ case SLJIT_SIG_GREATER:
+ inst = BLEZ;
+ jump->flags |= IS_BIT26_COND;
+ break;
+ case SLJIT_SIG_LESS_EQUAL:
+ inst = BGTZ;
+ jump->flags |= IS_BIT26_COND;
+ break;
+ }
+ }
+ PTR_FAIL_IF(push_inst(compiler, inst | S(src1) | BRANCH_LENGTH, UNMOVABLE_INS));
+ }
+ else {
+ if (type == SLJIT_LESS || type == SLJIT_GREATER_EQUAL || type == SLJIT_SIG_LESS || type == SLJIT_SIG_GREATER_EQUAL) {
+ RESOLVE_IMM1();
+ if ((src2 & SLJIT_IMM) && src2w <= SIMM_MAX && src2w >= SIMM_MIN)
+ PTR_FAIL_IF(push_inst(compiler, (type <= SLJIT_LESS_EQUAL ? SLTIU : SLTI) | S(src1) | T(TMP_REG1) | IMM(src2w), DR(TMP_REG1)));
+ else {
+ RESOLVE_IMM2();
+ PTR_FAIL_IF(push_inst(compiler, (type <= SLJIT_LESS_EQUAL ? SLTU : SLT) | S(src1) | T(src2) | D(TMP_REG1), DR(TMP_REG1)));
+ }
+ type = (type == SLJIT_LESS || type == SLJIT_SIG_LESS) ? SLJIT_NOT_EQUAL : SLJIT_EQUAL;
+ }
+ else {
+ RESOLVE_IMM2();
+ if ((src1 & SLJIT_IMM) && src1w <= SIMM_MAX && src1w >= SIMM_MIN)
+ PTR_FAIL_IF(push_inst(compiler, (type <= SLJIT_LESS_EQUAL ? SLTIU : SLTI) | S(src2) | T(TMP_REG1) | IMM(src1w), DR(TMP_REG1)));
+ else {
+ RESOLVE_IMM1();
+ PTR_FAIL_IF(push_inst(compiler, (type <= SLJIT_LESS_EQUAL ? SLTU : SLT) | S(src2) | T(src1) | D(TMP_REG1), DR(TMP_REG1)));
+ }
+ type = (type == SLJIT_GREATER || type == SLJIT_SIG_GREATER) ? SLJIT_NOT_EQUAL : SLJIT_EQUAL;
+ }
+
+ jump->flags |= IS_BIT26_COND;
+ PTR_FAIL_IF(push_inst(compiler, (type == SLJIT_EQUAL ? BNE : BEQ) | S(TMP_REG1) | TA(0) | BRANCH_LENGTH, UNMOVABLE_INS));
+ }
+
+ PTR_FAIL_IF(push_inst(compiler, JR | S(TMP_REG2), UNMOVABLE_INS));
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+
+ /* Maximum number of instructions required for generating a constant. */
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ compiler->size += 2;
+#else
+ compiler->size += 6;
+#endif
+ return jump;
+}
+
+#undef RESOLVE_IMM1
+#undef RESOLVE_IMM2
+
+#undef BRANCH_LENGTH
+#undef BR_Z
+#undef BR_NZ
+#undef BR_T
+#undef BR_F
+
+#undef FLOAT_DATA
+#undef FMT
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ struct sljit_jump *jump = NULL;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
+
+ if (src & SLJIT_IMM) {
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ FAIL_IF(!jump);
+ set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_JAL : 0));
+ jump->u.target = (sljit_uw)srcw;
+
+ if (compiler->delay_slot != UNMOVABLE_INS)
+ jump->flags |= IS_MOVABLE;
+
+ src = TMP_REG2;
+ } else if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(TMP_REG2), src, srcw));
+ src = TMP_REG2;
+ }
+
+ if (type <= SLJIT_JUMP)
+ FAIL_IF(push_inst(compiler, JR | S(src), UNMOVABLE_INS));
+ else
+ FAIL_IF(push_inst(compiler, JALR | S(src) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
+
+ if (jump != NULL) {
+ jump->addr = compiler->size;
+
+ /* Maximum number of instructions required for generating a constant. */
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ compiler->size += 2;
+#else
+ compiler->size += 6;
+#endif
+ }
+
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ sljit_s32 src_ar, dst_ar, invert;
+ sljit_s32 saved_op = op;
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ sljit_s32 mem_type = WORD_DATA;
+#else
+ sljit_s32 mem_type = ((op & SLJIT_32) || op == SLJIT_MOV32) ? (INT_DATA | SIGNED_DATA) : WORD_DATA;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ op = GET_OPCODE(op);
+ dst_ar = DR((op < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG2);
+
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+
+ if (op >= SLJIT_ADD && (dst & SLJIT_MEM))
+ FAIL_IF(emit_op_mem2(compiler, mem_type | LOAD_DATA, DR(TMP_REG1), dst, dstw, dst, dstw));
+
+ if (type < SLJIT_F_EQUAL) {
+ src_ar = OTHER_FLAG;
+ invert = type & 0x1;
+
+ switch (type) {
+ case SLJIT_EQUAL:
+ case SLJIT_NOT_EQUAL:
+ FAIL_IF(push_inst(compiler, SLTIU | SA(EQUAL_FLAG) | TA(dst_ar) | IMM(1), dst_ar));
+ src_ar = dst_ar;
+ break;
+ case SLJIT_OVERFLOW:
+ case SLJIT_NOT_OVERFLOW:
+ if (compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)) {
+ src_ar = OTHER_FLAG;
+ break;
+ }
+ FAIL_IF(push_inst(compiler, SLTIU | SA(OTHER_FLAG) | TA(dst_ar) | IMM(1), dst_ar));
+ src_ar = dst_ar;
+ invert ^= 0x1;
+ break;
+ }
+ } else {
+ invert = 0;
+
+ switch (type) {
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_F_GREATER:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_ORDERED:
+ invert = 1;
+ break;
+ }
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ FAIL_IF(push_inst(compiler, MFC1 | TA(dst_ar) | FS(TMP_FREG3), dst_ar));
+#else /* SLJIT_MIPS_REV < 6 */
+ FAIL_IF(push_inst(compiler, CFC1 | TA(dst_ar) | DA(FCSR_REG), dst_ar));
+#endif /* SLJIT_MIPS_REV >= 6 */
+ FAIL_IF(push_inst(compiler, SRL | TA(dst_ar) | DA(dst_ar) | SH_IMM(23), dst_ar));
+ FAIL_IF(push_inst(compiler, ANDI | SA(dst_ar) | TA(dst_ar) | IMM(1), dst_ar));
+ src_ar = dst_ar;
+ }
+
+ if (invert) {
+ FAIL_IF(push_inst(compiler, XORI | SA(src_ar) | TA(dst_ar) | IMM(1), dst_ar));
+ src_ar = dst_ar;
+ }
+
+ if (op < SLJIT_ADD) {
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, mem_type, src_ar, dst, dstw);
+
+ if (src_ar != dst_ar)
+ return push_inst(compiler, ADDU_W | SA(src_ar) | TA(0) | DA(dst_ar), dst_ar);
+ return SLJIT_SUCCESS;
+ }
+
+ /* OTHER_FLAG cannot be specified as src2 argument at the moment. */
+ if (DR(TMP_REG2) != src_ar)
+ FAIL_IF(push_inst(compiler, ADDU_W | SA(src_ar) | TA(0) | D(TMP_REG2), DR(TMP_REG2)));
+
+ mem_type |= CUMULATIVE_OP | LOGICAL_OP | IMM_OP | ALT_KEEP_CACHE;
+
+ if (dst & SLJIT_MEM)
+ return emit_op(compiler, saved_op, mem_type, dst, dstw, TMP_REG1, 0, TMP_REG2, 0);
+ return emit_op(compiler, saved_op, mem_type, dst, dstw, dst, dstw, TMP_REG2, 0);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1 && SLJIT_MIPS_REV < 6)
+ sljit_ins ins;
+#endif /* SLJIT_MIPS_REV >= 1 && SLJIT_MIPS_REV < 6 */
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1 && SLJIT_MIPS_REV < 6)
+
+ if (SLJIT_UNLIKELY(src & SLJIT_IMM)) {
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (type & SLJIT_32)
+ srcw = (sljit_s32)srcw;
+#endif
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), srcw));
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ switch (type & ~SLJIT_32) {
+ case SLJIT_EQUAL:
+ ins = MOVZ | TA(EQUAL_FLAG);
+ break;
+ case SLJIT_NOT_EQUAL:
+ ins = MOVN | TA(EQUAL_FLAG);
+ break;
+ case SLJIT_LESS:
+ case SLJIT_GREATER:
+ case SLJIT_SIG_LESS:
+ case SLJIT_SIG_GREATER:
+ case SLJIT_OVERFLOW:
+ ins = MOVN | TA(OTHER_FLAG);
+ break;
+ case SLJIT_GREATER_EQUAL:
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_SIG_GREATER_EQUAL:
+ case SLJIT_SIG_LESS_EQUAL:
+ case SLJIT_NOT_OVERFLOW:
+ ins = MOVZ | TA(OTHER_FLAG);
+ break;
+ case SLJIT_F_EQUAL:
+ case SLJIT_F_LESS:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL:
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_UNORDERED_OR_LESS:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ case SLJIT_UNORDERED:
+ ins = MOVT;
+ break;
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_F_GREATER:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_ORDERED:
+ ins = MOVF;
+ break;
+ default:
+ ins = MOVZ | TA(OTHER_FLAG);
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+ return push_inst(compiler, ins | S(src) | D(dst_reg), DR(dst_reg));
+
+#else /* SLJIT_MIPS_REV < 1 || SLJIT_MIPS_REV >= 6 */
+ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);
+#endif /* SLJIT_MIPS_REV >= 1 */
+}
+
+static sljit_s32 update_mem_addr(struct sljit_compiler *compiler, sljit_s32 *mem, sljit_sw *memw, sljit_s16 max_offset)
+{
+ sljit_s32 arg = *mem;
+ sljit_sw argw = *memw;
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ argw &= 0x3;
+
+ if (SLJIT_UNLIKELY(argw)) {
+ FAIL_IF(push_inst(compiler, SLL_W | T(OFFS_REG(arg)) | D(TMP_REG1) | SH_IMM(argw), DR(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, ADDU_W | S(TMP_REG1) | T(arg & REG_MASK) | D(TMP_REG1), DR(TMP_REG1)));
+ } else
+ FAIL_IF(push_inst(compiler, ADDU_W | S(arg & REG_MASK) | T(OFFS_REG(arg)) | D(TMP_REG1), DR(TMP_REG1)));
+
+ *mem = TMP_REG1;
+ *memw = 0;
+
+ return SLJIT_SUCCESS;
+ }
+
+ if (argw <= max_offset && argw >= SIMM_MIN) {
+ *mem = arg & REG_MASK;
+ return SLJIT_SUCCESS;
+ }
+
+ *mem = TMP_REG1;
+
+ if ((sljit_s16)argw > max_offset) {
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), argw));
+ *memw = 0;
+ } else {
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), TO_ARGW_HI(argw)));
+ *memw = (sljit_s16)argw;
+ }
+
+ if ((arg & REG_MASK) == 0)
+ return SLJIT_SUCCESS;
+
+ return push_inst(compiler, ADDU_W | S(TMP_REG1) | T(arg & REG_MASK) | D(TMP_REG1), DR(TMP_REG1));
+}
+
+#if (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#define MEM16_IMM_FIRST(memw) IMM((memw) + 1)
+#define MEM16_IMM_SECOND(memw) IMM(memw)
+#define MEMF64_FS_FIRST(freg) FS(freg)
+#define MEMF64_FS_SECOND(freg) (FS(freg) | ((sljit_ins)1 << 11))
+#else /* !SLJIT_LITTLE_ENDIAN */
+#define MEM16_IMM_FIRST(memw) IMM(memw)
+#define MEM16_IMM_SECOND(memw) IMM((memw) + 1)
+#define MEMF64_FS_FIRST(freg) (FS(freg) | ((sljit_ins)1 << 11))
+#define MEMF64_FS_SECOND(freg) FS(freg)
+#endif /* SLJIT_LITTLE_ENDIAN */
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#define MEM_CHECK_UNALIGNED(type) ((type) & (SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16))
+#else /* !SLJIT_CONFIG_MIPS_32 */
+#define MEM_CHECK_UNALIGNED(type) ((type) & (SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32))
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 op = type & 0xff;
+ sljit_s32 flags = 0;
+ sljit_ins ins;
+#if !(defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ sljit_ins ins_right;
+#endif /* !(SLJIT_MIPS_REV >= 6) */
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (reg & REG_PAIR_MASK) {
+ ADJUST_LOCAL_OFFSET(mem, memw);
+
+#if !(defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ if (MEM_CHECK_UNALIGNED(type)) {
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - (2 * SSIZE_OF(sw) - 1)));
+
+ if (!(type & SLJIT_MEM_STORE) && (mem == REG_PAIR_FIRST(reg) || mem == REG_PAIR_SECOND(reg))) {
+ FAIL_IF(push_inst(compiler, ADDU_W | S(mem) | TA(0) | D(TMP_REG1), DR(TMP_REG1)));
+ mem = TMP_REG1;
+ }
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ ins = ((type & SLJIT_MEM_STORE) ? SWL : LWL) | S(mem);
+ ins_right = ((type & SLJIT_MEM_STORE) ? SWR : LWR) | S(mem);
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ ins = ((type & SLJIT_MEM_STORE) ? SDL : LDL) | S(mem);
+ ins_right = ((type & SLJIT_MEM_STORE) ? SDR : LDR) | S(mem);
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+ FAIL_IF(push_inst(compiler, ins | T(REG_PAIR_FIRST(reg)) | IMM(memw), DR(REG_PAIR_FIRST(reg))));
+ FAIL_IF(push_inst(compiler, ins_right | T(REG_PAIR_FIRST(reg)) | IMM(memw + (SSIZE_OF(sw) - 1)), DR(REG_PAIR_FIRST(reg))));
+ FAIL_IF(push_inst(compiler, ins | T(REG_PAIR_SECOND(reg)) | IMM(memw + SSIZE_OF(sw)), DR(REG_PAIR_SECOND(reg))));
+ return push_inst(compiler, ins_right | T(REG_PAIR_SECOND(reg)) | IMM((memw + 2 * SSIZE_OF(sw) - 1)), DR(REG_PAIR_SECOND(reg)));
+ }
+#endif /* !(SLJIT_MIPS_REV >= 6) */
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - SSIZE_OF(sw)));
+
+ ins = ((type & SLJIT_MEM_STORE) ? STORE_W : LOAD_W) | S(mem);
+
+ if (!(type & SLJIT_MEM_STORE) && mem == REG_PAIR_FIRST(reg)) {
+ FAIL_IF(push_inst(compiler, ins | T(REG_PAIR_SECOND(reg)) | IMM(memw + SSIZE_OF(sw)), DR(REG_PAIR_SECOND(reg))));
+ return push_inst(compiler, ins | T(REG_PAIR_FIRST(reg)) | IMM(memw), DR(REG_PAIR_FIRST(reg)));
+ }
+
+ FAIL_IF(push_inst(compiler, ins | T(REG_PAIR_FIRST(reg)) | IMM(memw), DR(REG_PAIR_FIRST(reg))));
+ return push_inst(compiler, ins | T(REG_PAIR_SECOND(reg)) | IMM(memw + SSIZE_OF(sw)), DR(REG_PAIR_SECOND(reg)));
+ }
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+#else /* !(SLJIT_MIPS_REV >= 6) */
+ ADJUST_LOCAL_OFFSET(mem, memw);
+
+ switch (op) {
+ case SLJIT_MOV_U8:
+ case SLJIT_MOV_S8:
+ flags = BYTE_DATA;
+ if (!(type & SLJIT_MEM_STORE))
+ flags |= LOAD_DATA;
+
+ if (op == SLJIT_MOV_S8)
+ flags |= SIGNED_DATA;
+
+ return emit_op_mem(compiler, flags, DR(reg), mem, memw);
+
+ case SLJIT_MOV_U16:
+ case SLJIT_MOV_S16:
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - 1));
+ SLJIT_ASSERT(FAST_IS_REG(mem) && mem != TMP_REG2);
+
+ if (type & SLJIT_MEM_STORE) {
+ FAIL_IF(push_inst(compiler, SRA_W | T(reg) | D(TMP_REG2) | SH_IMM(8), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, data_transfer_insts[BYTE_DATA] | S(mem) | T(TMP_REG2) | MEM16_IMM_FIRST(memw), MOVABLE_INS));
+ return push_inst(compiler, data_transfer_insts[BYTE_DATA] | S(mem) | T(reg) | MEM16_IMM_SECOND(memw), MOVABLE_INS);
+ }
+
+ flags = BYTE_DATA | LOAD_DATA;
+
+ if (op == SLJIT_MOV_S16)
+ flags |= SIGNED_DATA;
+
+ FAIL_IF(push_inst(compiler, data_transfer_insts[flags] | S(mem) | T(TMP_REG2) | MEM16_IMM_FIRST(memw), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, data_transfer_insts[BYTE_DATA | LOAD_DATA] | S(mem) | T(reg) | MEM16_IMM_SECOND(memw), DR(reg)));
+ FAIL_IF(push_inst(compiler, SLL_W | T(TMP_REG2) | D(TMP_REG2) | SH_IMM(8), DR(TMP_REG2)));
+ return push_inst(compiler, OR | S(reg) | T(TMP_REG2) | D(reg), DR(reg));
+
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ if (type & SLJIT_MEM_UNALIGNED_32) {
+ flags = WORD_DATA;
+ if (!(type & SLJIT_MEM_STORE))
+ flags |= LOAD_DATA;
+
+ return emit_op_mem(compiler, flags, DR(reg), mem, memw);
+ }
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - 7));
+ SLJIT_ASSERT(FAST_IS_REG(mem) && mem != TMP_REG2);
+
+ if (type & SLJIT_MEM_STORE) {
+ FAIL_IF(push_inst(compiler, SDL | S(mem) | T(reg) | IMM(memw), MOVABLE_INS));
+ return push_inst(compiler, SDR | S(mem) | T(reg) | IMM(memw + 7), MOVABLE_INS);
+ }
+
+ if (mem == reg) {
+ FAIL_IF(push_inst(compiler, ADDU_W | S(mem) | TA(0) | D(TMP_REG1), DR(TMP_REG1)));
+ mem = TMP_REG1;
+ }
+
+ FAIL_IF(push_inst(compiler, LDL | S(mem) | T(reg) | IMM(memw), DR(reg)));
+ return push_inst(compiler, LDR | S(mem) | T(reg) | IMM(memw + 7), DR(reg));
+#endif /* SLJIT_CONFIG_MIPS_32 */
+ }
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - 3));
+ SLJIT_ASSERT(FAST_IS_REG(mem) && mem != TMP_REG2);
+
+ if (type & SLJIT_MEM_STORE) {
+ FAIL_IF(push_inst(compiler, SWL | S(mem) | T(reg) | IMM(memw), MOVABLE_INS));
+ return push_inst(compiler, SWR | S(mem) | T(reg) | IMM(memw + 3), MOVABLE_INS);
+ }
+
+ if (mem == reg) {
+ FAIL_IF(push_inst(compiler, ADDU_W | S(mem) | TA(0) | D(TMP_REG1), DR(TMP_REG1)));
+ mem = TMP_REG1;
+ }
+
+ FAIL_IF(push_inst(compiler, LWL | S(mem) | T(reg) | IMM(memw), DR(reg)));
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ return push_inst(compiler, LWR | S(mem) | T(reg) | IMM(memw + 3), DR(reg));
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ FAIL_IF(push_inst(compiler, LWR | S(mem) | T(reg) | IMM(memw + 3), DR(reg)));
+
+ if (op != SLJIT_MOV_U32)
+ return SLJIT_SUCCESS;
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 2)
+ return push_inst(compiler, DINSU | T(reg) | SA(0) | (31 << 11) | (0 << 11), DR(reg));
+#else /* SLJIT_MIPS_REV < 1 */
+ FAIL_IF(push_inst(compiler, DSLL32 | T(reg) | D(reg) | SH_IMM(0), DR(reg)));
+ return push_inst(compiler, DSRL32 | T(reg) | D(reg) | SH_IMM(0), DR(reg));
+#endif /* SLJIT_MIPS_REV >= 2 */
+#endif /* SLJIT_CONFIG_MIPS_32 */
+#endif /* SLJIT_MIPS_REV >= 6 */
+}
+
+#if !(defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw));
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - (type & SLJIT_32) ? 3 : 7));
+ SLJIT_ASSERT(FAST_IS_REG(mem) && mem != TMP_REG2);
+
+ if (type & SLJIT_MEM_STORE) {
+ if (type & SLJIT_32) {
+ FAIL_IF(push_inst(compiler, MFC1 | T(TMP_REG2) | FS(freg), DR(TMP_REG2)));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+ FAIL_IF(push_inst(compiler, SWL | S(mem) | T(TMP_REG2) | IMM(memw), MOVABLE_INS));
+ return push_inst(compiler, SWR | S(mem) | T(TMP_REG2) | IMM(memw + 3), MOVABLE_INS);
+ }
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ FAIL_IF(push_inst(compiler, MFC1 | T(TMP_REG2) | MEMF64_FS_FIRST(freg), DR(TMP_REG2)));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+ FAIL_IF(push_inst(compiler, SWL | S(mem) | T(TMP_REG2) | IMM(memw), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, SWR | S(mem) | T(TMP_REG2) | IMM(memw + 3), MOVABLE_INS));
+
+ FAIL_IF(push_inst(compiler, MFC1 | T(TMP_REG2) | MEMF64_FS_SECOND(freg), DR(TMP_REG2)));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+ FAIL_IF(push_inst(compiler, SWL | S(mem) | T(TMP_REG2) | IMM(memw + 4), MOVABLE_INS));
+ return push_inst(compiler, SWR | S(mem) | T(TMP_REG2) | IMM(memw + 7), MOVABLE_INS);
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ FAIL_IF(push_inst(compiler, MFC1 | (1 << 21) | T(TMP_REG2) | FS(freg), DR(TMP_REG2)));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+ FAIL_IF(push_inst(compiler, SDL | S(mem) | T(TMP_REG2) | IMM(memw), MOVABLE_INS));
+ return push_inst(compiler, SDR | S(mem) | T(TMP_REG2) | IMM(memw + 7), MOVABLE_INS);
+#endif /* SLJIT_CONFIG_MIPS_32 */
+ }
+
+ if (type & SLJIT_32) {
+ FAIL_IF(push_inst(compiler, LWL | S(mem) | T(TMP_REG2) | IMM(memw), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, LWR | S(mem) | T(TMP_REG2) | IMM(memw + 3), DR(TMP_REG2)));
+
+ FAIL_IF(push_inst(compiler, MTC1 | T(TMP_REG2) | FS(freg), MOVABLE_INS));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+ return SLJIT_SUCCESS;
+ }
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ FAIL_IF(push_inst(compiler, LWL | S(mem) | T(TMP_REG2) | IMM(memw), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, LWR | S(mem) | T(TMP_REG2) | IMM(memw + 3), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, MTC1 | T(TMP_REG2) | MEMF64_FS_FIRST(freg), MOVABLE_INS));
+
+ FAIL_IF(push_inst(compiler, LWL | S(mem) | T(TMP_REG2) | IMM(memw + 4), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, LWR | S(mem) | T(TMP_REG2) | IMM(memw + 7), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, MTC1 | T(TMP_REG2) | MEMF64_FS_SECOND(freg), MOVABLE_INS));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ FAIL_IF(push_inst(compiler, LDL | S(mem) | T(TMP_REG2) | IMM(memw), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, LDR | S(mem) | T(TMP_REG2) | IMM(memw + 7), DR(TMP_REG2)));
+
+ FAIL_IF(push_inst(compiler, MTC1 | (1 << 21) | T(TMP_REG2) | FS(freg), MOVABLE_INS));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+#endif /* SLJIT_CONFIG_MIPS_32 */
+ return SLJIT_SUCCESS;
+}
+
+#endif /* !SLJIT_MIPS_REV || SLJIT_MIPS_REV < 6 */
+
+#undef MEM16_IMM_FIRST
+#undef MEM16_IMM_SECOND
+#undef MEMF64_FS_FIRST
+#undef MEMF64_FS_SECOND
+#undef MEM_CHECK_UNALIGNED
+
+#undef TO_ARGW_HI
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ struct sljit_const *const_;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const));
+ PTR_FAIL_IF(!const_);
+ set_const(const_, compiler);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+ PTR_FAIL_IF(emit_const(compiler, dst_r, init_value));
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, DR(TMP_REG2), dst, dstw));
+
+ return const_;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ struct sljit_put_label *put_label;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label));
+ PTR_FAIL_IF(!put_label);
+ set_put_label(put_label, compiler, 0);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+ PTR_FAIL_IF(push_inst(compiler, (sljit_ins)dst_r, UNMOVABLE_INS));
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ compiler->size += 1;
+#else
+ compiler->size += 5;
+#endif
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, DR(TMP_REG2), dst, dstw));
+
+ return put_label;
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativePPC_32.c b/contrib/libs/pcre2/src/sljit/sljitNativePPC_32.c
new file mode 100644
index 00000000000..9449e4b9d76
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativePPC_32.c
@@ -0,0 +1,340 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* ppc 32-bit arch dependent functions. */
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, sljit_sw imm)
+{
+ if (imm <= SIMM_MAX && imm >= SIMM_MIN)
+ return push_inst(compiler, ADDI | D(reg) | A(0) | IMM(imm));
+
+ if (!(imm & ~0xffff))
+ return push_inst(compiler, ORI | S(TMP_ZERO) | A(reg) | IMM(imm));
+
+ FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(imm >> 16)));
+ return (imm & 0xffff) ? push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm)) : SLJIT_SUCCESS;
+}
+
+/* Simplified mnemonics: clrlwi. */
+#define INS_CLEAR_LEFT(dst, src, from) \
+ (RLWINM | S(src) | A(dst) | RLWI_MBE(from, 31))
+
+static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
+ sljit_s32 dst, sljit_s32 src1, sljit_s32 src2)
+{
+ sljit_u32 imm;
+
+ switch (op) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV_P:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ if (dst != src2)
+ return push_inst(compiler, OR | S(src2) | A(dst) | B(src2));
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U8:
+ case SLJIT_MOV_S8:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ if (op == SLJIT_MOV_S8)
+ return push_inst(compiler, EXTSB | S(src2) | A(dst));
+ return push_inst(compiler, INS_CLEAR_LEFT(dst, src2, 24));
+ }
+ else if ((flags & REG_DEST) && op == SLJIT_MOV_S8)
+ return push_inst(compiler, EXTSB | S(src2) | A(dst));
+ else {
+ SLJIT_ASSERT(dst == src2);
+ }
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U16:
+ case SLJIT_MOV_S16:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ if (op == SLJIT_MOV_S16)
+ return push_inst(compiler, EXTSH | S(src2) | A(dst));
+ return push_inst(compiler, INS_CLEAR_LEFT(dst, src2, 16));
+ }
+ else {
+ SLJIT_ASSERT(dst == src2);
+ }
+ return SLJIT_SUCCESS;
+
+ case SLJIT_NOT:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ return push_inst(compiler, NOR | RC(flags) | S(src2) | A(dst) | B(src2));
+
+ case SLJIT_CLZ:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ return push_inst(compiler, CNTLZW | S(src2) | A(dst));
+
+ case SLJIT_CTZ:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ FAIL_IF(push_inst(compiler, NEG | D(TMP_REG1) | A(src2)));
+ FAIL_IF(push_inst(compiler, AND | S(src2) | A(dst) | B(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, CNTLZW | S(dst) | A(dst)));
+ FAIL_IF(push_inst(compiler, ADDI | D(TMP_REG1) | A(dst) | IMM(-32)));
+ /* The highest bits are set, if dst < 32, zero otherwise. */
+ FAIL_IF(push_inst(compiler, SRWI(27) | S(TMP_REG1) | A(TMP_REG1)));
+ return push_inst(compiler, XOR | S(dst) | A(dst) | B(TMP_REG1));
+
+ case SLJIT_ADD:
+ if (flags & ALT_FORM1) {
+ /* Setting XER SO is not enough, CR SO is also needed. */
+ return push_inst(compiler, ADD | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2));
+ }
+
+ if (flags & ALT_FORM2) {
+ /* Flags does not set: BIN_IMM_EXTS unnecessary. */
+ SLJIT_ASSERT(src2 == TMP_REG2);
+
+ if (flags & ALT_FORM3)
+ return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm);
+
+ imm = compiler->imm;
+
+ if (flags & ALT_FORM4) {
+ FAIL_IF(push_inst(compiler, ADDIS | D(dst) | A(src1) | (((imm >> 16) & 0xffff) + ((imm >> 15) & 0x1))));
+ src1 = dst;
+ }
+
+ return push_inst(compiler, ADDI | D(dst) | A(src1) | (imm & 0xffff));
+ }
+ if (flags & ALT_FORM3) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ADDIC | D(dst) | A(src1) | compiler->imm);
+ }
+ SLJIT_ASSERT(!(flags & ALT_FORM4));
+ if (!(flags & ALT_SET_FLAGS))
+ return push_inst(compiler, ADD | D(dst) | A(src1) | B(src2));
+ if (flags & ALT_FORM5)
+ return push_inst(compiler, ADDC | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2));
+ return push_inst(compiler, ADD | RC(flags) | D(dst) | A(src1) | B(src2));
+
+ case SLJIT_ADDC:
+ return push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2));
+
+ case SLJIT_SUB:
+ if (flags & ALT_FORM1) {
+ if (flags & ALT_FORM2) {
+ FAIL_IF(push_inst(compiler, CMPLI | CRD(0) | A(src1) | compiler->imm));
+ if (!(flags & ALT_FORM3))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, ADDI | D(dst) | A(src1) | (-compiler->imm & 0xffff));
+ }
+ FAIL_IF(push_inst(compiler, CMPL | CRD(0) | A(src1) | B(src2)));
+ if (!(flags & ALT_FORM3))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
+ }
+
+ if (flags & ALT_FORM2) {
+ if (flags & ALT_FORM3) {
+ FAIL_IF(push_inst(compiler, CMPI | CRD(0) | A(src1) | compiler->imm));
+ if (!(flags & ALT_FORM4))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, ADDI | D(dst) | A(src1) | (-compiler->imm & 0xffff));
+ }
+ FAIL_IF(push_inst(compiler, CMP | CRD(0) | A(src1) | B(src2)));
+ if (!(flags & ALT_FORM4))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
+ }
+
+ if (flags & ALT_FORM3) {
+ /* Setting XER SO is not enough, CR SO is also needed. */
+ if (src1 != TMP_ZERO)
+ return push_inst(compiler, SUBF | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
+ return push_inst(compiler, NEG | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src2));
+ }
+
+ if (flags & ALT_FORM4) {
+ /* Flags does not set: BIN_IMM_EXTS unnecessary. */
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, SUBFIC | D(dst) | A(src1) | compiler->imm);
+ }
+
+ if (!(flags & ALT_SET_FLAGS)) {
+ SLJIT_ASSERT(src1 != TMP_ZERO);
+ return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
+ }
+
+ if (flags & ALT_FORM5)
+ return push_inst(compiler, SUBFC | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
+
+ if (src1 != TMP_ZERO)
+ return push_inst(compiler, SUBF | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
+ return push_inst(compiler, NEG | RC(ALT_SET_FLAGS) | D(dst) | A(src2));
+
+ case SLJIT_SUBC:
+ return push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1));
+
+ case SLJIT_MUL:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, MULLI | D(dst) | A(src1) | compiler->imm);
+ }
+ return push_inst(compiler, MULLW | OE(flags) | RC(flags) | D(dst) | A(src2) | B(src1));
+
+ case SLJIT_AND:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ANDI | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM2) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ANDIS | S(src1) | A(dst) | compiler->imm);
+ }
+ return push_inst(compiler, AND | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_OR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ORI | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM2) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ORIS | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM3) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ FAIL_IF(push_inst(compiler, ORI | S(src1) | A(dst) | IMM(imm)));
+ return push_inst(compiler, ORIS | S(dst) | A(dst) | IMM(imm >> 16));
+ }
+ return push_inst(compiler, OR | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_XOR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, XORI | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM2) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, XORIS | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM3) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ FAIL_IF(push_inst(compiler, XORI | S(src1) | A(dst) | IMM(imm)));
+ return push_inst(compiler, XORIS | S(dst) | A(dst) | IMM(imm >> 16));
+ }
+ return push_inst(compiler, XOR | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm & 0x1f;
+ return push_inst(compiler, SLWI(imm) | RC(flags) | S(src1) | A(dst));
+ }
+
+ if (op == SLJIT_MSHL) {
+ FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x1f));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, SLW | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm & 0x1f;
+ /* Since imm can be 0, SRWI() cannot be used. */
+ return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | RLWI_SH((32 - imm) & 0x1f) | RLWI_MBE(imm, 31));
+ }
+
+ if (op == SLJIT_MLSHR) {
+ FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x1f));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, SRW | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm & 0x1f;
+ return push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (imm << 11));
+ }
+
+ if (op == SLJIT_MASHR) {
+ FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x1f));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, SRAW | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ if (op == SLJIT_ROTR)
+ imm = (sljit_u32)(-(sljit_s32)imm);
+
+ imm &= 0x1f;
+ return push_inst(compiler, RLWINM | S(src1) | A(dst) | RLWI_SH(imm) | RLWI_MBE(0, 31));
+ }
+
+ if (op == SLJIT_ROTR) {
+ FAIL_IF(push_inst(compiler, SUBFIC | D(TMP_REG2) | A(src2) | 0));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, RLWNM | S(src1) | A(dst) | B(src2) | RLWI_MBE(0, 31));
+ }
+
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 reg, sljit_sw init_value)
+{
+ FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(init_value >> 16)));
+ return push_inst(compiler, ORI | S(reg) | A(reg) | IMM(init_value));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ sljit_ins *inst = (sljit_ins *)addr;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 0);
+ SLJIT_ASSERT((inst[0] & 0xfc1f0000) == ADDIS && (inst[1] & 0xfc000000) == ORI);
+ inst[0] = (inst[0] & 0xffff0000) | ((new_target >> 16) & 0xffff);
+ inst[1] = (inst[1] & 0xffff0000) | (new_target & 0xffff);
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 1);
+ inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 2);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativePPC_64.c b/contrib/libs/pcre2/src/sljit/sljitNativePPC_64.c
new file mode 100644
index 00000000000..80549108bfb
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativePPC_64.c
@@ -0,0 +1,579 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* ppc 64-bit arch dependent functions. */
+
+#if defined(__GNUC__) || (defined(__IBM_GCC_ASM) && __IBM_GCC_ASM)
+#define ASM_SLJIT_CLZ(src, dst) \
+ __asm__ volatile ( "cntlzd %0, %1" : "=r"(dst) : "r"(src) )
+#elif defined(__xlc__)
+#error "Please enable GCC syntax for inline assembly statements"
+#else
+#error "Must implement count leading zeroes"
+#endif
+
+/* Computes SLDI(63 - shift). */
+#define PUSH_SLDI_NEG(reg, shift) \
+ push_inst(compiler, RLDICR | S(reg) | A(reg) | RLDI_SH(63 - shift) | RLDI_ME(shift))
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, sljit_sw imm)
+{
+ sljit_uw tmp;
+ sljit_uw shift;
+ sljit_uw tmp2;
+ sljit_uw shift2;
+
+ if (imm <= SIMM_MAX && imm >= SIMM_MIN)
+ return push_inst(compiler, ADDI | D(reg) | A(0) | IMM(imm));
+
+ if (!(imm & ~0xffff))
+ return push_inst(compiler, ORI | S(TMP_ZERO) | A(reg) | IMM(imm));
+
+ if (imm <= 0x7fffffffl && imm >= -0x80000000l) {
+ FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(imm >> 16)));
+ return (imm & 0xffff) ? push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm)) : SLJIT_SUCCESS;
+ }
+
+ /* Count leading zeroes. */
+ tmp = (sljit_uw)((imm >= 0) ? imm : ~imm);
+ ASM_SLJIT_CLZ(tmp, shift);
+ SLJIT_ASSERT(shift > 0);
+ shift--;
+ tmp = ((sljit_uw)imm << shift);
+
+ if ((tmp & ~0xffff000000000000ul) == 0) {
+ FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | (sljit_ins)(tmp >> 48)));
+ shift += 15;
+ return PUSH_SLDI_NEG(reg, shift);
+ }
+
+ if ((tmp & ~0xffffffff00000000ul) == 0) {
+ FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | (sljit_ins)(tmp >> 48)));
+ FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | IMM(tmp >> 32)));
+ shift += 31;
+ return PUSH_SLDI_NEG(reg, shift);
+ }
+
+ /* Cut out the 16 bit from immediate. */
+ shift += 15;
+ tmp2 = (sljit_uw)imm & (((sljit_uw)1 << (63 - shift)) - 1);
+
+ if (tmp2 <= 0xffff) {
+ FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | (sljit_ins)(tmp >> 48)));
+ FAIL_IF(PUSH_SLDI_NEG(reg, shift));
+ return push_inst(compiler, ORI | S(reg) | A(reg) | (sljit_ins)tmp2);
+ }
+
+ if (tmp2 <= 0xffffffff) {
+ FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | IMM(tmp >> 48)));
+ FAIL_IF(PUSH_SLDI_NEG(reg, shift));
+ FAIL_IF(push_inst(compiler, ORIS | S(reg) | A(reg) | (sljit_ins)(tmp2 >> 16)));
+ return (imm & 0xffff) ? push_inst(compiler, ORI | S(reg) | A(reg) | IMM(tmp2)) : SLJIT_SUCCESS;
+ }
+
+ ASM_SLJIT_CLZ(tmp2, shift2);
+ tmp2 <<= shift2;
+
+ if ((tmp2 & ~0xffff000000000000ul) == 0) {
+ FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | (sljit_ins)(tmp >> 48)));
+ shift2 += 15;
+ shift += (63 - shift2);
+ FAIL_IF(PUSH_SLDI_NEG(reg, shift));
+ FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | (sljit_ins)(tmp2 >> 48)));
+ return PUSH_SLDI_NEG(reg, shift2);
+ }
+
+ /* The general version. */
+ FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | (sljit_ins)((sljit_uw)imm >> 48)));
+ FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm >> 32)));
+ FAIL_IF(PUSH_SLDI_NEG(reg, 31));
+ FAIL_IF(push_inst(compiler, ORIS | S(reg) | A(reg) | IMM(imm >> 16)));
+ return push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm));
+}
+
+#undef PUSH_SLDI_NEG
+
+#define CLRLDI(dst, src, n) \
+ (RLDICL | S(src) | A(dst) | RLDI_SH(0) | RLDI_MB(n))
+
+/* Sign extension for integer operations. */
+#define UN_EXTS() \
+ if ((flags & (ALT_SIGN_EXT | REG2_SOURCE)) == (ALT_SIGN_EXT | REG2_SOURCE)) { \
+ FAIL_IF(push_inst(compiler, EXTSW | S(src2) | A(TMP_REG2))); \
+ src2 = TMP_REG2; \
+ }
+
+#define BIN_EXTS() \
+ if (flags & ALT_SIGN_EXT) { \
+ if (flags & REG1_SOURCE) { \
+ FAIL_IF(push_inst(compiler, EXTSW | S(src1) | A(TMP_REG1))); \
+ src1 = TMP_REG1; \
+ } \
+ if (flags & REG2_SOURCE) { \
+ FAIL_IF(push_inst(compiler, EXTSW | S(src2) | A(TMP_REG2))); \
+ src2 = TMP_REG2; \
+ } \
+ }
+
+#define BIN_IMM_EXTS() \
+ if ((flags & (ALT_SIGN_EXT | REG1_SOURCE)) == (ALT_SIGN_EXT | REG1_SOURCE)) { \
+ FAIL_IF(push_inst(compiler, EXTSW | S(src1) | A(TMP_REG1))); \
+ src1 = TMP_REG1; \
+ }
+
+static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
+ sljit_s32 dst, sljit_s32 src1, sljit_s32 src2)
+{
+ sljit_u32 imm;
+
+ switch (op) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ if (dst != src2)
+ return push_inst(compiler, OR | S(src2) | A(dst) | B(src2));
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ if (op == SLJIT_MOV_S32)
+ return push_inst(compiler, EXTSW | S(src2) | A(dst));
+ return push_inst(compiler, CLRLDI(dst, src2, 32));
+ }
+ else {
+ SLJIT_ASSERT(dst == src2);
+ }
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U8:
+ case SLJIT_MOV_S8:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ if (op == SLJIT_MOV_S8)
+ return push_inst(compiler, EXTSB | S(src2) | A(dst));
+ return push_inst(compiler, CLRLDI(dst, src2, 56));
+ }
+ else if ((flags & REG_DEST) && op == SLJIT_MOV_S8)
+ return push_inst(compiler, EXTSB | S(src2) | A(dst));
+ else {
+ SLJIT_ASSERT(dst == src2);
+ }
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U16:
+ case SLJIT_MOV_S16:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ if (op == SLJIT_MOV_S16)
+ return push_inst(compiler, EXTSH | S(src2) | A(dst));
+ return push_inst(compiler, CLRLDI(dst, src2, 48));
+ }
+ else {
+ SLJIT_ASSERT(dst == src2);
+ }
+ return SLJIT_SUCCESS;
+
+ case SLJIT_NOT:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ UN_EXTS();
+ return push_inst(compiler, NOR | RC(flags) | S(src2) | A(dst) | B(src2));
+
+ case SLJIT_CLZ:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ return push_inst(compiler, ((flags & ALT_FORM1) ? CNTLZW : CNTLZD) | S(src2) | A(dst));
+
+ case SLJIT_CTZ:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ FAIL_IF(push_inst(compiler, NEG | D(TMP_REG1) | A(src2)));
+ FAIL_IF(push_inst(compiler, AND | S(src2) | A(dst) | B(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, ((flags & ALT_FORM1) ? CNTLZW : CNTLZD) | S(dst) | A(dst)));
+ FAIL_IF(push_inst(compiler, ADDI | D(TMP_REG1) | A(dst) | IMM((flags & ALT_FORM1) ? -32 : -64)));
+ /* The highest bits are set, if dst < bit width, zero otherwise. */
+ FAIL_IF(push_inst(compiler, ((flags & ALT_FORM1) ? SRWI(27) : SRDI(58)) | S(TMP_REG1) | A(TMP_REG1)));
+ return push_inst(compiler, XOR | S(dst) | A(dst) | B(TMP_REG1));
+
+ case SLJIT_ADD:
+ if (flags & ALT_FORM1) {
+ if (flags & ALT_SIGN_EXT) {
+ FAIL_IF(push_inst(compiler, SLDI(32) | S(src1) | A(TMP_REG1)));
+ src1 = TMP_REG1;
+ FAIL_IF(push_inst(compiler, SLDI(32) | S(src2) | A(TMP_REG2)));
+ src2 = TMP_REG2;
+ }
+ /* Setting XER SO is not enough, CR SO is also needed. */
+ FAIL_IF(push_inst(compiler, ADD | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2)));
+ if (flags & ALT_SIGN_EXT)
+ return push_inst(compiler, SRDI(32) | S(dst) | A(dst));
+ return SLJIT_SUCCESS;
+ }
+
+ if (flags & ALT_FORM2) {
+ /* Flags does not set: BIN_IMM_EXTS unnecessary. */
+ SLJIT_ASSERT(src2 == TMP_REG2);
+
+ if (flags & ALT_FORM3)
+ return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm);
+
+ imm = compiler->imm;
+
+ if (flags & ALT_FORM4) {
+ FAIL_IF(push_inst(compiler, ADDIS | D(dst) | A(src1) | (((imm >> 16) & 0xffff) + ((imm >> 15) & 0x1))));
+ src1 = dst;
+ }
+
+ return push_inst(compiler, ADDI | D(dst) | A(src1) | (imm & 0xffff));
+ }
+ if (flags & ALT_FORM3) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ BIN_IMM_EXTS();
+ return push_inst(compiler, ADDIC | D(dst) | A(src1) | compiler->imm);
+ }
+ if (flags & ALT_FORM4) {
+ if (flags & ALT_FORM5)
+ FAIL_IF(push_inst(compiler, ADDI | D(dst) | A(src1) | compiler->imm));
+ else
+ FAIL_IF(push_inst(compiler, ADD | D(dst) | A(src1) | B(src2)));
+ return push_inst(compiler, CMPI | A(dst) | 0);
+ }
+ if (!(flags & ALT_SET_FLAGS))
+ return push_inst(compiler, ADD | D(dst) | A(src1) | B(src2));
+ BIN_EXTS();
+ if (flags & ALT_FORM5)
+ return push_inst(compiler, ADDC | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2));
+ return push_inst(compiler, ADD | RC(flags) | D(dst) | A(src1) | B(src2));
+
+ case SLJIT_ADDC:
+ BIN_EXTS();
+ return push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2));
+
+ case SLJIT_SUB:
+ if (flags & ALT_FORM1) {
+ if (flags & ALT_FORM2) {
+ FAIL_IF(push_inst(compiler, CMPLI | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | compiler->imm));
+ if (!(flags & ALT_FORM3))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, ADDI | D(dst) | A(src1) | (-compiler->imm & 0xffff));
+ }
+ FAIL_IF(push_inst(compiler, CMPL | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2)));
+ if (!(flags & ALT_FORM3))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
+ }
+
+ if (flags & ALT_FORM2) {
+ if (flags & ALT_FORM3) {
+ FAIL_IF(push_inst(compiler, CMPI | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | compiler->imm));
+ if (!(flags & ALT_FORM4))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, ADDI | D(dst) | A(src1) | (-compiler->imm & 0xffff));
+ }
+ FAIL_IF(push_inst(compiler, CMP | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2)));
+ if (!(flags & ALT_FORM4))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
+ }
+
+ if (flags & ALT_FORM3) {
+ if (flags & ALT_SIGN_EXT) {
+ if (src1 != TMP_ZERO) {
+ FAIL_IF(push_inst(compiler, SLDI(32) | S(src1) | A(TMP_REG1)));
+ src1 = TMP_REG1;
+ }
+ if (src2 != TMP_ZERO) {
+ FAIL_IF(push_inst(compiler, SLDI(32) | S(src2) | A(TMP_REG2)));
+ src2 = TMP_REG2;
+ }
+ }
+
+ /* Setting XER SO is not enough, CR SO is also needed. */
+ if (src1 != TMP_ZERO)
+ FAIL_IF(push_inst(compiler, SUBF | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1)));
+ else
+ FAIL_IF(push_inst(compiler, NEG | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src2)));
+
+ if (flags & ALT_SIGN_EXT)
+ return push_inst(compiler, SRDI(32) | S(dst) | A(dst));
+ return SLJIT_SUCCESS;
+ }
+
+ if (flags & ALT_FORM4) {
+ /* Flags does not set: BIN_IMM_EXTS unnecessary. */
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, SUBFIC | D(dst) | A(src1) | compiler->imm);
+ }
+
+ if (!(flags & ALT_SET_FLAGS)) {
+ SLJIT_ASSERT(src1 != TMP_ZERO);
+ return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
+ }
+
+ BIN_EXTS();
+ if (flags & ALT_FORM5)
+ return push_inst(compiler, SUBFC | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
+
+ if (src1 != TMP_ZERO)
+ return push_inst(compiler, SUBF | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
+ return push_inst(compiler, NEG | RC(ALT_SET_FLAGS) | D(dst) | A(src2));
+
+ case SLJIT_SUBC:
+ BIN_EXTS();
+ return push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1));
+
+ case SLJIT_MUL:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, MULLI | D(dst) | A(src1) | compiler->imm);
+ }
+ BIN_EXTS();
+ if (flags & ALT_FORM2)
+ return push_inst(compiler, MULLW | OE(flags) | RC(flags) | D(dst) | A(src2) | B(src1));
+ return push_inst(compiler, MULLD | OE(flags) | RC(flags) | D(dst) | A(src2) | B(src1));
+
+ case SLJIT_AND:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ANDI | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM2) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ANDIS | S(src1) | A(dst) | compiler->imm);
+ }
+ return push_inst(compiler, AND | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_OR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ORI | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM2) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ORIS | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM3) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ FAIL_IF(push_inst(compiler, ORI | S(src1) | A(dst) | IMM(imm)));
+ return push_inst(compiler, ORIS | S(dst) | A(dst) | IMM(imm >> 16));
+ }
+ return push_inst(compiler, OR | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_XOR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, XORI | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM2) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, XORIS | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM3) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ FAIL_IF(push_inst(compiler, XORI | S(src1) | A(dst) | IMM(imm)));
+ return push_inst(compiler, XORIS | S(dst) | A(dst) | IMM(imm >> 16));
+ }
+ return push_inst(compiler, XOR | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ if (flags & ALT_FORM2) {
+ imm &= 0x1f;
+ return push_inst(compiler, SLWI(imm) | RC(flags) | S(src1) | A(dst));
+ }
+
+ imm &= 0x3f;
+ return push_inst(compiler, SLDI(imm) | RC(flags) | S(src1) | A(dst));
+ }
+
+ if (op == SLJIT_MSHL) {
+ FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | ((flags & ALT_FORM2) ? 0x1f : 0x3f)));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, ((flags & ALT_FORM2) ? SLW : SLD) | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ if (flags & ALT_FORM2) {
+ imm &= 0x1f;
+ /* Since imm can be 0, SRWI() cannot be used. */
+ return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | RLWI_SH((32 - imm) & 0x1f) | RLWI_MBE(imm, 31));
+ }
+
+ imm &= 0x3f;
+ /* Since imm can be 0, SRDI() cannot be used. */
+ return push_inst(compiler, RLDICL | RC(flags) | S(src1) | A(dst) | RLDI_SH((64 - imm) & 0x3f) | RLDI_MB(imm));
+ }
+
+ if (op == SLJIT_MLSHR) {
+ FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | ((flags & ALT_FORM2) ? 0x1f : 0x3f)));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, ((flags & ALT_FORM2) ? SRW : SRD) | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ if (flags & ALT_FORM2) {
+ imm &= 0x1f;
+ return push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (imm << 11));
+ }
+
+ imm &= 0x3f;
+ return push_inst(compiler, SRADI | RC(flags) | S(src1) | A(dst) | RLDI_SH(imm));
+ }
+
+ if (op == SLJIT_MASHR) {
+ FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | ((flags & ALT_FORM2) ? 0x1f : 0x3f)));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, ((flags & ALT_FORM2) ? SRAW : SRAD) | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ if (op == SLJIT_ROTR)
+ imm = (sljit_u32)(-(sljit_s32)imm);
+
+ if (flags & ALT_FORM2) {
+ imm &= 0x1f;
+ return push_inst(compiler, RLWINM | S(src1) | A(dst) | RLWI_SH(imm) | RLWI_MBE(0, 31));
+ }
+
+ imm &= 0x3f;
+ return push_inst(compiler, RLDICL | S(src1) | A(dst) | RLDI_SH(imm));
+ }
+
+ if (op == SLJIT_ROTR) {
+ FAIL_IF(push_inst(compiler, SUBFIC | D(TMP_REG2) | A(src2) | 0));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, ((flags & ALT_FORM2) ? (RLWNM | RLWI_MBE(0, 31)) : (RLDCL | RLDI_MB(0))) | S(src1) | A(dst) | B(src2));
+ }
+
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src)
+{
+ sljit_s32 arg_count = 0;
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_s32 reg = 0;
+
+ if (src)
+ reg = *src & REG_MASK;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK);
+
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ case SLJIT_ARG_TYPE_F32:
+ arg_count++;
+ break;
+ default:
+ arg_count++;
+ word_arg_count++;
+
+ if (arg_count != word_arg_count && arg_count == reg) {
+ FAIL_IF(push_inst(compiler, OR | S(reg) | A(TMP_CALL_REG) | B(reg)));
+ *src = TMP_CALL_REG;
+ }
+ break;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ while (types) {
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ case SLJIT_ARG_TYPE_F32:
+ arg_count--;
+ break;
+ default:
+ if (arg_count != word_arg_count)
+ FAIL_IF(push_inst(compiler, OR | S(word_arg_count) | A(arg_count) | B(word_arg_count)));
+
+ arg_count--;
+ word_arg_count--;
+ break;
+ }
+
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 reg, sljit_sw init_value)
+{
+ FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(init_value >> 48)));
+ FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | IMM(init_value >> 32)));
+ FAIL_IF(push_inst(compiler, SLDI(32) | S(reg) | A(reg)));
+ FAIL_IF(push_inst(compiler, ORIS | S(reg) | A(reg) | IMM(init_value >> 16)));
+ return push_inst(compiler, ORI | S(reg) | A(reg) | IMM(init_value));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ sljit_ins *inst = (sljit_ins*)addr;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 0);
+ inst[0] = (inst[0] & 0xffff0000u) | ((sljit_ins)(new_target >> 48) & 0xffff);
+ inst[1] = (inst[1] & 0xffff0000u) | ((sljit_ins)(new_target >> 32) & 0xffff);
+ inst[3] = (inst[3] & 0xffff0000u) | ((sljit_ins)(new_target >> 16) & 0xffff);
+ inst[4] = (inst[4] & 0xffff0000u) | ((sljit_ins)new_target & 0xffff);
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 1);
+ inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 5);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativePPC_common.c b/contrib/libs/pcre2/src/sljit/sljitNativePPC_common.c
new file mode 100644
index 00000000000..790faffe46e
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativePPC_common.c
@@ -0,0 +1,2851 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+ return "PowerPC" SLJIT_CPUINFO;
+}
+
+/* Length of an instruction word.
+ Both for ppc-32 and ppc-64. */
+typedef sljit_u32 sljit_ins;
+
+#if ((defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) && (defined _AIX)) \
+ || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define SLJIT_PPC_STACK_FRAME_V2 1
+#endif
+
+#ifdef _AIX
+#error #include <sys/cache.h>
+#endif
+
+#if (defined _CALL_ELF && _CALL_ELF == 2)
+#define SLJIT_PASS_ENTRY_ADDR_TO_CALL 1
+#endif
+
+#if (defined SLJIT_CACHE_FLUSH_OWN_IMPL && SLJIT_CACHE_FLUSH_OWN_IMPL)
+
+static void ppc_cache_flush(sljit_ins *from, sljit_ins *to)
+{
+#ifdef _AIX
+ _sync_cache_range((caddr_t)from, (int)((size_t)to - (size_t)from));
+#elif defined(__GNUC__) || (defined(__IBM_GCC_ASM) && __IBM_GCC_ASM)
+# if defined(_ARCH_PWR) || defined(_ARCH_PWR2)
+ /* Cache flush for POWER architecture. */
+ while (from < to) {
+ __asm__ volatile (
+ "clf 0, %0\n"
+ "dcs\n"
+ : : "r"(from)
+ );
+ from++;
+ }
+ __asm__ volatile ( "ics" );
+# elif defined(_ARCH_COM) && !defined(_ARCH_PPC)
+# error "Cache flush is not implemented for PowerPC/POWER common mode."
+# else
+ /* Cache flush for PowerPC architecture. */
+ while (from < to) {
+ __asm__ volatile (
+ "dcbf 0, %0\n"
+ "sync\n"
+ "icbi 0, %0\n"
+ : : "r"(from)
+ );
+ from++;
+ }
+ __asm__ volatile ( "isync" );
+# endif
+# ifdef __xlc__
+# warning "This file may fail to compile if -qfuncsect is used"
+# endif
+#elif defined(__xlc__)
+#error "Please enable GCC syntax for inline assembly statements with -qasm=gcc"
+#else
+#error "This platform requires a cache flush implementation."
+#endif /* _AIX */
+}
+
+#endif /* (defined SLJIT_CACHE_FLUSH_OWN_IMPL && SLJIT_CACHE_FLUSH_OWN_IMPL) */
+
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
+#define TMP_ZERO (SLJIT_NUMBER_OF_REGISTERS + 4)
+
+#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL)
+#define TMP_CALL_REG (SLJIT_NUMBER_OF_REGISTERS + 5)
+#else
+#define TMP_CALL_REG TMP_REG2
+#endif
+
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
+
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 7] = {
+ 0, 3, 4, 5, 6, 7, 8, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 1, 9, 10, 31, 12
+};
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 0, 13
+};
+
+/* --------------------------------------------------------------------- */
+/* Instrucion forms */
+/* --------------------------------------------------------------------- */
+#define D(d) ((sljit_ins)reg_map[d] << 21)
+#define S(s) ((sljit_ins)reg_map[s] << 21)
+#define A(a) ((sljit_ins)reg_map[a] << 16)
+#define B(b) ((sljit_ins)reg_map[b] << 11)
+#define C(c) ((sljit_ins)reg_map[c] << 6)
+#define FD(fd) ((sljit_ins)freg_map[fd] << 21)
+#define FS(fs) ((sljit_ins)freg_map[fs] << 21)
+#define FA(fa) ((sljit_ins)freg_map[fa] << 16)
+#define FB(fb) ((sljit_ins)freg_map[fb] << 11)
+#define FC(fc) ((sljit_ins)freg_map[fc] << 6)
+#define IMM(imm) ((sljit_ins)(imm) & 0xffff)
+#define CRD(d) ((sljit_ins)(d) << 21)
+
+/* Instruction bit sections.
+ OE and Rc flag (see ALT_SET_FLAGS). */
+#define OE(flags) ((flags) & ALT_SET_FLAGS)
+/* Rc flag (see ALT_SET_FLAGS). */
+#define RC(flags) (((flags) & ALT_SET_FLAGS) >> 10)
+#define HI(opcode) ((sljit_ins)(opcode) << 26)
+#define LO(opcode) ((sljit_ins)(opcode) << 1)
+
+#define ADD (HI(31) | LO(266))
+#define ADDC (HI(31) | LO(10))
+#define ADDE (HI(31) | LO(138))
+#define ADDI (HI(14))
+#define ADDIC (HI(13))
+#define ADDIS (HI(15))
+#define ADDME (HI(31) | LO(234))
+#define AND (HI(31) | LO(28))
+#define ANDI (HI(28))
+#define ANDIS (HI(29))
+#define Bx (HI(18))
+#define BCx (HI(16))
+#define BCCTR (HI(19) | LO(528) | (3 << 11))
+#define BLR (HI(19) | LO(16) | (0x14 << 21))
+#define CNTLZD (HI(31) | LO(58))
+#define CNTLZW (HI(31) | LO(26))
+#define CMP (HI(31) | LO(0))
+#define CMPI (HI(11))
+#define CMPL (HI(31) | LO(32))
+#define CMPLI (HI(10))
+#define CROR (HI(19) | LO(449))
+#define DCBT (HI(31) | LO(278))
+#define DIVD (HI(31) | LO(489))
+#define DIVDU (HI(31) | LO(457))
+#define DIVW (HI(31) | LO(491))
+#define DIVWU (HI(31) | LO(459))
+#define EXTSB (HI(31) | LO(954))
+#define EXTSH (HI(31) | LO(922))
+#define EXTSW (HI(31) | LO(986))
+#define FABS (HI(63) | LO(264))
+#define FADD (HI(63) | LO(21))
+#define FADDS (HI(59) | LO(21))
+#define FCFID (HI(63) | LO(846))
+#define FCMPU (HI(63) | LO(0))
+#define FCTIDZ (HI(63) | LO(815))
+#define FCTIWZ (HI(63) | LO(15))
+#define FDIV (HI(63) | LO(18))
+#define FDIVS (HI(59) | LO(18))
+#define FMR (HI(63) | LO(72))
+#define FMUL (HI(63) | LO(25))
+#define FMULS (HI(59) | LO(25))
+#define FNEG (HI(63) | LO(40))
+#define FRSP (HI(63) | LO(12))
+#define FSUB (HI(63) | LO(20))
+#define FSUBS (HI(59) | LO(20))
+#define LD (HI(58) | 0)
+#define LFD (HI(50))
+#define LWZ (HI(32))
+#define MFCR (HI(31) | LO(19))
+#define MFLR (HI(31) | LO(339) | 0x80000)
+#define MFXER (HI(31) | LO(339) | 0x10000)
+#define MTCTR (HI(31) | LO(467) | 0x90000)
+#define MTLR (HI(31) | LO(467) | 0x80000)
+#define MTXER (HI(31) | LO(467) | 0x10000)
+#define MULHD (HI(31) | LO(73))
+#define MULHDU (HI(31) | LO(9))
+#define MULHW (HI(31) | LO(75))
+#define MULHWU (HI(31) | LO(11))
+#define MULLD (HI(31) | LO(233))
+#define MULLI (HI(7))
+#define MULLW (HI(31) | LO(235))
+#define NEG (HI(31) | LO(104))
+#define NOP (HI(24))
+#define NOR (HI(31) | LO(124))
+#define OR (HI(31) | LO(444))
+#define ORI (HI(24))
+#define ORIS (HI(25))
+#define RLDCL (HI(30) | LO(8))
+#define RLDICL (HI(30) | LO(0 << 1))
+#define RLDICR (HI(30) | LO(1 << 1))
+#define RLDIMI (HI(30) | LO(3 << 1))
+#define RLWIMI (HI(20))
+#define RLWINM (HI(21))
+#define RLWNM (HI(23))
+#define SLD (HI(31) | LO(27))
+#define SLW (HI(31) | LO(24))
+#define SRAD (HI(31) | LO(794))
+#define SRADI (HI(31) | LO(413 << 1))
+#define SRAW (HI(31) | LO(792))
+#define SRAWI (HI(31) | LO(824))
+#define SRD (HI(31) | LO(539))
+#define SRW (HI(31) | LO(536))
+#define STD (HI(62) | 0)
+#define STDU (HI(62) | 1)
+#define STDUX (HI(31) | LO(181))
+#define STFD (HI(54))
+#define STFIWX (HI(31) | LO(983))
+#define STW (HI(36))
+#define STWU (HI(37))
+#define STWUX (HI(31) | LO(183))
+#define SUBF (HI(31) | LO(40))
+#define SUBFC (HI(31) | LO(8))
+#define SUBFE (HI(31) | LO(136))
+#define SUBFIC (HI(8))
+#define XOR (HI(31) | LO(316))
+#define XORI (HI(26))
+#define XORIS (HI(27))
+
+#define SIMM_MAX (0x7fff)
+#define SIMM_MIN (-0x8000)
+#define UIMM_MAX (0xffff)
+
+/* Shift helpers. */
+#define RLWI_SH(sh) ((sljit_ins)(sh) << 11)
+#define RLWI_MBE(mb, me) (((sljit_ins)(mb) << 6) | ((sljit_ins)(me) << 1))
+#define RLDI_SH(sh) ((((sljit_ins)(sh) & 0x1f) << 11) | (((sljit_ins)(sh) & 0x20) >> 4))
+#define RLDI_MB(mb) ((((sljit_ins)(mb) & 0x1f) << 6) | ((sljit_ins)(mb) & 0x20))
+#define RLDI_ME(me) RLDI_MB(me)
+
+#define SLWI(shift) (RLWINM | RLWI_SH(shift) | RLWI_MBE(0, 31 - (shift)))
+#define SLDI(shift) (RLDICR | RLDI_SH(shift) | RLDI_ME(63 - (shift)))
+/* shift > 0 */
+#define SRWI(shift) (RLWINM | RLWI_SH(32 - (shift)) | RLWI_MBE((shift), 31))
+#define SRDI(shift) (RLDICL | RLDI_SH(64 - (shift)) | RLDI_MB(shift))
+
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+#define SLWI_W(shift) SLWI(shift)
+#else /* !SLJIT_CONFIG_PPC_32 */
+#define SLWI_W(shift) SLDI(shift)
+#endif /* SLJIT_CONFIG_PPC_32 */
+
+#if (defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL)
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_function_context(void** func_ptr, struct sljit_function_context* context, sljit_uw addr, void* func)
+{
+ sljit_uw* ptrs;
+
+ if (func_ptr)
+ *func_ptr = (void*)context;
+
+ ptrs = (sljit_uw*)func;
+ context->addr = addr ? addr : ptrs[0];
+ context->r2 = ptrs[1];
+ context->r11 = ptrs[2];
+}
+#endif
+
+static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_ins ins)
+{
+ sljit_ins *ptr = (sljit_ins*)ensure_buf(compiler, sizeof(sljit_ins));
+ FAIL_IF(!ptr);
+ *ptr = ins;
+ compiler->size++;
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_ins *code_ptr, sljit_ins *code, sljit_sw executable_offset)
+{
+ sljit_sw diff;
+ sljit_uw target_addr;
+ sljit_uw extra_jump_flags;
+
+#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL) && (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ if (jump->flags & (SLJIT_REWRITABLE_JUMP | IS_CALL))
+ return 0;
+#else
+ if (jump->flags & SLJIT_REWRITABLE_JUMP)
+ return 0;
+#endif
+
+ if (jump->flags & JUMP_ADDR)
+ target_addr = jump->u.target;
+ else {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ target_addr = (sljit_uw)(code + jump->u.label->size) + (sljit_uw)executable_offset;
+ }
+
+#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL) && (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (jump->flags & IS_CALL)
+ goto keep_address;
+#endif
+
+ diff = ((sljit_sw)target_addr - (sljit_sw)(code_ptr) - executable_offset) & ~0x3l;
+
+ extra_jump_flags = 0;
+ if (jump->flags & IS_COND) {
+ if (diff <= 0x7fff && diff >= -0x8000) {
+ jump->flags |= PATCH_B;
+ return 1;
+ }
+ if (target_addr <= 0xffff) {
+ jump->flags |= PATCH_B | PATCH_ABS_B;
+ return 1;
+ }
+ extra_jump_flags = REMOVE_COND;
+
+ diff -= SSIZE_OF(ins);
+ }
+
+ if (diff <= 0x01ffffff && diff >= -0x02000000) {
+ jump->flags |= PATCH_B | extra_jump_flags;
+ return 1;
+ }
+
+ if (target_addr <= 0x03ffffff) {
+ jump->flags |= PATCH_B | PATCH_ABS_B | extra_jump_flags;
+ return 1;
+ }
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL)
+keep_address:
+#endif
+ if (target_addr <= 0x7fffffff) {
+ jump->flags |= PATCH_ABS32;
+ return 1;
+ }
+
+ if (target_addr <= 0x7fffffffffffl) {
+ jump->flags |= PATCH_ABS48;
+ return 1;
+ }
+#endif
+
+ return 0;
+}
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+
+static SLJIT_INLINE sljit_sw put_label_get_length(struct sljit_put_label *put_label, sljit_uw max_label)
+{
+ if (max_label < 0x100000000l) {
+ put_label->flags = 0;
+ return 1;
+ }
+
+ if (max_label < 0x1000000000000l) {
+ put_label->flags = 1;
+ return 3;
+ }
+
+ put_label->flags = 2;
+ return 4;
+}
+
+static SLJIT_INLINE void put_label_set(struct sljit_put_label *put_label)
+{
+ sljit_uw addr = put_label->label->addr;
+ sljit_ins *inst = (sljit_ins *)put_label->addr;
+ sljit_u32 reg = *inst;
+
+ if (put_label->flags == 0) {
+ SLJIT_ASSERT(addr < 0x100000000l);
+ inst[0] = ORIS | S(TMP_ZERO) | A(reg) | IMM(addr >> 16);
+ }
+ else {
+ if (put_label->flags == 1) {
+ SLJIT_ASSERT(addr < 0x1000000000000l);
+ inst[0] = ORI | S(TMP_ZERO) | A(reg) | IMM(addr >> 32);
+ }
+ else {
+ inst[0] = ORIS | S(TMP_ZERO) | A(reg) | IMM(addr >> 48);
+ inst[1] = ORI | S(reg) | A(reg) | IMM((addr >> 32) & 0xffff);
+ inst++;
+ }
+
+ inst[1] = SLDI(32) | S(reg) | A(reg);
+ inst[2] = ORIS | S(reg) | A(reg) | IMM((addr >> 16) & 0xffff);
+ inst += 2;
+ }
+
+ inst[1] = ORI | S(reg) | A(reg) | IMM(addr & 0xffff);
+}
+
+#endif /* SLJIT_CONFIG_PPC_64 */
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf;
+ sljit_ins *code;
+ sljit_ins *code_ptr;
+ sljit_ins *buf_ptr;
+ sljit_ins *buf_end;
+ sljit_uw word_count;
+ sljit_uw next_addr;
+ sljit_sw executable_offset;
+ sljit_uw addr;
+
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ struct sljit_const *const_;
+ struct sljit_put_label *put_label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_generate_code(compiler));
+ reverse_buf(compiler);
+
+#if (defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL)
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ compiler->size += (compiler->size & 0x1) + (sizeof(struct sljit_function_context) / sizeof(sljit_ins));
+#else
+ compiler->size += (sizeof(struct sljit_function_context) / sizeof(sljit_ins));
+#endif
+#endif
+ code = (sljit_ins*)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_ins), compiler->exec_allocator_data);
+ PTR_FAIL_WITH_EXEC_IF(code);
+ buf = compiler->buf;
+
+ code_ptr = code;
+ word_count = 0;
+ next_addr = 0;
+ executable_offset = SLJIT_EXEC_OFFSET(code);
+
+ label = compiler->labels;
+ jump = compiler->jumps;
+ const_ = compiler->consts;
+ put_label = compiler->put_labels;
+
+ do {
+ buf_ptr = (sljit_ins*)buf->memory;
+ buf_end = buf_ptr + (buf->used_size >> 2);
+ do {
+ *code_ptr = *buf_ptr++;
+ if (next_addr == word_count) {
+ SLJIT_ASSERT(!label || label->size >= word_count);
+ SLJIT_ASSERT(!jump || jump->addr >= word_count);
+ SLJIT_ASSERT(!const_ || const_->addr >= word_count);
+ SLJIT_ASSERT(!put_label || put_label->addr >= word_count);
+
+ /* These structures are ordered by their address. */
+ if (label && label->size == word_count) {
+ /* Just recording the address. */
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+ if (jump && jump->addr == word_count) {
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ jump->addr = (sljit_uw)(code_ptr - 3);
+#else
+ jump->addr = (sljit_uw)(code_ptr - 6);
+#endif
+ if (detect_jump_type(jump, code_ptr, code, executable_offset)) {
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ code_ptr[-3] = code_ptr[0];
+ code_ptr -= 3;
+#else
+ if (jump->flags & PATCH_ABS32) {
+ code_ptr -= 3;
+ code_ptr[-1] = code_ptr[2];
+ code_ptr[0] = code_ptr[3];
+ }
+ else if (jump->flags & PATCH_ABS48) {
+ code_ptr--;
+ code_ptr[-1] = code_ptr[0];
+ code_ptr[0] = code_ptr[1];
+ /* rldicr rX,rX,32,31 -> rX,rX,16,47 */
+ SLJIT_ASSERT((code_ptr[-3] & 0xfc00ffff) == 0x780007c6);
+ code_ptr[-3] ^= 0x8422;
+ /* oris -> ori */
+ code_ptr[-2] ^= 0x4000000;
+ }
+ else {
+ code_ptr[-6] = code_ptr[0];
+ code_ptr -= 6;
+ }
+#endif
+ if (jump->flags & REMOVE_COND) {
+ code_ptr[0] = BCx | (2 << 2) | ((code_ptr[0] ^ (8 << 21)) & 0x03ff0001);
+ code_ptr++;
+ jump->addr += sizeof(sljit_ins);
+ code_ptr[0] = Bx;
+ jump->flags -= IS_COND;
+ }
+ }
+ jump = jump->next;
+ }
+ if (const_ && const_->addr == word_count) {
+ const_->addr = (sljit_uw)code_ptr;
+ const_ = const_->next;
+ }
+ if (put_label && put_label->addr == word_count) {
+ SLJIT_ASSERT(put_label->label);
+ put_label->addr = (sljit_uw)code_ptr;
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ code_ptr += put_label_get_length(put_label, (sljit_uw)(SLJIT_ADD_EXEC_OFFSET(code, executable_offset) + put_label->label->size));
+ word_count += 4;
+#endif
+ put_label = put_label->next;
+ }
+ next_addr = compute_next_addr(label, jump, const_, put_label);
+ }
+ code_ptr++;
+ word_count++;
+ } while (buf_ptr < buf_end);
+
+ buf = buf->next;
+ } while (buf);
+
+ if (label && label->size == word_count) {
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+
+ SLJIT_ASSERT(!label);
+ SLJIT_ASSERT(!jump);
+ SLJIT_ASSERT(!const_);
+ SLJIT_ASSERT(!put_label);
+
+#if (defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL)
+ SLJIT_ASSERT(code_ptr - code <= (sljit_sw)(compiler->size - (sizeof(struct sljit_function_context) / sizeof(sljit_ins))));
+#else
+ SLJIT_ASSERT(code_ptr - code <= (sljit_sw)compiler->size);
+#endif
+
+ jump = compiler->jumps;
+ while (jump) {
+ do {
+ addr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target;
+ buf_ptr = (sljit_ins *)jump->addr;
+
+ if (jump->flags & PATCH_B) {
+ if (jump->flags & IS_COND) {
+ if (!(jump->flags & PATCH_ABS_B)) {
+ addr -= (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset);
+ SLJIT_ASSERT((sljit_sw)addr <= 0x7fff && (sljit_sw)addr >= -0x8000);
+ *buf_ptr = BCx | ((sljit_ins)addr & 0xfffc) | ((*buf_ptr) & 0x03ff0001);
+ }
+ else {
+ SLJIT_ASSERT(addr <= 0xffff);
+ *buf_ptr = BCx | ((sljit_ins)addr & 0xfffc) | 0x2 | ((*buf_ptr) & 0x03ff0001);
+ }
+ }
+ else {
+ if (!(jump->flags & PATCH_ABS_B)) {
+ addr -= (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset);
+ SLJIT_ASSERT((sljit_sw)addr <= 0x01ffffff && (sljit_sw)addr >= -0x02000000);
+ *buf_ptr = Bx | ((sljit_ins)addr & 0x03fffffc) | ((*buf_ptr) & 0x1);
+ }
+ else {
+ SLJIT_ASSERT(addr <= 0x03ffffff);
+ *buf_ptr = Bx | ((sljit_ins)addr & 0x03fffffc) | 0x2 | ((*buf_ptr) & 0x1);
+ }
+ }
+ break;
+ }
+
+ /* Set the fields of immediate loads. */
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ SLJIT_ASSERT(((buf_ptr[0] | buf_ptr[1]) & 0xffff) == 0);
+ buf_ptr[0] |= (sljit_ins)(addr >> 16) & 0xffff;
+ buf_ptr[1] |= (sljit_ins)addr & 0xffff;
+#else
+ if (jump->flags & PATCH_ABS32) {
+ SLJIT_ASSERT(addr <= 0x7fffffff);
+ SLJIT_ASSERT(((buf_ptr[0] | buf_ptr[1]) & 0xffff) == 0);
+ buf_ptr[0] |= (sljit_ins)(addr >> 16) & 0xffff;
+ buf_ptr[1] |= (sljit_ins)addr & 0xffff;
+ break;
+ }
+
+ if (jump->flags & PATCH_ABS48) {
+ SLJIT_ASSERT(addr <= 0x7fffffffffff);
+ SLJIT_ASSERT(((buf_ptr[0] | buf_ptr[1] | buf_ptr[3]) & 0xffff) == 0);
+ buf_ptr[0] |= (sljit_ins)(addr >> 32) & 0xffff;
+ buf_ptr[1] |= (sljit_ins)(addr >> 16) & 0xffff;
+ buf_ptr[3] |= (sljit_ins)addr & 0xffff;
+ break;
+ }
+
+ SLJIT_ASSERT(((buf_ptr[0] | buf_ptr[1] | buf_ptr[3] | buf_ptr[4]) & 0xffff) == 0);
+ buf_ptr[0] |= (sljit_ins)(addr >> 48) & 0xffff;
+ buf_ptr[1] |= (sljit_ins)(addr >> 32) & 0xffff;
+ buf_ptr[3] |= (sljit_ins)(addr >> 16) & 0xffff;
+ buf_ptr[4] |= (sljit_ins)addr & 0xffff;
+#endif
+ } while (0);
+ jump = jump->next;
+ }
+
+ put_label = compiler->put_labels;
+ while (put_label) {
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ addr = put_label->label->addr;
+ buf_ptr = (sljit_ins *)put_label->addr;
+
+ SLJIT_ASSERT((buf_ptr[0] & 0xfc1f0000) == ADDIS && (buf_ptr[1] & 0xfc000000) == ORI);
+ buf_ptr[0] |= (addr >> 16) & 0xffff;
+ buf_ptr[1] |= addr & 0xffff;
+#else
+ put_label_set(put_label);
+#endif
+ put_label = put_label->next;
+ }
+
+ compiler->error = SLJIT_ERR_COMPILED;
+ compiler->executable_offset = executable_offset;
+ compiler->executable_size = (sljit_uw)(code_ptr - code) * sizeof(sljit_ins);
+
+ code = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+
+#if (defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL)
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (((sljit_sw)code_ptr) & 0x4)
+ code_ptr++;
+#endif
+ sljit_set_function_context(NULL, (struct sljit_function_context*)code_ptr, (sljit_uw)code, (void*)sljit_generate_code);
+#endif
+
+ code_ptr = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+
+ SLJIT_CACHE_FLUSH(code, code_ptr);
+ SLJIT_UPDATE_WX_FLAGS(code, code_ptr, 1);
+
+#if (defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL)
+ return code_ptr;
+#else
+ return code;
+#endif
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#else
+ /* Available by default. */
+ return 1;
+#endif
+
+ /* A saved register is set to a zero value. */
+ case SLJIT_HAS_ZERO_REGISTER:
+ case SLJIT_HAS_CLZ:
+ case SLJIT_HAS_ROT:
+ case SLJIT_HAS_PREFETCH:
+ return 1;
+
+ case SLJIT_HAS_CTZ:
+ return 2;
+
+ default:
+ return 0;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type)
+{
+ return (type >= SLJIT_UNORDERED && type <= SLJIT_ORDERED_LESS_EQUAL);
+}
+
+/* --------------------------------------------------------------------- */
+/* Entry, exit */
+/* --------------------------------------------------------------------- */
+
+/* inp_flags: */
+
+/* Creates an index in data_transfer_insts array. */
+#define LOAD_DATA 0x01
+#define INDEXED 0x02
+#define SIGNED_DATA 0x04
+
+#define WORD_DATA 0x00
+#define BYTE_DATA 0x08
+#define HALF_DATA 0x10
+#define INT_DATA 0x18
+/* Separates integer and floating point registers */
+#define GPR_REG 0x1f
+#define DOUBLE_DATA 0x20
+
+#define MEM_MASK 0x7f
+
+/* Other inp_flags. */
+
+/* Integer opertion and set flags -> requires exts on 64 bit systems. */
+#define ALT_SIGN_EXT 0x000100
+/* This flag affects the RC() and OERC() macros. */
+#define ALT_SET_FLAGS 0x000400
+#define ALT_FORM1 0x001000
+#define ALT_FORM2 0x002000
+#define ALT_FORM3 0x004000
+#define ALT_FORM4 0x008000
+#define ALT_FORM5 0x010000
+
+/* Source and destination is register. */
+#define REG_DEST 0x000001
+#define REG1_SOURCE 0x000002
+#define REG2_SOURCE 0x000004
+/*
+ALT_SIGN_EXT 0x000100
+ALT_SET_FLAGS 0x000200
+ALT_FORM1 0x001000
+...
+ALT_FORM5 0x010000 */
+
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+#include "sljitNativePPC_32.c"
+#else
+#include "sljitNativePPC_64.c"
+#endif
+
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+#define STACK_STORE STW
+#define STACK_LOAD LWZ
+#else
+#define STACK_STORE STD
+#define STACK_LOAD LD
+#endif
+
+#if (defined SLJIT_PPC_STACK_FRAME_V2 && SLJIT_PPC_STACK_FRAME_V2)
+#define LR_SAVE_OFFSET 2 * SSIZE_OF(sw)
+#else
+#define LR_SAVE_OFFSET SSIZE_OF(sw)
+#endif
+
+#define STACK_MAX_DISTANCE (0x8000 - SSIZE_OF(sw) - LR_SAVE_OFFSET)
+
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 inp_flags, sljit_s32 reg,
+ sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg);
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 i, tmp, base, offset;
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ sljit_s32 arg_count = 0;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 0)
+ + GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+
+ if (!(options & SLJIT_ENTER_REG_ARG))
+ local_size += SSIZE_OF(sw);
+
+ local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf;
+ compiler->local_size = local_size;
+
+ FAIL_IF(push_inst(compiler, MFLR | D(0)));
+
+ base = SLJIT_SP;
+ offset = local_size;
+
+ if (local_size <= STACK_MAX_DISTANCE) {
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ FAIL_IF(push_inst(compiler, STWU | S(SLJIT_SP) | A(SLJIT_SP) | IMM(-local_size)));
+#else
+ FAIL_IF(push_inst(compiler, STDU | S(SLJIT_SP) | A(SLJIT_SP) | IMM(-local_size)));
+#endif
+ } else {
+ base = TMP_REG1;
+ FAIL_IF(push_inst(compiler, OR | S(SLJIT_SP) | A(TMP_REG1) | B(SLJIT_SP)));
+ FAIL_IF(load_immediate(compiler, TMP_REG2, -local_size));
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ FAIL_IF(push_inst(compiler, STWUX | S(SLJIT_SP) | A(SLJIT_SP) | B(TMP_REG2)));
+#else
+ FAIL_IF(push_inst(compiler, STDUX | S(SLJIT_SP) | A(SLJIT_SP) | B(TMP_REG2)));
+#endif
+ local_size = 0;
+ offset = 0;
+ }
+
+ tmp = SLJIT_FS0 - fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, STFD | FS(i) | A(base) | IMM(offset)));
+ }
+
+ for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, STFD | FS(i) | A(base) | IMM(offset)));
+ }
+
+ if (!(options & SLJIT_ENTER_REG_ARG)) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STACK_STORE | S(TMP_ZERO) | A(base) | IMM(offset)));
+ }
+
+ tmp = SLJIT_S0 - saveds;
+ for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STACK_STORE | S(i) | A(base) | IMM(offset)));
+ }
+
+ for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STACK_STORE | S(i) | A(base) | IMM(offset)));
+ }
+
+ FAIL_IF(push_inst(compiler, STACK_STORE | S(0) | A(base) | IMM(local_size + LR_SAVE_OFFSET)));
+
+ if (options & SLJIT_ENTER_REG_ARG)
+ return SLJIT_SUCCESS;
+
+ FAIL_IF(push_inst(compiler, ADDI | D(TMP_ZERO) | A(0) | 0));
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ saved_arg_count = 0;
+
+ while (arg_types > 0) {
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) {
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ do {
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ tmp = SLJIT_S0 - saved_arg_count;
+ saved_arg_count++;
+ } else if (arg_count != word_arg_count)
+ tmp = SLJIT_R0 + word_arg_count;
+ else
+ break;
+
+ FAIL_IF(push_inst(compiler, OR | S(SLJIT_R0 + arg_count) | A(tmp) | B(SLJIT_R0 + arg_count)));
+ } while (0);
+#else
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ FAIL_IF(push_inst(compiler, OR | S(SLJIT_R0 + word_arg_count) | A(SLJIT_S0 - saved_arg_count) | B(SLJIT_R0 + word_arg_count)));
+ saved_arg_count++;
+ }
+#endif
+ word_arg_count++;
+ }
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ arg_count++;
+#endif
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 0)
+ + GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+
+ if (!(options & SLJIT_ENTER_REG_ARG))
+ local_size += SSIZE_OF(sw);
+
+ compiler->local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 is_return_to)
+{
+ sljit_s32 i, tmp, base, offset;
+ sljit_s32 local_size = compiler->local_size;
+
+ base = SLJIT_SP;
+ if (local_size > STACK_MAX_DISTANCE) {
+ base = TMP_REG1;
+ if (local_size > 2 * STACK_MAX_DISTANCE + LR_SAVE_OFFSET) {
+ FAIL_IF(push_inst(compiler, STACK_LOAD | D(base) | A(SLJIT_SP) | IMM(0)));
+ local_size = 0;
+ } else {
+ FAIL_IF(push_inst(compiler, ADDI | D(TMP_REG1) | A(SLJIT_SP) | IMM(local_size - STACK_MAX_DISTANCE)));
+ local_size = STACK_MAX_DISTANCE;
+ }
+ }
+
+ offset = local_size;
+ if (!is_return_to)
+ FAIL_IF(push_inst(compiler, STACK_LOAD | S(0) | A(base) | IMM(offset + LR_SAVE_OFFSET)));
+
+ tmp = SLJIT_FS0 - compiler->fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, LFD | FS(i) | A(base) | IMM(offset)));
+ }
+
+ for (i = compiler->fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, LFD | FS(i) | A(base) | IMM(offset)));
+ }
+
+ if (!(compiler->options & SLJIT_ENTER_REG_ARG)) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STACK_LOAD | S(TMP_ZERO) | A(base) | IMM(offset)));
+ }
+
+ tmp = SLJIT_S0 - compiler->saveds;
+ for (i = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options); i > tmp; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STACK_LOAD | S(i) | A(base) | IMM(offset)));
+ }
+
+ for (i = compiler->scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STACK_LOAD | S(i) | A(base) | IMM(offset)));
+ }
+
+ if (!is_return_to)
+ push_inst(compiler, MTLR | S(0));
+
+ if (local_size > 0)
+ return push_inst(compiler, ADDI | D(SLJIT_SP) | A(base) | IMM(local_size));
+
+ SLJIT_ASSERT(base == TMP_REG1);
+ return push_inst(compiler, OR | S(base) | A(SLJIT_SP) | B(base));
+}
+
+#undef STACK_STORE
+#undef STACK_LOAD
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+ return push_inst(compiler, BLR);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_CALL_REG, src, srcw, TMP_CALL_REG));
+ src = TMP_CALL_REG;
+ srcw = 0;
+ } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, OR | S(src) | A(TMP_CALL_REG) | B(src)));
+ src = TMP_CALL_REG;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 1));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+/* s/l - store/load (1 bit)
+ i/x - immediate/indexed form
+ u/s - signed/unsigned (1 bit)
+ w/b/h/i - word/byte/half/int allowed (2 bit)
+
+ Some opcodes are repeated (e.g. store signed / unsigned byte is the same instruction). */
+
+/* 64 bit only: [reg+imm] must be aligned to 4 bytes. */
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define INT_ALIGNED 0x10000
+#endif
+
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+#define ARCH_32_64(a, b) a
+#define INST_CODE_AND_DST(inst, flags, reg) \
+ ((sljit_ins)(inst) | (sljit_ins)(((flags) & MEM_MASK) <= GPR_REG ? D(reg) : FD(reg)))
+#else
+#define ARCH_32_64(a, b) b
+#define INST_CODE_AND_DST(inst, flags, reg) \
+ (((sljit_ins)(inst) & ~(sljit_ins)INT_ALIGNED) | (sljit_ins)(((flags) & MEM_MASK) <= GPR_REG ? D(reg) : FD(reg)))
+#endif
+
+static const sljit_ins data_transfer_insts[64 + 16] = {
+
+/* -------- Integer -------- */
+
+/* Word. */
+
+/* w u i s */ ARCH_32_64(HI(36) /* stw */, HI(62) | INT_ALIGNED | 0x0 /* std */),
+/* w u i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x0 /* ld */),
+/* w u x s */ ARCH_32_64(HI(31) | LO(151) /* stwx */, HI(31) | LO(149) /* stdx */),
+/* w u x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(21) /* ldx */),
+
+/* w s i s */ ARCH_32_64(HI(36) /* stw */, HI(62) | INT_ALIGNED | 0x0 /* std */),
+/* w s i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x0 /* ld */),
+/* w s x s */ ARCH_32_64(HI(31) | LO(151) /* stwx */, HI(31) | LO(149) /* stdx */),
+/* w s x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(21) /* ldx */),
+
+/* Byte. */
+
+/* b u i s */ HI(38) /* stb */,
+/* b u i l */ HI(34) /* lbz */,
+/* b u x s */ HI(31) | LO(215) /* stbx */,
+/* b u x l */ HI(31) | LO(87) /* lbzx */,
+
+/* b s i s */ HI(38) /* stb */,
+/* b s i l */ HI(34) /* lbz */ /* EXTS_REQ */,
+/* b s x s */ HI(31) | LO(215) /* stbx */,
+/* b s x l */ HI(31) | LO(87) /* lbzx */ /* EXTS_REQ */,
+
+/* Half. */
+
+/* h u i s */ HI(44) /* sth */,
+/* h u i l */ HI(40) /* lhz */,
+/* h u x s */ HI(31) | LO(407) /* sthx */,
+/* h u x l */ HI(31) | LO(279) /* lhzx */,
+
+/* h s i s */ HI(44) /* sth */,
+/* h s i l */ HI(42) /* lha */,
+/* h s x s */ HI(31) | LO(407) /* sthx */,
+/* h s x l */ HI(31) | LO(343) /* lhax */,
+
+/* Int. */
+
+/* i u i s */ HI(36) /* stw */,
+/* i u i l */ HI(32) /* lwz */,
+/* i u x s */ HI(31) | LO(151) /* stwx */,
+/* i u x l */ HI(31) | LO(23) /* lwzx */,
+
+/* i s i s */ HI(36) /* stw */,
+/* i s i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x2 /* lwa */),
+/* i s x s */ HI(31) | LO(151) /* stwx */,
+/* i s x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(341) /* lwax */),
+
+/* -------- Floating point -------- */
+
+/* d i s */ HI(54) /* stfd */,
+/* d i l */ HI(50) /* lfd */,
+/* d x s */ HI(31) | LO(727) /* stfdx */,
+/* d x l */ HI(31) | LO(599) /* lfdx */,
+
+/* s i s */ HI(52) /* stfs */,
+/* s i l */ HI(48) /* lfs */,
+/* s x s */ HI(31) | LO(663) /* stfsx */,
+/* s x l */ HI(31) | LO(535) /* lfsx */,
+};
+
+static const sljit_ins updated_data_transfer_insts[64] = {
+
+/* -------- Integer -------- */
+
+/* Word. */
+
+/* w u i s */ ARCH_32_64(HI(37) /* stwu */, HI(62) | INT_ALIGNED | 0x1 /* stdu */),
+/* w u i l */ ARCH_32_64(HI(33) /* lwzu */, HI(58) | INT_ALIGNED | 0x1 /* ldu */),
+/* w u x s */ ARCH_32_64(HI(31) | LO(183) /* stwux */, HI(31) | LO(181) /* stdux */),
+/* w u x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(53) /* ldux */),
+
+/* w s i s */ ARCH_32_64(HI(37) /* stwu */, HI(62) | INT_ALIGNED | 0x1 /* stdu */),
+/* w s i l */ ARCH_32_64(HI(33) /* lwzu */, HI(58) | INT_ALIGNED | 0x1 /* ldu */),
+/* w s x s */ ARCH_32_64(HI(31) | LO(183) /* stwux */, HI(31) | LO(181) /* stdux */),
+/* w s x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(53) /* ldux */),
+
+/* Byte. */
+
+/* b u i s */ HI(39) /* stbu */,
+/* b u i l */ HI(35) /* lbzu */,
+/* b u x s */ HI(31) | LO(247) /* stbux */,
+/* b u x l */ HI(31) | LO(119) /* lbzux */,
+
+/* b s i s */ HI(39) /* stbu */,
+/* b s i l */ 0 /* no such instruction */,
+/* b s x s */ HI(31) | LO(247) /* stbux */,
+/* b s x l */ 0 /* no such instruction */,
+
+/* Half. */
+
+/* h u i s */ HI(45) /* sthu */,
+/* h u i l */ HI(41) /* lhzu */,
+/* h u x s */ HI(31) | LO(439) /* sthux */,
+/* h u x l */ HI(31) | LO(311) /* lhzux */,
+
+/* h s i s */ HI(45) /* sthu */,
+/* h s i l */ HI(43) /* lhau */,
+/* h s x s */ HI(31) | LO(439) /* sthux */,
+/* h s x l */ HI(31) | LO(375) /* lhaux */,
+
+/* Int. */
+
+/* i u i s */ HI(37) /* stwu */,
+/* i u i l */ HI(33) /* lwzu */,
+/* i u x s */ HI(31) | LO(183) /* stwux */,
+/* i u x l */ HI(31) | LO(55) /* lwzux */,
+
+/* i s i s */ HI(37) /* stwu */,
+/* i s i l */ ARCH_32_64(HI(33) /* lwzu */, 0 /* no such instruction */),
+/* i s x s */ HI(31) | LO(183) /* stwux */,
+/* i s x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(373) /* lwaux */),
+
+/* -------- Floating point -------- */
+
+/* d i s */ HI(55) /* stfdu */,
+/* d i l */ HI(51) /* lfdu */,
+/* d x s */ HI(31) | LO(759) /* stfdux */,
+/* d x l */ HI(31) | LO(631) /* lfdux */,
+
+/* s i s */ HI(53) /* stfsu */,
+/* s i l */ HI(49) /* lfsu */,
+/* s x s */ HI(31) | LO(695) /* stfsux */,
+/* s x l */ HI(31) | LO(567) /* lfsux */,
+};
+
+#undef ARCH_32_64
+
+/* Simple cases, (no caching is required). */
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 inp_flags, sljit_s32 reg,
+ sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg)
+{
+ sljit_ins inst;
+ sljit_s32 offs_reg;
+
+ /* Should work when (arg & REG_MASK) == 0. */
+ SLJIT_ASSERT(A(0) == 0);
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ argw &= 0x3;
+ offs_reg = OFFS_REG(arg);
+
+ if (argw != 0) {
+ FAIL_IF(push_inst(compiler, SLWI_W(argw) | S(OFFS_REG(arg)) | A(tmp_reg)));
+ offs_reg = tmp_reg;
+ }
+
+ inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK];
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ SLJIT_ASSERT(!(inst & INT_ALIGNED));
+#endif /* SLJIT_CONFIG_PPC_64 */
+
+ return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg & REG_MASK) | B(offs_reg));
+ }
+
+ inst = data_transfer_insts[inp_flags & MEM_MASK];
+ arg &= REG_MASK;
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if ((inst & INT_ALIGNED) && (argw & 0x3) != 0) {
+ FAIL_IF(load_immediate(compiler, tmp_reg, argw));
+
+ inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK];
+ return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg) | B(tmp_reg));
+ }
+#endif /* SLJIT_CONFIG_PPC_64 */
+
+ if (argw <= SIMM_MAX && argw >= SIMM_MIN)
+ return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg) | IMM(argw));
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (argw <= 0x7fff7fffl && argw >= -0x80000000l) {
+#endif /* SLJIT_CONFIG_PPC_64 */
+ FAIL_IF(push_inst(compiler, ADDIS | D(tmp_reg) | A(arg) | IMM((argw + 0x8000) >> 16)));
+ return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(tmp_reg) | IMM(argw));
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ }
+
+ FAIL_IF(load_immediate(compiler, tmp_reg, argw));
+
+ inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK];
+ return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg) | B(tmp_reg));
+#endif /* SLJIT_CONFIG_PPC_64 */
+}
+
+static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 input_flags,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ /* arg1 goes to TMP_REG1 or src reg
+ arg2 goes to TMP_REG2, imm or src reg
+ result goes to TMP_REG2, so put result can use TMP_REG1. */
+ sljit_s32 dst_r = TMP_REG2;
+ sljit_s32 src1_r;
+ sljit_s32 src2_r;
+ sljit_s32 sugg_src2_r = TMP_REG2;
+ sljit_s32 flags = input_flags & (ALT_FORM1 | ALT_FORM2 | ALT_FORM3 | ALT_FORM4 | ALT_FORM5 | ALT_SIGN_EXT | ALT_SET_FLAGS);
+
+ /* Destination check. */
+ if (FAST_IS_REG(dst)) {
+ dst_r = dst;
+ /* The REG_DEST is only used by SLJIT_MOV operations, although
+ * it is set for op2 operations with unset destination. */
+ flags |= REG_DEST;
+
+ if (op >= SLJIT_MOV && op <= SLJIT_MOV_P)
+ sugg_src2_r = dst_r;
+ }
+
+ /* Source 1. */
+ if (FAST_IS_REG(src1)) {
+ src1_r = src1;
+ flags |= REG1_SOURCE;
+ }
+ else if (src1 & SLJIT_IMM) {
+ src1_r = TMP_ZERO;
+ if (src1w != 0) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, src1w));
+ src1_r = TMP_REG1;
+ }
+ }
+ else {
+ FAIL_IF(emit_op_mem(compiler, input_flags | LOAD_DATA, TMP_REG1, src1, src1w, TMP_REG1));
+ src1_r = TMP_REG1;
+ }
+
+ /* Source 2. */
+ if (FAST_IS_REG(src2)) {
+ src2_r = src2;
+ flags |= REG2_SOURCE;
+
+ if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOV_P)
+ dst_r = src2_r;
+ }
+ else if (src2 & SLJIT_IMM) {
+ src2_r = TMP_ZERO;
+ if (src2w != 0) {
+ FAIL_IF(load_immediate(compiler, sugg_src2_r, src2w));
+ src2_r = sugg_src2_r;
+ }
+ }
+ else {
+ FAIL_IF(emit_op_mem(compiler, input_flags | LOAD_DATA, sugg_src2_r, src2, src2w, TMP_REG2));
+ src2_r = sugg_src2_r;
+ }
+
+ FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r));
+
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+
+ return emit_op_mem(compiler, input_flags, dst_r, dst, dstw, TMP_REG1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ sljit_s32 int_op = op & SLJIT_32;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op0(compiler, op));
+
+ op = GET_OPCODE(op);
+ switch (op) {
+ case SLJIT_BREAKPOINT:
+ case SLJIT_NOP:
+ return push_inst(compiler, NOP);
+ case SLJIT_LMUL_UW:
+ case SLJIT_LMUL_SW:
+ FAIL_IF(push_inst(compiler, OR | S(SLJIT_R0) | A(TMP_REG1) | B(SLJIT_R0)));
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ FAIL_IF(push_inst(compiler, MULLD | D(SLJIT_R0) | A(TMP_REG1) | B(SLJIT_R1)));
+ return push_inst(compiler, (op == SLJIT_LMUL_UW ? MULHDU : MULHD) | D(SLJIT_R1) | A(TMP_REG1) | B(SLJIT_R1));
+#else
+ FAIL_IF(push_inst(compiler, MULLW | D(SLJIT_R0) | A(TMP_REG1) | B(SLJIT_R1)));
+ return push_inst(compiler, (op == SLJIT_LMUL_UW ? MULHWU : MULHW) | D(SLJIT_R1) | A(TMP_REG1) | B(SLJIT_R1));
+#endif
+ case SLJIT_DIVMOD_UW:
+ case SLJIT_DIVMOD_SW:
+ FAIL_IF(push_inst(compiler, OR | S(SLJIT_R0) | A(TMP_REG1) | B(SLJIT_R0)));
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ FAIL_IF(push_inst(compiler, (int_op ? (op == SLJIT_DIVMOD_UW ? DIVWU : DIVW) : (op == SLJIT_DIVMOD_UW ? DIVDU : DIVD)) | D(SLJIT_R0) | A(SLJIT_R0) | B(SLJIT_R1)));
+ FAIL_IF(push_inst(compiler, (int_op ? MULLW : MULLD) | D(SLJIT_R1) | A(SLJIT_R0) | B(SLJIT_R1)));
+#else
+ FAIL_IF(push_inst(compiler, (op == SLJIT_DIVMOD_UW ? DIVWU : DIVW) | D(SLJIT_R0) | A(SLJIT_R0) | B(SLJIT_R1)));
+ FAIL_IF(push_inst(compiler, MULLW | D(SLJIT_R1) | A(SLJIT_R0) | B(SLJIT_R1)));
+#endif
+ return push_inst(compiler, SUBF | D(SLJIT_R1) | A(SLJIT_R1) | B(TMP_REG1));
+ case SLJIT_DIV_UW:
+ case SLJIT_DIV_SW:
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ return push_inst(compiler, (int_op ? (op == SLJIT_DIV_UW ? DIVWU : DIVW) : (op == SLJIT_DIV_UW ? DIVDU : DIVD)) | D(SLJIT_R0) | A(SLJIT_R0) | B(SLJIT_R1));
+#else
+ return push_inst(compiler, (op == SLJIT_DIV_UW ? DIVWU : DIVW) | D(SLJIT_R0) | A(SLJIT_R0) | B(SLJIT_R1));
+#endif
+ case SLJIT_ENDBR:
+ case SLJIT_SKIP_FRAMES_BEFORE_RETURN:
+ return SLJIT_SUCCESS;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_prefetch(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (!(src & OFFS_REG_MASK)) {
+ if (srcw == 0 && (src & REG_MASK))
+ return push_inst(compiler, DCBT | A(0) | B(src & REG_MASK));
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
+ /* Works with SLJIT_MEM0() case as well. */
+ return push_inst(compiler, DCBT | A(src & REG_MASK) | B(TMP_REG1));
+ }
+
+ srcw &= 0x3;
+
+ if (srcw == 0)
+ return push_inst(compiler, DCBT | A(src & REG_MASK) | B(OFFS_REG(src)));
+
+ FAIL_IF(push_inst(compiler, SLWI_W(srcw) | S(OFFS_REG(src)) | A(TMP_REG1)));
+ return push_inst(compiler, DCBT | A(src & REG_MASK) | B(TMP_REG1));
+}
+
+#define EMIT_MOV(type, type_flags, type_cast) \
+ emit_op(compiler, (src & SLJIT_IMM) ? SLJIT_MOV : type, flags | (type_flags), dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? type_cast srcw : srcw)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 flags = HAS_FLAGS(op) ? ALT_SET_FLAGS : 0;
+ sljit_s32 op_flags = GET_ALL_FLAGS(op);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ op = GET_OPCODE(op);
+
+ if (GET_FLAG_TYPE(op_flags) == SLJIT_OVERFLOW)
+ FAIL_IF(push_inst(compiler, MTXER | S(TMP_ZERO)));
+
+ if (op < SLJIT_NOT && FAST_IS_REG(src) && src == dst) {
+ if (!TYPE_CAST_NEEDED(op))
+ return SLJIT_SUCCESS;
+ }
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (op_flags & SLJIT_32) {
+ if (op < SLJIT_NOT) {
+ if (src & SLJIT_MEM) {
+ if (op == SLJIT_MOV_S32)
+ op = SLJIT_MOV_U32;
+ }
+ else if (src & SLJIT_IMM) {
+ if (op == SLJIT_MOV_U32)
+ op = SLJIT_MOV_S32;
+ }
+ }
+ else {
+ /* Most operations expect sign extended arguments. */
+ flags |= INT_DATA | SIGNED_DATA;
+ if (HAS_FLAGS(op_flags))
+ flags |= ALT_SIGN_EXT;
+ }
+ }
+#endif
+
+ switch (op) {
+ case SLJIT_MOV:
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+#endif
+ case SLJIT_MOV_P:
+ return emit_op(compiler, SLJIT_MOV, flags | WORD_DATA, dst, dstw, TMP_REG1, 0, src, srcw);
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ case SLJIT_MOV_U32:
+ return EMIT_MOV(SLJIT_MOV_U32, INT_DATA, (sljit_u32));
+
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ return EMIT_MOV(SLJIT_MOV_S32, INT_DATA | SIGNED_DATA, (sljit_s32));
+#endif
+
+ case SLJIT_MOV_U8:
+ return EMIT_MOV(SLJIT_MOV_U8, BYTE_DATA, (sljit_u8));
+
+ case SLJIT_MOV_S8:
+ return EMIT_MOV(SLJIT_MOV_S8, BYTE_DATA | SIGNED_DATA, (sljit_s8));
+
+ case SLJIT_MOV_U16:
+ return EMIT_MOV(SLJIT_MOV_U16, HALF_DATA, (sljit_u16));
+
+ case SLJIT_MOV_S16:
+ return EMIT_MOV(SLJIT_MOV_S16, HALF_DATA | SIGNED_DATA, (sljit_s16));
+
+ case SLJIT_NOT:
+ return emit_op(compiler, SLJIT_NOT, flags, dst, dstw, TMP_REG1, 0, src, srcw);
+
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ return emit_op(compiler, op, flags | (!(op_flags & SLJIT_32) ? 0 : ALT_FORM1), dst, dstw, TMP_REG1, 0, src, srcw);
+#else
+ return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw);
+#endif
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#undef EMIT_MOV
+
+#define TEST_SL_IMM(src, srcw) \
+ (((src) & SLJIT_IMM) && (srcw) <= SIMM_MAX && (srcw) >= SIMM_MIN)
+
+#define TEST_UL_IMM(src, srcw) \
+ (((src) & SLJIT_IMM) && !((srcw) & ~0xffff))
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define TEST_SH_IMM(src, srcw) \
+ (((src) & SLJIT_IMM) && !((srcw) & 0xffff) && (srcw) <= 0x7fffffffl && (srcw) >= -0x80000000l)
+#else
+#define TEST_SH_IMM(src, srcw) \
+ (((src) & SLJIT_IMM) && !((srcw) & 0xffff))
+#endif
+
+#define TEST_UH_IMM(src, srcw) \
+ (((src) & SLJIT_IMM) && !((srcw) & ~(sljit_sw)0xffff0000))
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define TEST_ADD_IMM(src, srcw) \
+ (((src) & SLJIT_IMM) && (srcw) <= 0x7fff7fffl && (srcw) >= -0x80000000l)
+#else
+#define TEST_ADD_IMM(src, srcw) \
+ ((src) & SLJIT_IMM)
+#endif
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define TEST_UI_IMM(src, srcw) \
+ (((src) & SLJIT_IMM) && !((srcw) & ~0xffffffff))
+#else
+#define TEST_UI_IMM(src, srcw) \
+ ((src) & SLJIT_IMM)
+#endif
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define TEST_ADD_FORM1(op) \
+ (GET_FLAG_TYPE(op) == SLJIT_OVERFLOW \
+ || (op & (SLJIT_32 | SLJIT_SET_Z | VARIABLE_FLAG_MASK)) == (SLJIT_32 | SLJIT_SET_Z | SLJIT_SET_CARRY))
+#define TEST_SUB_FORM2(op) \
+ ((GET_FLAG_TYPE(op) >= SLJIT_SIG_LESS && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL) \
+ || (op & (SLJIT_32 | SLJIT_SET_Z | VARIABLE_FLAG_MASK)) == (SLJIT_32 | SLJIT_SET_Z))
+#define TEST_SUB_FORM3(op) \
+ (GET_FLAG_TYPE(op) == SLJIT_OVERFLOW \
+ || (op & (SLJIT_32 | SLJIT_SET_Z)) == (SLJIT_32 | SLJIT_SET_Z))
+#else
+#define TEST_ADD_FORM1(op) \
+ (GET_FLAG_TYPE(op) == SLJIT_OVERFLOW)
+#define TEST_SUB_FORM2(op) \
+ (GET_FLAG_TYPE(op) >= SLJIT_SIG_LESS && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL)
+#define TEST_SUB_FORM3(op) \
+ (GET_FLAG_TYPE(op) == SLJIT_OVERFLOW)
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 flags = HAS_FLAGS(op) ? ALT_SET_FLAGS : 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (op & SLJIT_32) {
+ /* Most operations expect sign extended arguments. */
+ flags |= INT_DATA | SIGNED_DATA;
+ if (src1 & SLJIT_IMM)
+ src1w = (sljit_s32)(src1w);
+ if (src2 & SLJIT_IMM)
+ src2w = (sljit_s32)(src2w);
+ if (HAS_FLAGS(op))
+ flags |= ALT_SIGN_EXT;
+ }
+#endif
+ if (GET_FLAG_TYPE(op) == SLJIT_OVERFLOW)
+ FAIL_IF(push_inst(compiler, MTXER | S(TMP_ZERO)));
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+
+ if (TEST_ADD_FORM1(op))
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM1, dst, dstw, src1, src1w, src2, src2w);
+
+ if (!HAS_FLAGS(op) && ((src1 | src2) & SLJIT_IMM)) {
+ if (TEST_SL_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_SL_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)src1w & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ if (TEST_SH_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)(src2w >> 16) & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_SH_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)(src1w >> 16) & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM3, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ /* Range between -1 and -32768 is covered above. */
+ if (TEST_ADD_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w & 0xffffffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM4, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_ADD_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)src1w & 0xffffffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM4, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ }
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if ((op & (SLJIT_32 | SLJIT_SET_Z)) == (SLJIT_32 | SLJIT_SET_Z)) {
+ if (TEST_SL_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM4 | ALT_FORM5, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_SL_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)src1w & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM4 | ALT_FORM5, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM4, dst, dstw, src1, src1w, src2, src2w);
+ }
+#endif
+ if (HAS_FLAGS(op)) {
+ if (TEST_SL_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_SL_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)src1w & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM3, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ }
+ return emit_op(compiler, SLJIT_ADD, flags | ((GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY)) ? ALT_FORM5 : 0), dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_ADDC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ return emit_op(compiler, SLJIT_ADDC, flags, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_SUB:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+
+ if (GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_LESS_EQUAL) {
+ if (dst == TMP_REG2) {
+ if (TEST_UL_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w & 0xffff;
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1 | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1, dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ if ((src2 & SLJIT_IMM) && src2w >= 0 && src2w <= (SIMM_MAX + 1)) {
+ compiler->imm = (sljit_ins)src2w;
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1 | ALT_FORM2 | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1 | ALT_FORM3, dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ if (dst == TMP_REG2 && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL) {
+ if (TEST_SL_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w & 0xffff;
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM2 | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM2, dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ if (TEST_SUB_FORM2(op)) {
+ if ((src2 & SLJIT_IMM) && src2w >= -SIMM_MAX && src2w <= SIMM_MAX) {
+ compiler->imm = (sljit_ins)src2w & 0xffff;
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM2 | ALT_FORM3 | ALT_FORM4, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM2 | ALT_FORM4, dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ if (TEST_SUB_FORM3(op))
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM3, dst, dstw, src1, src1w, src2, src2w);
+
+ if (TEST_SL_IMM(src2, -src2w)) {
+ compiler->imm = (sljit_ins)(-src2w) & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | (!HAS_FLAGS(op) ? ALT_FORM2 : ALT_FORM3), dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+
+ if (TEST_SL_IMM(src1, src1w) && !(op & SLJIT_SET_Z)) {
+ compiler->imm = (sljit_ins)src1w & 0xffff;
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM4, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+
+ if (!HAS_FLAGS(op)) {
+ if (TEST_SH_IMM(src2, -src2w)) {
+ compiler->imm = (sljit_ins)((-src2w) >> 16) & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ /* Range between -1 and -32768 is covered above. */
+ if (TEST_ADD_IMM(src2, -src2w)) {
+ compiler->imm = (sljit_ins)-src2w;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM4, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ }
+
+ /* We know ALT_SIGN_EXT is set if it is an SLJIT_32 on 64 bit systems. */
+ return emit_op(compiler, SLJIT_SUB, flags | ((GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY)) ? ALT_FORM5 : 0), dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_SUBC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ return emit_op(compiler, SLJIT_SUBC, flags, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_MUL:
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (op & SLJIT_32)
+ flags |= ALT_FORM2;
+#endif
+ if (!HAS_FLAGS(op)) {
+ if (TEST_SL_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w & 0xffff;
+ return emit_op(compiler, SLJIT_MUL, flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_SL_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)src1w & 0xffff;
+ return emit_op(compiler, SLJIT_MUL, flags | ALT_FORM1, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ }
+ else
+ FAIL_IF(push_inst(compiler, MTXER | S(TMP_ZERO)));
+ return emit_op(compiler, SLJIT_MUL, flags, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_AND:
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ /* Commutative unsigned operations. */
+ if (!HAS_FLAGS(op) || GET_OPCODE(op) == SLJIT_AND) {
+ if (TEST_UL_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w;
+ return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_UL_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)src1w;
+ return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM1, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ if (TEST_UH_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)(src2w >> 16) & 0xffff;
+ return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_UH_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)(src1w >> 16) & 0xffff;
+ return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM2, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ }
+ if (!HAS_FLAGS(op) && GET_OPCODE(op) != SLJIT_AND) {
+ /* Unlike or and xor, the and resets unwanted bits as well. */
+ if (TEST_UI_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w;
+ return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_UI_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)src1w;
+ return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM3, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ }
+ return emit_op(compiler, GET_OPCODE(op), flags, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (op & SLJIT_32)
+ flags |= ALT_FORM2;
+#endif
+ if (src2 & SLJIT_IMM) {
+ compiler->imm = (sljit_ins)src2w;
+ return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ return emit_op(compiler, GET_OPCODE(op), flags, dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, TMP_REG2, 0, src1, src1w, src2, src2w);
+}
+
+#undef TEST_ADD_FORM1
+#undef TEST_SUB_FORM2
+#undef TEST_SUB_FORM3
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 is_right;
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ sljit_s32 inp_flags = ((op & SLJIT_32) ? INT_DATA : WORD_DATA) | LOAD_DATA;
+ sljit_sw bit_length = (op & SLJIT_32) ? 32 : 64;
+#else /* !SLJIT_CONFIG_PPC_64 */
+ sljit_s32 inp_flags = WORD_DATA | LOAD_DATA;
+ sljit_sw bit_length = 32;
+#endif /* SLJIT_CONFIG_PPC_64 */
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w));
+
+ is_right = (GET_OPCODE(op) == SLJIT_LSHR || GET_OPCODE(op) == SLJIT_MLSHR);
+
+ if (src_dst == src1) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, (is_right ? SLJIT_ROTR : SLJIT_ROTL) | (op & SLJIT_32), src_dst, 0, src_dst, 0, src2, src2w);
+ }
+
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ if (src2 & SLJIT_IMM) {
+ src2w &= bit_length - 1;
+
+ if (src2w == 0)
+ return SLJIT_SUCCESS;
+ } else if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inp_flags, TMP_REG2, src2, src2w, TMP_REG2));
+ src2 = TMP_REG2;
+ }
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inp_flags, TMP_REG1, src1, src1w, TMP_REG1));
+ src1 = TMP_REG1;
+ } else if (src1 & SLJIT_IMM) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, src1w));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (!(op & SLJIT_32)) {
+ if (is_right) {
+ FAIL_IF(push_inst(compiler, SRDI(src2w) | S(src_dst) | A(src_dst)));
+ return push_inst(compiler, RLDIMI | S(src1) | A(src_dst) | RLDI_SH(64 - src2w) | RLDI_MB(0));
+ }
+
+ FAIL_IF(push_inst(compiler, SLDI(src2w) | S(src_dst) | A(src_dst)));
+ /* Computes SRDI(64 - src2w). */
+ FAIL_IF(push_inst(compiler, RLDICL | S(src1) | A(TMP_REG1) | RLDI_SH(src2w) | RLDI_MB(64 - src2w)));
+ return push_inst(compiler, OR | S(src_dst) | A(src_dst) | B(TMP_REG1));
+ }
+#endif /* SLJIT_CONFIG_PPC_64 */
+
+ if (is_right) {
+ FAIL_IF(push_inst(compiler, SRWI(src2w) | S(src_dst) | A(src_dst)));
+ return push_inst(compiler, RLWIMI | S(src1) | A(src_dst) | RLWI_SH(32 - src2w) | RLWI_MBE(0, src2w - 1));
+ }
+
+ FAIL_IF(push_inst(compiler, SLWI(src2w) | S(src_dst) | A(src_dst)));
+ return push_inst(compiler, RLWIMI | S(src1) | A(src_dst) | RLWI_SH(src2w) | RLWI_MBE(32 - src2w, 31));
+ }
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (!(op & SLJIT_32)) {
+ if (GET_OPCODE(op) == SLJIT_MSHL || GET_OPCODE(op) == SLJIT_MLSHR) {
+ FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x3f));
+ src2 = TMP_REG2;
+ }
+
+ FAIL_IF(push_inst(compiler, (is_right ? SRD : SLD) | S(src_dst) | A(src_dst) | B(src2)));
+ FAIL_IF(push_inst(compiler, (is_right ? SLDI(1) : SRDI(1)) | S(src1) | A(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, XORI | S(src2) | A(TMP_REG2) | 0x3f));
+ FAIL_IF(push_inst(compiler, (is_right ? SLD : SRD) | S(TMP_REG1) | A(TMP_REG1) | B(TMP_REG2)));
+ return push_inst(compiler, OR | S(src_dst) | A(src_dst) | B(TMP_REG1));
+ }
+#endif /* SLJIT_CONFIG_PPC_64 */
+
+ if (GET_OPCODE(op) == SLJIT_MSHL || GET_OPCODE(op) == SLJIT_MLSHR) {
+ FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x1f));
+ src2 = TMP_REG2;
+ }
+
+ FAIL_IF(push_inst(compiler, (is_right ? SRW : SLW) | S(src_dst) | A(src_dst) | B(src2)));
+ FAIL_IF(push_inst(compiler, (is_right ? SLWI(1) : SRWI(1)) | S(src1) | A(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, XORI | S(src2) | A(TMP_REG2) | 0x1f));
+ FAIL_IF(push_inst(compiler, (is_right ? SLW : SRW) | S(TMP_REG1) | A(TMP_REG1) | B(TMP_REG2)));
+ return push_inst(compiler, OR | S(src_dst) | A(src_dst) | B(TMP_REG1));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_src(compiler, op, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ switch (op) {
+ case SLJIT_FAST_RETURN:
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst(compiler, MTLR | S(src)));
+ else {
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG2, src, srcw, TMP_REG2));
+ FAIL_IF(push_inst(compiler, MTLR | S(TMP_REG2)));
+ }
+
+ return push_inst(compiler, BLR);
+ case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN:
+ return SLJIT_SUCCESS;
+ case SLJIT_PREFETCH_L1:
+ case SLJIT_PREFETCH_L2:
+ case SLJIT_PREFETCH_L3:
+ case SLJIT_PREFETCH_ONCE:
+ return emit_prefetch(compiler, src, srcw);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_register_index(reg));
+ return reg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
+ return freg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_custom(compiler, instruction, size));
+
+ return push_inst(compiler, *(sljit_ins*)instruction);
+}
+
+/* --------------------------------------------------------------------- */
+/* Floating point operators */
+/* --------------------------------------------------------------------- */
+
+#define FLOAT_DATA(op) (DOUBLE_DATA | ((op & SLJIT_32) >> 6))
+#define SELECT_FOP(op, single, double) ((sljit_ins)((op & SLJIT_32) ? single : double))
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define FLOAT_TMP_MEM_OFFSET (6 * sizeof(sljit_sw))
+#else
+#define FLOAT_TMP_MEM_OFFSET (2 * sizeof(sljit_sw))
+
+#if (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#define FLOAT_TMP_MEM_OFFSET_LOW (2 * sizeof(sljit_sw))
+#define FLOAT_TMP_MEM_OFFSET_HI (3 * sizeof(sljit_sw))
+#else
+#define FLOAT_TMP_MEM_OFFSET_LOW (3 * sizeof(sljit_sw))
+#define FLOAT_TMP_MEM_OFFSET_HI (2 * sizeof(sljit_sw))
+#endif
+
+#endif /* SLJIT_CONFIG_PPC_64 */
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (src & SLJIT_MEM) {
+ /* We can ignore the temporary data store on the stack from caching point of view. */
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src, srcw, TMP_REG1));
+ src = TMP_FREG1;
+ }
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ op = GET_OPCODE(op);
+ FAIL_IF(push_inst(compiler, (op == SLJIT_CONV_S32_FROM_F64 ? FCTIWZ : FCTIDZ) | FD(TMP_FREG1) | FB(src)));
+
+ if (op == SLJIT_CONV_SW_FROM_F64) {
+ if (FAST_IS_REG(dst)) {
+ FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1));
+ return emit_op_mem(compiler, WORD_DATA | LOAD_DATA, dst, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1);
+ }
+ return emit_op_mem(compiler, DOUBLE_DATA, TMP_FREG1, dst, dstw, TMP_REG1);
+ }
+#else
+ FAIL_IF(push_inst(compiler, FCTIWZ | FD(TMP_FREG1) | FB(src)));
+#endif
+
+ if (FAST_IS_REG(dst)) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, FLOAT_TMP_MEM_OFFSET));
+ FAIL_IF(push_inst(compiler, STFIWX | FS(TMP_FREG1) | A(SLJIT_SP) | B(TMP_REG1)));
+ return emit_op_mem(compiler, INT_DATA | LOAD_DATA, dst, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1);
+ }
+
+ SLJIT_ASSERT(dst & SLJIT_MEM);
+
+ if (dst & OFFS_REG_MASK) {
+ dstw &= 0x3;
+ if (dstw) {
+ FAIL_IF(push_inst(compiler, SLWI_W(dstw) | S(OFFS_REG(dst)) | A(TMP_REG1)));
+ dstw = TMP_REG1;
+ }
+ else
+ dstw = OFFS_REG(dst);
+ }
+ else {
+ if ((dst & REG_MASK) && !dstw) {
+ dstw = dst & REG_MASK;
+ dst = 0;
+ }
+ else {
+ /* This works regardless we have SLJIT_MEM1 or SLJIT_MEM0. */
+ FAIL_IF(load_immediate(compiler, TMP_REG1, dstw));
+ dstw = TMP_REG1;
+ }
+ }
+
+ return push_inst(compiler, STFIWX | FS(TMP_FREG1) | A(dst & REG_MASK) | B(dstw));
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (src & SLJIT_IMM) {
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32)
+ srcw = (sljit_s32)srcw;
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
+ src = TMP_REG1;
+ }
+ else if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32) {
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst(compiler, EXTSW | S(src) | A(TMP_REG1)));
+ else
+ FAIL_IF(emit_op_mem(compiler, INT_DATA | SIGNED_DATA | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+
+ if (FAST_IS_REG(src)) {
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA, src, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1));
+ FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1));
+ }
+ else
+ FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, src, srcw, TMP_REG1));
+
+ FAIL_IF(push_inst(compiler, FCFID | FD(dst_r) | FB(TMP_FREG1)));
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, TMP_REG1);
+ if (op & SLJIT_32)
+ return push_inst(compiler, FRSP | FD(dst_r) | FB(dst_r));
+ return SLJIT_SUCCESS;
+
+#else
+
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+ sljit_s32 invert_sign = 1;
+
+ if (src & SLJIT_IMM) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, srcw ^ (sljit_sw)0x80000000));
+ src = TMP_REG1;
+ invert_sign = 0;
+ }
+ else if (!FAST_IS_REG(src)) {
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | SIGNED_DATA | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+
+ /* First, a special double floating point value is constructed: (2^53 + (input xor (2^31)))
+ The double precision format has exactly 53 bit precision, so the lower 32 bit represents
+ the lower 32 bit of such value. The result of xor 2^31 is the same as adding 0x80000000
+ to the input, which shifts it into the 0 - 0xffffffff range. To get the converted floating
+ point value, we need to subtract 2^53 + 2^31 from the constructed value. */
+ FAIL_IF(push_inst(compiler, ADDIS | D(TMP_REG2) | A(0) | 0x4330));
+ if (invert_sign)
+ FAIL_IF(push_inst(compiler, XORIS | S(src) | A(TMP_REG1) | 0x8000));
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_HI, TMP_REG1));
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW, TMP_REG2));
+ FAIL_IF(push_inst(compiler, ADDIS | D(TMP_REG1) | A(0) | 0x8000));
+ FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1));
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW, TMP_REG2));
+ FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG2, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1));
+
+ FAIL_IF(push_inst(compiler, FSUB | FD(dst_r) | FA(TMP_FREG1) | FB(TMP_FREG2)));
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, TMP_REG1);
+ if (op & SLJIT_32)
+ return push_inst(compiler, FRSP | FD(dst_r) | FB(dst_r));
+ return SLJIT_SUCCESS;
+
+#endif
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, TMP_REG1));
+ src1 = TMP_FREG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, TMP_REG2));
+ src2 = TMP_FREG2;
+ }
+
+ FAIL_IF(push_inst(compiler, FCMPU | CRD(4) | FA(src1) | FB(src2)));
+
+ switch (GET_FLAG_TYPE(op)) {
+ case SLJIT_UNORDERED_OR_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ return push_inst(compiler, CROR | ((4 + 2) << 21) | ((4 + 2) << 16) | ((4 + 3) << 11));
+ case SLJIT_UNORDERED_OR_LESS:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ return push_inst(compiler, CROR | ((4 + 0) << 21) | ((4 + 0) << 16) | ((4 + 3) << 11));
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ return push_inst(compiler, CROR | ((4 + 1) << 21) | ((4 + 1) << 16) | ((4 + 3) << 11));
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+
+ SLJIT_COMPILE_ASSERT((SLJIT_32 == 0x100) && !(DOUBLE_DATA & 0x4), float_transfer_bit_error);
+ SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
+
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32)
+ op ^= SLJIT_32;
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, dst_r, src, srcw, TMP_REG1));
+ src = dst_r;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_CONV_F64_FROM_F32:
+ op ^= SLJIT_32;
+ if (op & SLJIT_32) {
+ FAIL_IF(push_inst(compiler, FRSP | FD(dst_r) | FB(src)));
+ break;
+ }
+ /* Fall through. */
+ case SLJIT_MOV_F64:
+ if (src != dst_r) {
+ if (dst_r != TMP_FREG1)
+ FAIL_IF(push_inst(compiler, FMR | FD(dst_r) | FB(src)));
+ else
+ dst_r = src;
+ }
+ break;
+ case SLJIT_NEG_F64:
+ FAIL_IF(push_inst(compiler, FNEG | FD(dst_r) | FB(src)));
+ break;
+ case SLJIT_ABS_F64:
+ FAIL_IF(push_inst(compiler, FABS | FD(dst_r) | FB(src)));
+ break;
+ }
+
+ if (dst & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op), dst_r, dst, dstw, TMP_REG1));
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG2;
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, TMP_REG1));
+ src1 = TMP_FREG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, TMP_REG2));
+ src2 = TMP_FREG2;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD_F64:
+ FAIL_IF(push_inst(compiler, SELECT_FOP(op, FADDS, FADD) | FD(dst_r) | FA(src1) | FB(src2)));
+ break;
+
+ case SLJIT_SUB_F64:
+ FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSUBS, FSUB) | FD(dst_r) | FA(src1) | FB(src2)));
+ break;
+
+ case SLJIT_MUL_F64:
+ FAIL_IF(push_inst(compiler, SELECT_FOP(op, FMULS, FMUL) | FD(dst_r) | FA(src1) | FC(src2) /* FMUL use FC as src2 */));
+ break;
+
+ case SLJIT_DIV_F64:
+ FAIL_IF(push_inst(compiler, SELECT_FOP(op, FDIVS, FDIV) | FD(dst_r) | FA(src1) | FB(src2)));
+ break;
+ }
+
+ if (dst & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op), TMP_FREG2, dst, dstw, TMP_REG1));
+
+ return SLJIT_SUCCESS;
+}
+
+#undef SELECT_FOP
+
+/* --------------------------------------------------------------------- */
+/* Other instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ if (FAST_IS_REG(dst))
+ return push_inst(compiler, MFLR | D(dst));
+
+ /* Memory. */
+ FAIL_IF(push_inst(compiler, MFLR | D(TMP_REG2)));
+ return emit_op(compiler, SLJIT_MOV, WORD_DATA, dst, dstw, TMP_REG1, 0, TMP_REG2, 0);
+}
+
+/* --------------------------------------------------------------------- */
+/* Conditional instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ struct sljit_label *label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_label(compiler));
+
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ return compiler->last_label;
+
+ label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
+ PTR_FAIL_IF(!label);
+ set_label(label, compiler);
+ return label;
+}
+
+static sljit_ins get_bo_bi_flags(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ switch (type) {
+ case SLJIT_NOT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_SUB)
+ return (4 << 21) | (2 << 16);
+ /* fallthrough */
+
+ case SLJIT_EQUAL:
+ return (12 << 21) | (2 << 16);
+
+ case SLJIT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_SUB)
+ return (12 << 21) | (2 << 16);
+ /* fallthrough */
+
+ case SLJIT_NOT_EQUAL:
+ return (4 << 21) | (2 << 16);
+
+ case SLJIT_LESS:
+ case SLJIT_SIG_LESS:
+ return (12 << 21) | (0 << 16);
+
+ case SLJIT_GREATER_EQUAL:
+ case SLJIT_SIG_GREATER_EQUAL:
+ return (4 << 21) | (0 << 16);
+
+ case SLJIT_GREATER:
+ case SLJIT_SIG_GREATER:
+ return (12 << 21) | (1 << 16);
+
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_SIG_LESS_EQUAL:
+ return (4 << 21) | (1 << 16);
+
+ case SLJIT_OVERFLOW:
+ return (12 << 21) | (3 << 16);
+
+ case SLJIT_NOT_OVERFLOW:
+ return (4 << 21) | (3 << 16);
+
+ case SLJIT_F_LESS:
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_UNORDERED_OR_LESS:
+ return (12 << 21) | ((4 + 0) << 16);
+
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ return (4 << 21) | ((4 + 0) << 16);
+
+ case SLJIT_F_GREATER:
+ case SLJIT_ORDERED_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ return (12 << 21) | ((4 + 1) << 16);
+
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ return (4 << 21) | ((4 + 1) << 16);
+
+ case SLJIT_F_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL:
+ return (12 << 21) | ((4 + 2) << 16);
+
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ return (4 << 21) | ((4 + 2) << 16);
+
+ case SLJIT_UNORDERED:
+ return (12 << 21) | ((4 + 3) << 16);
+
+ case SLJIT_ORDERED:
+ return (4 << 21) | ((4 + 3) << 16);
+
+ default:
+ SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL_REG_ARG);
+ return (20 << 21);
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ struct sljit_jump *jump;
+ sljit_ins bo_bi_flags;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_jump(compiler, type));
+
+ bo_bi_flags = get_bo_bi_flags(compiler, type & 0xff);
+ if (!bo_bi_flags)
+ return NULL;
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, (sljit_u32)type & SLJIT_REWRITABLE_JUMP);
+ type &= 0xff;
+
+ if (type == SLJIT_CARRY || type == SLJIT_NOT_CARRY)
+ PTR_FAIL_IF(push_inst(compiler, ADDE | RC(ALT_SET_FLAGS) | D(TMP_REG1) | A(TMP_ZERO) | B(TMP_ZERO)));
+
+ /* In PPC, we don't need to touch the arguments. */
+ if (type < SLJIT_JUMP)
+ jump->flags |= IS_COND;
+#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL)
+ if (type >= SLJIT_CALL)
+ jump->flags |= IS_CALL;
+#endif
+
+ PTR_FAIL_IF(emit_const(compiler, TMP_CALL_REG, 0));
+ PTR_FAIL_IF(push_inst(compiler, MTCTR | S(TMP_CALL_REG)));
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, BCCTR | bo_bi_flags | (type >= SLJIT_FAST_CALL ? 1 : 0)));
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ PTR_FAIL_IF(call_with_args(compiler, arg_types, NULL));
+#endif
+
+ if (type & SLJIT_CALL_RETURN) {
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, 0));
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ struct sljit_jump *jump = NULL;
+ sljit_s32 src_r;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
+
+ if (FAST_IS_REG(src)) {
+#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL)
+ if (type >= SLJIT_CALL && src != TMP_CALL_REG) {
+ FAIL_IF(push_inst(compiler, OR | S(src) | A(TMP_CALL_REG) | B(src)));
+ src_r = TMP_CALL_REG;
+ }
+ else
+ src_r = src;
+#else /* SLJIT_PASS_ENTRY_ADDR_TO_CALL */
+ src_r = src;
+#endif /* SLJIT_PASS_ENTRY_ADDR_TO_CALL */
+ } else if (src & SLJIT_IMM) {
+ /* These jumps are converted to jump/call instructions when possible. */
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ FAIL_IF(!jump);
+ set_jump(jump, compiler, JUMP_ADDR);
+ jump->u.target = (sljit_uw)srcw;
+
+#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL)
+ if (type >= SLJIT_CALL)
+ jump->flags |= IS_CALL;
+#endif /* SLJIT_PASS_ENTRY_ADDR_TO_CALL */
+
+ FAIL_IF(emit_const(compiler, TMP_CALL_REG, 0));
+ src_r = TMP_CALL_REG;
+ } else {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_CALL_REG, src, srcw, TMP_CALL_REG));
+ src_r = TMP_CALL_REG;
+ }
+
+ FAIL_IF(push_inst(compiler, MTCTR | S(src_r)));
+ if (jump)
+ jump->addr = compiler->size;
+ return push_inst(compiler, BCCTR | (20 << 21) | (type >= SLJIT_FAST_CALL ? 1 : 0));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_CALL_REG, src, srcw, TMP_CALL_REG));
+ src = TMP_CALL_REG;
+ }
+
+ if (type & SLJIT_CALL_RETURN) {
+ if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, OR | S(src) | A(TMP_CALL_REG) | B(src)));
+ src = TMP_CALL_REG;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+ type = SLJIT_JUMP;
+ }
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ FAIL_IF(call_with_args(compiler, arg_types, &src));
+#endif
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ sljit_s32 reg, invert;
+ sljit_u32 bit, from_xer;
+ sljit_s32 saved_op = op;
+ sljit_sw saved_dstw = dstw;
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ sljit_s32 input_flags = ((op & SLJIT_32) || op == SLJIT_MOV32) ? INT_DATA : WORD_DATA;
+#else
+ sljit_s32 input_flags = WORD_DATA;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ op = GET_OPCODE(op);
+ reg = (op < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG2;
+
+ if (op >= SLJIT_ADD && (dst & SLJIT_MEM))
+ FAIL_IF(emit_op_mem(compiler, input_flags | LOAD_DATA, TMP_REG1, dst, dstw, TMP_REG1));
+
+ invert = 0;
+ bit = 0;
+ from_xer = 0;
+
+ switch (type) {
+ case SLJIT_LESS:
+ case SLJIT_SIG_LESS:
+ break;
+
+ case SLJIT_GREATER_EQUAL:
+ case SLJIT_SIG_GREATER_EQUAL:
+ invert = 1;
+ break;
+
+ case SLJIT_GREATER:
+ case SLJIT_SIG_GREATER:
+ bit = 1;
+ break;
+
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_SIG_LESS_EQUAL:
+ bit = 1;
+ invert = 1;
+ break;
+
+ case SLJIT_EQUAL:
+ bit = 2;
+ break;
+
+ case SLJIT_NOT_EQUAL:
+ bit = 2;
+ invert = 1;
+ break;
+
+ case SLJIT_OVERFLOW:
+ from_xer = 1;
+ bit = 1;
+ break;
+
+ case SLJIT_NOT_OVERFLOW:
+ from_xer = 1;
+ bit = 1;
+ invert = 1;
+ break;
+
+ case SLJIT_CARRY:
+ from_xer = 1;
+ bit = 2;
+ invert = (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_SUB) != 0;
+ break;
+
+ case SLJIT_NOT_CARRY:
+ from_xer = 1;
+ bit = 2;
+ invert = (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_ADD) != 0;
+ break;
+
+ case SLJIT_F_LESS:
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_UNORDERED_OR_LESS:
+ bit = 4 + 0;
+ break;
+
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ bit = 4 + 0;
+ invert = 1;
+ break;
+
+ case SLJIT_F_GREATER:
+ case SLJIT_ORDERED_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ bit = 4 + 1;
+ break;
+
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ bit = 4 + 1;
+ invert = 1;
+ break;
+
+ case SLJIT_F_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL:
+ bit = 4 + 2;
+ break;
+
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ bit = 4 + 2;
+ invert = 1;
+ break;
+
+ case SLJIT_UNORDERED:
+ bit = 4 + 3;
+ break;
+
+ case SLJIT_ORDERED:
+ bit = 4 + 3;
+ invert = 1;
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+ FAIL_IF(push_inst(compiler, (from_xer ? MFXER : MFCR) | D(reg)));
+ /* Simplified mnemonics: extrwi. */
+ FAIL_IF(push_inst(compiler, RLWINM | S(reg) | A(reg) | RLWI_SH(1 + bit) | RLWI_MBE(31, 31)));
+
+ if (invert)
+ FAIL_IF(push_inst(compiler, XORI | S(reg) | A(reg) | 0x1));
+
+ if (op < SLJIT_ADD) {
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+ return emit_op_mem(compiler, input_flags, reg, dst, dstw, TMP_REG1);
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+
+ if (dst & SLJIT_MEM)
+ return sljit_emit_op2(compiler, saved_op, dst, saved_dstw, TMP_REG1, 0, TMP_REG2, 0);
+ return sljit_emit_op2(compiler, saved_op, dst, 0, dst, 0, TMP_REG2, 0);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);;
+}
+
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+
+#define EMIT_MEM_LOAD_IMM(inst, mem, memw) \
+ ((sljit_s16)(memw) > SIMM_MAX - SSIZE_OF(sw))
+
+#else /* !SLJIT_CONFIG_PPC_32 */
+
+#define EMIT_MEM_LOAD_IMM(inst, mem, memw) \
+ ((((inst) & INT_ALIGNED) && ((memw) & 0x3) != 0) \
+ || ((sljit_s16)(memw) > SIMM_MAX - SSIZE_OF(sw)) \
+ || ((memw) > 0x7fff7fffl || (memw) < -0x80000000l)) \
+
+#endif /* SLJIT_CONFIG_PPC_32 */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_ins inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (!(reg & REG_PAIR_MASK))
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+
+ ADJUST_LOCAL_OFFSET(mem, memw);
+
+ inst = data_transfer_insts[WORD_DATA | ((type & SLJIT_MEM_STORE) ? 0 : LOAD_DATA)];
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ memw &= 0x3;
+
+ if (memw != 0) {
+ FAIL_IF(push_inst(compiler, SLWI_W(memw) | S(OFFS_REG(mem)) | A(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, ADD | D(TMP_REG1) | A(TMP_REG1) | B(mem & REG_MASK)));
+ } else
+ FAIL_IF(push_inst(compiler, ADD | D(TMP_REG1) | A(mem & REG_MASK) | B(OFFS_REG(mem))));
+
+ mem = TMP_REG1;
+ memw = 0;
+ } else {
+ if (EMIT_MEM_LOAD_IMM(inst, mem, memw)) {
+ if ((mem & REG_MASK) != 0) {
+ SLJIT_SKIP_CHECKS(compiler);
+ FAIL_IF(sljit_emit_op2(compiler, SLJIT_ADD, TMP_REG1, 0, mem & REG_MASK, 0, SLJIT_IMM, memw));
+ } else
+ FAIL_IF(load_immediate(compiler, TMP_REG1, memw));
+
+ memw = 0;
+ mem = TMP_REG1;
+ } else if (memw > SIMM_MAX || memw < SIMM_MIN) {
+ FAIL_IF(push_inst(compiler, ADDIS | D(TMP_REG1) | A(mem & REG_MASK) | IMM((memw + 0x8000) >> 16)));
+
+ memw &= 0xffff;
+ mem = TMP_REG1;
+ } else {
+ memw &= 0xffff;
+ mem &= REG_MASK;
+ }
+ }
+
+ SLJIT_ASSERT((memw >= 0 && memw <= SIMM_MAX - SSIZE_OF(sw)) || (memw >= 0x8000 && memw <= 0xffff));
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ inst &= (sljit_ins)~INT_ALIGNED;
+#endif /* SLJIT_CONFIG_PPC_64 */
+
+ if (!(type & SLJIT_MEM_STORE) && mem == REG_PAIR_FIRST(reg)) {
+ FAIL_IF(push_inst(compiler, inst | D(REG_PAIR_SECOND(reg)) | A(mem) | IMM(memw + SSIZE_OF(sw))));
+ return push_inst(compiler, inst | D(REG_PAIR_FIRST(reg)) | A(mem) | IMM(memw));
+ }
+
+ FAIL_IF(push_inst(compiler, inst | D(REG_PAIR_FIRST(reg)) | A(mem) | IMM(memw)));
+ return push_inst(compiler, inst | D(REG_PAIR_SECOND(reg)) | A(mem) | IMM(memw + SSIZE_OF(sw)));
+}
+
+#undef EMIT_MEM_LOAD_IMM
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 mem_flags;
+ sljit_ins inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem_update(compiler, type, reg, mem, memw));
+
+ if (type & SLJIT_MEM_POST)
+ return SLJIT_ERR_UNSUPPORTED;
+
+ switch (type & 0xff) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+#endif
+ mem_flags = WORD_DATA;
+ break;
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV32:
+ mem_flags = INT_DATA;
+ break;
+
+ case SLJIT_MOV_S32:
+ mem_flags = INT_DATA;
+
+ if (!(type & SLJIT_MEM_STORE) && !(type & SLJIT_32)) {
+ if (mem & OFFS_REG_MASK)
+ mem_flags |= SIGNED_DATA;
+ else
+ return SLJIT_ERR_UNSUPPORTED;
+ }
+ break;
+#endif
+
+ case SLJIT_MOV_U8:
+ case SLJIT_MOV_S8:
+ mem_flags = BYTE_DATA;
+ break;
+
+ case SLJIT_MOV_U16:
+ mem_flags = HALF_DATA;
+ break;
+
+ case SLJIT_MOV_S16:
+ mem_flags = HALF_DATA | SIGNED_DATA;
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ mem_flags = WORD_DATA;
+ break;
+ }
+
+ if (!(type & SLJIT_MEM_STORE))
+ mem_flags |= LOAD_DATA;
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ if (memw != 0)
+ return SLJIT_ERR_UNSUPPORTED;
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ inst = updated_data_transfer_insts[mem_flags | INDEXED];
+ FAIL_IF(push_inst(compiler, INST_CODE_AND_DST(inst, 0, reg) | A(mem & REG_MASK) | B(OFFS_REG(mem))));
+ }
+ else {
+ if (memw > SIMM_MAX || memw < SIMM_MIN)
+ return SLJIT_ERR_UNSUPPORTED;
+
+ inst = updated_data_transfer_insts[mem_flags];
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if ((inst & INT_ALIGNED) && (memw & 0x3) != 0)
+ return SLJIT_ERR_UNSUPPORTED;
+#endif
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ FAIL_IF(push_inst(compiler, INST_CODE_AND_DST(inst, 0, reg) | A(mem & REG_MASK) | IMM(memw)));
+ }
+
+ if ((mem_flags & LOAD_DATA) && (type & 0xff) == SLJIT_MOV_S8)
+ return push_inst(compiler, EXTSB | S(reg) | A(reg));
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 mem_flags;
+ sljit_ins inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fmem_update(compiler, type, freg, mem, memw));
+
+ if (type & SLJIT_MEM_POST)
+ return SLJIT_ERR_UNSUPPORTED;
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ if (memw != 0)
+ return SLJIT_ERR_UNSUPPORTED;
+ }
+ else {
+ if (memw > SIMM_MAX || memw < SIMM_MIN)
+ return SLJIT_ERR_UNSUPPORTED;
+ }
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ mem_flags = FLOAT_DATA(type);
+
+ if (!(type & SLJIT_MEM_STORE))
+ mem_flags |= LOAD_DATA;
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ inst = updated_data_transfer_insts[mem_flags | INDEXED];
+ return push_inst(compiler, INST_CODE_AND_DST(inst, DOUBLE_DATA, freg) | A(mem & REG_MASK) | B(OFFS_REG(mem)));
+ }
+
+ inst = updated_data_transfer_insts[mem_flags];
+ return push_inst(compiler, INST_CODE_AND_DST(inst, DOUBLE_DATA, freg) | A(mem & REG_MASK) | IMM(memw));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ struct sljit_const *const_;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const));
+ PTR_FAIL_IF(!const_);
+ set_const(const_, compiler);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+ PTR_FAIL_IF(emit_const(compiler, dst_r, init_value));
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, dst, dstw, TMP_REG1, 0, TMP_REG2, 0));
+
+ return const_;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ struct sljit_put_label *put_label;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label));
+ PTR_FAIL_IF(!put_label);
+ set_put_label(put_label, compiler, 0);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ PTR_FAIL_IF(emit_const(compiler, dst_r, 0));
+#else
+ PTR_FAIL_IF(push_inst(compiler, (sljit_ins)dst_r));
+ compiler->size += 4;
+#endif
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, dst, dstw, TMP_REG1, 0, TMP_REG2, 0));
+
+ return put_label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_32.c b/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_32.c
new file mode 100644
index 00000000000..b38e6924c80
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_32.c
@@ -0,0 +1,73 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_r, sljit_sw imm, sljit_s32 tmp_r)
+{
+ SLJIT_UNUSED_ARG(tmp_r);
+ SLJIT_ASSERT(dst_r != tmp_r);
+
+ if (imm <= SIMM_MAX && imm >= SIMM_MIN)
+ return push_inst(compiler, ADDI | RD(dst_r) | RS1(TMP_ZERO) | IMM_I(imm));
+
+ if (imm & 0x800)
+ imm += 0x1000;
+
+ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(imm & ~0xfff)));
+
+ if ((imm & 0xfff) == 0)
+ return SLJIT_SUCCESS;
+
+ return push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(imm));
+}
+
+static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value, sljit_ins last_ins)
+{
+ if ((init_value & 0x800) != 0)
+ init_value += 0x1000;
+
+ FAIL_IF(push_inst(compiler, LUI | RD(dst) | (sljit_ins)(init_value & ~0xfff)));
+ return push_inst(compiler, last_ins | RS1(dst) | IMM_I(init_value));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ sljit_ins *inst = (sljit_ins*)addr;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ if ((new_target & 0x800) != 0)
+ new_target += 0x1000;
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 0);
+
+ SLJIT_ASSERT((inst[0] & 0x7f) == LUI);
+ inst[0] = (inst[0] & 0xfff) | (sljit_ins)((sljit_sw)new_target & ~0xfff);
+ SLJIT_ASSERT((inst[1] & 0x707f) == ADDI || (inst[1] & 0x707f) == JALR);
+ inst[1] = (inst[1] & 0xfffff) | IMM_I(new_target);
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 1);
+ inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 5);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_64.c b/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_64.c
new file mode 100644
index 00000000000..32cec7848d2
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_64.c
@@ -0,0 +1,183 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_r, sljit_sw imm, sljit_s32 tmp_r)
+{
+ sljit_sw high;
+
+ SLJIT_ASSERT(dst_r != tmp_r);
+
+ if (imm <= SIMM_MAX && imm >= SIMM_MIN)
+ return push_inst(compiler, ADDI | RD(dst_r) | RS1(TMP_ZERO) | IMM_I(imm));
+
+ if (imm <= 0x7fffffffl && imm >= S32_MIN) {
+ if (imm > S32_MAX) {
+ SLJIT_ASSERT((imm & 0x800) != 0);
+ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)0x80000000u));
+ return push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(imm));
+ }
+
+ if ((imm & 0x800) != 0)
+ imm += 0x1000;
+
+ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(imm & ~0xfff)));
+
+ if ((imm & 0xfff) == 0)
+ return SLJIT_SUCCESS;
+
+ return push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(imm));
+ }
+
+ /* Trailing zeroes could be used to produce shifted immediates. */
+
+ if (imm <= 0x7ffffffffffl && imm >= -0x80000000000l) {
+ high = imm >> 12;
+
+ if (imm & 0x800)
+ high = ~high;
+
+ if (high > S32_MAX) {
+ SLJIT_ASSERT((high & 0x800) != 0);
+ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)0x80000000u));
+ FAIL_IF(push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(high)));
+ } else {
+ if ((high & 0x800) != 0)
+ high += 0x1000;
+
+ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(high & ~0xfff)));
+
+ if ((high & 0xfff) != 0)
+ FAIL_IF(push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(high)));
+ }
+
+ FAIL_IF(push_inst(compiler, SLLI | RD(dst_r) | RS1(dst_r) | IMM_I(12)));
+
+ if ((imm & 0xfff) != 0)
+ return push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(imm));
+
+ return SLJIT_SUCCESS;
+ }
+
+ high = imm >> 32;
+ imm = (sljit_s32)imm;
+
+ if ((imm & 0x80000000l) != 0)
+ high = ~high;
+
+ if (high <= 0x7ffff && high >= -0x80000) {
+ FAIL_IF(push_inst(compiler, LUI | RD(tmp_r) | (sljit_ins)(high << 12)));
+ high = 0x1000;
+ } else {
+ if ((high & 0x800) != 0)
+ high += 0x1000;
+
+ FAIL_IF(push_inst(compiler, LUI | RD(tmp_r) | (sljit_ins)(high & ~0xfff)));
+ high &= 0xfff;
+ }
+
+ if (imm <= SIMM_MAX && imm >= SIMM_MIN) {
+ FAIL_IF(push_inst(compiler, ADDI | RD(dst_r) | RS1(TMP_ZERO) | IMM_I(imm)));
+ imm = 0;
+ } else if (imm > S32_MAX) {
+ SLJIT_ASSERT((imm & 0x800) != 0);
+
+ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)0x80000000u));
+ imm = 0x1000 | (imm & 0xfff);
+ } else {
+ if ((imm & 0x800) != 0)
+ imm += 0x1000;
+
+ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(imm & ~0xfff)));
+ imm &= 0xfff;
+ }
+
+ if ((high & 0xfff) != 0)
+ FAIL_IF(push_inst(compiler, ADDI | RD(tmp_r) | RS1(tmp_r) | IMM_I(high)));
+
+ if (imm & 0x1000)
+ FAIL_IF(push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(imm)));
+ else if (imm != 0)
+ FAIL_IF(push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(imm)));
+
+ FAIL_IF(push_inst(compiler, SLLI | RD(tmp_r) | RS1(tmp_r) | IMM_I((high & 0x1000) ? 20 : 32)));
+ return push_inst(compiler, XOR | RD(dst_r) | RS1(dst_r) | RS2(tmp_r));
+}
+
+static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value, sljit_ins last_ins)
+{
+ sljit_sw high;
+
+ if ((init_value & 0x800) != 0)
+ init_value += 0x1000;
+
+ high = init_value >> 32;
+
+ if ((init_value & 0x80000000l) != 0)
+ high = ~high;
+
+ if ((high & 0x800) != 0)
+ high += 0x1000;
+
+ FAIL_IF(push_inst(compiler, LUI | RD(TMP_REG3) | (sljit_ins)(high & ~0xfff)));
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I(high)));
+ FAIL_IF(push_inst(compiler, LUI | RD(dst) | (sljit_ins)(init_value & ~0xfff)));
+ FAIL_IF(push_inst(compiler, SLLI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I(32)));
+ FAIL_IF(push_inst(compiler, XOR | RD(dst) | RS1(dst) | RS2(TMP_REG3)));
+ return push_inst(compiler, last_ins | RS1(dst) | IMM_I(init_value));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ sljit_ins *inst = (sljit_ins*)addr;
+ sljit_sw high;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ if ((new_target & 0x800) != 0)
+ new_target += 0x1000;
+
+ high = (sljit_sw)new_target >> 32;
+
+ if ((new_target & 0x80000000l) != 0)
+ high = ~high;
+
+ if ((high & 0x800) != 0)
+ high += 0x1000;
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 0);
+
+ SLJIT_ASSERT((inst[0] & 0x7f) == LUI);
+ inst[0] = (inst[0] & 0xfff) | (sljit_ins)(high & ~0xfff);
+ SLJIT_ASSERT((inst[1] & 0x707f) == ADDI);
+ inst[1] = (inst[1] & 0xfffff) | IMM_I(high);
+ SLJIT_ASSERT((inst[2] & 0x7f) == LUI);
+ inst[2] = (inst[2] & 0xfff) | (sljit_ins)((sljit_sw)new_target & ~0xfff);
+ SLJIT_ASSERT((inst[5] & 0x707f) == ADDI || (inst[5] & 0x707f) == JALR);
+ inst[5] = (inst[5] & 0xfffff) | IMM_I(new_target);
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 1);
+
+ inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 5);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_common.c b/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_common.c
new file mode 100644
index 00000000000..58a48c649c9
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_common.c
@@ -0,0 +1,2762 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ return "RISC-V-32" SLJIT_CPUINFO;
+#else /* !SLJIT_CONFIG_RISCV_32 */
+ return "RISC-V-64" SLJIT_CPUINFO;
+#endif /* SLJIT_CONFIG_RISCV_32 */
+}
+
+/* Length of an instruction word
+ Both for riscv-32 and riscv-64 */
+typedef sljit_u32 sljit_ins;
+
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
+#define TMP_REG3 (SLJIT_NUMBER_OF_REGISTERS + 4)
+#define TMP_ZERO 0
+
+/* Flags are kept in volatile registers. */
+#define EQUAL_FLAG (SLJIT_NUMBER_OF_REGISTERS + 5)
+#define RETURN_ADDR_REG TMP_REG2
+#define OTHER_FLAG (SLJIT_NUMBER_OF_REGISTERS + 6)
+
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
+
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 7] = {
+ 0, 10, 11, 12, 13, 14, 15, 16, 17, 29, 30, 31, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 9, 8, 2, 6, 1, 7, 5, 28
+};
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = {
+ 0, 10, 11, 12, 13, 14, 15, 16, 17, 2, 3, 4, 5, 6, 7, 28, 29, 30, 31, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 9, 8, 0, 1,
+};
+
+/* --------------------------------------------------------------------- */
+/* Instrucion forms */
+/* --------------------------------------------------------------------- */
+
+#define RD(rd) ((sljit_ins)reg_map[rd] << 7)
+#define RS1(rs1) ((sljit_ins)reg_map[rs1] << 15)
+#define RS2(rs2) ((sljit_ins)reg_map[rs2] << 20)
+#define FRD(rd) ((sljit_ins)freg_map[rd] << 7)
+#define FRS1(rs1) ((sljit_ins)freg_map[rs1] << 15)
+#define FRS2(rs2) ((sljit_ins)freg_map[rs2] << 20)
+#define IMM_I(imm) ((sljit_ins)(imm) << 20)
+#define IMM_S(imm) ((((sljit_ins)(imm) & 0xfe0) << 20) | (((sljit_ins)(imm) & 0x1f) << 7))
+
+/* Represents funct(i) parts of the instructions. */
+#define OPC(o) ((sljit_ins)(o))
+#define F3(f) ((sljit_ins)(f) << 12)
+#define F12(f) ((sljit_ins)(f) << 20)
+#define F7(f) ((sljit_ins)(f) << 25)
+
+#define ADD (F7(0x0) | F3(0x0) | OPC(0x33))
+#define ADDI (F3(0x0) | OPC(0x13))
+#define AND (F7(0x0) | F3(0x7) | OPC(0x33))
+#define ANDI (F3(0x7) | OPC(0x13))
+#define AUIPC (OPC(0x17))
+#define BEQ (F3(0x0) | OPC(0x63))
+#define BNE (F3(0x1) | OPC(0x63))
+#define BLT (F3(0x4) | OPC(0x63))
+#define BGE (F3(0x5) | OPC(0x63))
+#define BLTU (F3(0x6) | OPC(0x63))
+#define BGEU (F3(0x7) | OPC(0x63))
+#define DIV (F7(0x1) | F3(0x4) | OPC(0x33))
+#define DIVU (F7(0x1) | F3(0x5) | OPC(0x33))
+#define EBREAK (F12(0x1) | F3(0x0) | OPC(0x73))
+#define FADD_S (F7(0x0) | F3(0x7) | OPC(0x53))
+#define FDIV_S (F7(0xc) | F3(0x7) | OPC(0x53))
+#define FEQ_S (F7(0x50) | F3(0x2) | OPC(0x53))
+#define FLD (F3(0x3) | OPC(0x7))
+#define FLE_S (F7(0x50) | F3(0x0) | OPC(0x53))
+#define FLT_S (F7(0x50) | F3(0x1) | OPC(0x53))
+#define FSD (F3(0x3) | OPC(0x27))
+/* These conversion opcodes are partly defined. */
+#define FCVT_S_D (F7(0x20) | OPC(0x53))
+#define FCVT_S_W (F7(0x68) | OPC(0x53))
+#define FCVT_W_S (F7(0x60) | F3(0x1) | OPC(0x53))
+#define FMUL_S (F7(0x8) | F3(0x7) | OPC(0x53))
+#define FSGNJ_S (F7(0x10) | F3(0x0) | OPC(0x53))
+#define FSGNJN_S (F7(0x10) | F3(0x1) | OPC(0x53))
+#define FSGNJX_S (F7(0x10) | F3(0x2) | OPC(0x53))
+#define FSUB_S (F7(0x4) | F3(0x7) | OPC(0x53))
+#define JAL (OPC(0x6f))
+#define JALR (F3(0x0) | OPC(0x67))
+#define LD (F3(0x3) | OPC(0x3))
+#define LUI (OPC(0x37))
+#define LW (F3(0x2) | OPC(0x3))
+#define MUL (F7(0x1) | F3(0x0) | OPC(0x33))
+#define MULH (F7(0x1) | F3(0x1) | OPC(0x33))
+#define MULHU (F7(0x1) | F3(0x3) | OPC(0x33))
+#define OR (F7(0x0) | F3(0x6) | OPC(0x33))
+#define ORI (F3(0x6) | OPC(0x13))
+#define REM (F7(0x1) | F3(0x6) | OPC(0x33))
+#define REMU (F7(0x1) | F3(0x7) | OPC(0x33))
+#define SD (F3(0x3) | OPC(0x23))
+#define SLL (F7(0x0) | F3(0x1) | OPC(0x33))
+#define SLLI (IMM_I(0x0) | F3(0x1) | OPC(0x13))
+#define SLT (F7(0x0) | F3(0x2) | OPC(0x33))
+#define SLTI (F3(0x2) | OPC(0x13))
+#define SLTU (F7(0x0) | F3(0x3) | OPC(0x33))
+#define SLTUI (F3(0x3) | OPC(0x13))
+#define SRL (F7(0x0) | F3(0x5) | OPC(0x33))
+#define SRLI (IMM_I(0x0) | F3(0x5) | OPC(0x13))
+#define SRA (F7(0x20) | F3(0x5) | OPC(0x33))
+#define SRAI (IMM_I(0x400) | F3(0x5) | OPC(0x13))
+#define SUB (F7(0x20) | F3(0x0) | OPC(0x33))
+#define SW (F3(0x2) | OPC(0x23))
+#define XOR (F7(0x0) | F3(0x4) | OPC(0x33))
+#define XORI (F3(0x4) | OPC(0x13))
+
+#define SIMM_MAX (0x7ff)
+#define SIMM_MIN (-0x800)
+#define BRANCH_MAX (0xfff)
+#define BRANCH_MIN (-0x1000)
+#define JUMP_MAX (0xfffff)
+#define JUMP_MIN (-0x100000)
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+#define S32_MAX (0x7ffff7ffl)
+#define S32_MIN (-0x80000000l)
+#define S44_MAX (0x7fffffff7ffl)
+#define S52_MAX (0x7ffffffffffffl)
+#endif
+
+static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_ins ins)
+{
+ sljit_ins *ptr = (sljit_ins*)ensure_buf(compiler, sizeof(sljit_ins));
+ FAIL_IF(!ptr);
+ *ptr = ins;
+ compiler->size++;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 push_imm_s_inst(struct sljit_compiler *compiler, sljit_ins ins, sljit_sw imm)
+{
+ return push_inst(compiler, ins | IMM_S(imm));
+}
+
+static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_ins *code, sljit_sw executable_offset)
+{
+ sljit_sw diff;
+ sljit_uw target_addr;
+ sljit_ins *inst;
+
+ inst = (sljit_ins *)jump->addr;
+
+ if (jump->flags & SLJIT_REWRITABLE_JUMP)
+ goto exit;
+
+ if (jump->flags & JUMP_ADDR)
+ target_addr = jump->u.target;
+ else {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ target_addr = (sljit_uw)(code + jump->u.label->size) + (sljit_uw)executable_offset;
+ }
+
+ diff = (sljit_sw)target_addr - (sljit_sw)inst - executable_offset;
+
+ if (jump->flags & IS_COND) {
+ inst--;
+ diff += SSIZE_OF(ins);
+
+ if (diff >= BRANCH_MIN && diff <= BRANCH_MAX) {
+ jump->flags |= PATCH_B;
+ inst[0] = (inst[0] & 0x1fff07f) ^ 0x1000;
+ jump->addr = (sljit_uw)inst;
+ return inst;
+ }
+
+ inst++;
+ diff -= SSIZE_OF(ins);
+ }
+
+ if (diff >= JUMP_MIN && diff <= JUMP_MAX) {
+ if (jump->flags & IS_COND) {
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ inst[-1] -= (sljit_ins)(1 * sizeof(sljit_ins)) << 7;
+#else
+ inst[-1] -= (sljit_ins)(5 * sizeof(sljit_ins)) << 7;
+#endif
+ }
+
+ jump->flags |= PATCH_J;
+ return inst;
+ }
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ if (diff >= S32_MIN && diff <= S32_MAX) {
+ if (jump->flags & IS_COND)
+ inst[-1] -= (sljit_ins)(4 * sizeof(sljit_ins)) << 7;
+
+ jump->flags |= PATCH_REL32;
+ inst[1] = inst[0];
+ return inst + 1;
+ }
+
+ if (target_addr <= (sljit_uw)S32_MAX) {
+ if (jump->flags & IS_COND)
+ inst[-1] -= (sljit_ins)(4 * sizeof(sljit_ins)) << 7;
+
+ jump->flags |= PATCH_ABS32;
+ inst[1] = inst[0];
+ return inst + 1;
+ }
+
+ if (target_addr <= S44_MAX) {
+ if (jump->flags & IS_COND)
+ inst[-1] -= (sljit_ins)(2 * sizeof(sljit_ins)) << 7;
+
+ jump->flags |= PATCH_ABS44;
+ inst[3] = inst[0];
+ return inst + 3;
+ }
+
+ if (target_addr <= S52_MAX) {
+ if (jump->flags & IS_COND)
+ inst[-1] -= (sljit_ins)(1 * sizeof(sljit_ins)) << 7;
+
+ jump->flags |= PATCH_ABS52;
+ inst[4] = inst[0];
+ return inst + 4;
+ }
+#endif
+
+exit:
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ inst[1] = inst[0];
+ return inst + 1;
+#else
+ inst[5] = inst[0];
+ return inst + 5;
+#endif
+}
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+
+static SLJIT_INLINE sljit_sw put_label_get_length(struct sljit_put_label *put_label, sljit_uw max_label)
+{
+ if (max_label <= (sljit_uw)S32_MAX) {
+ put_label->flags = PATCH_ABS32;
+ return 1;
+ }
+
+ if (max_label <= S44_MAX) {
+ put_label->flags = PATCH_ABS44;
+ return 3;
+ }
+
+ if (max_label <= S52_MAX) {
+ put_label->flags = PATCH_ABS52;
+ return 4;
+ }
+
+ put_label->flags = 0;
+ return 5;
+}
+
+#endif /* SLJIT_CONFIG_RISCV_64 */
+
+static SLJIT_INLINE void load_addr_to_reg(void *dst, sljit_u32 reg)
+{
+ struct sljit_jump *jump = NULL;
+ struct sljit_put_label *put_label;
+ sljit_uw flags;
+ sljit_ins *inst;
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ sljit_sw high;
+#endif
+ sljit_uw addr;
+
+ if (reg != 0) {
+ jump = (struct sljit_jump*)dst;
+ flags = jump->flags;
+ inst = (sljit_ins*)jump->addr;
+ addr = (flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target;
+ } else {
+ put_label = (struct sljit_put_label*)dst;
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ flags = put_label->flags;
+#endif
+ inst = (sljit_ins*)put_label->addr;
+ addr = put_label->label->addr;
+ reg = *inst;
+ }
+
+ if ((addr & 0x800) != 0)
+ addr += 0x1000;
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ inst[0] = LUI | RD(reg) | (sljit_ins)((sljit_sw)addr & ~0xfff);
+#else /* !SLJIT_CONFIG_RISCV_32 */
+
+ if (flags & PATCH_ABS32) {
+ SLJIT_ASSERT(addr <= S32_MAX);
+ inst[0] = LUI | RD(reg) | (sljit_ins)((sljit_sw)addr & ~0xfff);
+ } else if (flags & PATCH_ABS44) {
+ high = (sljit_sw)addr >> 12;
+ SLJIT_ASSERT((sljit_uw)high <= 0x7fffffff);
+
+ if (high > S32_MAX) {
+ SLJIT_ASSERT((high & 0x800) != 0);
+ inst[0] = LUI | RD(reg) | (sljit_ins)0x80000000u;
+ inst[1] = XORI | RD(reg) | RS1(reg) | IMM_I(high);
+ } else {
+ if ((high & 0x800) != 0)
+ high += 0x1000;
+
+ inst[0] = LUI | RD(reg) | (sljit_ins)(high & ~0xfff);
+ inst[1] = ADDI | RD(reg) | RS1(reg) | IMM_I(high);
+ }
+
+ inst[2] = SLLI | RD(reg) | RS1(reg) | IMM_I(12);
+ inst += 2;
+ } else {
+ high = (sljit_sw)addr >> 32;
+
+ if ((addr & 0x80000000l) != 0)
+ high = ~high;
+
+ if ((high & 0x800) != 0)
+ high += 0x1000;
+
+ if (flags & PATCH_ABS52) {
+ SLJIT_ASSERT(addr <= S52_MAX);
+ inst[0] = LUI | RD(TMP_REG3) | (sljit_ins)(high << 12);
+ } else {
+ inst[0] = LUI | RD(TMP_REG3) | (sljit_ins)(high & ~0xfff);
+ inst[1] = ADDI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I(high);
+ inst++;
+ }
+
+ inst[1] = LUI | RD(reg) | (sljit_ins)((sljit_sw)addr & ~0xfff);
+ inst[2] = SLLI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I((flags & PATCH_ABS52) ? 20 : 32);
+ inst[3] = XOR | RD(reg) | RS1(reg) | RS2(TMP_REG3);
+ inst += 3;
+ }
+#endif /* !SLJIT_CONFIG_RISCV_32 */
+
+ if (jump != NULL) {
+ SLJIT_ASSERT((inst[1] & 0x707f) == JALR);
+ inst[1] = (inst[1] & 0xfffff) | IMM_I(addr);
+ } else
+ inst[1] = ADDI | RD(reg) | RS1(reg) | IMM_I(addr);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf;
+ sljit_ins *code;
+ sljit_ins *code_ptr;
+ sljit_ins *buf_ptr;
+ sljit_ins *buf_end;
+ sljit_uw word_count;
+ sljit_uw next_addr;
+ sljit_sw executable_offset;
+ sljit_uw addr;
+
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ struct sljit_const *const_;
+ struct sljit_put_label *put_label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_generate_code(compiler));
+ reverse_buf(compiler);
+
+ code = (sljit_ins*)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_ins), compiler->exec_allocator_data);
+ PTR_FAIL_WITH_EXEC_IF(code);
+ buf = compiler->buf;
+
+ code_ptr = code;
+ word_count = 0;
+ next_addr = 0;
+ executable_offset = SLJIT_EXEC_OFFSET(code);
+
+ label = compiler->labels;
+ jump = compiler->jumps;
+ const_ = compiler->consts;
+ put_label = compiler->put_labels;
+
+ do {
+ buf_ptr = (sljit_ins*)buf->memory;
+ buf_end = buf_ptr + (buf->used_size >> 2);
+ do {
+ *code_ptr = *buf_ptr++;
+ if (next_addr == word_count) {
+ SLJIT_ASSERT(!label || label->size >= word_count);
+ SLJIT_ASSERT(!jump || jump->addr >= word_count);
+ SLJIT_ASSERT(!const_ || const_->addr >= word_count);
+ SLJIT_ASSERT(!put_label || put_label->addr >= word_count);
+
+ /* These structures are ordered by their address. */
+ if (label && label->size == word_count) {
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+ if (jump && jump->addr == word_count) {
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ word_count += 1;
+#else
+ word_count += 5;
+#endif
+ jump->addr = (sljit_uw)code_ptr;
+ code_ptr = detect_jump_type(jump, code, executable_offset);
+ jump = jump->next;
+ }
+ if (const_ && const_->addr == word_count) {
+ const_->addr = (sljit_uw)code_ptr;
+ const_ = const_->next;
+ }
+ if (put_label && put_label->addr == word_count) {
+ SLJIT_ASSERT(put_label->label);
+ put_label->addr = (sljit_uw)code_ptr;
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ code_ptr += 1;
+ word_count += 1;
+#else
+ code_ptr += put_label_get_length(put_label, (sljit_uw)(SLJIT_ADD_EXEC_OFFSET(code, executable_offset) + put_label->label->size));
+ word_count += 5;
+#endif
+ put_label = put_label->next;
+ }
+ next_addr = compute_next_addr(label, jump, const_, put_label);
+ }
+ code_ptr++;
+ word_count++;
+ } while (buf_ptr < buf_end);
+
+ buf = buf->next;
+ } while (buf);
+
+ if (label && label->size == word_count) {
+ label->addr = (sljit_uw)code_ptr;
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+
+ SLJIT_ASSERT(!label);
+ SLJIT_ASSERT(!jump);
+ SLJIT_ASSERT(!const_);
+ SLJIT_ASSERT(!put_label);
+ SLJIT_ASSERT(code_ptr - code <= (sljit_sw)compiler->size);
+
+ jump = compiler->jumps;
+ while (jump) {
+ do {
+ if (!(jump->flags & (PATCH_B | PATCH_J | PATCH_REL32))) {
+ load_addr_to_reg(jump, TMP_REG1);
+ break;
+ }
+
+ addr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target;
+ buf_ptr = (sljit_ins *)jump->addr;
+ addr -= (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset);
+
+ if (jump->flags & PATCH_B) {
+ SLJIT_ASSERT((sljit_sw)addr >= BRANCH_MIN && (sljit_sw)addr <= BRANCH_MAX);
+ addr = ((addr & 0x800) >> 4) | ((addr & 0x1e) << 7) | ((addr & 0x7e0) << 20) | ((addr & 0x1000) << 19);
+ buf_ptr[0] |= (sljit_ins)addr;
+ break;
+ }
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ if (jump->flags & PATCH_REL32) {
+ SLJIT_ASSERT((sljit_sw)addr >= S32_MIN && (sljit_sw)addr <= S32_MAX);
+
+ if ((addr & 0x800) != 0)
+ addr += 0x1000;
+
+ buf_ptr[0] = AUIPC | RD(TMP_REG1) | (sljit_ins)((sljit_sw)addr & ~0xfff);
+ SLJIT_ASSERT((buf_ptr[1] & 0x707f) == JALR);
+ buf_ptr[1] |= IMM_I(addr);
+ break;
+ }
+#endif
+
+ SLJIT_ASSERT((sljit_sw)addr >= JUMP_MIN && (sljit_sw)addr <= JUMP_MAX);
+ addr = (addr & 0xff000) | ((addr & 0x800) << 9) | ((addr & 0x7fe) << 20) | ((addr & 0x100000) << 11);
+ buf_ptr[0] = JAL | RD((jump->flags & IS_CALL) ? RETURN_ADDR_REG : TMP_ZERO) | (sljit_ins)addr;
+ } while (0);
+ jump = jump->next;
+ }
+
+ put_label = compiler->put_labels;
+ while (put_label) {
+ load_addr_to_reg(put_label, 0);
+ put_label = put_label->next;
+ }
+
+ compiler->error = SLJIT_ERR_COMPILED;
+ compiler->executable_offset = executable_offset;
+ compiler->executable_size = (sljit_uw)(code_ptr - code) * sizeof(sljit_ins);
+
+ code = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+ code_ptr = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+
+ SLJIT_CACHE_FLUSH(code, code_ptr);
+ SLJIT_UPDATE_WX_FLAGS(code, code_ptr, 1);
+ return code;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+ case SLJIT_HAS_ZERO_REGISTER:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type)
+{
+ return (type >= SLJIT_ORDERED_EQUAL && type <= SLJIT_ORDERED_LESS_EQUAL);
+}
+
+/* --------------------------------------------------------------------- */
+/* Entry, exit */
+/* --------------------------------------------------------------------- */
+
+/* Creates an index in data_transfer_insts array. */
+#define LOAD_DATA 0x01
+#define WORD_DATA 0x00
+#define BYTE_DATA 0x02
+#define HALF_DATA 0x04
+#define INT_DATA 0x06
+#define SIGNED_DATA 0x08
+/* Separates integer and floating point registers */
+#define GPR_REG 0x0f
+#define DOUBLE_DATA 0x10
+#define SINGLE_DATA 0x12
+
+#define MEM_MASK 0x1f
+
+#define ARG_TEST 0x00020
+#define ALT_KEEP_CACHE 0x00040
+#define CUMULATIVE_OP 0x00080
+#define IMM_OP 0x00100
+#define MOVE_OP 0x00200
+#define SRC2_IMM 0x00400
+
+#define UNUSED_DEST 0x00800
+#define REG_DEST 0x01000
+#define REG1_SOURCE 0x02000
+#define REG2_SOURCE 0x04000
+#define SLOW_SRC1 0x08000
+#define SLOW_SRC2 0x10000
+#define SLOW_DEST 0x20000
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+#define STACK_STORE SW
+#define STACK_LOAD LW
+#else
+#define STACK_STORE SD
+#define STACK_LOAD LD
+#endif
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+#include "sljitNativeRISCV_32.c"
+#else
+#include "sljitNativeRISCV_64.c"
+#endif
+
+#define STACK_MAX_DISTANCE (-SIMM_MIN)
+
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw);
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 i, tmp, offset;
+ sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 1);
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if ((local_size & SSIZE_OF(sw)) != 0)
+ local_size += SSIZE_OF(sw);
+ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+ }
+#else
+ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+#endif
+ local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf;
+ compiler->local_size = local_size;
+
+ if (local_size <= STACK_MAX_DISTANCE) {
+ /* Frequent case. */
+ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(-local_size)));
+ offset = local_size - SSIZE_OF(sw);
+ local_size = 0;
+ } else {
+ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(STACK_MAX_DISTANCE)));
+ local_size -= STACK_MAX_DISTANCE;
+
+ if (local_size > STACK_MAX_DISTANCE)
+ FAIL_IF(load_immediate(compiler, TMP_REG1, local_size, TMP_REG3));
+ offset = STACK_MAX_DISTANCE - SSIZE_OF(sw);
+ }
+
+ FAIL_IF(push_imm_s_inst(compiler, STACK_STORE | RS1(SLJIT_SP) | RS2(RETURN_ADDR_REG), offset));
+
+ tmp = SLJIT_S0 - saveds;
+ for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_imm_s_inst(compiler, STACK_STORE | RS1(SLJIT_SP) | RS2(i), offset));
+ }
+
+ for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_imm_s_inst(compiler, STACK_STORE | RS1(SLJIT_SP) | RS2(i), offset));
+ }
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ /* This alignment is valid because offset is not used after storing FPU regs. */
+ if ((offset & SSIZE_OF(sw)) != 0)
+ offset -= SSIZE_OF(sw);
+#endif
+
+ tmp = SLJIT_FS0 - fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_imm_s_inst(compiler, FSD | RS1(SLJIT_SP) | FRS2(i), offset));
+ }
+
+ for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_imm_s_inst(compiler, FSD | RS1(SLJIT_SP) | FRS2(i), offset));
+ }
+
+ if (local_size > STACK_MAX_DISTANCE)
+ FAIL_IF(push_inst(compiler, SUB | RD(SLJIT_SP) | RS1(SLJIT_SP) | RS2(TMP_REG1)));
+ else if (local_size > 0)
+ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(-local_size)));
+
+ if (options & SLJIT_ENTER_REG_ARG)
+ return SLJIT_SUCCESS;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ saved_arg_count = 0;
+ tmp = SLJIT_R0;
+
+ while (arg_types > 0) {
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) {
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_S0 - saved_arg_count) | RS1(tmp) | IMM_I(0)));
+ saved_arg_count++;
+ }
+ tmp++;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#undef STACK_MAX_DISTANCE
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 1);
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if ((local_size & SSIZE_OF(sw)) != 0)
+ local_size += SSIZE_OF(sw);
+ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+ }
+#else
+ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+#endif
+ compiler->local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf;
+
+ return SLJIT_SUCCESS;
+}
+
+#define STACK_MAX_DISTANCE (-SIMM_MIN - 16)
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 is_return_to)
+{
+ sljit_s32 i, tmp, offset;
+ sljit_s32 local_size = compiler->local_size;
+
+ if (local_size > STACK_MAX_DISTANCE) {
+ local_size -= STACK_MAX_DISTANCE;
+
+ if (local_size > STACK_MAX_DISTANCE) {
+ FAIL_IF(load_immediate(compiler, TMP_REG2, local_size, TMP_REG3));
+ FAIL_IF(push_inst(compiler, ADD | RD(SLJIT_SP) | RS1(SLJIT_SP) | RS2(TMP_REG2)));
+ } else
+ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(local_size)));
+
+ local_size = STACK_MAX_DISTANCE;
+ }
+
+ SLJIT_ASSERT(local_size > 0);
+
+ offset = local_size - SSIZE_OF(sw);
+ if (!is_return_to)
+ FAIL_IF(push_inst(compiler, STACK_LOAD | RD(RETURN_ADDR_REG) | RS1(SLJIT_SP) | IMM_I(offset)));
+
+ tmp = SLJIT_S0 - compiler->saveds;
+ for (i = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options); i > tmp; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STACK_LOAD | RD(i) | RS1(SLJIT_SP) | IMM_I(offset)));
+ }
+
+ for (i = compiler->scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STACK_LOAD | RD(i) | RS1(SLJIT_SP) | IMM_I(offset)));
+ }
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ /* This alignment is valid because offset is not used after storing FPU regs. */
+ if ((offset & SSIZE_OF(sw)) != 0)
+ offset -= SSIZE_OF(sw);
+#endif
+
+ tmp = SLJIT_FS0 - compiler->fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, FLD | FRD(i) | RS1(SLJIT_SP) | IMM_I(offset)));
+ }
+
+ for (i = compiler->fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, FLD | FRD(i) | RS1(SLJIT_SP) | IMM_I(offset)));
+ }
+
+ return push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(local_size));
+}
+
+#undef STACK_MAX_DISTANCE
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+ return push_inst(compiler, JALR | RD(TMP_ZERO) | RS1(RETURN_ADDR_REG) | IMM_I(0));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw));
+ src = TMP_REG1;
+ srcw = 0;
+ } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(src) | IMM_I(0)));
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 1));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+#define ARCH_32_64(a, b) a
+#else
+#define ARCH_32_64(a, b) b
+#endif
+
+static const sljit_ins data_transfer_insts[16 + 4] = {
+/* u w s */ ARCH_32_64(F3(0x2) | OPC(0x23) /* sw */, F3(0x3) | OPC(0x23) /* sd */),
+/* u w l */ ARCH_32_64(F3(0x2) | OPC(0x3) /* lw */, F3(0x3) | OPC(0x3) /* ld */),
+/* u b s */ F3(0x0) | OPC(0x23) /* sb */,
+/* u b l */ F3(0x4) | OPC(0x3) /* lbu */,
+/* u h s */ F3(0x1) | OPC(0x23) /* sh */,
+/* u h l */ F3(0x5) | OPC(0x3) /* lhu */,
+/* u i s */ F3(0x2) | OPC(0x23) /* sw */,
+/* u i l */ ARCH_32_64(F3(0x2) | OPC(0x3) /* lw */, F3(0x6) | OPC(0x3) /* lwu */),
+
+/* s w s */ ARCH_32_64(F3(0x2) | OPC(0x23) /* sw */, F3(0x3) | OPC(0x23) /* sd */),
+/* s w l */ ARCH_32_64(F3(0x2) | OPC(0x3) /* lw */, F3(0x3) | OPC(0x3) /* ld */),
+/* s b s */ F3(0x0) | OPC(0x23) /* sb */,
+/* s b l */ F3(0x0) | OPC(0x3) /* lb */,
+/* s h s */ F3(0x1) | OPC(0x23) /* sh */,
+/* s h l */ F3(0x1) | OPC(0x3) /* lh */,
+/* s i s */ F3(0x2) | OPC(0x23) /* sw */,
+/* s i l */ F3(0x2) | OPC(0x3) /* lw */,
+
+/* d s */ F3(0x3) | OPC(0x27) /* fsd */,
+/* d l */ F3(0x3) | OPC(0x7) /* fld */,
+/* s s */ F3(0x2) | OPC(0x27) /* fsw */,
+/* s l */ F3(0x2) | OPC(0x7) /* flw */,
+};
+
+#undef ARCH_32_64
+
+static sljit_s32 push_mem_inst(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 base, sljit_sw offset)
+{
+ sljit_ins ins;
+
+ SLJIT_ASSERT(FAST_IS_REG(base) && offset <= 0xfff && offset >= SIMM_MIN);
+
+ ins = data_transfer_insts[flags & MEM_MASK] | RS1(base);
+ if (flags & LOAD_DATA)
+ ins |= ((flags & MEM_MASK) <= GPR_REG ? RD(reg) : FRD(reg)) | IMM_I(offset);
+ else
+ ins |= ((flags & MEM_MASK) <= GPR_REG ? RS2(reg) : FRS2(reg)) | IMM_S(offset);
+
+ return push_inst(compiler, ins);
+}
+
+/* Can perform an operation using at most 1 instruction. */
+static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
+{
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+
+ if (!(arg & OFFS_REG_MASK) && argw <= SIMM_MAX && argw >= SIMM_MIN) {
+ /* Works for both absoulte and relative addresses. */
+ if (SLJIT_UNLIKELY(flags & ARG_TEST))
+ return 1;
+
+ FAIL_IF(push_mem_inst(compiler, flags, reg, arg & REG_MASK, argw));
+ return -1;
+ }
+ return 0;
+}
+
+#define TO_ARGW_HI(argw) (((argw) & ~0xfff) + (((argw) & 0x800) ? 0x1000 : 0))
+
+/* See getput_arg below.
+ Note: can_cache is called only for binary operators. */
+static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw)
+{
+ SLJIT_ASSERT((arg & SLJIT_MEM) && (next_arg & SLJIT_MEM));
+
+ /* Simple operation except for updates. */
+ if (arg & OFFS_REG_MASK) {
+ argw &= 0x3;
+ next_argw &= 0x3;
+ if (argw && argw == next_argw && (arg == next_arg || (arg & OFFS_REG_MASK) == (next_arg & OFFS_REG_MASK)))
+ return 1;
+ return 0;
+ }
+
+ if (arg == next_arg) {
+ if (((next_argw - argw) <= SIMM_MAX && (next_argw - argw) >= SIMM_MIN)
+ || TO_ARGW_HI(argw) == TO_ARGW_HI(next_argw))
+ return 1;
+ return 0;
+ }
+
+ return 0;
+}
+
+/* Emit the necessary instructions. See can_cache above. */
+static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw)
+{
+ sljit_s32 base = arg & REG_MASK;
+ sljit_s32 tmp_r = TMP_REG1;
+ sljit_sw offset, argw_hi;
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+ if (!(next_arg & SLJIT_MEM)) {
+ next_arg = 0;
+ next_argw = 0;
+ }
+
+ /* Since tmp can be the same as base or offset registers,
+ * these might be unavailable after modifying tmp. */
+ if ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA))
+ tmp_r = reg;
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ argw &= 0x3;
+
+ /* Using the cache. */
+ if (argw == compiler->cache_argw) {
+ if (arg == compiler->cache_arg)
+ return push_mem_inst(compiler, flags, reg, TMP_REG3, 0);
+
+ if ((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg) {
+ if (arg == next_arg && argw == (next_argw & 0x3)) {
+ compiler->cache_arg = arg;
+ compiler->cache_argw = argw;
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG3) | RS1(TMP_REG3) | RS2(base)));
+ return push_mem_inst(compiler, flags, reg, TMP_REG3, 0);
+ }
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(base) | RS2(TMP_REG3)));
+ return push_mem_inst(compiler, flags, reg, tmp_r, 0);
+ }
+ }
+
+ if (SLJIT_UNLIKELY(argw)) {
+ compiler->cache_arg = SLJIT_MEM | (arg & OFFS_REG_MASK);
+ compiler->cache_argw = argw;
+ FAIL_IF(push_inst(compiler, SLLI | RD(TMP_REG3) | RS1(OFFS_REG(arg)) | IMM_I(argw)));
+ }
+
+ if (arg == next_arg && argw == (next_argw & 0x3)) {
+ compiler->cache_arg = arg;
+ compiler->cache_argw = argw;
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG3) | RS1(base) | RS2(!argw ? OFFS_REG(arg) : TMP_REG3)));
+ tmp_r = TMP_REG3;
+ }
+ else
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(base) | RS2(!argw ? OFFS_REG(arg) : TMP_REG3)));
+ return push_mem_inst(compiler, flags, reg, tmp_r, 0);
+ }
+
+ if (compiler->cache_arg == arg && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN)
+ return push_mem_inst(compiler, flags, reg, TMP_REG3, argw - compiler->cache_argw);
+
+ if (compiler->cache_arg == SLJIT_MEM && (argw - compiler->cache_argw <= SIMM_MAX) && (argw - compiler->cache_argw >= SIMM_MIN)) {
+ offset = argw - compiler->cache_argw;
+ } else {
+ compiler->cache_arg = SLJIT_MEM;
+
+ argw_hi = TO_ARGW_HI(argw);
+
+ if (next_arg && next_argw - argw <= SIMM_MAX && next_argw - argw >= SIMM_MIN && argw_hi != TO_ARGW_HI(next_argw)) {
+ FAIL_IF(load_immediate(compiler, TMP_REG3, argw, tmp_r));
+ compiler->cache_argw = argw;
+ offset = 0;
+ } else {
+ FAIL_IF(load_immediate(compiler, TMP_REG3, argw_hi, tmp_r));
+ compiler->cache_argw = argw_hi;
+ offset = argw & 0xfff;
+ argw = argw_hi;
+ }
+ }
+
+ if (!base)
+ return push_mem_inst(compiler, flags, reg, TMP_REG3, offset);
+
+ if (arg == next_arg && next_argw - argw <= SIMM_MAX && next_argw - argw >= SIMM_MIN) {
+ compiler->cache_arg = arg;
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG3) | RS1(TMP_REG3) | RS2(base)));
+ return push_mem_inst(compiler, flags, reg, TMP_REG3, offset);
+ }
+
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(TMP_REG3) | RS2(base)));
+ return push_mem_inst(compiler, flags, reg, tmp_r, offset);
+}
+
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
+{
+ sljit_s32 base = arg & REG_MASK;
+ sljit_s32 tmp_r = TMP_REG1;
+
+ if (getput_arg_fast(compiler, flags, reg, arg, argw))
+ return compiler->error;
+
+ if ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA))
+ tmp_r = reg;
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ argw &= 0x3;
+
+ if (SLJIT_UNLIKELY(argw)) {
+ FAIL_IF(push_inst(compiler, SLLI | RD(tmp_r) | RS1(OFFS_REG(arg)) | IMM_I(argw)));
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(tmp_r) | RS2(base)));
+ }
+ else
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(base) | RS2(OFFS_REG(arg))));
+
+ argw = 0;
+ } else {
+ FAIL_IF(load_immediate(compiler, tmp_r, TO_ARGW_HI(argw), TMP_REG3));
+
+ if (base != 0)
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(tmp_r) | RS2(base)));
+ }
+
+ return push_mem_inst(compiler, flags, reg, tmp_r, argw & 0xfff);
+}
+
+static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg1, sljit_sw arg1w, sljit_s32 arg2, sljit_sw arg2w)
+{
+ if (getput_arg_fast(compiler, flags, reg, arg1, arg1w))
+ return compiler->error;
+ return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w);
+}
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+#define WORD 0
+#define IMM_EXTEND(v) (IMM_I(v))
+#else /* !SLJIT_CONFIG_RISCV_32 */
+#define WORD word
+#define IMM_EXTEND(v) (IMM_I((op & SLJIT_32) ? (v) : (32 + (v))))
+#endif /* SLJIT_CONFIG_RISCV_32 */
+
+static sljit_s32 emit_clz_ctz(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw src)
+{
+ sljit_s32 is_clz = (GET_OPCODE(op) == SLJIT_CLZ);
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ sljit_ins word = (op & SLJIT_32) >> 5;
+ sljit_ins max = (op & SLJIT_32) ? 32 : 64;
+#else /* !SLJIT_CONFIG_RISCV_64 */
+ sljit_ins max = 32;
+#endif /* SLJIT_CONFIG_RISCV_64 */
+
+ SLJIT_ASSERT(WORD == 0 || WORD == 0x8);
+
+ /* The OTHER_FLAG is the counter. */
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(OTHER_FLAG) | RS1(TMP_ZERO) | IMM_I(max)));
+
+ /* The TMP_REG2 is the next value. */
+ if (src != TMP_REG2)
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(TMP_REG2) | RS1(src) | IMM_I(0)));
+
+ FAIL_IF(push_inst(compiler, BEQ | RS1(TMP_REG2) | RS2(TMP_ZERO) | ((sljit_ins)((is_clz ? 4 : 5) * SSIZE_OF(ins)) << 7) | ((sljit_ins)(8 * SSIZE_OF(ins)) << 20)));
+
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(OTHER_FLAG) | RS1(TMP_ZERO) | IMM_I(0)));
+ if (!is_clz) {
+ FAIL_IF(push_inst(compiler, ANDI | RD(TMP_REG1) | RS1(TMP_REG2) | IMM_I(1)));
+ FAIL_IF(push_inst(compiler, BNE | RS1(TMP_REG1) | RS2(TMP_ZERO) | ((sljit_ins)(2 * SSIZE_OF(ins)) << 7) | ((sljit_ins)(8 * SSIZE_OF(ins)) << 20)));
+ } else
+ FAIL_IF(push_inst(compiler, BLT | RS1(TMP_REG2) | RS2(TMP_ZERO) | ((sljit_ins)(2 * SSIZE_OF(ins)) << 7) | ((sljit_ins)(8 * SSIZE_OF(ins)) << 20)));
+
+ /* The TMP_REG1 is the next shift. */
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(TMP_REG1) | RS1(TMP_ZERO) | IMM_I(max)));
+
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(EQUAL_FLAG) | RS1(TMP_REG2) | IMM_I(0)));
+ FAIL_IF(push_inst(compiler, SRLI | WORD | RD(TMP_REG1) | RS1(TMP_REG1) | IMM_I(1)));
+
+ FAIL_IF(push_inst(compiler, (is_clz ? SRL : SLL) | WORD | RD(TMP_REG2) | RS1(EQUAL_FLAG) | RS2(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, BNE | RS1(TMP_REG2) | RS2(TMP_ZERO) | ((sljit_ins)0xfe000e80 - ((2 * SSIZE_OF(ins)) << 7))));
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(TMP_REG2) | RS1(TMP_REG1) | IMM_I(-1)));
+ FAIL_IF(push_inst(compiler, (is_clz ? SRL : SLL) | WORD | RD(TMP_REG2) | RS1(EQUAL_FLAG) | RS2(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, OR | RD(OTHER_FLAG) | RS1(OTHER_FLAG) | RS2(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, BEQ | RS1(TMP_REG2) | RS2(TMP_ZERO) | ((sljit_ins)0xfe000e80 - ((5 * SSIZE_OF(ins)) << 7))));
+
+ return push_inst(compiler, ADDI | WORD | RD(dst) | RS1(OTHER_FLAG) | IMM_I(0));
+}
+
+#define EMIT_LOGICAL(op_imm, op_reg) \
+ if (flags & SRC2_IMM) { \
+ if (op & SLJIT_SET_Z) \
+ FAIL_IF(push_inst(compiler, op_imm | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(src2))); \
+ if (!(flags & UNUSED_DEST)) \
+ FAIL_IF(push_inst(compiler, op_imm | RD(dst) | RS1(src1) | IMM_I(src2))); \
+ } \
+ else { \
+ if (op & SLJIT_SET_Z) \
+ FAIL_IF(push_inst(compiler, op_reg | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); \
+ if (!(flags & UNUSED_DEST)) \
+ FAIL_IF(push_inst(compiler, op_reg | RD(dst) | RS1(src1) | RS2(src2))); \
+ }
+
+#define EMIT_SHIFT(imm, reg) \
+ op_imm = (imm); \
+ op_reg = (reg);
+
+static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
+ sljit_s32 dst, sljit_s32 src1, sljit_sw src2)
+{
+ sljit_s32 is_overflow, is_carry, carry_src_r, is_handled;
+ sljit_ins op_imm, op_reg;
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ sljit_ins word = (op & SLJIT_32) >> 5;
+#endif /* SLJIT_CONFIG_RISCV_64 */
+
+ SLJIT_ASSERT(WORD == 0 || WORD == 0x8);
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if (dst != src2)
+ return push_inst(compiler, ADDI | RD(dst) | RS1(src2) | IMM_I(0));
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U8:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE))
+ return push_inst(compiler, ANDI | RD(dst) | RS1(src2) | IMM_I(0xff));
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_S8:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ FAIL_IF(push_inst(compiler, SLLI | WORD | RD(dst) | RS1(src2) | IMM_EXTEND(24)));
+ return push_inst(compiler, SRAI | WORD | RD(dst) | RS1(dst) | IMM_EXTEND(24));
+ }
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U16:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ FAIL_IF(push_inst(compiler, SLLI | WORD | RD(dst) | RS1(src2) | IMM_EXTEND(16)));
+ return push_inst(compiler, SRLI | WORD | RD(dst) | RS1(dst) | IMM_EXTEND(16));
+ }
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_S16:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ FAIL_IF(push_inst(compiler, SLLI | WORD | RD(dst) | RS1(src2) | IMM_EXTEND(16)));
+ return push_inst(compiler, SRAI | WORD | RD(dst) | RS1(dst) | IMM_EXTEND(16));
+ }
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ case SLJIT_MOV_U32:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ FAIL_IF(push_inst(compiler, SLLI | RD(dst) | RS1(src2) | IMM_I(32)));
+ return push_inst(compiler, SRLI | RD(dst) | RS1(dst) | IMM_I(32));
+ }
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_S32:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE))
+ return push_inst(compiler, ADDI | 0x8 | RD(dst) | RS1(src2) | IMM_I(0));
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+#endif /* SLJIT_CONFIG_RISCV_64 */
+
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ return emit_clz_ctz(compiler, op, dst, src2);
+
+ case SLJIT_ADD:
+ /* Overflow computation (both add and sub): overflow = src1_sign ^ src2_sign ^ result_sign ^ carry_flag */
+ is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW;
+ carry_src_r = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
+ if (flags & SRC2_IMM) {
+ if (is_overflow) {
+ if (src2 >= 0)
+ FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(0)));
+ else
+ FAIL_IF(push_inst(compiler, XORI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(-1)));
+ }
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(src2)));
+
+ /* Only the zero flag is needed. */
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(src2)));
+ }
+ else {
+ if (is_overflow)
+ FAIL_IF(push_inst(compiler, XOR | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2)));
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ADD | WORD | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2)));
+
+ if (is_overflow || carry_src_r != 0) {
+ if (src1 != dst)
+ carry_src_r = (sljit_s32)src1;
+ else if (src2 != dst)
+ carry_src_r = (sljit_s32)src2;
+ else {
+ FAIL_IF(push_inst(compiler, ADDI | RD(OTHER_FLAG) | RS1(src1) | IMM_I(0)));
+ carry_src_r = OTHER_FLAG;
+ }
+ }
+
+ /* Only the zero flag is needed. */
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
+ FAIL_IF(push_inst(compiler, ADD | WORD | RD(dst) | RS1(src1) | RS2(src2)));
+ }
+
+ /* Carry is zero if a + b >= a or a + b >= b, otherwise it is 1. */
+ if (is_overflow || carry_src_r != 0) {
+ if (flags & SRC2_IMM)
+ FAIL_IF(push_inst(compiler, SLTUI | RD(OTHER_FLAG) | RS1(dst) | IMM_I(src2)));
+ else
+ FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(dst) | RS2(carry_src_r)));
+ }
+
+ if (!is_overflow)
+ return SLJIT_SUCCESS;
+
+ FAIL_IF(push_inst(compiler, XOR | RD(TMP_REG1) | RS1(dst) | RS2(EQUAL_FLAG)));
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(dst) | IMM_I(0)));
+ FAIL_IF(push_inst(compiler, SRLI | WORD | RD(TMP_REG1) | RS1(TMP_REG1) | IMM_EXTEND(31)));
+ return push_inst(compiler, XOR | RD(OTHER_FLAG) | RS1(TMP_REG1) | RS2(OTHER_FLAG));
+
+ case SLJIT_ADDC:
+ carry_src_r = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
+ if (flags & SRC2_IMM) {
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(src2)));
+ } else {
+ if (carry_src_r != 0) {
+ if (src1 != dst)
+ carry_src_r = (sljit_s32)src1;
+ else if (src2 != dst)
+ carry_src_r = (sljit_s32)src2;
+ else {
+ FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(0)));
+ carry_src_r = EQUAL_FLAG;
+ }
+ }
+
+ FAIL_IF(push_inst(compiler, ADD | WORD | RD(dst) | RS1(src1) | RS2(src2)));
+ }
+
+ /* Carry is zero if a + b >= a or a + b >= b, otherwise it is 1. */
+ if (carry_src_r != 0) {
+ if (flags & SRC2_IMM)
+ FAIL_IF(push_inst(compiler, SLTUI | RD(EQUAL_FLAG) | RS1(dst) | IMM_I(src2)));
+ else
+ FAIL_IF(push_inst(compiler, SLTU | RD(EQUAL_FLAG) | RS1(dst) | RS2(carry_src_r)));
+ }
+
+ FAIL_IF(push_inst(compiler, ADD | WORD | RD(dst) | RS1(dst) | RS2(OTHER_FLAG)));
+
+ if (carry_src_r == 0)
+ return SLJIT_SUCCESS;
+
+ /* Set ULESS_FLAG (dst == 0) && (OTHER_FLAG == 1). */
+ FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(dst) | RS2(OTHER_FLAG)));
+ /* Set carry flag. */
+ return push_inst(compiler, OR | RD(OTHER_FLAG) | RS1(OTHER_FLAG) | RS2(EQUAL_FLAG));
+
+ case SLJIT_SUB:
+ if ((flags & SRC2_IMM) && src2 == SIMM_MIN) {
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG2) | RS1(TMP_ZERO) | IMM_I(src2)));
+ src2 = TMP_REG2;
+ flags &= ~SRC2_IMM;
+ }
+
+ is_handled = 0;
+
+ if (flags & SRC2_IMM) {
+ if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) {
+ FAIL_IF(push_inst(compiler, SLTUI | RD(OTHER_FLAG) | RS1(src1) | IMM_I(src2)));
+ is_handled = 1;
+ }
+ else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) {
+ FAIL_IF(push_inst(compiler, SLTI | RD(OTHER_FLAG) | RS1(src1) | IMM_I(src2)));
+ is_handled = 1;
+ }
+ }
+
+ if (!is_handled && GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL) {
+ is_handled = 1;
+
+ if (flags & SRC2_IMM) {
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG2) | RS1(TMP_ZERO) | IMM_I(src2)));
+ src2 = TMP_REG2;
+ flags &= ~SRC2_IMM;
+ }
+
+ switch (GET_FLAG_TYPE(op)) {
+ case SLJIT_LESS:
+ case SLJIT_GREATER_EQUAL:
+ FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(src1) | RS2(src2)));
+ break;
+ case SLJIT_GREATER:
+ case SLJIT_LESS_EQUAL:
+ FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(src2) | RS2(src1)));
+ break;
+ case SLJIT_SIG_LESS:
+ case SLJIT_SIG_GREATER_EQUAL:
+ FAIL_IF(push_inst(compiler, SLT | RD(OTHER_FLAG) | RS1(src1) | RS2(src2)));
+ break;
+ case SLJIT_SIG_GREATER:
+ case SLJIT_SIG_LESS_EQUAL:
+ FAIL_IF(push_inst(compiler, SLT | RD(OTHER_FLAG) | RS1(src2) | RS2(src1)));
+ break;
+ }
+ }
+
+ if (is_handled) {
+ if (flags & SRC2_IMM) {
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(-src2)));
+ if (!(flags & UNUSED_DEST))
+ return push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(-src2));
+ }
+ else {
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SUB | WORD | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2)));
+ if (!(flags & UNUSED_DEST))
+ return push_inst(compiler, SUB | WORD | RD(dst) | RS1(src1) | RS2(src2));
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW;
+ is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
+ if (flags & SRC2_IMM) {
+ if (is_overflow) {
+ if (src2 >= 0)
+ FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(0)));
+ else
+ FAIL_IF(push_inst(compiler, XORI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(-1)));
+ }
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(-src2)));
+
+ if (is_overflow || is_carry)
+ FAIL_IF(push_inst(compiler, SLTUI | RD(OTHER_FLAG) | RS1(src1) | IMM_I(src2)));
+
+ /* Only the zero flag is needed. */
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(-src2)));
+ }
+ else {
+ if (is_overflow)
+ FAIL_IF(push_inst(compiler, XOR | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2)));
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SUB | WORD | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2)));
+
+ if (is_overflow || is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(src1) | RS2(src2)));
+
+ /* Only the zero flag is needed. */
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
+ FAIL_IF(push_inst(compiler, SUB | WORD | RD(dst) | RS1(src1) | RS2(src2)));
+ }
+
+ if (!is_overflow)
+ return SLJIT_SUCCESS;
+
+ FAIL_IF(push_inst(compiler, XOR | RD(TMP_REG1) | RS1(dst) | RS2(EQUAL_FLAG)));
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(dst) | IMM_I(0)));
+ FAIL_IF(push_inst(compiler, SRLI | WORD | RD(TMP_REG1) | RS1(TMP_REG1) | IMM_EXTEND(31)));
+ return push_inst(compiler, XOR | RD(OTHER_FLAG) | RS1(TMP_REG1) | RS2(OTHER_FLAG));
+
+ case SLJIT_SUBC:
+ if ((flags & SRC2_IMM) && src2 == SIMM_MIN) {
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG2) | RS1(TMP_ZERO) | IMM_I(src2)));
+ src2 = TMP_REG2;
+ flags &= ~SRC2_IMM;
+ }
+
+ is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
+ if (flags & SRC2_IMM) {
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTUI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(src2)));
+
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(-src2)));
+ }
+ else {
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2)));
+
+ FAIL_IF(push_inst(compiler, SUB | WORD | RD(dst) | RS1(src1) | RS2(src2)));
+ }
+
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | RD(TMP_REG1) | RS1(dst) | RS2(OTHER_FLAG)));
+
+ FAIL_IF(push_inst(compiler, SUB | WORD | RD(dst) | RS1(dst) | RS2(OTHER_FLAG)));
+
+ if (!is_carry)
+ return SLJIT_SUCCESS;
+
+ return push_inst(compiler, OR | RD(OTHER_FLAG) | RS1(EQUAL_FLAG) | RS2(TMP_REG1));
+
+ case SLJIT_MUL:
+ SLJIT_ASSERT(!(flags & SRC2_IMM));
+
+ if (GET_FLAG_TYPE(op) != SLJIT_OVERFLOW)
+ return push_inst(compiler, MUL | WORD | RD(dst) | RS1(src1) | RS2(src2));
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ if (word) {
+ FAIL_IF(push_inst(compiler, MUL | RD(OTHER_FLAG) | RS1(src1) | RS2(src2)));
+ FAIL_IF(push_inst(compiler, MUL | 0x8 | RD(dst) | RS1(src1) | RS2(src2)));
+ return push_inst(compiler, SUB | RD(OTHER_FLAG) | RS1(dst) | RS2(OTHER_FLAG));
+ }
+#endif /* SLJIT_CONFIG_RISCV_64 */
+
+ FAIL_IF(push_inst(compiler, MULH | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2)));
+ FAIL_IF(push_inst(compiler, MUL | RD(dst) | RS1(src1) | RS2(src2)));
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ FAIL_IF(push_inst(compiler, SRAI | RD(OTHER_FLAG) | RS1(dst) | IMM_I(31)));
+#else /* !SLJIT_CONFIG_RISCV_32 */
+ FAIL_IF(push_inst(compiler, SRAI | RD(OTHER_FLAG) | RS1(dst) | IMM_I(63)));
+#endif /* SLJIT_CONFIG_RISCV_32 */
+ return push_inst(compiler, SUB | RD(OTHER_FLAG) | RS1(EQUAL_FLAG) | RS2(OTHER_FLAG));
+
+ case SLJIT_AND:
+ EMIT_LOGICAL(ANDI, AND);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_OR:
+ EMIT_LOGICAL(ORI, OR);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_XOR:
+ EMIT_LOGICAL(XORI, XOR);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ EMIT_SHIFT(SLLI, SLL);
+ break;
+
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ EMIT_SHIFT(SRLI, SRL);
+ break;
+
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ EMIT_SHIFT(SRAI, SRA);
+ break;
+
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ if (flags & SRC2_IMM) {
+ SLJIT_ASSERT(src2 != 0);
+
+ op_imm = (GET_OPCODE(op) == SLJIT_ROTL) ? SLLI : SRLI;
+ FAIL_IF(push_inst(compiler, op_imm | WORD | RD(OTHER_FLAG) | RS1(src1) | IMM_I(src2)));
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ src2 = ((op & SLJIT_32) ? 32 : 64) - src2;
+#else /* !SLJIT_CONFIG_RISCV_64 */
+ src2 = 32 - src2;
+#endif /* SLJIT_CONFIG_RISCV_64 */
+ op_imm = (GET_OPCODE(op) == SLJIT_ROTL) ? SRLI : SLLI;
+ FAIL_IF(push_inst(compiler, op_imm | WORD | RD(dst) | RS1(src1) | IMM_I(src2)));
+ return push_inst(compiler, OR | RD(dst) | RS1(dst) | RS2(OTHER_FLAG));
+ }
+
+ if (src2 == TMP_ZERO) {
+ if (dst != src1)
+ return push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(0));
+ return SLJIT_SUCCESS;
+ }
+
+ FAIL_IF(push_inst(compiler, SUB | WORD | RD(EQUAL_FLAG) | RS1(TMP_ZERO) | RS2(src2)));
+ op_reg = (GET_OPCODE(op) == SLJIT_ROTL) ? SLL : SRL;
+ FAIL_IF(push_inst(compiler, op_reg | WORD | RD(OTHER_FLAG) | RS1(src1) | RS2(src2)));
+ op_reg = (GET_OPCODE(op) == SLJIT_ROTL) ? SRL : SLL;
+ FAIL_IF(push_inst(compiler, op_reg | WORD | RD(dst) | RS1(src1) | RS2(EQUAL_FLAG)));
+ return push_inst(compiler, OR | RD(dst) | RS1(dst) | RS2(OTHER_FLAG));
+
+ default:
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+ }
+
+ if (flags & SRC2_IMM) {
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, op_imm | WORD | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(src2)));
+
+ if (flags & UNUSED_DEST)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, op_imm | WORD | RD(dst) | RS1(src1) | IMM_I(src2));
+ }
+
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, op_reg | WORD | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2)));
+
+ if (flags & UNUSED_DEST)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, op_reg | WORD | RD(dst) | RS1(src1) | RS2(src2));
+}
+
+#undef IMM_EXTEND
+
+static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ /* arg1 goes to TMP_REG1 or src reg
+ arg2 goes to TMP_REG2, imm or src reg
+ TMP_REG3 can be used for caching
+ result goes to TMP_REG2, so put result can use TMP_REG1 and TMP_REG3. */
+ sljit_s32 dst_r = TMP_REG2;
+ sljit_s32 src1_r;
+ sljit_sw src2_r = 0;
+ sljit_s32 sugg_src2_r = TMP_REG2;
+
+ if (!(flags & ALT_KEEP_CACHE)) {
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+ }
+
+ if (dst == TMP_REG2) {
+ SLJIT_ASSERT(HAS_FLAGS(op));
+ flags |= UNUSED_DEST;
+ }
+ else if (FAST_IS_REG(dst)) {
+ dst_r = dst;
+ flags |= REG_DEST;
+ if (flags & MOVE_OP)
+ sugg_src2_r = dst_r;
+ }
+ else if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, flags | ARG_TEST, TMP_REG1, dst, dstw))
+ flags |= SLOW_DEST;
+
+ if (flags & IMM_OP) {
+ if ((src2 & SLJIT_IMM) && src2w != 0 && src2w <= SIMM_MAX && src2w >= SIMM_MIN) {
+ flags |= SRC2_IMM;
+ src2_r = src2w;
+ }
+ else if ((flags & CUMULATIVE_OP) && (src1 & SLJIT_IMM) && src1w != 0 && src1w <= SIMM_MAX && src1w >= SIMM_MIN) {
+ flags |= SRC2_IMM;
+ src2_r = src1w;
+
+ /* And swap arguments. */
+ src1 = src2;
+ src1w = src2w;
+ src2 = SLJIT_IMM;
+ /* src2w = src2_r unneeded. */
+ }
+ }
+
+ /* Source 1. */
+ if (FAST_IS_REG(src1)) {
+ src1_r = src1;
+ flags |= REG1_SOURCE;
+ }
+ else if (src1 & SLJIT_IMM) {
+ if (src1w) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, src1w, TMP_REG3));
+ src1_r = TMP_REG1;
+ }
+ else
+ src1_r = TMP_ZERO;
+ }
+ else {
+ if (getput_arg_fast(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w))
+ FAIL_IF(compiler->error);
+ else
+ flags |= SLOW_SRC1;
+ src1_r = TMP_REG1;
+ }
+
+ /* Source 2. */
+ if (FAST_IS_REG(src2)) {
+ src2_r = src2;
+ flags |= REG2_SOURCE;
+ if ((flags & (REG_DEST | MOVE_OP)) == MOVE_OP)
+ dst_r = (sljit_s32)src2_r;
+ }
+ else if (src2 & SLJIT_IMM) {
+ if (!(flags & SRC2_IMM)) {
+ if (src2w) {
+ FAIL_IF(load_immediate(compiler, sugg_src2_r, src2w, TMP_REG3));
+ src2_r = sugg_src2_r;
+ }
+ else {
+ src2_r = TMP_ZERO;
+ if (flags & MOVE_OP) {
+ if (dst & SLJIT_MEM)
+ dst_r = 0;
+ else
+ op = SLJIT_MOV;
+ }
+ }
+ }
+ }
+ else {
+ if (getput_arg_fast(compiler, flags | LOAD_DATA, sugg_src2_r, src2, src2w))
+ FAIL_IF(compiler->error);
+ else
+ flags |= SLOW_SRC2;
+ src2_r = sugg_src2_r;
+ }
+
+ if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) {
+ SLJIT_ASSERT(src2_r == TMP_REG2);
+ if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG2, src2, src2w, src1, src1w));
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw));
+ }
+ else {
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w));
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG2, src2, src2w, dst, dstw));
+ }
+ }
+ else if (flags & SLOW_SRC1)
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw));
+ else if (flags & SLOW_SRC2)
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, sugg_src2_r, src2, src2w, dst, dstw));
+
+ FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r));
+
+ if (dst & SLJIT_MEM) {
+ if (!(flags & SLOW_DEST)) {
+ getput_arg_fast(compiler, flags, dst_r, dst, dstw);
+ return compiler->error;
+ }
+ return getput_arg(compiler, flags, dst_r, dst, dstw, 0, 0);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ sljit_ins word = (op & SLJIT_32) >> 5;
+
+ SLJIT_ASSERT(word == 0 || word == 0x8);
+#endif /* SLJIT_CONFIG_RISCV_64 */
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op0(compiler, op));
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_BREAKPOINT:
+ return push_inst(compiler, EBREAK);
+ case SLJIT_NOP:
+ return push_inst(compiler, ADDI | RD(TMP_ZERO) | RS1(TMP_ZERO) | IMM_I(0));
+ case SLJIT_LMUL_UW:
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(SLJIT_R1) | IMM_I(0)));
+ FAIL_IF(push_inst(compiler, MULHU | RD(SLJIT_R1) | RS1(SLJIT_R0) | RS2(SLJIT_R1)));
+ return push_inst(compiler, MUL | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(TMP_REG1));
+ case SLJIT_LMUL_SW:
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(SLJIT_R1) | IMM_I(0)));
+ FAIL_IF(push_inst(compiler, MULH | RD(SLJIT_R1) | RS1(SLJIT_R0) | RS2(SLJIT_R1)));
+ return push_inst(compiler, MUL | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(TMP_REG1));
+ case SLJIT_DIVMOD_UW:
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(SLJIT_R0) | IMM_I(0)));
+ FAIL_IF(push_inst(compiler, DIVU | WORD | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(SLJIT_R1)));
+ return push_inst(compiler, REMU | WORD | RD(SLJIT_R1) | RS1(TMP_REG1) | RS2(SLJIT_R1));
+ case SLJIT_DIVMOD_SW:
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(SLJIT_R0) | IMM_I(0)));
+ FAIL_IF(push_inst(compiler, DIV | WORD | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(SLJIT_R1)));
+ return push_inst(compiler, REM | WORD | RD(SLJIT_R1) | RS1(TMP_REG1) | RS2(SLJIT_R1));
+ case SLJIT_DIV_UW:
+ return push_inst(compiler, DIVU | WORD | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(SLJIT_R1));
+ case SLJIT_DIV_SW:
+ return push_inst(compiler, DIV | WORD | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(SLJIT_R1));
+ case SLJIT_ENDBR:
+ case SLJIT_SKIP_FRAMES_BEFORE_RETURN:
+ return SLJIT_SUCCESS;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 flags = 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ if (op & SLJIT_32)
+ flags = INT_DATA | SIGNED_DATA;
+#endif
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV:
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+#endif
+ case SLJIT_MOV_P:
+ return emit_op(compiler, SLJIT_MOV, WORD_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, srcw);
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ case SLJIT_MOV_U32:
+ return emit_op(compiler, SLJIT_MOV_U32, INT_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u32)srcw : srcw);
+
+ case SLJIT_MOV_S32:
+ /* Logical operators have no W variant, so sign extended input is necessary for them. */
+ case SLJIT_MOV32:
+ return emit_op(compiler, SLJIT_MOV_S32, INT_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s32)srcw : srcw);
+#endif
+
+ case SLJIT_MOV_U8:
+ return emit_op(compiler, op, BYTE_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw);
+
+ case SLJIT_MOV_S8:
+ return emit_op(compiler, op, BYTE_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw);
+
+ case SLJIT_MOV_U16:
+ return emit_op(compiler, op, HALF_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw);
+
+ case SLJIT_MOV_S16:
+ return emit_op(compiler, op, HALF_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw);
+
+ case SLJIT_NOT:
+ return emit_op(compiler, SLJIT_XOR | (op & (SLJIT_32 | SLJIT_SET_Z)), flags, dst, dstw, src, srcw, SLJIT_IMM, -1);
+
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw);
+ }
+
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 flags = 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ if (op & SLJIT_32) {
+ flags |= INT_DATA | SIGNED_DATA;
+ if (src1 & SLJIT_IMM)
+ src1w = (sljit_s32)src1w;
+ if (src2 & SLJIT_IMM)
+ src2w = (sljit_s32)src2w;
+ }
+#endif
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD:
+ case SLJIT_ADDC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ return emit_op(compiler, op, flags | CUMULATIVE_OP | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_SUB:
+ case SLJIT_SUBC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_MUL:
+ compiler->status_flags_state = 0;
+ return emit_op(compiler, op, flags | CUMULATIVE_OP, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_AND:
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ return emit_op(compiler, op, flags | CUMULATIVE_OP | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ if (src2 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ src2w &= 0x1f;
+#else /* !SLJIT_CONFIG_RISCV_32 */
+ if (op & SLJIT_32)
+ src2w &= 0x1f;
+ else
+ src2w &= 0x3f;
+#endif /* SLJIT_CONFIG_RISCV_32 */
+ }
+
+ return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, TMP_REG2, 0, src1, src1w, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 is_left;
+ sljit_ins ins1, ins2, ins3;
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ sljit_ins word = (op & SLJIT_32) >> 5;
+ sljit_s32 inp_flags = ((op & SLJIT_32) ? INT_DATA : WORD_DATA) | LOAD_DATA;
+ sljit_sw bit_length = (op & SLJIT_32) ? 32 : 64;
+#else /* !SLJIT_CONFIG_RISCV_64 */
+ sljit_s32 inp_flags = WORD_DATA | LOAD_DATA;
+ sljit_sw bit_length = 32;
+#endif /* SLJIT_CONFIG_RISCV_64 */
+
+ SLJIT_ASSERT(WORD == 0 || WORD == 0x8);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w));
+
+ is_left = (GET_OPCODE(op) == SLJIT_SHL || GET_OPCODE(op) == SLJIT_MSHL);
+
+ if (src_dst == src1) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, (is_left ? SLJIT_ROTL : SLJIT_ROTR) | (op & SLJIT_32), src_dst, 0, src_dst, 0, src2, src2w);
+ }
+
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ if (src2 & SLJIT_IMM) {
+ src2w &= bit_length - 1;
+
+ if (src2w == 0)
+ return SLJIT_SUCCESS;
+ } else if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inp_flags, TMP_REG2, src2, src2w));
+ src2 = TMP_REG2;
+ }
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inp_flags, TMP_REG1, src1, src1w));
+ src1 = TMP_REG1;
+ } else if (src1 & SLJIT_IMM) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, src1w, TMP_REG3));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_IMM) {
+ if (is_left) {
+ ins1 = SLLI | WORD | IMM_I(src2w);
+ src2w = bit_length - src2w;
+ ins2 = SRLI | WORD | IMM_I(src2w);
+ } else {
+ ins1 = SRLI | WORD | IMM_I(src2w);
+ src2w = bit_length - src2w;
+ ins2 = SLLI | WORD | IMM_I(src2w);
+ }
+
+ FAIL_IF(push_inst(compiler, ins1 | RD(src_dst) | RS1(src_dst)));
+ FAIL_IF(push_inst(compiler, ins2 | RD(TMP_REG1) | RS1(src1)));
+ return push_inst(compiler, OR | RD(src_dst) | RS1(src_dst) | RS2(TMP_REG1));
+ }
+
+ if (is_left) {
+ ins1 = SLL;
+ ins2 = SRLI;
+ ins3 = SRL;
+ } else {
+ ins1 = SRL;
+ ins2 = SLLI;
+ ins3 = SLL;
+ }
+
+ FAIL_IF(push_inst(compiler, ins1 | WORD | RD(src_dst) | RS1(src_dst) | RS2(src2)));
+
+ if (!(op & SLJIT_SHIFT_INTO_NON_ZERO)) {
+ FAIL_IF(push_inst(compiler, ins2 | WORD | RD(TMP_REG1) | RS1(src1) | IMM_I(1)));
+ FAIL_IF(push_inst(compiler, XORI | RD(TMP_REG2) | RS1(src2) | IMM_I((sljit_ins)bit_length - 1)));
+ src1 = TMP_REG1;
+ } else
+ FAIL_IF(push_inst(compiler, SUB | WORD | RD(TMP_REG2) | RS1(TMP_ZERO) | RS2(src2)));
+
+ FAIL_IF(push_inst(compiler, ins3 | WORD | RD(TMP_REG1) | RS1(src1) | RS2(TMP_REG2)));
+ return push_inst(compiler, OR | RD(src_dst) | RS1(src_dst) | RS2(TMP_REG1));
+}
+
+#undef WORD
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_src(compiler, op, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ switch (op) {
+ case SLJIT_FAST_RETURN:
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst(compiler, ADDI | RD(RETURN_ADDR_REG) | RS1(src) | IMM_I(0)));
+ else
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, RETURN_ADDR_REG, src, srcw));
+
+ return push_inst(compiler, JALR | RD(TMP_ZERO) | RS1(RETURN_ADDR_REG) | IMM_I(0));
+ case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN:
+ return SLJIT_SUCCESS;
+ case SLJIT_PREFETCH_L1:
+ case SLJIT_PREFETCH_L2:
+ case SLJIT_PREFETCH_L3:
+ case SLJIT_PREFETCH_ONCE:
+ return SLJIT_SUCCESS;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_register_index(reg));
+ return reg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
+ return freg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_custom(compiler, instruction, size));
+
+ return push_inst(compiler, *(sljit_ins*)instruction);
+}
+
+/* --------------------------------------------------------------------- */
+/* Floating point operators */
+/* --------------------------------------------------------------------- */
+
+#define FLOAT_DATA(op) (DOUBLE_DATA | ((op & SLJIT_32) >> 7))
+#define FMT(op) ((sljit_ins)((op & SLJIT_32) ^ SLJIT_32) << 17)
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+# define flags (sljit_u32)0
+#else
+ sljit_u32 flags = ((sljit_u32)(GET_OPCODE(op) == SLJIT_CONV_SW_FROM_F64)) << 21;
+#endif
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw));
+ src = TMP_FREG1;
+ }
+
+ FAIL_IF(push_inst(compiler, FCVT_W_S | FMT(op) | flags | RD(dst_r) | FRS1(src)));
+
+ /* Store the integer value from a VFP register. */
+ if (dst & SLJIT_MEM) {
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ return emit_op_mem2(compiler, WORD_DATA, TMP_REG2, dst, dstw, 0, 0);
+#else
+ return emit_op_mem2(compiler, flags ? WORD_DATA : INT_DATA, TMP_REG2, dst, dstw, 0, 0);
+#endif
+ }
+ return SLJIT_SUCCESS;
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+# undef flags
+#endif
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_ins inst;
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ sljit_u32 flags = ((sljit_u32)(GET_OPCODE(op) == SLJIT_CONV_F64_FROM_SW)) << 21;
+#endif
+
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (src & SLJIT_MEM) {
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ FAIL_IF(emit_op_mem2(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw, dst, dstw));
+#else
+ FAIL_IF(emit_op_mem2(compiler, (flags ? WORD_DATA : INT_DATA) | LOAD_DATA, TMP_REG1, src, srcw, dst, dstw));
+#endif
+ src = TMP_REG1;
+ } else if (src & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32)
+ srcw = (sljit_s32)srcw;
+#endif
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, srcw, TMP_REG3));
+ src = TMP_REG1;
+ }
+
+ inst = FCVT_S_W | FMT(op) | FRD(dst_r) | RS1(src);
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ if (op & SLJIT_32)
+ inst |= F3(0x7);
+#else
+ inst |= flags;
+
+ if (op != SLJIT_CONV_F64_FROM_S32)
+ inst |= F3(0x7);
+#endif
+
+ FAIL_IF(push_inst(compiler, inst));
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, 0, 0);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_ins inst;
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w));
+ src1 = TMP_FREG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, 0, 0));
+ src2 = TMP_FREG2;
+ }
+
+ switch (GET_FLAG_TYPE(op)) {
+ case SLJIT_F_EQUAL:
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ inst = FEQ_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src1) | FRS2(src2);
+ break;
+ case SLJIT_F_LESS:
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ inst = FLT_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src1) | FRS2(src2);
+ break;
+ case SLJIT_ORDERED_GREATER:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ inst = FLT_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src2) | FRS2(src1);
+ break;
+ case SLJIT_F_GREATER:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ inst = FLE_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src1) | FRS2(src2);
+ break;
+ case SLJIT_UNORDERED_OR_LESS:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ inst = FLE_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src2) | FRS2(src1);
+ break;
+ case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */
+ case SLJIT_ORDERED_NOT_EQUAL: /* Not supported. */
+ FAIL_IF(push_inst(compiler, FLT_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src1) | FRS2(src2)));
+ FAIL_IF(push_inst(compiler, FLT_S | FMT(op) | RD(TMP_REG1) | FRS1(src2) | FRS2(src1)));
+ inst = OR | RD(OTHER_FLAG) | RS1(OTHER_FLAG) | RS2(TMP_REG1);
+ break;
+ default: /* SLJIT_UNORDERED, SLJIT_ORDERED */
+ FAIL_IF(push_inst(compiler, FADD_S | FMT(op) | FRD(TMP_FREG1) | FRS1(src1) | FRS2(src2)));
+ inst = FEQ_S | FMT(op) | RD(OTHER_FLAG) | FRS1(TMP_FREG1) | FRS2(TMP_FREG1);
+ break;
+ }
+
+ return push_inst(compiler, inst);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+
+ SLJIT_COMPILE_ASSERT((SLJIT_32 == 0x100) && !(DOUBLE_DATA & 0x2), float_transfer_bit_error);
+ SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
+
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32)
+ op ^= SLJIT_32;
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, dst_r, src, srcw, dst, dstw));
+ src = dst_r;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV_F64:
+ if (src != dst_r) {
+ if (dst_r != TMP_FREG1)
+ FAIL_IF(push_inst(compiler, FSGNJ_S | FMT(op) | FRD(dst_r) | FRS1(src) | FRS2(src)));
+ else
+ dst_r = src;
+ }
+ break;
+ case SLJIT_NEG_F64:
+ FAIL_IF(push_inst(compiler, FSGNJN_S | FMT(op) | FRD(dst_r) | FRS1(src) | FRS2(src)));
+ break;
+ case SLJIT_ABS_F64:
+ FAIL_IF(push_inst(compiler, FSGNJX_S | FMT(op) | FRD(dst_r) | FRS1(src) | FRS2(src)));
+ break;
+ case SLJIT_CONV_F64_FROM_F32:
+ /* The SLJIT_32 bit is inverted because sljit_f32 needs to be loaded from the memory. */
+ FAIL_IF(push_inst(compiler, FCVT_S_D | ((op & SLJIT_32) ? (1 << 25) : ((1 << 20) | F3(7))) | FRD(dst_r) | FRS1(src)));
+ op ^= SLJIT_32;
+ break;
+ }
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem2(compiler, FLOAT_DATA(op), dst_r, dst, dstw, 0, 0);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r, flags = 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG2;
+
+ if (src1 & SLJIT_MEM) {
+ if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w)) {
+ FAIL_IF(compiler->error);
+ src1 = TMP_FREG1;
+ } else
+ flags |= SLOW_SRC1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w)) {
+ FAIL_IF(compiler->error);
+ src2 = TMP_FREG2;
+ } else
+ flags |= SLOW_SRC2;
+ }
+
+ if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) {
+ if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, src1, src1w));
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw));
+ }
+ else {
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w));
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw));
+ }
+ }
+ else if (flags & SLOW_SRC1)
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw));
+ else if (flags & SLOW_SRC2)
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw));
+
+ if (flags & SLOW_SRC1)
+ src1 = TMP_FREG1;
+ if (flags & SLOW_SRC2)
+ src2 = TMP_FREG2;
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD_F64:
+ FAIL_IF(push_inst(compiler, FADD_S | FMT(op) | FRD(dst_r) | FRS1(src1) | FRS2(src2)));
+ break;
+
+ case SLJIT_SUB_F64:
+ FAIL_IF(push_inst(compiler, FSUB_S | FMT(op) | FRD(dst_r) | FRS1(src1) | FRS2(src2)));
+ break;
+
+ case SLJIT_MUL_F64:
+ FAIL_IF(push_inst(compiler, FMUL_S | FMT(op) | FRD(dst_r) | FRS1(src1) | FRS2(src2)));
+ break;
+
+ case SLJIT_DIV_F64:
+ FAIL_IF(push_inst(compiler, FDIV_S | FMT(op) | FRD(dst_r) | FRS1(src1) | FRS2(src2)));
+ break;
+ }
+
+ if (dst_r == TMP_FREG2)
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG2, dst, dstw, 0, 0));
+
+ return SLJIT_SUCCESS;
+}
+
+#undef FLOAT_DATA
+#undef FMT
+
+/* --------------------------------------------------------------------- */
+/* Other instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ if (FAST_IS_REG(dst))
+ return push_inst(compiler, ADDI | RD(dst) | RS1(RETURN_ADDR_REG) | IMM_I(0));
+
+ /* Memory. */
+ return emit_op_mem(compiler, WORD_DATA, RETURN_ADDR_REG, dst, dstw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Conditional instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ struct sljit_label *label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_label(compiler));
+
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ return compiler->last_label;
+
+ label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
+ PTR_FAIL_IF(!label);
+ set_label(label, compiler);
+ return label;
+}
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+#define BRANCH_LENGTH ((sljit_ins)(3 * sizeof(sljit_ins)) << 7)
+#else
+#define BRANCH_LENGTH ((sljit_ins)(7 * sizeof(sljit_ins)) << 7)
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ struct sljit_jump *jump;
+ sljit_ins inst;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_jump(compiler, type));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ type &= 0xff;
+
+ switch (type) {
+ case SLJIT_EQUAL:
+ inst = BNE | RS1(EQUAL_FLAG) | RS2(TMP_ZERO) | BRANCH_LENGTH;
+ break;
+ case SLJIT_NOT_EQUAL:
+ inst = BEQ | RS1(EQUAL_FLAG) | RS2(TMP_ZERO) | BRANCH_LENGTH;
+ break;
+ case SLJIT_LESS:
+ case SLJIT_GREATER:
+ case SLJIT_SIG_LESS:
+ case SLJIT_SIG_GREATER:
+ case SLJIT_OVERFLOW:
+ case SLJIT_CARRY:
+ case SLJIT_F_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL: /* Not supported. */
+ case SLJIT_F_LESS:
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_ORDERED_GREATER:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ case SLJIT_ORDERED:
+ inst = BEQ | RS1(OTHER_FLAG) | RS2(TMP_ZERO) | BRANCH_LENGTH;
+ break;
+ case SLJIT_GREATER_EQUAL:
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_SIG_GREATER_EQUAL:
+ case SLJIT_SIG_LESS_EQUAL:
+ case SLJIT_NOT_OVERFLOW:
+ case SLJIT_NOT_CARRY:
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ case SLJIT_F_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_UNORDERED_OR_LESS:
+ case SLJIT_UNORDERED:
+ inst = BNE | RS1(OTHER_FLAG) | RS2(TMP_ZERO) | BRANCH_LENGTH;
+ break;
+ default:
+ /* Not conditional branch. */
+ inst = 0;
+ break;
+ }
+
+ if (inst != 0) {
+ PTR_FAIL_IF(push_inst(compiler, inst));
+ jump->flags |= IS_COND;
+ }
+
+ jump->addr = compiler->size;
+ inst = JALR | RS1(TMP_REG1) | IMM_I(0);
+
+ if (type >= SLJIT_FAST_CALL) {
+ jump->flags |= IS_CALL;
+ inst |= RD(RETURN_ADDR_REG);
+ }
+
+ PTR_FAIL_IF(push_inst(compiler, inst));
+
+ /* Maximum number of instructions required for generating a constant. */
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ compiler->size += 1;
+#else
+ compiler->size += 5;
+#endif
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ SLJIT_UNUSED_ARG(arg_types);
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ if (type & SLJIT_CALL_RETURN) {
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, 0));
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ struct sljit_jump *jump;
+ sljit_s32 flags;
+ sljit_ins inst;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_cmp(compiler, type, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ flags = WORD_DATA | LOAD_DATA;
+#else /* !SLJIT_CONFIG_RISCV_32 */
+ flags = ((type & SLJIT_32) ? INT_DATA : WORD_DATA) | LOAD_DATA;
+#endif /* SLJIT_CONFIG_RISCV_32 */
+
+ if (src1 & SLJIT_MEM) {
+ PTR_FAIL_IF(emit_op_mem2(compiler, flags, TMP_REG1, src1, src1w, src2, src2w));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ PTR_FAIL_IF(emit_op_mem2(compiler, flags, TMP_REG2, src2, src2w, 0, 0));
+ src2 = TMP_REG2;
+ }
+
+ if (src1 & SLJIT_IMM) {
+ if (src1w != 0) {
+ PTR_FAIL_IF(load_immediate(compiler, TMP_REG1, src1w, TMP_REG3));
+ src1 = TMP_REG1;
+ }
+ else
+ src1 = TMP_ZERO;
+ }
+
+ if (src2 & SLJIT_IMM) {
+ if (src2w != 0) {
+ PTR_FAIL_IF(load_immediate(compiler, TMP_REG2, src2w, TMP_REG3));
+ src2 = TMP_REG2;
+ }
+ else
+ src2 = TMP_ZERO;
+ }
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, (sljit_u32)((type & SLJIT_REWRITABLE_JUMP) | IS_COND));
+ type &= 0xff;
+
+ switch (type) {
+ case SLJIT_EQUAL:
+ inst = BNE | RS1(src1) | RS2(src2) | BRANCH_LENGTH;
+ break;
+ case SLJIT_NOT_EQUAL:
+ inst = BEQ | RS1(src1) | RS2(src2) | BRANCH_LENGTH;
+ break;
+ case SLJIT_LESS:
+ inst = BGEU | RS1(src1) | RS2(src2) | BRANCH_LENGTH;
+ break;
+ case SLJIT_GREATER_EQUAL:
+ inst = BLTU | RS1(src1) | RS2(src2) | BRANCH_LENGTH;
+ break;
+ case SLJIT_GREATER:
+ inst = BGEU | RS1(src2) | RS2(src1) | BRANCH_LENGTH;
+ break;
+ case SLJIT_LESS_EQUAL:
+ inst = BLTU | RS1(src2) | RS2(src1) | BRANCH_LENGTH;
+ break;
+ case SLJIT_SIG_LESS:
+ inst = BGE | RS1(src1) | RS2(src2) | BRANCH_LENGTH;
+ break;
+ case SLJIT_SIG_GREATER_EQUAL:
+ inst = BLT | RS1(src1) | RS2(src2) | BRANCH_LENGTH;
+ break;
+ case SLJIT_SIG_GREATER:
+ inst = BGE | RS1(src2) | RS2(src1) | BRANCH_LENGTH;
+ break;
+ case SLJIT_SIG_LESS_EQUAL:
+ inst = BLT | RS1(src2) | RS2(src1) | BRANCH_LENGTH;
+ break;
+ }
+
+ PTR_FAIL_IF(push_inst(compiler, inst));
+
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, JALR | RD(TMP_ZERO) | RS1(TMP_REG1) | IMM_I(0)));
+
+ /* Maximum number of instructions required for generating a constant. */
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ compiler->size += 1;
+#else
+ compiler->size += 5;
+#endif
+ return jump;
+}
+
+#undef BRANCH_LENGTH
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ struct sljit_jump *jump;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
+
+ if (!(src & SLJIT_IMM)) {
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw));
+ src = TMP_REG1;
+ }
+ return push_inst(compiler, JALR | RD((type >= SLJIT_FAST_CALL) ? RETURN_ADDR_REG : TMP_ZERO) | RS1(src) | IMM_I(0));
+ }
+
+ /* These jumps are converted to jump/call instructions when possible. */
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ FAIL_IF(!jump);
+ set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_CALL : 0));
+ jump->u.target = (sljit_uw)srcw;
+
+ jump->addr = compiler->size;
+ FAIL_IF(push_inst(compiler, JALR | RD((type >= SLJIT_FAST_CALL) ? RETURN_ADDR_REG : TMP_ZERO) | RS1(TMP_REG1) | IMM_I(0)));
+
+ /* Maximum number of instructions required for generating a constant. */
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ compiler->size += 1;
+#else
+ compiler->size += 5;
+#endif
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(arg_types);
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw));
+ src = TMP_REG1;
+ }
+
+ if (type & SLJIT_CALL_RETURN) {
+ if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(src) | IMM_I(0)));
+ src = TMP_REG1;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+ type = SLJIT_JUMP;
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ sljit_s32 src_r, dst_r, invert;
+ sljit_s32 saved_op = op;
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ sljit_s32 mem_type = WORD_DATA;
+#else
+ sljit_s32 mem_type = ((op & SLJIT_32) || op == SLJIT_MOV32) ? (INT_DATA | SIGNED_DATA) : WORD_DATA;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ op = GET_OPCODE(op);
+ dst_r = (op < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG2;
+
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+
+ if (op >= SLJIT_ADD && (dst & SLJIT_MEM))
+ FAIL_IF(emit_op_mem2(compiler, mem_type | LOAD_DATA, TMP_REG1, dst, dstw, dst, dstw));
+
+ if (type < SLJIT_F_EQUAL) {
+ src_r = OTHER_FLAG;
+ invert = type & 0x1;
+
+ switch (type) {
+ case SLJIT_EQUAL:
+ case SLJIT_NOT_EQUAL:
+ FAIL_IF(push_inst(compiler, SLTUI | RD(dst_r) | RS1(EQUAL_FLAG) | IMM_I(1)));
+ src_r = dst_r;
+ break;
+ case SLJIT_OVERFLOW:
+ case SLJIT_NOT_OVERFLOW:
+ if (compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)) {
+ src_r = OTHER_FLAG;
+ break;
+ }
+ FAIL_IF(push_inst(compiler, SLTUI | RD(dst_r) | RS1(OTHER_FLAG) | IMM_I(1)));
+ src_r = dst_r;
+ invert ^= 0x1;
+ break;
+ }
+ } else {
+ invert = 0;
+ src_r = OTHER_FLAG;
+
+ switch (type) {
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ case SLJIT_F_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_UNORDERED_OR_LESS:
+ case SLJIT_UNORDERED:
+ invert = 1;
+ break;
+ }
+ }
+
+ if (invert) {
+ FAIL_IF(push_inst(compiler, XORI | RD(dst_r) | RS1(src_r) | IMM_I(1)));
+ src_r = dst_r;
+ }
+
+ if (op < SLJIT_ADD) {
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, mem_type, src_r, dst, dstw);
+
+ if (src_r != dst_r)
+ return push_inst(compiler, ADDI | RD(dst_r) | RS1(src_r) | IMM_I(0));
+ return SLJIT_SUCCESS;
+ }
+
+ mem_type |= CUMULATIVE_OP | IMM_OP | ALT_KEEP_CACHE;
+
+ if (dst & SLJIT_MEM)
+ return emit_op(compiler, saved_op, mem_type, dst, dstw, TMP_REG1, 0, src_r, 0);
+ return emit_op(compiler, saved_op, mem_type, dst, dstw, dst, dstw, src_r, 0);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 flags;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (!(reg & REG_PAIR_MASK))
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ memw &= 0x3;
+
+ if (SLJIT_UNLIKELY(memw != 0)) {
+ FAIL_IF(push_inst(compiler, SLLI | RD(TMP_REG1) | RS1(OFFS_REG(mem)) | IMM_I(memw)));
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RS1(TMP_REG1) | RS2(mem & REG_MASK)));
+ } else
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RS1(mem & REG_MASK) | RS2(OFFS_REG(mem))));
+
+ mem = TMP_REG1;
+ memw = 0;
+ } else if (memw > SIMM_MAX - SSIZE_OF(sw) || memw < SIMM_MIN) {
+ if (((memw + 0x800) & 0xfff) <= 0xfff - SSIZE_OF(sw)) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, TO_ARGW_HI(memw), TMP_REG3));
+ memw &= 0xfff;
+ } else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, memw, TMP_REG3));
+ memw = 0;
+ }
+
+ if (mem & REG_MASK)
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RS1(TMP_REG1) | RS2(mem & REG_MASK)));
+
+ mem = TMP_REG1;
+ } else {
+ mem &= REG_MASK;
+ memw &= 0xfff;
+ }
+
+ SLJIT_ASSERT((memw >= 0 && memw <= SIMM_MAX - SSIZE_OF(sw)) || (memw > SIMM_MAX && memw <= 0xfff));
+
+ if (!(type & SLJIT_MEM_STORE) && mem == REG_PAIR_FIRST(reg)) {
+ FAIL_IF(push_mem_inst(compiler, WORD_DATA | LOAD_DATA, REG_PAIR_SECOND(reg), mem, (memw + SSIZE_OF(sw)) & 0xfff));
+ return push_mem_inst(compiler, WORD_DATA | LOAD_DATA, REG_PAIR_FIRST(reg), mem, memw);
+ }
+
+ flags = WORD_DATA | (!(type & SLJIT_MEM_STORE) ? LOAD_DATA : 0);
+
+ FAIL_IF(push_mem_inst(compiler, flags, REG_PAIR_FIRST(reg), mem, memw));
+ return push_mem_inst(compiler, flags, REG_PAIR_SECOND(reg), mem, (memw + SSIZE_OF(sw)) & 0xfff);
+}
+
+#undef TO_ARGW_HI
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ struct sljit_const *const_;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const));
+ PTR_FAIL_IF(!const_);
+ set_const(const_, compiler);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+ PTR_FAIL_IF(emit_const(compiler, dst_r, init_value, ADDI | RD(dst_r)));
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw));
+
+ return const_;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ struct sljit_put_label *put_label;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label));
+ PTR_FAIL_IF(!put_label);
+ set_put_label(put_label, compiler, 0);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+ PTR_FAIL_IF(push_inst(compiler, (sljit_ins)dst_r));
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ compiler->size += 1;
+#else
+ compiler->size += 5;
+#endif
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw));
+
+ return put_label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeS390X.c b/contrib/libs/pcre2/src/sljit/sljitNativeS390X.c
new file mode 100644
index 00000000000..8b51bad9bc3
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeS390X.c
@@ -0,0 +1,3747 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/auxv.h>
+
+#ifdef __ARCH__
+#define ENABLE_STATIC_FACILITY_DETECTION 1
+#else
+#define ENABLE_STATIC_FACILITY_DETECTION 0
+#endif
+#define ENABLE_DYNAMIC_FACILITY_DETECTION 1
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+ return "s390x" SLJIT_CPUINFO;
+}
+
+/* Instructions. */
+typedef sljit_uw sljit_ins;
+
+/* Instruction tags (most significant halfword). */
+static const sljit_ins sljit_ins_const = (sljit_ins)1 << 48;
+
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
+
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 4] = {
+ 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 0, 1
+};
+
+/* there are also a[2-15] available, but they are slower to access and
+ * their use is limited as mundaym explained:
+ * https://github.com/zherczeg/sljit/pull/91#discussion_r486895689
+ */
+
+/* General Purpose Registers [0-15]. */
+typedef sljit_uw sljit_gpr;
+
+/*
+ * WARNING
+ * the following code is non standard and should be improved for
+ * consistency, but doesn't use SLJIT_NUMBER_OF_REGISTERS based
+ * registers because r0 and r1 are the ABI recommended volatiles.
+ * there is a gpr() function that maps sljit to physical register numbers
+ * that should be used instead of the usual index into reg_map[] and
+ * will be retired ASAP (TODO: carenas)
+ */
+
+static const sljit_gpr r0 = 0; /* reg_map[SLJIT_NUMBER_OF_REGISTERS + 2]: 0 in address calculations; reserved */
+static const sljit_gpr r1 = 1; /* reg_map[SLJIT_NUMBER_OF_REGISTERS + 3]: reserved */
+static const sljit_gpr r2 = 2; /* reg_map[1]: 1st argument */
+static const sljit_gpr r3 = 3; /* reg_map[2]: 2nd argument */
+static const sljit_gpr r4 = 4; /* reg_map[3]: 3rd argument */
+static const sljit_gpr r5 = 5; /* reg_map[4]: 4th argument */
+static const sljit_gpr r6 = 6; /* reg_map[5]: 5th argument; 1st saved register */
+static const sljit_gpr r7 = 7; /* reg_map[6] */
+static const sljit_gpr r8 = 8; /* reg_map[7] */
+static const sljit_gpr r9 = 9; /* reg_map[8] */
+static const sljit_gpr r10 = 10; /* reg_map[9] */
+static const sljit_gpr r11 = 11; /* reg_map[10] */
+static const sljit_gpr r12 = 12; /* reg_map[11]: GOT */
+static const sljit_gpr r13 = 13; /* reg_map[12]: Literal Pool pointer */
+static const sljit_gpr r14 = 14; /* reg_map[0]: return address and flag register */
+static const sljit_gpr r15 = 15; /* reg_map[SLJIT_NUMBER_OF_REGISTERS + 1]: stack pointer */
+
+/* WARNING: r12 and r13 shouldn't be used as per ABI recommendation */
+/* TODO(carenas): r12 might conflict in PIC code, reserve? */
+/* TODO(carenas): r13 is usually pointed to "pool" per ABI, using a tmp
+ * like we do know might be faster though, reserve?
+ */
+
+/* TODO(carenas): should be named TMP_REG[1-2] for consistency */
+#define tmp0 r0
+#define tmp1 r1
+
+/* TODO(carenas): flags should move to a different register so that
+ * link register doesn't need to change
+ */
+
+/* When reg cannot be unused. */
+#define IS_GPR_REG(reg) ((reg > 0) && (reg) <= SLJIT_SP)
+
+/* Link register. */
+static const sljit_gpr link_r = 14; /* r14 */
+
+#define TMP_FREG1 (0)
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = {
+ 1, 0, 2, 4, 6, 3, 5, 7, 15, 14, 13, 12, 11, 10, 9, 8,
+};
+
+#define R0A(r) (r)
+#define R4A(r) ((r) << 4)
+#define R8A(r) ((r) << 8)
+#define R12A(r) ((r) << 12)
+#define R16A(r) ((r) << 16)
+#define R20A(r) ((r) << 20)
+#define R28A(r) ((r) << 28)
+#define R32A(r) ((r) << 32)
+#define R36A(r) ((r) << 36)
+
+#define R0(r) ((sljit_ins)reg_map[r])
+
+#define F0(r) ((sljit_ins)freg_map[r])
+#define F4(r) (R4A((sljit_ins)freg_map[r]))
+#define F20(r) (R20A((sljit_ins)freg_map[r]))
+#define F36(r) (R36A((sljit_ins)freg_map[r]))
+
+struct sljit_s390x_const {
+ struct sljit_const const_; /* must be first */
+ sljit_sw init_value; /* required to build literal pool */
+};
+
+/* Convert SLJIT register to hardware register. */
+static SLJIT_INLINE sljit_gpr gpr(sljit_s32 r)
+{
+ SLJIT_ASSERT(r >= 0 && r < (sljit_s32)(sizeof(reg_map) / sizeof(reg_map[0])));
+ return reg_map[r];
+}
+
+static SLJIT_INLINE sljit_gpr fgpr(sljit_s32 r)
+{
+ SLJIT_ASSERT(r >= 0 && r < (sljit_s32)(sizeof(freg_map) / sizeof(freg_map[0])));
+ return freg_map[r];
+}
+
+/* Size of instruction in bytes. Tags must already be cleared. */
+static SLJIT_INLINE sljit_uw sizeof_ins(sljit_ins ins)
+{
+ /* keep faulting instructions */
+ if (ins == 0)
+ return 2;
+
+ if ((ins & 0x00000000ffffL) == ins)
+ return 2;
+ if ((ins & 0x0000ffffffffL) == ins)
+ return 4;
+ if ((ins & 0xffffffffffffL) == ins)
+ return 6;
+
+ SLJIT_UNREACHABLE();
+ return (sljit_uw)-1;
+}
+
+static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_ins ins)
+{
+ sljit_ins *ibuf = (sljit_ins *)ensure_buf(compiler, sizeof(sljit_ins));
+ FAIL_IF(!ibuf);
+ *ibuf = ins;
+ compiler->size++;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 encode_inst(void **ptr, sljit_ins ins)
+{
+ sljit_u16 *ibuf = (sljit_u16 *)*ptr;
+ sljit_uw size = sizeof_ins(ins);
+
+ SLJIT_ASSERT((size & 6) == size);
+ switch (size) {
+ case 6:
+ *ibuf++ = (sljit_u16)(ins >> 32);
+ /* fallthrough */
+ case 4:
+ *ibuf++ = (sljit_u16)(ins >> 16);
+ /* fallthrough */
+ case 2:
+ *ibuf++ = (sljit_u16)(ins);
+ }
+ *ptr = (void*)ibuf;
+ return SLJIT_SUCCESS;
+}
+
+#define SLJIT_ADD_SUB_NO_COMPARE(status_flags_state) \
+ (((status_flags_state) & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)) \
+ && !((status_flags_state) & SLJIT_CURRENT_FLAGS_COMPARE))
+
+/* Map the given type to a 4-bit condition code mask. */
+static SLJIT_INLINE sljit_u8 get_cc(struct sljit_compiler *compiler, sljit_s32 type) {
+ const sljit_u8 cc0 = 1 << 3; /* equal {,to zero} */
+ const sljit_u8 cc1 = 1 << 2; /* less than {,zero} */
+ const sljit_u8 cc2 = 1 << 1; /* greater than {,zero} */
+ const sljit_u8 cc3 = 1 << 0; /* {overflow,NaN} */
+
+ switch (type) {
+ case SLJIT_EQUAL:
+ if (SLJIT_ADD_SUB_NO_COMPARE(compiler->status_flags_state)) {
+ sljit_s32 type = GET_FLAG_TYPE(compiler->status_flags_state);
+ if (type >= SLJIT_SIG_LESS && type <= SLJIT_SIG_LESS_EQUAL)
+ return cc0;
+ if (type == SLJIT_OVERFLOW)
+ return (cc0 | cc3);
+ return (cc0 | cc2);
+ }
+ /* fallthrough */
+
+ case SLJIT_F_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ return cc0;
+
+ case SLJIT_NOT_EQUAL:
+ if (SLJIT_ADD_SUB_NO_COMPARE(compiler->status_flags_state)) {
+ sljit_s32 type = GET_FLAG_TYPE(compiler->status_flags_state);
+ if (type >= SLJIT_SIG_LESS && type <= SLJIT_SIG_LESS_EQUAL)
+ return (cc1 | cc2 | cc3);
+ if (type == SLJIT_OVERFLOW)
+ return (cc1 | cc2);
+ return (cc1 | cc3);
+ }
+ /* fallthrough */
+
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ return (cc1 | cc2 | cc3);
+
+ case SLJIT_LESS:
+ return cc1;
+
+ case SLJIT_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ return (cc0 | cc2 | cc3);
+
+ case SLJIT_GREATER:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_COMPARE)
+ return cc2;
+ return cc3;
+
+ case SLJIT_LESS_EQUAL:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_COMPARE)
+ return (cc0 | cc1);
+ return (cc0 | cc1 | cc2);
+
+ case SLJIT_SIG_LESS:
+ case SLJIT_F_LESS:
+ case SLJIT_ORDERED_LESS:
+ return cc1;
+
+ case SLJIT_NOT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_SUB)
+ return (cc2 | cc3);
+ /* fallthrough */
+
+ case SLJIT_SIG_LESS_EQUAL:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ return (cc0 | cc1);
+
+ case SLJIT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_SUB)
+ return (cc0 | cc1);
+ /* fallthrough */
+
+ case SLJIT_SIG_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ /* Overflow is considered greater, see SLJIT_SUB. */
+ return cc2 | cc3;
+
+ case SLJIT_SIG_GREATER_EQUAL:
+ return (cc0 | cc2 | cc3);
+
+ case SLJIT_OVERFLOW:
+ if (compiler->status_flags_state & SLJIT_SET_Z)
+ return (cc2 | cc3);
+ /* fallthrough */
+
+ case SLJIT_UNORDERED:
+ return cc3;
+
+ case SLJIT_NOT_OVERFLOW:
+ if (compiler->status_flags_state & SLJIT_SET_Z)
+ return (cc0 | cc1);
+ /* fallthrough */
+
+ case SLJIT_ORDERED:
+ return (cc0 | cc1 | cc2);
+
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ return (cc1 | cc2);
+
+ case SLJIT_F_GREATER:
+ case SLJIT_ORDERED_GREATER:
+ return cc2;
+
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ return (cc0 | cc2);
+
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ return (cc0 | cc1 | cc3);
+
+ case SLJIT_UNORDERED_OR_EQUAL:
+ return (cc0 | cc3);
+
+ case SLJIT_UNORDERED_OR_LESS:
+ return (cc1 | cc3);
+ }
+
+ SLJIT_UNREACHABLE();
+ return (sljit_u8)-1;
+}
+
+/* Facility to bit index mappings.
+ Note: some facilities share the same bit index. */
+typedef sljit_uw facility_bit;
+#define STORE_FACILITY_LIST_EXTENDED_FACILITY 7
+#define FAST_LONG_DISPLACEMENT_FACILITY 19
+#define EXTENDED_IMMEDIATE_FACILITY 21
+#define GENERAL_INSTRUCTION_EXTENSION_FACILITY 34
+#define DISTINCT_OPERAND_FACILITY 45
+#define HIGH_WORD_FACILITY 45
+#define POPULATION_COUNT_FACILITY 45
+#define LOAD_STORE_ON_CONDITION_1_FACILITY 45
+#define MISCELLANEOUS_INSTRUCTION_EXTENSIONS_1_FACILITY 49
+#define LOAD_STORE_ON_CONDITION_2_FACILITY 53
+#define MISCELLANEOUS_INSTRUCTION_EXTENSIONS_2_FACILITY 58
+#define VECTOR_FACILITY 129
+#define VECTOR_ENHANCEMENTS_1_FACILITY 135
+
+/* Report whether a facility is known to be present due to the compiler
+ settings. This function should always be compiled to a constant
+ value given a constant argument. */
+static SLJIT_INLINE int have_facility_static(facility_bit x)
+{
+#if ENABLE_STATIC_FACILITY_DETECTION
+ switch (x) {
+ case FAST_LONG_DISPLACEMENT_FACILITY:
+ return (__ARCH__ >= 6 /* z990 */);
+ case EXTENDED_IMMEDIATE_FACILITY:
+ case STORE_FACILITY_LIST_EXTENDED_FACILITY:
+ return (__ARCH__ >= 7 /* z9-109 */);
+ case GENERAL_INSTRUCTION_EXTENSION_FACILITY:
+ return (__ARCH__ >= 8 /* z10 */);
+ case DISTINCT_OPERAND_FACILITY:
+ return (__ARCH__ >= 9 /* z196 */);
+ case MISCELLANEOUS_INSTRUCTION_EXTENSIONS_1_FACILITY:
+ return (__ARCH__ >= 10 /* zEC12 */);
+ case LOAD_STORE_ON_CONDITION_2_FACILITY:
+ case VECTOR_FACILITY:
+ return (__ARCH__ >= 11 /* z13 */);
+ case MISCELLANEOUS_INSTRUCTION_EXTENSIONS_2_FACILITY:
+ case VECTOR_ENHANCEMENTS_1_FACILITY:
+ return (__ARCH__ >= 12 /* z14 */);
+ default:
+ SLJIT_UNREACHABLE();
+ }
+#endif
+ return 0;
+}
+
+static SLJIT_INLINE unsigned long get_hwcap()
+{
+ static unsigned long hwcap = 0;
+ if (SLJIT_UNLIKELY(!hwcap)) {
+ hwcap = getauxval(AT_HWCAP);
+ SLJIT_ASSERT(hwcap != 0);
+ }
+ return hwcap;
+}
+
+static SLJIT_INLINE int have_stfle()
+{
+ if (have_facility_static(STORE_FACILITY_LIST_EXTENDED_FACILITY))
+ return 1;
+
+ return (get_hwcap() & HWCAP_S390_STFLE);
+}
+
+/* Report whether the given facility is available. This function always
+ performs a runtime check. */
+static int have_facility_dynamic(facility_bit x)
+{
+#if ENABLE_DYNAMIC_FACILITY_DETECTION
+ static struct {
+ sljit_uw bits[4];
+ } cpu_features;
+ size_t size = sizeof(cpu_features);
+ const sljit_uw word_index = x >> 6;
+ const sljit_uw bit_index = ((1UL << 63) >> (x & 63));
+
+ SLJIT_ASSERT(x < size * 8);
+ if (SLJIT_UNLIKELY(!have_stfle()))
+ return 0;
+
+ if (SLJIT_UNLIKELY(cpu_features.bits[0] == 0)) {
+ __asm__ __volatile__ (
+ "lgr %%r0, %0;"
+ "stfle 0(%1);"
+ /* outputs */:
+ /* inputs */: "d" ((size / 8) - 1), "a" (&cpu_features)
+ /* clobbers */: "r0", "cc", "memory"
+ );
+ SLJIT_ASSERT(cpu_features.bits[0] != 0);
+ }
+ return (cpu_features.bits[word_index] & bit_index) != 0;
+#else
+ return 0;
+#endif
+}
+
+#define HAVE_FACILITY(name, bit) \
+static SLJIT_INLINE int name() \
+{ \
+ static int have = -1; \
+ /* Static check first. May allow the function to be optimized away. */ \
+ if (have_facility_static(bit)) \
+ have = 1; \
+ else if (SLJIT_UNLIKELY(have < 0)) \
+ have = have_facility_dynamic(bit) ? 1 : 0; \
+\
+ return have; \
+}
+
+HAVE_FACILITY(have_eimm, EXTENDED_IMMEDIATE_FACILITY)
+HAVE_FACILITY(have_ldisp, FAST_LONG_DISPLACEMENT_FACILITY)
+HAVE_FACILITY(have_genext, GENERAL_INSTRUCTION_EXTENSION_FACILITY)
+HAVE_FACILITY(have_lscond1, LOAD_STORE_ON_CONDITION_1_FACILITY)
+HAVE_FACILITY(have_lscond2, LOAD_STORE_ON_CONDITION_2_FACILITY)
+HAVE_FACILITY(have_misc2, MISCELLANEOUS_INSTRUCTION_EXTENSIONS_2_FACILITY)
+#undef HAVE_FACILITY
+
+#define is_u12(d) (0 <= (d) && (d) <= 0x00000fffL)
+#define is_u32(d) (0 <= (d) && (d) <= 0xffffffffL)
+
+#define CHECK_SIGNED(v, bitlen) \
+ ((v) >= -(1 << ((bitlen) - 1)) && (v) < (1 << ((bitlen) - 1)))
+
+#define is_s8(d) CHECK_SIGNED((d), 8)
+#define is_s16(d) CHECK_SIGNED((d), 16)
+#define is_s20(d) CHECK_SIGNED((d), 20)
+#define is_s32(d) ((d) == (sljit_s32)(d))
+
+static SLJIT_INLINE sljit_ins disp_s20(sljit_s32 d)
+{
+ SLJIT_ASSERT(is_s20(d));
+
+ sljit_uw dh = (d >> 12) & 0xff;
+ sljit_uw dl = (d << 8) & 0xfff00;
+ return (dh | dl) << 8;
+}
+
+/* TODO(carenas): variadic macro is not strictly needed */
+#define SLJIT_S390X_INSTRUCTION(op, ...) \
+static SLJIT_INLINE sljit_ins op(__VA_ARGS__)
+
+/* RR form instructions. */
+#define SLJIT_S390X_RR(name, pattern) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr dst, sljit_gpr src) \
+{ \
+ return (pattern) | ((dst & 0xf) << 4) | (src & 0xf); \
+}
+
+/* AND */
+SLJIT_S390X_RR(nr, 0x1400)
+
+/* BRANCH AND SAVE */
+SLJIT_S390X_RR(basr, 0x0d00)
+
+/* BRANCH ON CONDITION */
+SLJIT_S390X_RR(bcr, 0x0700) /* TODO(mundaym): type for mask? */
+
+/* DIVIDE */
+SLJIT_S390X_RR(dr, 0x1d00)
+
+/* EXCLUSIVE OR */
+SLJIT_S390X_RR(xr, 0x1700)
+
+/* LOAD */
+SLJIT_S390X_RR(lr, 0x1800)
+
+/* LOAD COMPLEMENT */
+SLJIT_S390X_RR(lcr, 0x1300)
+
+/* OR */
+SLJIT_S390X_RR(or, 0x1600)
+
+#undef SLJIT_S390X_RR
+
+/* RRE form instructions */
+#define SLJIT_S390X_RRE(name, pattern) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr dst, sljit_gpr src) \
+{ \
+ return (pattern) | R4A(dst) | R0A(src); \
+}
+
+/* AND */
+SLJIT_S390X_RRE(ngr, 0xb9800000)
+
+/* DIVIDE LOGICAL */
+SLJIT_S390X_RRE(dlr, 0xb9970000)
+SLJIT_S390X_RRE(dlgr, 0xb9870000)
+
+/* DIVIDE SINGLE */
+SLJIT_S390X_RRE(dsgr, 0xb90d0000)
+
+/* EXCLUSIVE OR */
+SLJIT_S390X_RRE(xgr, 0xb9820000)
+
+/* LOAD */
+SLJIT_S390X_RRE(lgr, 0xb9040000)
+SLJIT_S390X_RRE(lgfr, 0xb9140000)
+
+/* LOAD BYTE */
+SLJIT_S390X_RRE(lbr, 0xb9260000)
+SLJIT_S390X_RRE(lgbr, 0xb9060000)
+
+/* LOAD COMPLEMENT */
+SLJIT_S390X_RRE(lcgr, 0xb9030000)
+
+/* LOAD HALFWORD */
+SLJIT_S390X_RRE(lhr, 0xb9270000)
+SLJIT_S390X_RRE(lghr, 0xb9070000)
+
+/* LOAD LOGICAL */
+SLJIT_S390X_RRE(llgfr, 0xb9160000)
+
+/* LOAD LOGICAL CHARACTER */
+SLJIT_S390X_RRE(llcr, 0xb9940000)
+SLJIT_S390X_RRE(llgcr, 0xb9840000)
+
+/* LOAD LOGICAL HALFWORD */
+SLJIT_S390X_RRE(llhr, 0xb9950000)
+SLJIT_S390X_RRE(llghr, 0xb9850000)
+
+/* MULTIPLY LOGICAL */
+SLJIT_S390X_RRE(mlgr, 0xb9860000)
+
+/* MULTIPLY SINGLE */
+SLJIT_S390X_RRE(msgfr, 0xb91c0000)
+
+/* OR */
+SLJIT_S390X_RRE(ogr, 0xb9810000)
+
+/* SUBTRACT */
+SLJIT_S390X_RRE(sgr, 0xb9090000)
+
+#undef SLJIT_S390X_RRE
+
+/* RI-a form instructions */
+#define SLJIT_S390X_RIA(name, pattern, imm_type) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr reg, imm_type imm) \
+{ \
+ return (pattern) | R20A(reg) | (imm & 0xffff); \
+}
+
+/* ADD HALFWORD IMMEDIATE */
+SLJIT_S390X_RIA(aghi, 0xa70b0000, sljit_s16)
+
+/* LOAD HALFWORD IMMEDIATE */
+SLJIT_S390X_RIA(lhi, 0xa7080000, sljit_s16)
+SLJIT_S390X_RIA(lghi, 0xa7090000, sljit_s16)
+
+/* LOAD LOGICAL IMMEDIATE */
+SLJIT_S390X_RIA(llihh, 0xa50c0000, sljit_u16)
+SLJIT_S390X_RIA(llihl, 0xa50d0000, sljit_u16)
+SLJIT_S390X_RIA(llilh, 0xa50e0000, sljit_u16)
+SLJIT_S390X_RIA(llill, 0xa50f0000, sljit_u16)
+
+/* MULTIPLY HALFWORD IMMEDIATE */
+SLJIT_S390X_RIA(mhi, 0xa70c0000, sljit_s16)
+SLJIT_S390X_RIA(mghi, 0xa70d0000, sljit_s16)
+
+/* OR IMMEDIATE */
+SLJIT_S390X_RIA(oilh, 0xa50a0000, sljit_u16)
+
+#undef SLJIT_S390X_RIA
+
+/* RIL-a form instructions (requires extended immediate facility) */
+#define SLJIT_S390X_RILA(name, pattern, imm_type) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr reg, imm_type imm) \
+{ \
+ SLJIT_ASSERT(have_eimm()); \
+ return (pattern) | R36A(reg) | ((sljit_ins)imm & 0xffffffffu); \
+}
+
+/* ADD IMMEDIATE */
+SLJIT_S390X_RILA(agfi, 0xc20800000000, sljit_s32)
+
+/* ADD IMMEDIATE HIGH */
+SLJIT_S390X_RILA(aih, 0xcc0800000000, sljit_s32) /* TODO(mundaym): high-word facility? */
+
+/* AND IMMEDIATE */
+SLJIT_S390X_RILA(nihf, 0xc00a00000000, sljit_u32)
+
+/* EXCLUSIVE OR IMMEDIATE */
+SLJIT_S390X_RILA(xilf, 0xc00700000000, sljit_u32)
+
+/* INSERT IMMEDIATE */
+SLJIT_S390X_RILA(iihf, 0xc00800000000, sljit_u32)
+SLJIT_S390X_RILA(iilf, 0xc00900000000, sljit_u32)
+
+/* LOAD IMMEDIATE */
+SLJIT_S390X_RILA(lgfi, 0xc00100000000, sljit_s32)
+
+/* LOAD LOGICAL IMMEDIATE */
+SLJIT_S390X_RILA(llihf, 0xc00e00000000, sljit_u32)
+SLJIT_S390X_RILA(llilf, 0xc00f00000000, sljit_u32)
+
+/* SUBTRACT LOGICAL IMMEDIATE */
+SLJIT_S390X_RILA(slfi, 0xc20500000000, sljit_u32)
+
+#undef SLJIT_S390X_RILA
+
+/* RX-a form instructions */
+#define SLJIT_S390X_RXA(name, pattern) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr r, sljit_s32 d, sljit_gpr x, sljit_gpr b) \
+{ \
+ SLJIT_ASSERT((d & 0xfff) == d); \
+\
+ return (pattern) | R20A(r) | R16A(x) | R12A(b) | (sljit_ins)(d & 0xfff); \
+}
+
+/* LOAD */
+SLJIT_S390X_RXA(l, 0x58000000)
+
+/* LOAD ADDRESS */
+SLJIT_S390X_RXA(la, 0x41000000)
+
+/* LOAD HALFWORD */
+SLJIT_S390X_RXA(lh, 0x48000000)
+
+/* MULTIPLY SINGLE */
+SLJIT_S390X_RXA(ms, 0x71000000)
+
+/* STORE */
+SLJIT_S390X_RXA(st, 0x50000000)
+
+/* STORE CHARACTER */
+SLJIT_S390X_RXA(stc, 0x42000000)
+
+/* STORE HALFWORD */
+SLJIT_S390X_RXA(sth, 0x40000000)
+
+#undef SLJIT_S390X_RXA
+
+/* RXY-a instructions */
+#define SLJIT_S390X_RXYA(name, pattern, cond) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr r, sljit_s32 d, sljit_gpr x, sljit_gpr b) \
+{ \
+ SLJIT_ASSERT(cond); \
+\
+ return (pattern) | R36A(r) | R32A(x) | R28A(b) | disp_s20(d); \
+}
+
+/* LOAD */
+SLJIT_S390X_RXYA(ly, 0xe30000000058, have_ldisp())
+SLJIT_S390X_RXYA(lg, 0xe30000000004, 1)
+SLJIT_S390X_RXYA(lgf, 0xe30000000014, 1)
+
+/* LOAD BYTE */
+SLJIT_S390X_RXYA(lb, 0xe30000000076, have_ldisp())
+SLJIT_S390X_RXYA(lgb, 0xe30000000077, have_ldisp())
+
+/* LOAD HALFWORD */
+SLJIT_S390X_RXYA(lhy, 0xe30000000078, have_ldisp())
+SLJIT_S390X_RXYA(lgh, 0xe30000000015, 1)
+
+/* LOAD LOGICAL */
+SLJIT_S390X_RXYA(llgf, 0xe30000000016, 1)
+
+/* LOAD LOGICAL CHARACTER */
+SLJIT_S390X_RXYA(llc, 0xe30000000094, have_eimm())
+SLJIT_S390X_RXYA(llgc, 0xe30000000090, 1)
+
+/* LOAD LOGICAL HALFWORD */
+SLJIT_S390X_RXYA(llh, 0xe30000000095, have_eimm())
+SLJIT_S390X_RXYA(llgh, 0xe30000000091, 1)
+
+/* MULTIPLY SINGLE */
+SLJIT_S390X_RXYA(msy, 0xe30000000051, have_ldisp())
+SLJIT_S390X_RXYA(msg, 0xe3000000000c, 1)
+
+/* STORE */
+SLJIT_S390X_RXYA(sty, 0xe30000000050, have_ldisp())
+SLJIT_S390X_RXYA(stg, 0xe30000000024, 1)
+
+/* STORE CHARACTER */
+SLJIT_S390X_RXYA(stcy, 0xe30000000072, have_ldisp())
+
+/* STORE HALFWORD */
+SLJIT_S390X_RXYA(sthy, 0xe30000000070, have_ldisp())
+
+#undef SLJIT_S390X_RXYA
+
+/* RSY-a instructions */
+#define SLJIT_S390X_RSYA(name, pattern, cond) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr dst, sljit_gpr src, sljit_s32 d, sljit_gpr b) \
+{ \
+ SLJIT_ASSERT(cond); \
+\
+ return (pattern) | R36A(dst) | R32A(src) | R28A(b) | disp_s20(d); \
+}
+
+/* LOAD MULTIPLE */
+SLJIT_S390X_RSYA(lmg, 0xeb0000000004, 1)
+
+/* SHIFT LEFT LOGICAL */
+SLJIT_S390X_RSYA(sllg, 0xeb000000000d, 1)
+
+/* SHIFT RIGHT SINGLE */
+SLJIT_S390X_RSYA(srag, 0xeb000000000a, 1)
+
+/* STORE MULTIPLE */
+SLJIT_S390X_RSYA(stmg, 0xeb0000000024, 1)
+
+#undef SLJIT_S390X_RSYA
+
+/* RIE-f instructions (require general-instructions-extension facility) */
+#define SLJIT_S390X_RIEF(name, pattern) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr dst, sljit_gpr src, sljit_u8 start, sljit_u8 end, sljit_u8 rot) \
+{ \
+ sljit_ins i3, i4, i5; \
+\
+ SLJIT_ASSERT(have_genext()); \
+ i3 = (sljit_ins)start << 24; \
+ i4 = (sljit_ins)end << 16; \
+ i5 = (sljit_ins)rot << 8; \
+\
+ return (pattern) | R36A(dst & 0xf) | R32A(src & 0xf) | i3 | i4 | i5; \
+}
+
+/* ROTATE THEN AND SELECTED BITS */
+/* SLJIT_S390X_RIEF(rnsbg, 0xec0000000054) */
+
+/* ROTATE THEN EXCLUSIVE OR SELECTED BITS */
+/* SLJIT_S390X_RIEF(rxsbg, 0xec0000000057) */
+
+/* ROTATE THEN OR SELECTED BITS */
+SLJIT_S390X_RIEF(rosbg, 0xec0000000056)
+
+/* ROTATE THEN INSERT SELECTED BITS */
+/* SLJIT_S390X_RIEF(risbg, 0xec0000000055) */
+/* SLJIT_S390X_RIEF(risbgn, 0xec0000000059) */
+
+/* ROTATE THEN INSERT SELECTED BITS HIGH */
+SLJIT_S390X_RIEF(risbhg, 0xec000000005d)
+
+/* ROTATE THEN INSERT SELECTED BITS LOW */
+/* SLJIT_S390X_RIEF(risblg, 0xec0000000051) */
+
+#undef SLJIT_S390X_RIEF
+
+/* RRF-c instructions (require load/store-on-condition 1 facility) */
+#define SLJIT_S390X_RRFC(name, pattern) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr dst, sljit_gpr src, sljit_uw mask) \
+{ \
+ sljit_ins m3; \
+\
+ SLJIT_ASSERT(have_lscond1()); \
+ m3 = (sljit_ins)(mask & 0xf) << 12; \
+\
+ return (pattern) | m3 | R4A(dst) | R0A(src); \
+}
+
+/* LOAD HALFWORD IMMEDIATE ON CONDITION */
+SLJIT_S390X_RRFC(locr, 0xb9f20000)
+SLJIT_S390X_RRFC(locgr, 0xb9e20000)
+
+#undef SLJIT_S390X_RRFC
+
+/* RIE-g instructions (require load/store-on-condition 2 facility) */
+#define SLJIT_S390X_RIEG(name, pattern) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr reg, sljit_sw imm, sljit_uw mask) \
+{ \
+ sljit_ins m3, i2; \
+\
+ SLJIT_ASSERT(have_lscond2()); \
+ m3 = (sljit_ins)(mask & 0xf) << 32; \
+ i2 = (sljit_ins)(imm & 0xffffL) << 16; \
+\
+ return (pattern) | R36A(reg) | m3 | i2; \
+}
+
+/* LOAD HALFWORD IMMEDIATE ON CONDITION */
+SLJIT_S390X_RIEG(lochi, 0xec0000000042)
+SLJIT_S390X_RIEG(locghi, 0xec0000000046)
+
+#undef SLJIT_S390X_RIEG
+
+#define SLJIT_S390X_RILB(name, pattern, cond) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr reg, sljit_sw ri) \
+{ \
+ SLJIT_ASSERT(cond); \
+\
+ return (pattern) | R36A(reg) | (sljit_ins)(ri & 0xffffffff); \
+}
+
+/* BRANCH RELATIVE AND SAVE LONG */
+SLJIT_S390X_RILB(brasl, 0xc00500000000, 1)
+
+/* LOAD ADDRESS RELATIVE LONG */
+SLJIT_S390X_RILB(larl, 0xc00000000000, 1)
+
+/* LOAD RELATIVE LONG */
+SLJIT_S390X_RILB(lgrl, 0xc40800000000, have_genext())
+
+#undef SLJIT_S390X_RILB
+
+SLJIT_S390X_INSTRUCTION(br, sljit_gpr target)
+{
+ return 0x07f0 | target;
+}
+
+SLJIT_S390X_INSTRUCTION(brc, sljit_uw mask, sljit_sw target)
+{
+ sljit_ins m1 = (sljit_ins)(mask & 0xf) << 20;
+ sljit_ins ri2 = (sljit_ins)target & 0xffff;
+ return 0xa7040000L | m1 | ri2;
+}
+
+SLJIT_S390X_INSTRUCTION(brcl, sljit_uw mask, sljit_sw target)
+{
+ sljit_ins m1 = (sljit_ins)(mask & 0xf) << 36;
+ sljit_ins ri2 = (sljit_ins)target & 0xffffffff;
+ return 0xc00400000000L | m1 | ri2;
+}
+
+SLJIT_S390X_INSTRUCTION(flogr, sljit_gpr dst, sljit_gpr src)
+{
+ SLJIT_ASSERT(have_eimm());
+ return 0xb9830000 | R8A(dst) | R0A(src);
+}
+
+/* INSERT PROGRAM MASK */
+SLJIT_S390X_INSTRUCTION(ipm, sljit_gpr dst)
+{
+ return 0xb2220000 | R4A(dst);
+}
+
+/* SET PROGRAM MASK */
+SLJIT_S390X_INSTRUCTION(spm, sljit_gpr dst)
+{
+ return 0x0400 | R4A(dst);
+}
+
+/* ROTATE THEN INSERT SELECTED BITS HIGH (ZERO) */
+SLJIT_S390X_INSTRUCTION(risbhgz, sljit_gpr dst, sljit_gpr src, sljit_u8 start, sljit_u8 end, sljit_u8 rot)
+{
+ return risbhg(dst, src, start, 0x8 | end, rot);
+}
+
+#undef SLJIT_S390X_INSTRUCTION
+
+static sljit_s32 update_zero_overflow(struct sljit_compiler *compiler, sljit_s32 op, sljit_gpr dst_r)
+{
+ /* Condition codes: bits 18 and 19.
+ Transformation:
+ 0 (zero and no overflow) : unchanged
+ 1 (non-zero and no overflow) : unchanged
+ 2 (zero and overflow) : decreased by 1
+ 3 (non-zero and overflow) : decreased by 1 if non-zero */
+ FAIL_IF(push_inst(compiler, brc(0xc, 2 + 2 + ((op & SLJIT_32) ? 1 : 2) + 2 + 3 + 1)));
+ FAIL_IF(push_inst(compiler, ipm(tmp1)));
+ FAIL_IF(push_inst(compiler, (op & SLJIT_32) ? or(dst_r, dst_r) : ogr(dst_r, dst_r)));
+ FAIL_IF(push_inst(compiler, brc(0x8, 2 + 3)));
+ FAIL_IF(push_inst(compiler, slfi(tmp1, 0x10000000)));
+ FAIL_IF(push_inst(compiler, spm(tmp1)));
+ return SLJIT_SUCCESS;
+}
+
+/* load 64-bit immediate into register without clobbering flags */
+static sljit_s32 push_load_imm_inst(struct sljit_compiler *compiler, sljit_gpr target, sljit_sw v)
+{
+ /* 4 byte instructions */
+ if (is_s16(v))
+ return push_inst(compiler, lghi(target, (sljit_s16)v));
+
+ if (((sljit_uw)v & ~(sljit_uw)0x000000000000ffff) == 0)
+ return push_inst(compiler, llill(target, (sljit_u16)v));
+
+ if (((sljit_uw)v & ~(sljit_uw)0x00000000ffff0000) == 0)
+ return push_inst(compiler, llilh(target, (sljit_u16)(v >> 16)));
+
+ if (((sljit_uw)v & ~(sljit_uw)0x0000ffff00000000) == 0)
+ return push_inst(compiler, llihl(target, (sljit_u16)(v >> 32)));
+
+ if (((sljit_uw)v & ~(sljit_uw)0xffff000000000000) == 0)
+ return push_inst(compiler, llihh(target, (sljit_u16)(v >> 48)));
+
+ /* 6 byte instructions (requires extended immediate facility) */
+ if (have_eimm()) {
+ if (is_s32(v))
+ return push_inst(compiler, lgfi(target, (sljit_s32)v));
+
+ if (((sljit_uw)v >> 32) == 0)
+ return push_inst(compiler, llilf(target, (sljit_u32)v));
+
+ if (((sljit_uw)v << 32) == 0)
+ return push_inst(compiler, llihf(target, (sljit_u32)((sljit_uw)v >> 32)));
+
+ FAIL_IF(push_inst(compiler, llilf(target, (sljit_u32)v)));
+ return push_inst(compiler, iihf(target, (sljit_u32)(v >> 32)));
+ }
+
+ /* TODO(mundaym): instruction sequences that don't use extended immediates */
+ abort();
+}
+
+struct addr {
+ sljit_gpr base;
+ sljit_gpr index;
+ sljit_s32 offset;
+};
+
+/* transform memory operand into D(X,B) form with a signed 20-bit offset */
+static sljit_s32 make_addr_bxy(struct sljit_compiler *compiler,
+ struct addr *addr, sljit_s32 mem, sljit_sw off,
+ sljit_gpr tmp /* clobbered, must not be r0 */)
+{
+ sljit_gpr base = r0;
+ sljit_gpr index = r0;
+
+ SLJIT_ASSERT(tmp != r0);
+ if (mem & REG_MASK)
+ base = gpr(mem & REG_MASK);
+
+ if (mem & OFFS_REG_MASK) {
+ index = gpr(OFFS_REG(mem));
+ if (off != 0) {
+ /* shift and put the result into tmp */
+ SLJIT_ASSERT(0 <= off && off < 64);
+ FAIL_IF(push_inst(compiler, sllg(tmp, index, (sljit_s32)off, 0)));
+ index = tmp;
+ off = 0; /* clear offset */
+ }
+ }
+ else if (!is_s20(off)) {
+ FAIL_IF(push_load_imm_inst(compiler, tmp, off));
+ index = tmp;
+ off = 0; /* clear offset */
+ }
+ addr->base = base;
+ addr->index = index;
+ addr->offset = (sljit_s32)off;
+ return SLJIT_SUCCESS;
+}
+
+/* transform memory operand into D(X,B) form with an unsigned 12-bit offset */
+static sljit_s32 make_addr_bx(struct sljit_compiler *compiler,
+ struct addr *addr, sljit_s32 mem, sljit_sw off,
+ sljit_gpr tmp /* clobbered, must not be r0 */)
+{
+ sljit_gpr base = r0;
+ sljit_gpr index = r0;
+
+ SLJIT_ASSERT(tmp != r0);
+ if (mem & REG_MASK)
+ base = gpr(mem & REG_MASK);
+
+ if (mem & OFFS_REG_MASK) {
+ index = gpr(OFFS_REG(mem));
+ if (off != 0) {
+ /* shift and put the result into tmp */
+ SLJIT_ASSERT(0 <= off && off < 64);
+ FAIL_IF(push_inst(compiler, sllg(tmp, index, (sljit_s32)off, 0)));
+ index = tmp;
+ off = 0; /* clear offset */
+ }
+ }
+ else if (!is_u12(off)) {
+ FAIL_IF(push_load_imm_inst(compiler, tmp, off));
+ index = tmp;
+ off = 0; /* clear offset */
+ }
+ addr->base = base;
+ addr->index = index;
+ addr->offset = (sljit_s32)off;
+ return SLJIT_SUCCESS;
+}
+
+#define EVAL(op, r, addr) op(r, addr.offset, addr.index, addr.base)
+#define WHEN(cond, r, i1, i2, addr) \
+ (cond) ? EVAL(i1, r, addr) : EVAL(i2, r, addr)
+
+/* May clobber tmp1. */
+static sljit_s32 load_word(struct sljit_compiler *compiler, sljit_gpr dst_r,
+ sljit_s32 src, sljit_sw srcw,
+ sljit_s32 is_32bit)
+{
+ struct addr addr;
+ sljit_ins ins;
+
+ SLJIT_ASSERT(src & SLJIT_MEM);
+
+ if (is_32bit && ((src & OFFS_REG_MASK) || is_u12(srcw) || !is_s20(srcw))) {
+ FAIL_IF(make_addr_bx(compiler, &addr, src, srcw, tmp1));
+ return push_inst(compiler, 0x58000000 /* l */ | R20A(dst_r) | R16A(addr.index) | R12A(addr.base) | (sljit_ins)addr.offset);
+ }
+
+ FAIL_IF(make_addr_bxy(compiler, &addr, src, srcw, tmp1));
+
+ ins = is_32bit ? 0xe30000000058 /* ly */ : 0xe30000000004 /* lg */;
+ return push_inst(compiler, ins | R36A(dst_r) | R32A(addr.index) | R28A(addr.base) | disp_s20(addr.offset));
+}
+
+/* May clobber tmp1. */
+static sljit_s32 load_unsigned_word(struct sljit_compiler *compiler, sljit_gpr dst_r,
+ sljit_s32 src, sljit_sw srcw,
+ sljit_s32 is_32bit)
+{
+ struct addr addr;
+ sljit_ins ins;
+
+ SLJIT_ASSERT(src & SLJIT_MEM);
+
+ FAIL_IF(make_addr_bxy(compiler, &addr, src, srcw, tmp1));
+
+ ins = is_32bit ? 0xe30000000016 /* llgf */ : 0xe30000000004 /* lg */;
+ return push_inst(compiler, ins | R36A(dst_r) | R32A(addr.index) | R28A(addr.base) | disp_s20(addr.offset));
+}
+
+/* May clobber tmp1. */
+static sljit_s32 store_word(struct sljit_compiler *compiler, sljit_gpr src_r,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 is_32bit)
+{
+ struct addr addr;
+ sljit_ins ins;
+
+ SLJIT_ASSERT(dst & SLJIT_MEM);
+
+ if (is_32bit && ((dst & OFFS_REG_MASK) || is_u12(dstw) || !is_s20(dstw))) {
+ FAIL_IF(make_addr_bx(compiler, &addr, dst, dstw, tmp1));
+ return push_inst(compiler, 0x50000000 /* st */ | R20A(src_r) | R16A(addr.index) | R12A(addr.base) | (sljit_ins)addr.offset);
+ }
+
+ FAIL_IF(make_addr_bxy(compiler, &addr, dst, dstw, tmp1));
+
+ ins = is_32bit ? 0xe30000000050 /* sty */ : 0xe30000000024 /* stg */;
+ return push_inst(compiler, ins | R36A(src_r) | R32A(addr.index) | R28A(addr.base) | disp_s20(addr.offset));
+}
+
+#undef WHEN
+
+static sljit_s32 emit_move(struct sljit_compiler *compiler,
+ sljit_gpr dst_r,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_ASSERT(!IS_GPR_REG(src) || dst_r != gpr(src & REG_MASK));
+
+ if (src & SLJIT_IMM)
+ return push_load_imm_inst(compiler, dst_r, srcw);
+
+ if (src & SLJIT_MEM)
+ return load_word(compiler, dst_r, src, srcw, (compiler->mode & SLJIT_32) != 0);
+
+ sljit_gpr src_r = gpr(src & REG_MASK);
+ return push_inst(compiler, (compiler->mode & SLJIT_32) ? lr(dst_r, src_r) : lgr(dst_r, src_r));
+}
+
+static sljit_s32 emit_rr(struct sljit_compiler *compiler, sljit_ins ins,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_gpr dst_r = tmp0;
+ sljit_gpr src_r = tmp1;
+ sljit_s32 needs_move = 1;
+
+ if (FAST_IS_REG(dst)) {
+ dst_r = gpr(dst);
+
+ if (dst == src1)
+ needs_move = 0;
+ else if (dst == src2) {
+ dst_r = tmp0;
+ needs_move = 2;
+ }
+ }
+
+ if (needs_move)
+ FAIL_IF(emit_move(compiler, dst_r, src1, src1w));
+
+ if (FAST_IS_REG(src2))
+ src_r = gpr(src2);
+ else
+ FAIL_IF(emit_move(compiler, tmp1, src2, src2w));
+
+ FAIL_IF(push_inst(compiler, ins | R4A(dst_r) | R0A(src_r)));
+
+ if (needs_move != 2)
+ return SLJIT_SUCCESS;
+
+ dst_r = gpr(dst & REG_MASK);
+ return push_inst(compiler, (compiler->mode & SLJIT_32) ? lr(dst_r, tmp0) : lgr(dst_r, tmp0));
+}
+
+static sljit_s32 emit_rr1(struct sljit_compiler *compiler, sljit_ins ins,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w)
+{
+ sljit_gpr dst_r = FAST_IS_REG(dst) ? gpr(dst) : tmp0;
+ sljit_gpr src_r = tmp1;
+
+ if (FAST_IS_REG(src1))
+ src_r = gpr(src1);
+ else
+ FAIL_IF(emit_move(compiler, tmp1, src1, src1w));
+
+ return push_inst(compiler, ins | R4A(dst_r) | R0A(src_r));
+}
+
+static sljit_s32 emit_rrf(struct sljit_compiler *compiler, sljit_ins ins,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_gpr dst_r = FAST_IS_REG(dst) ? gpr(dst & REG_MASK) : tmp0;
+ sljit_gpr src1_r = tmp0;
+ sljit_gpr src2_r = tmp1;
+
+ if (FAST_IS_REG(src1))
+ src1_r = gpr(src1);
+ else
+ FAIL_IF(emit_move(compiler, tmp0, src1, src1w));
+
+ if (FAST_IS_REG(src2))
+ src2_r = gpr(src2);
+ else
+ FAIL_IF(emit_move(compiler, tmp1, src2, src2w));
+
+ return push_inst(compiler, ins | R4A(dst_r) | R0A(src1_r) | R12A(src2_r));
+}
+
+typedef enum {
+ RI_A,
+ RIL_A,
+} emit_ril_type;
+
+static sljit_s32 emit_ri(struct sljit_compiler *compiler, sljit_ins ins,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_sw src2w,
+ emit_ril_type type)
+{
+ sljit_gpr dst_r = tmp0;
+ sljit_s32 needs_move = 1;
+
+ if (FAST_IS_REG(dst)) {
+ dst_r = gpr(dst);
+
+ if (dst == src1)
+ needs_move = 0;
+ }
+
+ if (needs_move)
+ FAIL_IF(emit_move(compiler, dst_r, src1, src1w));
+
+ if (type == RIL_A)
+ return push_inst(compiler, ins | R36A(dst_r) | (src2w & 0xffffffff));
+ return push_inst(compiler, ins | R20A(dst_r) | (src2w & 0xffff));
+}
+
+static sljit_s32 emit_rie_d(struct sljit_compiler *compiler, sljit_ins ins,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_sw src2w)
+{
+ sljit_gpr dst_r = FAST_IS_REG(dst) ? gpr(dst) : tmp0;
+ sljit_gpr src_r = tmp0;
+
+ if (!FAST_IS_REG(src1))
+ FAIL_IF(emit_move(compiler, tmp0, src1, src1w));
+ else
+ src_r = gpr(src1 & REG_MASK);
+
+ return push_inst(compiler, ins | R36A(dst_r) | R32A(src_r) | (sljit_ins)(src2w & 0xffff) << 16);
+}
+
+typedef enum {
+ RX_A,
+ RXY_A,
+} emit_rx_type;
+
+static sljit_s32 emit_rx(struct sljit_compiler *compiler, sljit_ins ins,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w,
+ emit_rx_type type)
+{
+ sljit_gpr dst_r = tmp0;
+ sljit_s32 needs_move = 1;
+ sljit_gpr base, index;
+
+ SLJIT_ASSERT(src2 & SLJIT_MEM);
+
+ if (FAST_IS_REG(dst)) {
+ dst_r = gpr(dst);
+
+ if (dst == src1)
+ needs_move = 0;
+ else if (dst == (src2 & REG_MASK) || (dst == OFFS_REG(src2))) {
+ dst_r = tmp0;
+ needs_move = 2;
+ }
+ }
+
+ if (needs_move)
+ FAIL_IF(emit_move(compiler, dst_r, src1, src1w));
+
+ base = gpr(src2 & REG_MASK);
+ index = tmp0;
+
+ if (src2 & OFFS_REG_MASK) {
+ index = gpr(OFFS_REG(src2));
+
+ if (src2w != 0) {
+ FAIL_IF(push_inst(compiler, sllg(tmp1, index, src2w & 0x3, 0)));
+ src2w = 0;
+ index = tmp1;
+ }
+ } else if ((type == RX_A && !is_u12(src2w)) || (type == RXY_A && !is_s20(src2w))) {
+ FAIL_IF(push_load_imm_inst(compiler, tmp1, src2w));
+
+ if (src2 & REG_MASK)
+ index = tmp1;
+ else
+ base = tmp1;
+ src2w = 0;
+ }
+
+ if (type == RX_A)
+ ins |= R20A(dst_r) | R16A(index) | R12A(base) | (sljit_ins)src2w;
+ else
+ ins |= R36A(dst_r) | R32A(index) | R28A(base) | disp_s20((sljit_s32)src2w);
+
+ FAIL_IF(push_inst(compiler, ins));
+
+ if (needs_move != 2)
+ return SLJIT_SUCCESS;
+
+ dst_r = gpr(dst);
+ return push_inst(compiler, (compiler->mode & SLJIT_32) ? lr(dst_r, tmp0) : lgr(dst_r, tmp0));
+}
+
+static sljit_s32 emit_siy(struct sljit_compiler *compiler, sljit_ins ins,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_sw srcw)
+{
+ SLJIT_ASSERT(dst & SLJIT_MEM);
+
+ sljit_gpr dst_r = tmp1;
+
+ if (dst & OFFS_REG_MASK) {
+ sljit_gpr index = tmp1;
+
+ if ((dstw & 0x3) == 0)
+ index = gpr(OFFS_REG(dst));
+ else
+ FAIL_IF(push_inst(compiler, sllg(tmp1, index, dstw & 0x3, 0)));
+
+ FAIL_IF(push_inst(compiler, la(tmp1, 0, dst_r, index)));
+ dstw = 0;
+ }
+ else if (!is_s20(dstw)) {
+ FAIL_IF(push_load_imm_inst(compiler, tmp1, dstw));
+
+ if (dst & REG_MASK)
+ FAIL_IF(push_inst(compiler, la(tmp1, 0, dst_r, tmp1)));
+
+ dstw = 0;
+ }
+ else
+ dst_r = gpr(dst & REG_MASK);
+
+ return push_inst(compiler, ins | ((sljit_ins)(srcw & 0xff) << 32) | R28A(dst_r) | disp_s20((sljit_s32)dstw));
+}
+
+struct ins_forms {
+ sljit_ins op_r;
+ sljit_ins op_gr;
+ sljit_ins op_rk;
+ sljit_ins op_grk;
+ sljit_ins op;
+ sljit_ins op_y;
+ sljit_ins op_g;
+};
+
+static sljit_s32 emit_commutative(struct sljit_compiler *compiler, const struct ins_forms *forms,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 mode = compiler->mode;
+ sljit_ins ins, ins_k;
+
+ if ((src1 | src2) & SLJIT_MEM) {
+ sljit_ins ins12, ins20;
+
+ if (mode & SLJIT_32) {
+ ins12 = forms->op;
+ ins20 = forms->op_y;
+ }
+ else {
+ ins12 = 0;
+ ins20 = forms->op_g;
+ }
+
+ if (ins12 && ins20) {
+ /* Extra instructions needed for address computation can be executed independently. */
+ if ((src2 & SLJIT_MEM) && (!(src1 & SLJIT_MEM)
+ || ((src1 & OFFS_REG_MASK) ? (src1w & 0x3) == 0 : is_s20(src1w)))) {
+ if ((src2 & OFFS_REG_MASK) || is_u12(src2w) || !is_s20(src2w))
+ return emit_rx(compiler, ins12, dst, src1, src1w, src2, src2w, RX_A);
+
+ return emit_rx(compiler, ins20, dst, src1, src1w, src2, src2w, RXY_A);
+ }
+
+ if (src1 & SLJIT_MEM) {
+ if ((src1 & OFFS_REG_MASK) || is_u12(src1w) || !is_s20(src1w))
+ return emit_rx(compiler, ins12, dst, src2, src2w, src1, src1w, RX_A);
+
+ return emit_rx(compiler, ins20, dst, src2, src2w, src1, src1w, RXY_A);
+ }
+ }
+ else if (ins12 || ins20) {
+ emit_rx_type rx_type;
+
+ if (ins12) {
+ rx_type = RX_A;
+ ins = ins12;
+ }
+ else {
+ rx_type = RXY_A;
+ ins = ins20;
+ }
+
+ if ((src2 & SLJIT_MEM) && (!(src1 & SLJIT_MEM)
+ || ((src1 & OFFS_REG_MASK) ? (src1w & 0x3) == 0 : (rx_type == RX_A ? is_u12(src1w) : is_s20(src1w)))))
+ return emit_rx(compiler, ins, dst, src1, src1w, src2, src2w, rx_type);
+
+ if (src1 & SLJIT_MEM)
+ return emit_rx(compiler, ins, dst, src2, src2w, src1, src1w, rx_type);
+ }
+ }
+
+ if (mode & SLJIT_32) {
+ ins = forms->op_r;
+ ins_k = forms->op_rk;
+ }
+ else {
+ ins = forms->op_gr;
+ ins_k = forms->op_grk;
+ }
+
+ SLJIT_ASSERT(ins != 0 || ins_k != 0);
+
+ if (ins && FAST_IS_REG(dst)) {
+ if (dst == src1)
+ return emit_rr(compiler, ins, dst, src1, src1w, src2, src2w);
+
+ if (dst == src2)
+ return emit_rr(compiler, ins, dst, src2, src2w, src1, src1w);
+ }
+
+ if (ins_k == 0)
+ return emit_rr(compiler, ins, dst, src1, src1w, src2, src2w);
+
+ return emit_rrf(compiler, ins_k, dst, src1, src1w, src2, src2w);
+}
+
+static sljit_s32 emit_non_commutative(struct sljit_compiler *compiler, const struct ins_forms *forms,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 mode = compiler->mode;
+ sljit_ins ins;
+
+ if (src2 & SLJIT_MEM) {
+ sljit_ins ins12, ins20;
+
+ if (mode & SLJIT_32) {
+ ins12 = forms->op;
+ ins20 = forms->op_y;
+ }
+ else {
+ ins12 = 0;
+ ins20 = forms->op_g;
+ }
+
+ if (ins12 && ins20) {
+ if ((src2 & OFFS_REG_MASK) || is_u12(src2w) || !is_s20(src2w))
+ return emit_rx(compiler, ins12, dst, src1, src1w, src2, src2w, RX_A);
+
+ return emit_rx(compiler, ins20, dst, src1, src1w, src2, src2w, RXY_A);
+ }
+ else if (ins12)
+ return emit_rx(compiler, ins12, dst, src1, src1w, src2, src2w, RX_A);
+ else if (ins20)
+ return emit_rx(compiler, ins20, dst, src1, src1w, src2, src2w, RXY_A);
+ }
+
+ ins = (mode & SLJIT_32) ? forms->op_rk : forms->op_grk;
+
+ if (ins == 0 || (FAST_IS_REG(dst) && dst == src1))
+ return emit_rr(compiler, (mode & SLJIT_32) ? forms->op_r : forms->op_gr, dst, src1, src1w, src2, src2w);
+
+ return emit_rrf(compiler, ins, dst, src1, src1w, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ struct sljit_s390x_const *const_;
+ struct sljit_put_label *put_label;
+ sljit_sw executable_offset;
+ sljit_uw ins_size = 0; /* instructions */
+ sljit_uw pool_size = 0; /* literal pool */
+ sljit_uw pad_size;
+ sljit_uw i, j = 0;
+ struct sljit_memory_fragment *buf;
+ void *code, *code_ptr;
+ sljit_uw *pool, *pool_ptr;
+ sljit_sw source, offset; /* TODO(carenas): only need 32 bit */
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_generate_code(compiler));
+ reverse_buf(compiler);
+
+ /* branch handling */
+ label = compiler->labels;
+ jump = compiler->jumps;
+ put_label = compiler->put_labels;
+
+ /* TODO(carenas): compiler->executable_size could be calculated
+ * before to avoid the following loop (except for
+ * pool_size)
+ */
+ /* calculate the size of the code */
+ for (buf = compiler->buf; buf != NULL; buf = buf->next) {
+ sljit_uw len = buf->used_size / sizeof(sljit_ins);
+ sljit_ins *ibuf = (sljit_ins *)buf->memory;
+ for (i = 0; i < len; ++i, ++j) {
+ sljit_ins ins = ibuf[i];
+
+ /* TODO(carenas): instruction tag vs size/addr == j
+ * using instruction tags for const is creative
+ * but unlike all other architectures, and is not
+ * done consistently for all other objects.
+ * This might need reviewing later.
+ */
+ if (ins & sljit_ins_const) {
+ pool_size += sizeof(*pool);
+ ins &= ~sljit_ins_const;
+ }
+ if (label && label->size == j) {
+ label->size = ins_size;
+ label = label->next;
+ }
+ if (jump && jump->addr == j) {
+ if ((jump->flags & SLJIT_REWRITABLE_JUMP) || (jump->flags & JUMP_ADDR)) {
+ /* encoded: */
+ /* brasl %r14, <rel_addr> (or brcl <mask>, <rel_addr>) */
+ /* replace with: */
+ /* lgrl %r1, <pool_addr> */
+ /* bras %r14, %r1 (or bcr <mask>, %r1) */
+ pool_size += sizeof(*pool);
+ ins_size += 2;
+ }
+ jump = jump->next;
+ }
+ if (put_label && put_label->addr == j) {
+ pool_size += sizeof(*pool);
+ put_label = put_label->next;
+ }
+ ins_size += sizeof_ins(ins);
+ }
+ }
+
+ /* emit trailing label */
+ if (label && label->size == j) {
+ label->size = ins_size;
+ label = label->next;
+ }
+
+ SLJIT_ASSERT(!label);
+ SLJIT_ASSERT(!jump);
+ SLJIT_ASSERT(!put_label);
+
+ /* pad code size to 8 bytes so is accessible with half word offsets */
+ /* the literal pool needs to be doubleword aligned */
+ pad_size = ((ins_size + 7UL) & ~7UL) - ins_size;
+ SLJIT_ASSERT(pad_size < 8UL);
+
+ /* allocate target buffer */
+ code = SLJIT_MALLOC_EXEC(ins_size + pad_size + pool_size,
+ compiler->exec_allocator_data);
+ PTR_FAIL_WITH_EXEC_IF(code);
+ code_ptr = code;
+ executable_offset = SLJIT_EXEC_OFFSET(code);
+
+ /* TODO(carenas): pool is optional, and the ABI recommends it to
+ * be created before the function code, instead of
+ * globally; if generated code is too big could
+ * need offsets bigger than 32bit words and asser()
+ */
+ pool = (sljit_uw *)((sljit_uw)code + ins_size + pad_size);
+ pool_ptr = pool;
+ const_ = (struct sljit_s390x_const *)compiler->consts;
+
+ /* update label addresses */
+ label = compiler->labels;
+ while (label) {
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(
+ (sljit_uw)code_ptr + label->size, executable_offset);
+ label = label->next;
+ }
+
+ /* reset jumps */
+ jump = compiler->jumps;
+ put_label = compiler->put_labels;
+
+ /* emit the code */
+ j = 0;
+ for (buf = compiler->buf; buf != NULL; buf = buf->next) {
+ sljit_uw len = buf->used_size / sizeof(sljit_ins);
+ sljit_ins *ibuf = (sljit_ins *)buf->memory;
+ for (i = 0; i < len; ++i, ++j) {
+ sljit_ins ins = ibuf[i];
+ if (ins & sljit_ins_const) {
+ /* clear the const tag */
+ ins &= ~sljit_ins_const;
+
+ /* update instruction with relative address of constant */
+ source = (sljit_sw)code_ptr;
+ offset = (sljit_sw)pool_ptr - source;
+
+ SLJIT_ASSERT(!(offset & 1));
+ offset >>= 1; /* halfword (not byte) offset */
+ SLJIT_ASSERT(is_s32(offset));
+
+ ins |= (sljit_ins)offset & 0xffffffff;
+
+ /* update address */
+ const_->const_.addr = (sljit_uw)pool_ptr;
+
+ /* store initial value into pool and update pool address */
+ *(pool_ptr++) = (sljit_uw)const_->init_value;
+
+ /* move to next constant */
+ const_ = (struct sljit_s390x_const *)const_->const_.next;
+ }
+ if (jump && jump->addr == j) {
+ sljit_sw target = (sljit_sw)((jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target);
+ if ((jump->flags & SLJIT_REWRITABLE_JUMP) || (jump->flags & JUMP_ADDR)) {
+ jump->addr = (sljit_uw)pool_ptr;
+
+ /* load address into tmp1 */
+ source = (sljit_sw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ offset = (sljit_sw)SLJIT_ADD_EXEC_OFFSET(pool_ptr, executable_offset) - source;
+
+ SLJIT_ASSERT(!(offset & 1));
+ offset >>= 1;
+ SLJIT_ASSERT(is_s32(offset));
+
+ encode_inst(&code_ptr, lgrl(tmp1, offset & 0xffffffff));
+
+ /* store jump target into pool and update pool address */
+ *(pool_ptr++) = (sljit_uw)target;
+
+ /* branch to tmp1 */
+ sljit_ins op = (ins >> 32) & 0xf;
+ sljit_ins arg = (ins >> 36) & 0xf;
+ switch (op) {
+ case 4: /* brcl -> bcr */
+ ins = bcr(arg, tmp1);
+ break;
+ case 5: /* brasl -> basr */
+ ins = basr(arg, tmp1);
+ break;
+ default:
+ abort();
+ }
+ }
+ else {
+ jump->addr = (sljit_uw)code_ptr + 2;
+ source = (sljit_sw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ offset = target - source;
+
+ /* offset must be halfword aligned */
+ SLJIT_ASSERT(!(offset & 1));
+ offset >>= 1;
+ SLJIT_ASSERT(is_s32(offset)); /* TODO(mundaym): handle arbitrary offsets */
+
+ /* patch jump target */
+ ins |= (sljit_ins)offset & 0xffffffff;
+ }
+ jump = jump->next;
+ }
+ if (put_label && put_label->addr == j) {
+ source = (sljit_sw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+
+ SLJIT_ASSERT(put_label->label);
+ put_label->addr = (sljit_uw)code_ptr;
+
+ /* store target into pool */
+ *pool_ptr = put_label->label->addr;
+ offset = (sljit_sw)SLJIT_ADD_EXEC_OFFSET(pool_ptr, executable_offset) - source;
+ pool_ptr++;
+
+ SLJIT_ASSERT(!(offset & 1));
+ offset >>= 1;
+ SLJIT_ASSERT(is_s32(offset));
+ ins |= (sljit_ins)offset & 0xffffffff;
+
+ put_label = put_label->next;
+ }
+ encode_inst(&code_ptr, ins);
+ }
+ }
+ SLJIT_ASSERT((sljit_u8 *)code + ins_size == code_ptr);
+ SLJIT_ASSERT((sljit_u8 *)pool + pool_size == (sljit_u8 *)pool_ptr);
+
+ compiler->error = SLJIT_ERR_COMPILED;
+ compiler->executable_offset = executable_offset;
+ compiler->executable_size = ins_size;
+ code = SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+ code_ptr = SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ SLJIT_CACHE_FLUSH(code, code_ptr);
+ SLJIT_UPDATE_WX_FLAGS(code, code_ptr, 1);
+ return code;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ /* TODO(mundaym): implement all */
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+ case SLJIT_HAS_CLZ:
+ case SLJIT_HAS_ROT:
+ case SLJIT_HAS_PREFETCH:
+ return 1;
+ case SLJIT_HAS_CTZ:
+ return 2;
+ case SLJIT_HAS_CMOV:
+ return have_lscond1() ? 1 : 0;
+ }
+ return 0;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type)
+{
+ return (type >= SLJIT_UNORDERED && type <= SLJIT_ORDERED_LESS_EQUAL);
+}
+
+/* --------------------------------------------------------------------- */
+/* Entry, exit */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+ sljit_s32 offset, i, tmp;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ /* Saved registers are stored in callee allocated save area. */
+ SLJIT_ASSERT(gpr(SLJIT_FIRST_SAVED_REG) == r6 && gpr(SLJIT_S0) == r13);
+
+ offset = 2 * SSIZE_OF(sw);
+ if (saveds + scratches >= SLJIT_NUMBER_OF_REGISTERS) {
+ if (saved_arg_count == 0) {
+ FAIL_IF(push_inst(compiler, stmg(r6, r14, offset, r15)));
+ offset += 9 * SSIZE_OF(sw);
+ } else {
+ FAIL_IF(push_inst(compiler, stmg(r6, r13 - (sljit_gpr)saved_arg_count, offset, r15)));
+ offset += (8 - saved_arg_count) * SSIZE_OF(sw);
+ }
+ } else {
+ if (scratches == SLJIT_FIRST_SAVED_REG) {
+ FAIL_IF(push_inst(compiler, stg(r6, offset, 0, r15)));
+ offset += SSIZE_OF(sw);
+ } else if (scratches > SLJIT_FIRST_SAVED_REG) {
+ FAIL_IF(push_inst(compiler, stmg(r6, r6 + (sljit_gpr)(scratches - SLJIT_FIRST_SAVED_REG), offset, r15)));
+ offset += (scratches - (SLJIT_FIRST_SAVED_REG - 1)) * SSIZE_OF(sw);
+ }
+
+ if (saved_arg_count == 0) {
+ if (saveds == 0) {
+ FAIL_IF(push_inst(compiler, stg(r14, offset, 0, r15)));
+ offset += SSIZE_OF(sw);
+ } else {
+ FAIL_IF(push_inst(compiler, stmg(r14 - (sljit_gpr)saveds, r14, offset, r15)));
+ offset += (saveds + 1) * SSIZE_OF(sw);
+ }
+ } else if (saveds > saved_arg_count) {
+ if (saveds == saved_arg_count + 1) {
+ FAIL_IF(push_inst(compiler, stg(r14 - (sljit_gpr)saveds, offset, 0, r15)));
+ offset += SSIZE_OF(sw);
+ } else {
+ FAIL_IF(push_inst(compiler, stmg(r14 - (sljit_gpr)saveds, r13 - (sljit_gpr)saved_arg_count, offset, r15)));
+ offset += (saveds - saved_arg_count) * SSIZE_OF(sw);
+ }
+ }
+ }
+
+ if (saved_arg_count > 0) {
+ FAIL_IF(push_inst(compiler, stg(r14, offset, 0, r15)));
+ offset += SSIZE_OF(sw);
+ }
+
+ tmp = SLJIT_FS0 - fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ FAIL_IF(push_inst(compiler, 0x60000000 /* std */ | F20(i) | R12A(r15) | (sljit_ins)offset));
+ offset += SSIZE_OF(sw);
+ }
+
+ for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ FAIL_IF(push_inst(compiler, 0x60000000 /* std */ | F20(i) | R12A(r15) | (sljit_ins)offset));
+ offset += SSIZE_OF(sw);
+ }
+
+ local_size = (local_size + SLJIT_S390X_DEFAULT_STACK_FRAME_SIZE + 0xf) & ~0xf;
+ compiler->local_size = local_size;
+
+ FAIL_IF(push_inst(compiler, 0xe30000000071 /* lay */ | R36A(r15) | R28A(r15) | disp_s20(-local_size)));
+
+ if (options & SLJIT_ENTER_REG_ARG)
+ return SLJIT_SUCCESS;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ saved_arg_count = 0;
+ tmp = 0;
+ while (arg_types > 0) {
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) {
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ FAIL_IF(push_inst(compiler, lgr(gpr(SLJIT_S0 - saved_arg_count), gpr(SLJIT_R0 + tmp))));
+ saved_arg_count++;
+ }
+ tmp++;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ compiler->local_size = (local_size + SLJIT_S390X_DEFAULT_STACK_FRAME_SIZE + 0xf) & ~0xf;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_gpr last_reg)
+{
+ sljit_s32 offset, i, tmp;
+ sljit_s32 local_size = compiler->local_size;
+ sljit_s32 saveds = compiler->saveds;
+ sljit_s32 scratches = compiler->scratches;
+ sljit_s32 kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(compiler->options);
+
+ if (is_u12(local_size))
+ FAIL_IF(push_inst(compiler, 0x41000000 /* ly */ | R20A(r15) | R12A(r15) | (sljit_ins)local_size));
+ else
+ FAIL_IF(push_inst(compiler, 0xe30000000071 /* lay */ | R36A(r15) | R28A(r15) | disp_s20(local_size)));
+
+ offset = 2 * SSIZE_OF(sw);
+ if (saveds + scratches >= SLJIT_NUMBER_OF_REGISTERS) {
+ if (kept_saveds_count == 0) {
+ FAIL_IF(push_inst(compiler, lmg(r6, last_reg, offset, r15)));
+ offset += 9 * SSIZE_OF(sw);
+ } else {
+ FAIL_IF(push_inst(compiler, lmg(r6, r13 - (sljit_gpr)kept_saveds_count, offset, r15)));
+ offset += (8 - kept_saveds_count) * SSIZE_OF(sw);
+ }
+ } else {
+ if (scratches == SLJIT_FIRST_SAVED_REG) {
+ FAIL_IF(push_inst(compiler, lg(r6, offset, 0, r15)));
+ offset += SSIZE_OF(sw);
+ } else if (scratches > SLJIT_FIRST_SAVED_REG) {
+ FAIL_IF(push_inst(compiler, lmg(r6, r6 + (sljit_gpr)(scratches - SLJIT_FIRST_SAVED_REG), offset, r15)));
+ offset += (scratches - (SLJIT_FIRST_SAVED_REG - 1)) * SSIZE_OF(sw);
+ }
+
+ if (kept_saveds_count == 0) {
+ if (saveds == 0) {
+ if (last_reg == r14)
+ FAIL_IF(push_inst(compiler, lg(r14, offset, 0, r15)));
+ offset += SSIZE_OF(sw);
+ } else if (saveds == 1 && last_reg == r13) {
+ FAIL_IF(push_inst(compiler, lg(r13, offset, 0, r15)));
+ offset += 2 * SSIZE_OF(sw);
+ } else {
+ FAIL_IF(push_inst(compiler, lmg(r14 - (sljit_gpr)saveds, last_reg, offset, r15)));
+ offset += (saveds + 1) * SSIZE_OF(sw);
+ }
+ } else if (saveds > kept_saveds_count) {
+ if (saveds == kept_saveds_count + 1) {
+ FAIL_IF(push_inst(compiler, lg(r14 - (sljit_gpr)saveds, offset, 0, r15)));
+ offset += SSIZE_OF(sw);
+ } else {
+ FAIL_IF(push_inst(compiler, lmg(r14 - (sljit_gpr)saveds, r13 - (sljit_gpr)kept_saveds_count, offset, r15)));
+ offset += (saveds - kept_saveds_count) * SSIZE_OF(sw);
+ }
+ }
+ }
+
+ if (kept_saveds_count > 0) {
+ if (last_reg == r14)
+ FAIL_IF(push_inst(compiler, lg(r14, offset, 0, r15)));
+ offset += SSIZE_OF(sw);
+ }
+
+ tmp = SLJIT_FS0 - compiler->fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ FAIL_IF(push_inst(compiler, 0x68000000 /* ld */ | F20(i) | R12A(r15) | (sljit_ins)offset));
+ offset += SSIZE_OF(sw);
+ }
+
+ for (i = compiler->fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ FAIL_IF(push_inst(compiler, 0x68000000 /* ld */ | F20(i) | R12A(r15) | (sljit_ins)offset));
+ offset += SSIZE_OF(sw);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ FAIL_IF(emit_stack_frame_release(compiler, r14));
+ return push_inst(compiler, br(r14)); /* return */
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(load_word(compiler, tmp1, src, srcw, 0 /* 64-bit */));
+ src = TMP_REG2;
+ srcw = 0;
+ } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, lgr(tmp1, gpr(src))));
+ src = TMP_REG2;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, r13));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+ sljit_gpr arg0 = gpr(SLJIT_R0);
+ sljit_gpr arg1 = gpr(SLJIT_R1);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op0(compiler, op));
+
+ op = GET_OPCODE(op) | (op & SLJIT_32);
+ switch (op) {
+ case SLJIT_BREAKPOINT:
+ /* The following invalid instruction is emitted by gdb. */
+ return push_inst(compiler, 0x0001 /* 2-byte trap */);
+ case SLJIT_NOP:
+ return push_inst(compiler, 0x0700 /* 2-byte nop */);
+ case SLJIT_LMUL_UW:
+ FAIL_IF(push_inst(compiler, mlgr(arg0, arg0)));
+ break;
+ case SLJIT_LMUL_SW:
+ /* signed multiplication from: */
+ /* Hacker's Delight, Second Edition: Chapter 8-3. */
+ FAIL_IF(push_inst(compiler, srag(tmp0, arg0, 63, 0)));
+ FAIL_IF(push_inst(compiler, srag(tmp1, arg1, 63, 0)));
+ FAIL_IF(push_inst(compiler, ngr(tmp0, arg1)));
+ FAIL_IF(push_inst(compiler, ngr(tmp1, arg0)));
+
+ /* unsigned multiplication */
+ FAIL_IF(push_inst(compiler, mlgr(arg0, arg0)));
+
+ FAIL_IF(push_inst(compiler, sgr(arg0, tmp0)));
+ FAIL_IF(push_inst(compiler, sgr(arg0, tmp1)));
+ break;
+ case SLJIT_DIV_U32:
+ case SLJIT_DIVMOD_U32:
+ FAIL_IF(push_inst(compiler, lhi(tmp0, 0)));
+ FAIL_IF(push_inst(compiler, lr(tmp1, arg0)));
+ FAIL_IF(push_inst(compiler, dlr(tmp0, arg1)));
+ FAIL_IF(push_inst(compiler, lr(arg0, tmp1))); /* quotient */
+ if (op == SLJIT_DIVMOD_U32)
+ return push_inst(compiler, lr(arg1, tmp0)); /* remainder */
+
+ return SLJIT_SUCCESS;
+ case SLJIT_DIV_S32:
+ case SLJIT_DIVMOD_S32:
+ FAIL_IF(push_inst(compiler, lhi(tmp0, 0)));
+ FAIL_IF(push_inst(compiler, lr(tmp1, arg0)));
+ FAIL_IF(push_inst(compiler, dr(tmp0, arg1)));
+ FAIL_IF(push_inst(compiler, lr(arg0, tmp1))); /* quotient */
+ if (op == SLJIT_DIVMOD_S32)
+ return push_inst(compiler, lr(arg1, tmp0)); /* remainder */
+
+ return SLJIT_SUCCESS;
+ case SLJIT_DIV_UW:
+ case SLJIT_DIVMOD_UW:
+ FAIL_IF(push_inst(compiler, lghi(tmp0, 0)));
+ FAIL_IF(push_inst(compiler, lgr(tmp1, arg0)));
+ FAIL_IF(push_inst(compiler, dlgr(tmp0, arg1)));
+ FAIL_IF(push_inst(compiler, lgr(arg0, tmp1))); /* quotient */
+ if (op == SLJIT_DIVMOD_UW)
+ return push_inst(compiler, lgr(arg1, tmp0)); /* remainder */
+
+ return SLJIT_SUCCESS;
+ case SLJIT_DIV_SW:
+ case SLJIT_DIVMOD_SW:
+ FAIL_IF(push_inst(compiler, lgr(tmp1, arg0)));
+ FAIL_IF(push_inst(compiler, dsgr(tmp0, arg1)));
+ FAIL_IF(push_inst(compiler, lgr(arg0, tmp1))); /* quotient */
+ if (op == SLJIT_DIVMOD_SW)
+ return push_inst(compiler, lgr(arg1, tmp0)); /* remainder */
+
+ return SLJIT_SUCCESS;
+ case SLJIT_ENDBR:
+ return SLJIT_SUCCESS;
+ case SLJIT_SKIP_FRAMES_BEFORE_RETURN:
+ return SLJIT_SUCCESS;
+ default:
+ SLJIT_UNREACHABLE();
+ }
+ /* swap result registers */
+ FAIL_IF(push_inst(compiler, lgr(tmp0, arg0)));
+ FAIL_IF(push_inst(compiler, lgr(arg0, arg1)));
+ return push_inst(compiler, lgr(arg1, tmp0));
+}
+
+static sljit_s32 sljit_emit_clz_ctz(struct sljit_compiler *compiler, sljit_s32 op, sljit_gpr dst_r, sljit_gpr src_r)
+{
+ sljit_s32 is_ctz = (GET_OPCODE(op) == SLJIT_CTZ);
+
+ if ((op & SLJIT_32) && src_r != tmp0) {
+ FAIL_IF(push_inst(compiler, 0xb9160000 /* llgfr */ | R4A(tmp0) | R0A(src_r)));
+ src_r = tmp0;
+ }
+
+ if (is_ctz) {
+ FAIL_IF(push_inst(compiler, ((op & SLJIT_32) ? 0x1300 /* lcr */ : 0xb9030000 /* lcgr */) | R4A(tmp1) | R0A(src_r)));
+
+ if (src_r == tmp0)
+ FAIL_IF(push_inst(compiler, ((op & SLJIT_32) ? 0x1400 /* nr */ : 0xb9800000 /* ngr */) | R4A(tmp0) | R0A(tmp1)));
+ else
+ FAIL_IF(push_inst(compiler, 0xb9e40000 /* ngrk */ | R12A(tmp1) | R4A(tmp0) | R0A(src_r)));
+
+ src_r = tmp0;
+ }
+
+ FAIL_IF(push_inst(compiler, 0xb9830000 /* flogr */ | R4A(tmp0) | R0A(src_r)));
+
+ if (is_ctz)
+ FAIL_IF(push_inst(compiler, 0xec00000000d9 /* aghik */ | R36A(tmp1) | R32A(tmp0) | ((sljit_ins)(-64 & 0xffff) << 16)));
+
+ if (op & SLJIT_32) {
+ if (!is_ctz && dst_r != tmp0)
+ return push_inst(compiler, 0xec00000000d9 /* aghik */ | R36A(dst_r) | R32A(tmp0) | ((sljit_ins)(-32 & 0xffff) << 16));
+
+ FAIL_IF(push_inst(compiler, 0xc20800000000 /* agfi */ | R36A(tmp0) | (sljit_u32)-32));
+ }
+
+ if (is_ctz)
+ FAIL_IF(push_inst(compiler, 0xec0000000057 /* rxsbg */ | R36A(tmp0) | R32A(tmp1) | ((sljit_ins)((op & SLJIT_32) ? 59 : 58) << 24) | (63 << 16) | ((sljit_ins)((op & SLJIT_32) ? 5 : 6) << 8)));
+
+ if (dst_r == tmp0)
+ return SLJIT_SUCCESS;
+
+ return push_inst(compiler, ((op & SLJIT_32) ? 0x1800 /* lr */ : 0xb9040000 /* lgr */) | R4A(dst_r) | R0A(tmp0));
+}
+
+/* LEVAL will be defined later with different parameters as needed */
+#define WHEN2(cond, i1, i2) (cond) ? LEVAL(i1) : LEVAL(i2)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_ins ins;
+ struct addr mem;
+ sljit_gpr dst_r;
+ sljit_gpr src_r;
+ sljit_s32 opcode = GET_OPCODE(op);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ if (opcode >= SLJIT_MOV && opcode <= SLJIT_MOV_P) {
+ /* LOAD REGISTER */
+ if (FAST_IS_REG(dst) && FAST_IS_REG(src)) {
+ dst_r = gpr(dst);
+ src_r = gpr(src);
+ switch (opcode | (op & SLJIT_32)) {
+ /* 32-bit */
+ case SLJIT_MOV32_U8:
+ ins = llcr(dst_r, src_r);
+ break;
+ case SLJIT_MOV32_S8:
+ ins = lbr(dst_r, src_r);
+ break;
+ case SLJIT_MOV32_U16:
+ ins = llhr(dst_r, src_r);
+ break;
+ case SLJIT_MOV32_S16:
+ ins = lhr(dst_r, src_r);
+ break;
+ case SLJIT_MOV32:
+ if (dst_r == src_r)
+ return SLJIT_SUCCESS;
+ ins = lr(dst_r, src_r);
+ break;
+ /* 64-bit */
+ case SLJIT_MOV_U8:
+ ins = llgcr(dst_r, src_r);
+ break;
+ case SLJIT_MOV_S8:
+ ins = lgbr(dst_r, src_r);
+ break;
+ case SLJIT_MOV_U16:
+ ins = llghr(dst_r, src_r);
+ break;
+ case SLJIT_MOV_S16:
+ ins = lghr(dst_r, src_r);
+ break;
+ case SLJIT_MOV_U32:
+ ins = llgfr(dst_r, src_r);
+ break;
+ case SLJIT_MOV_S32:
+ ins = lgfr(dst_r, src_r);
+ break;
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+ if (dst_r == src_r)
+ return SLJIT_SUCCESS;
+ ins = lgr(dst_r, src_r);
+ break;
+ default:
+ ins = 0;
+ SLJIT_UNREACHABLE();
+ break;
+ }
+ FAIL_IF(push_inst(compiler, ins));
+ return SLJIT_SUCCESS;
+ }
+ /* LOAD IMMEDIATE */
+ if (FAST_IS_REG(dst) && (src & SLJIT_IMM)) {
+ switch (opcode) {
+ case SLJIT_MOV_U8:
+ srcw = (sljit_sw)((sljit_u8)(srcw));
+ break;
+ case SLJIT_MOV_S8:
+ srcw = (sljit_sw)((sljit_s8)(srcw));
+ break;
+ case SLJIT_MOV_U16:
+ srcw = (sljit_sw)((sljit_u16)(srcw));
+ break;
+ case SLJIT_MOV_S16:
+ srcw = (sljit_sw)((sljit_s16)(srcw));
+ break;
+ case SLJIT_MOV_U32:
+ srcw = (sljit_sw)((sljit_u32)(srcw));
+ break;
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ srcw = (sljit_sw)((sljit_s32)(srcw));
+ break;
+ }
+ return push_load_imm_inst(compiler, gpr(dst), srcw);
+ }
+ /* LOAD */
+ /* TODO(carenas): avoid reg being defined later */
+ #define LEVAL(i) EVAL(i, reg, mem)
+ if (FAST_IS_REG(dst) && (src & SLJIT_MEM)) {
+ sljit_gpr reg = gpr(dst);
+
+ FAIL_IF(make_addr_bxy(compiler, &mem, src, srcw, tmp1));
+ /* TODO(carenas): convert all calls below to LEVAL */
+ switch (opcode | (op & SLJIT_32)) {
+ case SLJIT_MOV32_U8:
+ ins = llc(reg, mem.offset, mem.index, mem.base);
+ break;
+ case SLJIT_MOV32_S8:
+ ins = lb(reg, mem.offset, mem.index, mem.base);
+ break;
+ case SLJIT_MOV32_U16:
+ ins = llh(reg, mem.offset, mem.index, mem.base);
+ break;
+ case SLJIT_MOV32_S16:
+ ins = WHEN2(is_u12(mem.offset), lh, lhy);
+ break;
+ case SLJIT_MOV32:
+ ins = WHEN2(is_u12(mem.offset), l, ly);
+ break;
+ case SLJIT_MOV_U8:
+ ins = LEVAL(llgc);
+ break;
+ case SLJIT_MOV_S8:
+ ins = lgb(reg, mem.offset, mem.index, mem.base);
+ break;
+ case SLJIT_MOV_U16:
+ ins = LEVAL(llgh);
+ break;
+ case SLJIT_MOV_S16:
+ ins = lgh(reg, mem.offset, mem.index, mem.base);
+ break;
+ case SLJIT_MOV_U32:
+ ins = LEVAL(llgf);
+ break;
+ case SLJIT_MOV_S32:
+ ins = lgf(reg, mem.offset, mem.index, mem.base);
+ break;
+ case SLJIT_MOV_P:
+ case SLJIT_MOV:
+ ins = lg(reg, mem.offset, mem.index, mem.base);
+ break;
+ default:
+ ins = 0;
+ SLJIT_UNREACHABLE();
+ break;
+ }
+ FAIL_IF(push_inst(compiler, ins));
+ return SLJIT_SUCCESS;
+ }
+ /* STORE and STORE IMMEDIATE */
+ if ((dst & SLJIT_MEM)
+ && (FAST_IS_REG(src) || (src & SLJIT_IMM))) {
+ sljit_gpr reg = FAST_IS_REG(src) ? gpr(src) : tmp0;
+ if (src & SLJIT_IMM) {
+ /* TODO(mundaym): MOVE IMMEDIATE? */
+ FAIL_IF(push_load_imm_inst(compiler, reg, srcw));
+ }
+ struct addr mem;
+ FAIL_IF(make_addr_bxy(compiler, &mem, dst, dstw, tmp1));
+ switch (opcode) {
+ case SLJIT_MOV_U8:
+ case SLJIT_MOV_S8:
+ return push_inst(compiler,
+ WHEN2(is_u12(mem.offset), stc, stcy));
+ case SLJIT_MOV_U16:
+ case SLJIT_MOV_S16:
+ return push_inst(compiler,
+ WHEN2(is_u12(mem.offset), sth, sthy));
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ return push_inst(compiler,
+ WHEN2(is_u12(mem.offset), st, sty));
+ case SLJIT_MOV_P:
+ case SLJIT_MOV:
+ FAIL_IF(push_inst(compiler, LEVAL(stg)));
+ return SLJIT_SUCCESS;
+ default:
+ SLJIT_UNREACHABLE();
+ }
+ }
+ #undef LEVAL
+ /* MOVE CHARACTERS */
+ if ((dst & SLJIT_MEM) && (src & SLJIT_MEM)) {
+ struct addr mem;
+ FAIL_IF(make_addr_bxy(compiler, &mem, src, srcw, tmp1));
+ switch (opcode) {
+ case SLJIT_MOV_U8:
+ case SLJIT_MOV_S8:
+ FAIL_IF(push_inst(compiler,
+ EVAL(llgc, tmp0, mem)));
+ FAIL_IF(make_addr_bxy(compiler, &mem, dst, dstw, tmp1));
+ return push_inst(compiler,
+ EVAL(stcy, tmp0, mem));
+ case SLJIT_MOV_U16:
+ case SLJIT_MOV_S16:
+ FAIL_IF(push_inst(compiler,
+ EVAL(llgh, tmp0, mem)));
+ FAIL_IF(make_addr_bxy(compiler, &mem, dst, dstw, tmp1));
+ return push_inst(compiler,
+ EVAL(sthy, tmp0, mem));
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ FAIL_IF(push_inst(compiler,
+ EVAL(ly, tmp0, mem)));
+ FAIL_IF(make_addr_bxy(compiler, &mem, dst, dstw, tmp1));
+ return push_inst(compiler,
+ EVAL(sty, tmp0, mem));
+ case SLJIT_MOV_P:
+ case SLJIT_MOV:
+ FAIL_IF(push_inst(compiler,
+ EVAL(lg, tmp0, mem)));
+ FAIL_IF(make_addr_bxy(compiler, &mem, dst, dstw, tmp1));
+ FAIL_IF(push_inst(compiler,
+ EVAL(stg, tmp0, mem)));
+ return SLJIT_SUCCESS;
+ default:
+ SLJIT_UNREACHABLE();
+ }
+ }
+ SLJIT_UNREACHABLE();
+ }
+
+ SLJIT_ASSERT((src & SLJIT_IMM) == 0); /* no immediates */
+
+ dst_r = FAST_IS_REG(dst) ? gpr(REG_MASK & dst) : tmp0;
+ src_r = FAST_IS_REG(src) ? gpr(REG_MASK & src) : tmp0;
+
+ compiler->status_flags_state = op & (VARIABLE_FLAG_MASK | SLJIT_SET_Z);
+
+ /* TODO(mundaym): optimize loads and stores */
+ switch (opcode) {
+ case SLJIT_NOT:
+ if (src & SLJIT_MEM)
+ FAIL_IF(load_word(compiler, src_r, src, srcw, op & SLJIT_32));
+
+ /* emulate ~x with x^-1 */
+ if (!(op & SLJIT_32)) {
+ FAIL_IF(push_load_imm_inst(compiler, tmp1, -1));
+ if (src_r != dst_r)
+ FAIL_IF(push_inst(compiler, lgr(dst_r, src_r)));
+
+ FAIL_IF(push_inst(compiler, xgr(dst_r, tmp1)));
+ break;
+ }
+
+ if (have_eimm())
+ FAIL_IF(push_inst(compiler, xilf(dst_r, 0xffffffff)));
+ else {
+ FAIL_IF(push_load_imm_inst(compiler, tmp1, -1));
+ if (src_r != dst_r)
+ FAIL_IF(push_inst(compiler, lr(dst_r, src_r)));
+
+ FAIL_IF(push_inst(compiler, xr(dst_r, tmp1)));
+ }
+ break;
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ if (src & SLJIT_MEM)
+ FAIL_IF(load_unsigned_word(compiler, src_r, src, srcw, op & SLJIT_32));
+
+ FAIL_IF(sljit_emit_clz_ctz(compiler, op, dst_r, src_r));
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ }
+
+ if ((op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)) == (SLJIT_SET_Z | SLJIT_SET_OVERFLOW))
+ FAIL_IF(update_zero_overflow(compiler, op, dst_r));
+
+ if (dst & SLJIT_MEM)
+ return store_word(compiler, dst_r, dst, dstw, op & SLJIT_32);
+
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE int is_commutative(sljit_s32 op)
+{
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD:
+ case SLJIT_ADDC:
+ case SLJIT_MUL:
+ case SLJIT_AND:
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ return 1;
+ }
+ return 0;
+}
+
+static const struct ins_forms add_forms = {
+ 0x1a00, /* ar */
+ 0xb9080000, /* agr */
+ 0xb9f80000, /* ark */
+ 0xb9e80000, /* agrk */
+ 0x5a000000, /* a */
+ 0xe3000000005a, /* ay */
+ 0xe30000000008, /* ag */
+};
+
+static const struct ins_forms logical_add_forms = {
+ 0x1e00, /* alr */
+ 0xb90a0000, /* algr */
+ 0xb9fa0000, /* alrk */
+ 0xb9ea0000, /* algrk */
+ 0x5e000000, /* al */
+ 0xe3000000005e, /* aly */
+ 0xe3000000000a, /* alg */
+};
+
+static sljit_s32 sljit_emit_add(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ int sets_overflow = (op & VARIABLE_FLAG_MASK) == SLJIT_SET_OVERFLOW;
+ int sets_zero_overflow = (op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)) == (SLJIT_SET_Z | SLJIT_SET_OVERFLOW);
+ const struct ins_forms *forms;
+ sljit_ins ins;
+
+ if (src2 & SLJIT_IMM) {
+ if (!sets_zero_overflow && is_s8(src2w) && (src1 & SLJIT_MEM) && (dst == src1 && dstw == src1w)) {
+ if (sets_overflow)
+ ins = (op & SLJIT_32) ? 0xeb000000006a /* asi */ : 0xeb000000007a /* agsi */;
+ else
+ ins = (op & SLJIT_32) ? 0xeb000000006e /* alsi */ : 0xeb000000007e /* algsi */;
+ return emit_siy(compiler, ins, dst, dstw, src2w);
+ }
+
+ if (is_s16(src2w)) {
+ if (sets_overflow)
+ ins = (op & SLJIT_32) ? 0xec00000000d8 /* ahik */ : 0xec00000000d9 /* aghik */;
+ else
+ ins = (op & SLJIT_32) ? 0xec00000000da /* alhsik */ : 0xec00000000db /* alghsik */;
+ FAIL_IF(emit_rie_d(compiler, ins, dst, src1, src1w, src2w));
+ goto done;
+ }
+
+ if (!sets_overflow) {
+ if ((op & SLJIT_32) || is_u32(src2w)) {
+ ins = (op & SLJIT_32) ? 0xc20b00000000 /* alfi */ : 0xc20a00000000 /* algfi */;
+ FAIL_IF(emit_ri(compiler, ins, dst, src1, src1w, src2w, RIL_A));
+ goto done;
+ }
+ if (is_u32(-src2w)) {
+ FAIL_IF(emit_ri(compiler, 0xc20400000000 /* slgfi */, dst, src1, src1w, -src2w, RIL_A));
+ goto done;
+ }
+ }
+ else if ((op & SLJIT_32) || is_s32(src2w)) {
+ ins = (op & SLJIT_32) ? 0xc20900000000 /* afi */ : 0xc20800000000 /* agfi */;
+ FAIL_IF(emit_ri(compiler, ins, dst, src1, src1w, src2w, RIL_A));
+ goto done;
+ }
+ }
+
+ forms = sets_overflow ? &add_forms : &logical_add_forms;
+ FAIL_IF(emit_commutative(compiler, forms, dst, src1, src1w, src2, src2w));
+
+done:
+ if (sets_zero_overflow)
+ FAIL_IF(update_zero_overflow(compiler, op, FAST_IS_REG(dst) ? gpr(dst & REG_MASK) : tmp0));
+
+ if (dst & SLJIT_MEM)
+ return store_word(compiler, tmp0, dst, dstw, op & SLJIT_32);
+
+ return SLJIT_SUCCESS;
+}
+
+static const struct ins_forms sub_forms = {
+ 0x1b00, /* sr */
+ 0xb9090000, /* sgr */
+ 0xb9f90000, /* srk */
+ 0xb9e90000, /* sgrk */
+ 0x5b000000, /* s */
+ 0xe3000000005b, /* sy */
+ 0xe30000000009, /* sg */
+};
+
+static const struct ins_forms logical_sub_forms = {
+ 0x1f00, /* slr */
+ 0xb90b0000, /* slgr */
+ 0xb9fb0000, /* slrk */
+ 0xb9eb0000, /* slgrk */
+ 0x5f000000, /* sl */
+ 0xe3000000005f, /* sly */
+ 0xe3000000000b, /* slg */
+};
+
+static sljit_s32 sljit_emit_sub(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 flag_type = GET_FLAG_TYPE(op);
+ int sets_signed = (flag_type >= SLJIT_SIG_LESS && flag_type <= SLJIT_NOT_OVERFLOW);
+ int sets_zero_overflow = (op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)) == (SLJIT_SET_Z | SLJIT_SET_OVERFLOW);
+ const struct ins_forms *forms;
+ sljit_ins ins;
+
+ if (dst == (sljit_s32)tmp0 && flag_type <= SLJIT_SIG_LESS_EQUAL) {
+ int compare_signed = flag_type >= SLJIT_SIG_LESS;
+
+ compiler->status_flags_state |= SLJIT_CURRENT_FLAGS_COMPARE;
+
+ if (src2 & SLJIT_IMM) {
+ if (compare_signed || ((op & VARIABLE_FLAG_MASK) == 0 && is_s32(src2w)))
+ {
+ if ((op & SLJIT_32) || is_s32(src2w)) {
+ ins = (op & SLJIT_32) ? 0xc20d00000000 /* cfi */ : 0xc20c00000000 /* cgfi */;
+ return emit_ri(compiler, ins, src1, src1, src1w, src2w, RIL_A);
+ }
+ }
+ else {
+ if ((op & SLJIT_32) || is_u32(src2w)) {
+ ins = (op & SLJIT_32) ? 0xc20f00000000 /* clfi */ : 0xc20e00000000 /* clgfi */;
+ return emit_ri(compiler, ins, src1, src1, src1w, src2w, RIL_A);
+ }
+ if (is_s16(src2w))
+ return emit_rie_d(compiler, 0xec00000000db /* alghsik */, (sljit_s32)tmp0, src1, src1w, src2w);
+ }
+ }
+ else if (src2 & SLJIT_MEM) {
+ if ((op & SLJIT_32) && ((src2 & OFFS_REG_MASK) || is_u12(src2w))) {
+ ins = compare_signed ? 0x59000000 /* c */ : 0x55000000 /* cl */;
+ return emit_rx(compiler, ins, src1, src1, src1w, src2, src2w, RX_A);
+ }
+
+ if (compare_signed)
+ ins = (op & SLJIT_32) ? 0xe30000000059 /* cy */ : 0xe30000000020 /* cg */;
+ else
+ ins = (op & SLJIT_32) ? 0xe30000000055 /* cly */ : 0xe30000000021 /* clg */;
+ return emit_rx(compiler, ins, src1, src1, src1w, src2, src2w, RXY_A);
+ }
+
+ if (compare_signed)
+ ins = (op & SLJIT_32) ? 0x1900 /* cr */ : 0xb9200000 /* cgr */;
+ else
+ ins = (op & SLJIT_32) ? 0x1500 /* clr */ : 0xb9210000 /* clgr */;
+ return emit_rr(compiler, ins, src1, src1, src1w, src2, src2w);
+ }
+
+ if (src1 == SLJIT_IMM && src1w == 0 && (flag_type == 0 || sets_signed)) {
+ ins = (op & SLJIT_32) ? 0x1300 /* lcr */ : 0xb9030000 /* lcgr */;
+ FAIL_IF(emit_rr1(compiler, ins, dst, src2, src2w));
+ goto done;
+ }
+
+ if (src2 & SLJIT_IMM) {
+ sljit_sw neg_src2w = -src2w;
+
+ if (sets_signed || neg_src2w != 0 || (op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)) == 0) {
+ if (!sets_zero_overflow && is_s8(neg_src2w) && (src1 & SLJIT_MEM) && (dst == src1 && dstw == src1w)) {
+ if (sets_signed)
+ ins = (op & SLJIT_32) ? 0xeb000000006a /* asi */ : 0xeb000000007a /* agsi */;
+ else
+ ins = (op & SLJIT_32) ? 0xeb000000006e /* alsi */ : 0xeb000000007e /* algsi */;
+ return emit_siy(compiler, ins, dst, dstw, neg_src2w);
+ }
+
+ if (is_s16(neg_src2w)) {
+ if (sets_signed)
+ ins = (op & SLJIT_32) ? 0xec00000000d8 /* ahik */ : 0xec00000000d9 /* aghik */;
+ else
+ ins = (op & SLJIT_32) ? 0xec00000000da /* alhsik */ : 0xec00000000db /* alghsik */;
+ FAIL_IF(emit_rie_d(compiler, ins, dst, src1, src1w, neg_src2w));
+ goto done;
+ }
+ }
+
+ if (!sets_signed) {
+ if ((op & SLJIT_32) || is_u32(src2w)) {
+ ins = (op & SLJIT_32) ? 0xc20500000000 /* slfi */ : 0xc20400000000 /* slgfi */;
+ FAIL_IF(emit_ri(compiler, ins, dst, src1, src1w, src2w, RIL_A));
+ goto done;
+ }
+ if (is_u32(neg_src2w)) {
+ FAIL_IF(emit_ri(compiler, 0xc20a00000000 /* algfi */, dst, src1, src1w, neg_src2w, RIL_A));
+ goto done;
+ }
+ }
+ else if ((op & SLJIT_32) || is_s32(neg_src2w)) {
+ ins = (op & SLJIT_32) ? 0xc20900000000 /* afi */ : 0xc20800000000 /* agfi */;
+ FAIL_IF(emit_ri(compiler, ins, dst, src1, src1w, neg_src2w, RIL_A));
+ goto done;
+ }
+ }
+
+ forms = sets_signed ? &sub_forms : &logical_sub_forms;
+ FAIL_IF(emit_non_commutative(compiler, forms, dst, src1, src1w, src2, src2w));
+
+done:
+ if (sets_signed) {
+ sljit_gpr dst_r = FAST_IS_REG(dst) ? gpr(dst & REG_MASK) : tmp0;
+
+ if ((op & VARIABLE_FLAG_MASK) != SLJIT_SET_OVERFLOW) {
+ /* In case of overflow, the sign bit of the two source operands must be different, and
+ - the first operand is greater if the sign bit of the result is set
+ - the first operand is less if the sign bit of the result is not set
+ The -result operation sets the corrent sign, because the result cannot be zero.
+ The overflow is considered greater, since the result must be equal to INT_MIN so its sign bit is set. */
+ FAIL_IF(push_inst(compiler, brc(0xe, 2 + 2)));
+ FAIL_IF(push_inst(compiler, (op & SLJIT_32) ? lcr(tmp1, dst_r) : lcgr(tmp1, dst_r)));
+ }
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(update_zero_overflow(compiler, op, dst_r));
+ }
+
+ if (dst & SLJIT_MEM)
+ return store_word(compiler, tmp0, dst, dstw, op & SLJIT_32);
+
+ return SLJIT_SUCCESS;
+}
+
+static const struct ins_forms multiply_forms = {
+ 0xb2520000, /* msr */
+ 0xb90c0000, /* msgr */
+ 0xb9fd0000, /* msrkc */
+ 0xb9ed0000, /* msgrkc */
+ 0x71000000, /* ms */
+ 0xe30000000051, /* msy */
+ 0xe3000000000c, /* msg */
+};
+
+static const struct ins_forms multiply_overflow_forms = {
+ 0,
+ 0,
+ 0xb9fd0000, /* msrkc */
+ 0xb9ed0000, /* msgrkc */
+ 0,
+ 0xe30000000053, /* msc */
+ 0xe30000000083, /* msgc */
+};
+
+static sljit_s32 sljit_emit_multiply(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_ins ins;
+
+ if (HAS_FLAGS(op)) {
+ /* if have_misc2 fails, this operation should be emulated. 32 bit emulation:
+ FAIL_IF(push_inst(compiler, lgfr(tmp0, src1_r)));
+ FAIL_IF(push_inst(compiler, msgfr(tmp0, src2_r)));
+ if (dst_r != tmp0) {
+ FAIL_IF(push_inst(compiler, lr(dst_r, tmp0)));
+ }
+ FAIL_IF(push_inst(compiler, aih(tmp0, 1)));
+ FAIL_IF(push_inst(compiler, nihf(tmp0, ~1U)));
+ FAIL_IF(push_inst(compiler, ipm(tmp1)));
+ FAIL_IF(push_inst(compiler, oilh(tmp1, 0x2000))); */
+
+ return emit_commutative(compiler, &multiply_overflow_forms, dst, src1, src1w, src2, src2w);
+ }
+
+ if (src2 & SLJIT_IMM) {
+ if (is_s16(src2w)) {
+ ins = (op & SLJIT_32) ? 0xa70c0000 /* mhi */ : 0xa70d0000 /* mghi */;
+ return emit_ri(compiler, ins, dst, src1, src1w, src2w, RI_A);
+ }
+
+ if (is_s32(src2w)) {
+ ins = (op & SLJIT_32) ? 0xc20100000000 /* msfi */ : 0xc20000000000 /* msgfi */;
+ return emit_ri(compiler, ins, dst, src1, src1w, src2w, RIL_A);
+ }
+ }
+
+ return emit_commutative(compiler, &multiply_forms, dst, src1, src1w, src2, src2w);
+}
+
+static sljit_s32 sljit_emit_bitwise_imm(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_uw imm, sljit_s32 count16)
+{
+ sljit_s32 mode = compiler->mode;
+ sljit_gpr dst_r = tmp0;
+ sljit_s32 needs_move = 1;
+
+ if (IS_GPR_REG(dst)) {
+ dst_r = gpr(dst & REG_MASK);
+ if (dst == src1)
+ needs_move = 0;
+ }
+
+ if (needs_move)
+ FAIL_IF(emit_move(compiler, dst_r, src1, src1w));
+
+ if (type == SLJIT_AND) {
+ if (!(mode & SLJIT_32))
+ FAIL_IF(push_inst(compiler, 0xc00a00000000 /* nihf */ | R36A(dst_r) | (imm >> 32)));
+ return push_inst(compiler, 0xc00b00000000 /* nilf */ | R36A(dst_r) | (imm & 0xffffffff));
+ }
+ else if (type == SLJIT_OR) {
+ if (count16 >= 3) {
+ FAIL_IF(push_inst(compiler, 0xc00c00000000 /* oihf */ | R36A(dst_r) | (imm >> 32)));
+ return push_inst(compiler, 0xc00d00000000 /* oilf */ | R36A(dst_r) | (imm & 0xffffffff));
+ }
+
+ if (count16 >= 2) {
+ if ((imm & 0x00000000ffffffffull) == 0)
+ return push_inst(compiler, 0xc00c00000000 /* oihf */ | R36A(dst_r) | (imm >> 32));
+ if ((imm & 0xffffffff00000000ull) == 0)
+ return push_inst(compiler, 0xc00d00000000 /* oilf */ | R36A(dst_r) | (imm & 0xffffffff));
+ }
+
+ if ((imm & 0xffff000000000000ull) != 0)
+ FAIL_IF(push_inst(compiler, 0xa5080000 /* oihh */ | R20A(dst_r) | (imm >> 48)));
+ if ((imm & 0x0000ffff00000000ull) != 0)
+ FAIL_IF(push_inst(compiler, 0xa5090000 /* oihl */ | R20A(dst_r) | ((imm >> 32) & 0xffff)));
+ if ((imm & 0x00000000ffff0000ull) != 0)
+ FAIL_IF(push_inst(compiler, 0xa50a0000 /* oilh */ | R20A(dst_r) | ((imm >> 16) & 0xffff)));
+ if ((imm & 0x000000000000ffffull) != 0 || imm == 0)
+ return push_inst(compiler, 0xa50b0000 /* oill */ | R20A(dst_r) | (imm & 0xffff));
+ return SLJIT_SUCCESS;
+ }
+
+ if ((imm & 0xffffffff00000000ull) != 0)
+ FAIL_IF(push_inst(compiler, 0xc00600000000 /* xihf */ | R36A(dst_r) | (imm >> 32)));
+ if ((imm & 0x00000000ffffffffull) != 0 || imm == 0)
+ return push_inst(compiler, 0xc00700000000 /* xilf */ | R36A(dst_r) | (imm & 0xffffffff));
+ return SLJIT_SUCCESS;
+}
+
+static const struct ins_forms bitwise_and_forms = {
+ 0x1400, /* nr */
+ 0xb9800000, /* ngr */
+ 0xb9f40000, /* nrk */
+ 0xb9e40000, /* ngrk */
+ 0x54000000, /* n */
+ 0xe30000000054, /* ny */
+ 0xe30000000080, /* ng */
+};
+
+static const struct ins_forms bitwise_or_forms = {
+ 0x1600, /* or */
+ 0xb9810000, /* ogr */
+ 0xb9f60000, /* ork */
+ 0xb9e60000, /* ogrk */
+ 0x56000000, /* o */
+ 0xe30000000056, /* oy */
+ 0xe30000000081, /* og */
+};
+
+static const struct ins_forms bitwise_xor_forms = {
+ 0x1700, /* xr */
+ 0xb9820000, /* xgr */
+ 0xb9f70000, /* xrk */
+ 0xb9e70000, /* xgrk */
+ 0x57000000, /* x */
+ 0xe30000000057, /* xy */
+ 0xe30000000082, /* xg */
+};
+
+static sljit_s32 sljit_emit_bitwise(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 type = GET_OPCODE(op);
+ const struct ins_forms *forms;
+
+ if ((src2 & SLJIT_IMM) && (!(op & SLJIT_SET_Z) || (type == SLJIT_AND && dst == (sljit_s32)tmp0))) {
+ sljit_s32 count16 = 0;
+ sljit_uw imm = (sljit_uw)src2w;
+
+ if (op & SLJIT_32)
+ imm &= 0xffffffffull;
+
+ if ((imm & 0x000000000000ffffull) != 0 || imm == 0)
+ count16++;
+ if ((imm & 0x00000000ffff0000ull) != 0)
+ count16++;
+ if ((imm & 0x0000ffff00000000ull) != 0)
+ count16++;
+ if ((imm & 0xffff000000000000ull) != 0)
+ count16++;
+
+ if (type == SLJIT_AND && dst == (sljit_s32)tmp0 && count16 == 1) {
+ sljit_gpr src_r = tmp0;
+
+ if (FAST_IS_REG(src1))
+ src_r = gpr(src1 & REG_MASK);
+ else
+ FAIL_IF(emit_move(compiler, tmp0, src1, src1w));
+
+ if ((imm & 0x000000000000ffffull) != 0 || imm == 0)
+ return push_inst(compiler, 0xa7010000 | R20A(src_r) | imm);
+ if ((imm & 0x00000000ffff0000ull) != 0)
+ return push_inst(compiler, 0xa7000000 | R20A(src_r) | (imm >> 16));
+ if ((imm & 0x0000ffff00000000ull) != 0)
+ return push_inst(compiler, 0xa7030000 | R20A(src_r) | (imm >> 32));
+ return push_inst(compiler, 0xa7020000 | R20A(src_r) | (imm >> 48));
+ }
+
+ if (!(op & SLJIT_SET_Z))
+ return sljit_emit_bitwise_imm(compiler, type, dst, src1, src1w, imm, count16);
+ }
+
+ if (type == SLJIT_AND)
+ forms = &bitwise_and_forms;
+ else if (type == SLJIT_OR)
+ forms = &bitwise_or_forms;
+ else
+ forms = &bitwise_xor_forms;
+
+ return emit_commutative(compiler, forms, dst, src1, src1w, src2, src2w);
+}
+
+static sljit_s32 sljit_emit_shift(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 type = GET_OPCODE(op);
+ sljit_gpr dst_r = FAST_IS_REG(dst) ? gpr(dst & REG_MASK) : tmp0;
+ sljit_gpr src_r = tmp0;
+ sljit_gpr base_r = tmp0;
+ sljit_ins imm = 0;
+ sljit_ins ins;
+
+ if (FAST_IS_REG(src1))
+ src_r = gpr(src1);
+ else
+ FAIL_IF(emit_move(compiler, tmp0, src1, src1w));
+
+ if (!(src2 & SLJIT_IMM)) {
+ if (FAST_IS_REG(src2))
+ base_r = gpr(src2);
+ else {
+ FAIL_IF(emit_move(compiler, tmp1, src2, src2w));
+ base_r = tmp1;
+ }
+
+ if ((op & SLJIT_32) && (type == SLJIT_MSHL || type == SLJIT_MLSHR || type == SLJIT_MASHR)) {
+ if (base_r != tmp1) {
+ FAIL_IF(push_inst(compiler, 0xec0000000055 /* risbg */ | R36A(tmp1) | R32A(base_r) | (59 << 24) | (1 << 23) | (63 << 16)));
+ base_r = tmp1;
+ } else
+ FAIL_IF(push_inst(compiler, 0xa5070000 /* nill */ | R20A(tmp1) | 0x1f));
+ }
+ } else
+ imm = (sljit_ins)(src2w & ((op & SLJIT_32) ? 0x1f : 0x3f));
+
+ if ((op & SLJIT_32) && dst_r == src_r) {
+ if (type == SLJIT_SHL || type == SLJIT_MSHL)
+ ins = 0x89000000 /* sll */;
+ else if (type == SLJIT_LSHR || type == SLJIT_MLSHR)
+ ins = 0x88000000 /* srl */;
+ else
+ ins = 0x8a000000 /* sra */;
+
+ FAIL_IF(push_inst(compiler, ins | R20A(dst_r) | R12A(base_r) | imm));
+ } else {
+ if (type == SLJIT_SHL || type == SLJIT_MSHL)
+ ins = (op & SLJIT_32) ? 0xeb00000000df /* sllk */ : 0xeb000000000d /* sllg */;
+ else if (type == SLJIT_LSHR || type == SLJIT_MLSHR)
+ ins = (op & SLJIT_32) ? 0xeb00000000de /* srlk */ : 0xeb000000000c /* srlg */;
+ else
+ ins = (op & SLJIT_32) ? 0xeb00000000dc /* srak */ : 0xeb000000000a /* srag */;
+
+ FAIL_IF(push_inst(compiler, ins | R36A(dst_r) | R32A(src_r) | R28A(base_r) | (imm << 16)));
+ }
+
+ if ((op & SLJIT_SET_Z) && type != SLJIT_ASHR)
+ return push_inst(compiler, (op & SLJIT_32) ? or(dst_r, dst_r) : ogr(dst_r, dst_r));
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 sljit_emit_rotate(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_gpr dst_r = FAST_IS_REG(dst) ? gpr(dst & REG_MASK) : tmp0;
+ sljit_gpr src_r = tmp0;
+ sljit_gpr base_r = tmp0;
+ sljit_ins imm = 0;
+ sljit_ins ins;
+
+ if (FAST_IS_REG(src1))
+ src_r = gpr(src1);
+ else
+ FAIL_IF(emit_move(compiler, tmp0, src1, src1w));
+
+ if (!(src2 & SLJIT_IMM)) {
+ if (FAST_IS_REG(src2))
+ base_r = gpr(src2);
+ else {
+ FAIL_IF(emit_move(compiler, tmp1, src2, src2w));
+ base_r = tmp1;
+ }
+ }
+
+ if (GET_OPCODE(op) == SLJIT_ROTR) {
+ if (!(src2 & SLJIT_IMM)) {
+ ins = (op & SLJIT_32) ? 0x1300 /* lcr */ : 0xb9030000 /* lcgr */;
+ FAIL_IF(push_inst(compiler, ins | R4A(tmp1) | R0A(base_r)));
+ base_r = tmp1;
+ } else
+ src2w = -src2w;
+ }
+
+ if (src2 & SLJIT_IMM)
+ imm = (sljit_ins)(src2w & ((op & SLJIT_32) ? 0x1f : 0x3f));
+
+ ins = (op & SLJIT_32) ? 0xeb000000001d /* rll */ : 0xeb000000001c /* rllg */;
+ return push_inst(compiler, ins | R36A(dst_r) | R32A(src_r) | R28A(base_r) | (imm << 16));
+}
+
+static const struct ins_forms addc_forms = {
+ 0xb9980000, /* alcr */
+ 0xb9880000, /* alcgr */
+ 0,
+ 0,
+ 0,
+ 0xe30000000098, /* alc */
+ 0xe30000000088, /* alcg */
+};
+
+static const struct ins_forms subc_forms = {
+ 0xb9990000, /* slbr */
+ 0xb9890000, /* slbgr */
+ 0,
+ 0,
+ 0,
+ 0xe30000000099, /* slb */
+ 0xe30000000089, /* slbg */
+};
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ compiler->mode = op & SLJIT_32;
+ compiler->status_flags_state = op & (VARIABLE_FLAG_MASK | SLJIT_SET_Z);
+
+ if (is_commutative(op) && (src1 & SLJIT_IMM) && !(src2 & SLJIT_IMM)) {
+ src1 ^= src2;
+ src2 ^= src1;
+ src1 ^= src2;
+
+ src1w ^= src2w;
+ src2w ^= src1w;
+ src1w ^= src2w;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD:
+ compiler->status_flags_state |= SLJIT_CURRENT_FLAGS_ADD;
+ return sljit_emit_add(compiler, op, dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_ADDC:
+ compiler->status_flags_state |= SLJIT_CURRENT_FLAGS_ADD;
+ FAIL_IF(emit_commutative(compiler, &addc_forms, dst, src1, src1w, src2, src2w));
+ if (dst & SLJIT_MEM)
+ return store_word(compiler, tmp0, dst, dstw, op & SLJIT_32);
+ return SLJIT_SUCCESS;
+ case SLJIT_SUB:
+ compiler->status_flags_state |= SLJIT_CURRENT_FLAGS_SUB;
+ return sljit_emit_sub(compiler, op, dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_SUBC:
+ compiler->status_flags_state |= SLJIT_CURRENT_FLAGS_SUB;
+ FAIL_IF(emit_non_commutative(compiler, &subc_forms, dst, src1, src1w, src2, src2w));
+ if (dst & SLJIT_MEM)
+ return store_word(compiler, tmp0, dst, dstw, op & SLJIT_32);
+ return SLJIT_SUCCESS;
+ case SLJIT_MUL:
+ FAIL_IF(sljit_emit_multiply(compiler, op, dst, src1, src1w, src2, src2w));
+ break;
+ case SLJIT_AND:
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ FAIL_IF(sljit_emit_bitwise(compiler, op, dst, src1, src1w, src2, src2w));
+ break;
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ FAIL_IF(sljit_emit_shift(compiler, op, dst, src1, src1w, src2, src2w));
+ break;
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ FAIL_IF(sljit_emit_rotate(compiler, op, dst, src1, src1w, src2, src2w));
+ break;
+ }
+
+ if (dst & SLJIT_MEM)
+ return store_word(compiler, tmp0, dst, dstw, op & SLJIT_32);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, (sljit_s32)tmp0, 0, src1, src1w, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 is_right;
+ sljit_sw bit_length = (op & SLJIT_32) ? 32 : 64;
+ sljit_gpr src_dst_r = gpr(src_dst);
+ sljit_gpr src1_r = tmp0;
+ sljit_gpr src2_r = tmp1;
+ sljit_ins ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w));
+
+ is_right = (GET_OPCODE(op) == SLJIT_LSHR || GET_OPCODE(op) == SLJIT_MLSHR);
+
+ if (src_dst == src1) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, (is_right ? SLJIT_ROTR : SLJIT_ROTL) | (op & SLJIT_32), src_dst, 0, src_dst, 0, src2, src2w);
+ }
+
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ if (src1 & SLJIT_MEM)
+ FAIL_IF(load_word(compiler, tmp0, src1, src1w, op & SLJIT_32));
+ else if (src1 & SLJIT_IMM)
+ FAIL_IF(push_load_imm_inst(compiler, tmp0, src1w));
+ else
+ src1_r = gpr(src1);
+
+ if (src2 & SLJIT_IMM) {
+ src2w &= bit_length - 1;
+
+ if (src2w == 0)
+ return SLJIT_SUCCESS;
+ } else if (!(src2 & SLJIT_MEM))
+ src2_r = gpr(src2);
+ else
+ FAIL_IF(load_word(compiler, tmp1, src2, src2w, op & SLJIT_32));
+
+ if (src2 & SLJIT_IMM) {
+ if (op & SLJIT_32) {
+ ins = is_right ? 0x88000000 /* srl */ : 0x89000000 /* sll */;
+ FAIL_IF(push_inst(compiler, ins | R20A(src_dst_r) | (sljit_ins)src2w));
+ } else {
+ ins = is_right ? 0xeb000000000c /* srlg */ : 0xeb000000000d /* sllg */;
+ FAIL_IF(push_inst(compiler, ins | R36A(src_dst_r) | R32A(src_dst_r) | ((sljit_ins)src2w << 16)));
+ }
+
+ ins = 0xec0000000055 /* risbg */;
+
+ if (is_right) {
+ src2w = bit_length - src2w;
+ ins |= ((sljit_ins)(64 - bit_length) << 24) | ((sljit_ins)(63 - src2w) << 16) | ((sljit_ins)src2w << 8);
+ } else
+ ins |= ((sljit_ins)(64 - src2w) << 24) | ((sljit_ins)63 << 16) | ((sljit_ins)src2w << 8);
+
+ return push_inst(compiler, ins | R36A(src_dst_r) | R32A(src1_r));
+ }
+
+ if (op & SLJIT_32) {
+ if (GET_OPCODE(op) == SLJIT_MSHL || GET_OPCODE(op) == SLJIT_MLSHR) {
+ if (src2_r != tmp1) {
+ FAIL_IF(push_inst(compiler, 0xec0000000055 /* risbg */ | R36A(tmp1) | R32A(src2_r) | (59 << 24) | (1 << 23) | (63 << 16)));
+ src2_r = tmp1;
+ } else
+ FAIL_IF(push_inst(compiler, 0xa5070000 /* nill */ | R20A(tmp1) | 0x1f));
+ }
+
+ ins = is_right ? 0x88000000 /* srl */ : 0x89000000 /* sll */;
+ FAIL_IF(push_inst(compiler, ins | R20A(src_dst_r) | R12A(src2_r)));
+
+ if (src2_r != tmp1) {
+ FAIL_IF(push_inst(compiler, 0xa50f0000 /* llill */ | R20A(tmp1) | 0x1f));
+ FAIL_IF(push_inst(compiler, 0x1700 /* xr */ | R4A(tmp1) | R0A(src2_r)));
+ } else
+ FAIL_IF(push_inst(compiler, 0xc00700000000 /* xilf */ | R36A(tmp1) | 0x1f));
+
+ if (src1_r == tmp0) {
+ ins = is_right ? 0x89000000 /* sll */ : 0x88000000 /* srl */;
+ FAIL_IF(push_inst(compiler, ins | R20A(tmp0) | R12A(tmp1) | 0x1));
+ } else {
+ ins = is_right ? 0xeb00000000df /* sllk */ : 0xeb00000000de /* srlk */;
+ FAIL_IF(push_inst(compiler, ins | R36A(tmp0) | R32A(src1_r) | R28A(tmp1) | (0x1 << 16)));
+ }
+
+ return push_inst(compiler, 0x1600 /* or */ | R4A(src_dst_r) | R0A(tmp0));
+ }
+
+ ins = is_right ? 0xeb000000000c /* srlg */ : 0xeb000000000d /* sllg */;
+ FAIL_IF(push_inst(compiler, ins | R36A(src_dst_r) | R32A(src_dst_r) | R28A(src2_r)));
+
+ ins = is_right ? 0xeb000000000d /* sllg */ : 0xeb000000000c /* srlg */;
+
+ if (!(op & SLJIT_SHIFT_INTO_NON_ZERO)) {
+ if (src2_r != tmp1)
+ FAIL_IF(push_inst(compiler, 0xa50f0000 /* llill */ | R20A(tmp1) | 0x3f));
+
+ FAIL_IF(push_inst(compiler, ins | R36A(tmp0) | R32A(src1_r) | (0x1 << 16)));
+ src1_r = tmp0;
+
+ if (src2_r != tmp1)
+ FAIL_IF(push_inst(compiler, 0xb9820000 /* xgr */ | R4A(tmp1) | R0A(src2_r)));
+ else
+ FAIL_IF(push_inst(compiler, 0xc00700000000 /* xilf */ | R36A(tmp1) | 0x3f));
+ } else
+ FAIL_IF(push_inst(compiler, 0xb9030000 /* lcgr */ | R4A(tmp1) | R0A(src2_r)));
+
+ FAIL_IF(push_inst(compiler, ins | R36A(tmp0) | R32A(src1_r) | R28A(tmp1)));
+ return push_inst(compiler, 0xb9810000 /* ogr */ | R4A(src_dst_r) | R0A(tmp0));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(
+ struct sljit_compiler *compiler,
+ sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+ sljit_gpr src_r;
+ struct addr addr;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_src(compiler, op, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ switch (op) {
+ case SLJIT_FAST_RETURN:
+ src_r = FAST_IS_REG(src) ? gpr(src) : tmp1;
+ if (src & SLJIT_MEM)
+ FAIL_IF(load_word(compiler, tmp1, src, srcw, 0));
+
+ return push_inst(compiler, br(src_r));
+ case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN:
+ return SLJIT_SUCCESS;
+ case SLJIT_PREFETCH_L1:
+ case SLJIT_PREFETCH_L2:
+ case SLJIT_PREFETCH_L3:
+ case SLJIT_PREFETCH_ONCE:
+ FAIL_IF(make_addr_bxy(compiler, &addr, src, srcw, tmp1));
+ return push_inst(compiler, 0xe31000000036 /* pfd */ | R32A(addr.index) | R28A(addr.base) | disp_s20(addr.offset));
+ default:
+ return SLJIT_SUCCESS;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_register_index(reg));
+ return (sljit_s32)gpr(reg);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
+ return (sljit_s32)fgpr(reg);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ sljit_ins ins = 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_custom(compiler, instruction, size));
+
+ memcpy((sljit_u8 *)&ins + sizeof(ins) - size, instruction, size);
+ return push_inst(compiler, ins);
+}
+
+/* --------------------------------------------------------------------- */
+/* Floating point operators */
+/* --------------------------------------------------------------------- */
+
+#define FLOAT_LOAD 0
+#define FLOAT_STORE 1
+
+static sljit_s32 float_mem(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ struct addr addr;
+ sljit_ins ins;
+
+ SLJIT_ASSERT(mem & SLJIT_MEM);
+
+ if ((mem & OFFS_REG_MASK) || is_u12(memw) || !is_s20(memw)) {
+ FAIL_IF(make_addr_bx(compiler, &addr, mem, memw, tmp1));
+
+ if (op & FLOAT_STORE)
+ ins = (op & SLJIT_32) ? 0x70000000 /* ste */ : 0x60000000 /* std */;
+ else
+ ins = (op & SLJIT_32) ? 0x78000000 /* le */ : 0x68000000 /* ld */;
+
+ return push_inst(compiler, ins | F20(reg) | R16A(addr.index) | R12A(addr.base) | (sljit_ins)addr.offset);
+ }
+
+ FAIL_IF(make_addr_bxy(compiler, &addr, mem, memw, tmp1));
+
+ if (op & FLOAT_STORE)
+ ins = (op & SLJIT_32) ? 0xed0000000066 /* stey */ : 0xed0000000067 /* stdy */;
+ else
+ ins = (op & SLJIT_32) ? 0xed0000000064 /* ley */ : 0xed0000000065 /* ldy */;
+
+ return push_inst(compiler, ins | F36(reg) | R32A(addr.index) | R28A(addr.base) | disp_s20(addr.offset));
+}
+
+static sljit_s32 emit_float(struct sljit_compiler *compiler, sljit_ins ins_r, sljit_ins ins,
+ sljit_s32 reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ struct addr addr;
+
+ if (!(src & SLJIT_MEM))
+ return push_inst(compiler, ins_r | F4(reg) | F0(src));
+
+ FAIL_IF(make_addr_bx(compiler, &addr, src, srcw, tmp1));
+ return push_inst(compiler, ins | F36(reg) | R32A(addr.index) | R28A(addr.base) | ((sljit_ins)addr.offset << 16));
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_ins dst_r = FAST_IS_REG(dst) ? gpr(dst) : tmp0;
+ sljit_ins ins;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(float_mem(compiler, FLOAT_LOAD | (op & SLJIT_32), TMP_FREG1, src, srcw));
+ src = TMP_FREG1;
+ }
+
+ /* M3 is set to 5 */
+ if (GET_OPCODE(op) == SLJIT_CONV_SW_FROM_F64)
+ ins = (op & SLJIT_32) ? 0xb3a85000 /* cgebr */ : 0xb3a95000 /* cgdbr */;
+ else
+ ins = (op & SLJIT_32) ? 0xb3985000 /* cfebr */ : 0xb3995000 /* cfdbr */;
+
+ FAIL_IF(push_inst(compiler, ins | R4A(dst_r) | F0(src)));
+
+ if (dst & SLJIT_MEM)
+ return store_word(compiler, dst_r, dst, dstw, GET_OPCODE(op) >= SLJIT_CONV_S32_FROM_F64);
+
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+ sljit_ins ins;
+
+ if (src & SLJIT_IMM) {
+ FAIL_IF(push_load_imm_inst(compiler, tmp0, srcw));
+ src = (sljit_s32)tmp0;
+ }
+ else if (src & SLJIT_MEM) {
+ FAIL_IF(load_word(compiler, tmp0, src, srcw, GET_OPCODE(op) >= SLJIT_CONV_F64_FROM_S32));
+ src = (sljit_s32)tmp0;
+ }
+
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_SW)
+ ins = (op & SLJIT_32) ? 0xb3a40000 /* cegbr */ : 0xb3a50000 /* cdgbr */;
+ else
+ ins = (op & SLJIT_32) ? 0xb3940000 /* cefbr */ : 0xb3950000 /* cdfbr */;
+
+ FAIL_IF(push_inst(compiler, ins | F4(dst_r) | R0(src)));
+
+ if (dst & SLJIT_MEM)
+ return float_mem(compiler, FLOAT_STORE | (op & SLJIT_32), TMP_FREG1, dst, dstw);
+
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_ins ins_r, ins;
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(float_mem(compiler, FLOAT_LOAD | (op & SLJIT_32), TMP_FREG1, src1, src1w));
+ src1 = TMP_FREG1;
+ }
+
+ if (op & SLJIT_32) {
+ ins_r = 0xb3090000 /* cebr */;
+ ins = 0xed0000000009 /* ceb */;
+ } else {
+ ins_r = 0xb3190000 /* cdbr */;
+ ins = 0xed0000000019 /* cdb */;
+ }
+
+ return emit_float(compiler, ins_r, ins, src1, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r;
+ sljit_ins ins;
+
+ CHECK_ERROR();
+
+ SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (op == SLJIT_CONV_F64_FROM_F32)
+ FAIL_IF(emit_float(compiler, 0xb3040000 /* ldebr */, 0xed0000000004 /* ldeb */, dst_r, src, srcw));
+ else {
+ if (src & SLJIT_MEM) {
+ FAIL_IF(float_mem(compiler, FLOAT_LOAD | (op == SLJIT_CONV_F32_FROM_F64 ? 0 : (op & SLJIT_32)), dst_r, src, srcw));
+ src = dst_r;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV_F64:
+ if (FAST_IS_REG(dst)) {
+ if (dst == src)
+ return SLJIT_SUCCESS;
+
+ ins = (op & SLJIT_32) ? 0x3800 /* ler */ : 0x2800 /* ldr */;
+ break;
+ }
+ return float_mem(compiler, FLOAT_STORE | (op & SLJIT_32), src, dst, dstw);
+ case SLJIT_CONV_F64_FROM_F32:
+ /* Only SLJIT_CONV_F32_FROM_F64. */
+ ins = 0xb3440000 /* ledbr */;
+ break;
+ case SLJIT_NEG_F64:
+ ins = (op & SLJIT_32) ? 0xb3030000 /* lcebr */ : 0xb3130000 /* lcdbr */;
+ break;
+ default:
+ SLJIT_ASSERT(GET_OPCODE(op) == SLJIT_ABS_F64);
+ ins = (op & SLJIT_32) ? 0xb3000000 /* lpebr */ : 0xb3100000 /* lpdbr */;
+ break;
+ }
+
+ FAIL_IF(push_inst(compiler, ins | F4(dst_r) | F0(src)));
+ }
+
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+
+ SLJIT_ASSERT(dst_r == TMP_FREG1);
+
+ return float_mem(compiler, FLOAT_STORE | (op & SLJIT_32), TMP_FREG1, dst, dstw);
+}
+
+#define FLOAT_MOV(op, dst_r, src_r) \
+ (((op & SLJIT_32) ? 0x3800 /* ler */ : 0x2800 /* ldr */) | F4(dst_r) | F0(src_r))
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r = TMP_FREG1;
+ sljit_ins ins_r, ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ do {
+ if (FAST_IS_REG(dst)) {
+ dst_r = dst;
+
+ if (dst == src1)
+ break;
+
+ if (dst == src2) {
+ if (GET_OPCODE(op) == SLJIT_ADD_F64 || GET_OPCODE(op) == SLJIT_MUL_F64) {
+ src2 = src1;
+ src2w = src1w;
+ src1 = dst;
+ break;
+ }
+
+ FAIL_IF(push_inst(compiler, FLOAT_MOV(op, TMP_FREG1, src2)));
+ src2 = TMP_FREG1;
+ }
+ }
+
+ if (src1 & SLJIT_MEM)
+ FAIL_IF(float_mem(compiler, FLOAT_LOAD | (op & SLJIT_32), dst_r, src1, src1w));
+ else
+ FAIL_IF(push_inst(compiler, FLOAT_MOV(op, dst_r, src1)));
+ } while (0);
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD_F64:
+ ins_r = (op & SLJIT_32) ? 0xb30a0000 /* aebr */ : 0xb31a0000 /* adbr */;
+ ins = (op & SLJIT_32) ? 0xed000000000a /* aeb */ : 0xed000000001a /* adb */;
+ break;
+ case SLJIT_SUB_F64:
+ ins_r = (op & SLJIT_32) ? 0xb30b0000 /* sebr */ : 0xb31b0000 /* sdbr */;
+ ins = (op & SLJIT_32) ? 0xed000000000b /* seb */ : 0xed000000001b /* sdb */;
+ break;
+ case SLJIT_MUL_F64:
+ ins_r = (op & SLJIT_32) ? 0xb3170000 /* meebr */ : 0xb31c0000 /* mdbr */;
+ ins = (op & SLJIT_32) ? 0xed0000000017 /* meeb */ : 0xed000000001c /* mdb */;
+ break;
+ default:
+ SLJIT_ASSERT(GET_OPCODE(op) == SLJIT_DIV_F64);
+ ins_r = (op & SLJIT_32) ? 0xb30d0000 /* debr */ : 0xb31d0000 /* ddbr */;
+ ins = (op & SLJIT_32) ? 0xed000000000d /* deb */ : 0xed000000001d /* ddb */;
+ break;
+ }
+
+ FAIL_IF(emit_float(compiler, ins_r, ins, dst_r, src2, src2w));
+
+ if (dst & SLJIT_MEM)
+ return float_mem(compiler, FLOAT_STORE | (op & SLJIT_32), TMP_FREG1, dst, dstw);
+
+ SLJIT_ASSERT(dst_r != TMP_FREG1);
+ return SLJIT_SUCCESS;
+}
+
+/* --------------------------------------------------------------------- */
+/* Other instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ if (FAST_IS_REG(dst))
+ return push_inst(compiler, lgr(gpr(dst), link_r));
+
+ /* memory */
+ return store_word(compiler, link_r, dst, dstw, 0);
+}
+
+/* --------------------------------------------------------------------- */
+/* Conditional instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ struct sljit_label *label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_label(compiler));
+
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ return compiler->last_label;
+
+ label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
+ PTR_FAIL_IF(!label);
+ set_label(label, compiler);
+ return label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ sljit_u8 mask = ((type & 0xff) < SLJIT_JUMP) ? get_cc(compiler, type & 0xff) : 0xf;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_jump(compiler, type));
+
+ /* record jump */
+ struct sljit_jump *jump = (struct sljit_jump *)
+ ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ jump->addr = compiler->size;
+
+ /* emit jump instruction */
+ type &= 0xff;
+ if (type >= SLJIT_FAST_CALL)
+ PTR_FAIL_IF(push_inst(compiler, brasl(link_r, 0)));
+ else
+ PTR_FAIL_IF(push_inst(compiler, brcl(mask, 0)));
+
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ SLJIT_UNUSED_ARG(arg_types);
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ if (type & SLJIT_CALL_RETURN) {
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, r14));
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ sljit_gpr src_r = FAST_IS_REG(src) ? gpr(src) : tmp1;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
+
+ if (src & SLJIT_IMM) {
+ SLJIT_ASSERT(!(srcw & 1)); /* target address must be even */
+ FAIL_IF(push_load_imm_inst(compiler, src_r, srcw));
+ }
+ else if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(load_word(compiler, src_r, src, srcw, 0 /* 64-bit */));
+ }
+
+ /* emit jump instruction */
+ if (type >= SLJIT_FAST_CALL)
+ return push_inst(compiler, basr(link_r, src_r));
+
+ return push_inst(compiler, br(src_r));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ SLJIT_ASSERT(gpr(TMP_REG2) == tmp1);
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(load_word(compiler, tmp1, src, srcw, 0 /* 64-bit */));
+ src = TMP_REG2;
+ srcw = 0;
+ }
+
+ if (type & SLJIT_CALL_RETURN) {
+ if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, lgr(tmp1, gpr(src))));
+ src = TMP_REG2;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, r14));
+ type = SLJIT_JUMP;
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ sljit_u8 mask = get_cc(compiler, type);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
+
+ sljit_gpr dst_r = FAST_IS_REG(dst) ? gpr(dst & REG_MASK) : tmp0;
+ sljit_gpr loc_r = tmp1;
+ switch (GET_OPCODE(op)) {
+ case SLJIT_AND:
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ compiler->status_flags_state = op & SLJIT_SET_Z;
+
+ /* dst is also source operand */
+ if (dst & SLJIT_MEM)
+ FAIL_IF(load_word(compiler, dst_r, dst, dstw, op & SLJIT_32));
+
+ break;
+ case SLJIT_MOV32:
+ op |= SLJIT_32;
+ /* fallthrough */
+ case SLJIT_MOV:
+ /* can write straight into destination */
+ loc_r = dst_r;
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ }
+
+ /* TODO(mundaym): fold into cmov helper function? */
+ #define LEVAL(i) i(loc_r, 1, mask)
+ if (have_lscond2()) {
+ FAIL_IF(push_load_imm_inst(compiler, loc_r, 0));
+ FAIL_IF(push_inst(compiler,
+ WHEN2(op & SLJIT_32, lochi, locghi)));
+ } else {
+ /* TODO(mundaym): no load/store-on-condition 2 facility (ipm? branch-and-set?) */
+ abort();
+ }
+ #undef LEVAL
+
+ /* apply bitwise op and set condition codes */
+ switch (GET_OPCODE(op)) {
+ #define LEVAL(i) i(dst_r, loc_r)
+ case SLJIT_AND:
+ FAIL_IF(push_inst(compiler,
+ WHEN2(op & SLJIT_32, nr, ngr)));
+ break;
+ case SLJIT_OR:
+ FAIL_IF(push_inst(compiler,
+ WHEN2(op & SLJIT_32, or, ogr)));
+ break;
+ case SLJIT_XOR:
+ FAIL_IF(push_inst(compiler,
+ WHEN2(op & SLJIT_32, xr, xgr)));
+ break;
+ #undef LEVAL
+ }
+
+ /* store result to memory if required */
+ if (dst & SLJIT_MEM)
+ return store_word(compiler, dst_r, dst, dstw, (op & SLJIT_32));
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_ins mask = get_cc(compiler, type & ~SLJIT_32);
+ sljit_gpr src_r;
+ sljit_ins ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+ if (type & SLJIT_32)
+ srcw = (sljit_s32)srcw;
+
+ if (have_lscond2() && (src & SLJIT_IMM) && is_s16(srcw)) {
+ ins = (type & SLJIT_32) ? 0xec0000000042 /* lochi */ : 0xec0000000046 /* locghi */;
+ return push_inst(compiler, ins | R36A(gpr(dst_reg)) | (mask << 32) | (sljit_ins)(srcw & 0xffff) << 16);
+ }
+
+ if (src & SLJIT_IMM) {
+ FAIL_IF(push_load_imm_inst(compiler, tmp0, srcw));
+ src_r = tmp0;
+ } else
+ src_r = gpr(src);
+
+ if (have_lscond1()) {
+ ins = (type & SLJIT_32) ? 0xb9f20000 /* locr */ : 0xb9e20000 /* locgr */;
+ return push_inst(compiler, ins | (mask << 12) | R4A(gpr(dst_reg)) | R0A(src_r));
+ }
+
+ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_ins ins, reg1, reg2, base, offs = 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (!(reg & REG_PAIR_MASK))
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+
+ ADJUST_LOCAL_OFFSET(mem, memw);
+
+ base = gpr(mem & REG_MASK);
+ reg1 = gpr(REG_PAIR_FIRST(reg));
+ reg2 = gpr(REG_PAIR_SECOND(reg));
+
+ if (mem & OFFS_REG_MASK) {
+ memw &= 0x3;
+ offs = gpr(OFFS_REG(mem));
+
+ if (memw != 0) {
+ FAIL_IF(push_inst(compiler, 0xeb000000000d /* sllg */ | R36A(tmp1) | R32A(offs) | ((sljit_ins)memw << 16)));
+ offs = tmp1;
+ } else if (!(type & SLJIT_MEM_STORE) && (base == reg1 || base == reg2) && (offs == reg1 || offs == reg2)) {
+ FAIL_IF(push_inst(compiler, 0xb9f80000 | R12A(tmp1) | R4A(base) | R0A(offs)));
+ base = tmp1;
+ offs = 0;
+ }
+
+ memw = 0;
+ } else if (memw < -0x80000 || memw > 0x7ffff - ((reg2 == reg1 + 1) ? 0 : SSIZE_OF(sw))) {
+ FAIL_IF(push_load_imm_inst(compiler, tmp1, memw));
+
+ if (base == 0)
+ base = tmp1;
+ else
+ offs = tmp1;
+
+ memw = 0;
+ }
+
+ if (offs == 0 && reg2 == (reg1 + 1)) {
+ ins = (type & SLJIT_MEM_STORE) ? 0xeb0000000024 /* stmg */ : 0xeb0000000004 /* lmg */;
+ return push_inst(compiler, ins | R36A(reg1) | R32A(reg2) | R28A(base) | disp_s20((sljit_s32)memw));
+ }
+
+ ins = ((type & SLJIT_MEM_STORE) ? 0xe30000000024 /* stg */ : 0xe30000000004 /* lg */) | R32A(offs) | R28A(base);
+
+ if (!(type & SLJIT_MEM_STORE) && base == reg1) {
+ FAIL_IF(push_inst(compiler, ins | R36A(reg2) | disp_s20((sljit_s32)memw + SSIZE_OF(sw))));
+ return push_inst(compiler, ins | R36A(reg1) | disp_s20((sljit_s32)memw));
+ }
+
+ FAIL_IF(push_inst(compiler, ins | R36A(reg1) | disp_s20((sljit_s32)memw)));
+ return push_inst(compiler, ins | R36A(reg2) | disp_s20((sljit_s32)memw + SSIZE_OF(sw)));
+}
+
+/* --------------------------------------------------------------------- */
+/* Other instructions */
+/* --------------------------------------------------------------------- */
+
+/* On s390x we build a literal pool to hold constants. This has two main
+ advantages:
+
+ 1. we only need one instruction in the instruction stream (LGRL)
+ 2. we can store 64 bit addresses and use 32 bit offsets
+
+ To retrofit the extra information needed to build the literal pool we
+ add a new sljit_s390x_const struct that contains the initial value but
+ can still be cast to a sljit_const. */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ struct sljit_s390x_const *const_;
+ sljit_gpr dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
+
+ const_ = (struct sljit_s390x_const*)ensure_abuf(compiler,
+ sizeof(struct sljit_s390x_const));
+ PTR_FAIL_IF(!const_);
+ set_const((struct sljit_const*)const_, compiler);
+ const_->init_value = init_value;
+
+ dst_r = FAST_IS_REG(dst) ? gpr(dst & REG_MASK) : tmp0;
+ if (have_genext())
+ PTR_FAIL_IF(push_inst(compiler, sljit_ins_const | lgrl(dst_r, 0)));
+ else {
+ PTR_FAIL_IF(push_inst(compiler, sljit_ins_const | larl(tmp1, 0)));
+ PTR_FAIL_IF(push_inst(compiler, lg(dst_r, 0, r0, tmp1)));
+ }
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(store_word(compiler, dst_r, dst, dstw, 0 /* always 64-bit */));
+
+ return (struct sljit_const*)const_;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ /* Update the constant pool. */
+ sljit_uw *ptr = (sljit_uw *)addr;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS(ptr, ptr + 1, 0);
+ *ptr = new_target;
+ SLJIT_UPDATE_WX_FLAGS(ptr, ptr + 1, 1);
+ SLJIT_CACHE_FLUSH(ptr, ptr + 1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label *sljit_emit_put_label(
+ struct sljit_compiler *compiler,
+ sljit_s32 dst, sljit_sw dstw)
+{
+ struct sljit_put_label *put_label;
+ sljit_gpr dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label));
+ PTR_FAIL_IF(!put_label);
+ set_put_label(put_label, compiler, 0);
+
+ dst_r = FAST_IS_REG(dst) ? gpr(dst & REG_MASK) : tmp0;
+
+ if (have_genext())
+ PTR_FAIL_IF(push_inst(compiler, lgrl(dst_r, 0)));
+ else {
+ PTR_FAIL_IF(push_inst(compiler, larl(tmp1, 0)));
+ PTR_FAIL_IF(push_inst(compiler, lg(dst_r, 0, r0, tmp1)));
+ }
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(store_word(compiler, dst_r, dst, dstw, 0));
+
+ return put_label;
+}
+
+/* TODO(carenas): EVAL probably should move up or be refactored */
+#undef WHEN2
+#undef EVAL
+
+#undef tmp1
+#undef tmp0
+
+/* TODO(carenas): undef other macros that spill like is_u12? */
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeX86_32.c b/contrib/libs/pcre2/src/sljit/sljitNativeX86_32.c
new file mode 100644
index 00000000000..08da03026d4
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeX86_32.c
@@ -0,0 +1,1298 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* x86 32-bit arch dependent functions. */
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+static sljit_s32 emit_do_imm(struct sljit_compiler *compiler, sljit_u8 opcode, sljit_sw imm)
+{
+ sljit_u8 *inst;
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + sizeof(sljit_sw));
+ FAIL_IF(!inst);
+ INC_SIZE(1 + sizeof(sljit_sw));
+ *inst++ = opcode;
+ sljit_unaligned_store_sw(inst, imm);
+ return SLJIT_SUCCESS;
+}
+
+/* Size contains the flags as well. */
+static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_uw size,
+ /* The register or immediate operand. */
+ sljit_s32 a, sljit_sw imma,
+ /* The general operand (not immediate). */
+ sljit_s32 b, sljit_sw immb)
+{
+ sljit_u8 *inst;
+ sljit_u8 *buf_ptr;
+ sljit_u8 reg_map_b;
+ sljit_uw flags = size;
+ sljit_uw inst_size;
+
+ /* Both cannot be switched on. */
+ SLJIT_ASSERT((flags & (EX86_BIN_INS | EX86_SHIFT_INS)) != (EX86_BIN_INS | EX86_SHIFT_INS));
+ /* Size flags not allowed for typed instructions. */
+ SLJIT_ASSERT(!(flags & (EX86_BIN_INS | EX86_SHIFT_INS)) || (flags & (EX86_BYTE_ARG | EX86_HALF_ARG)) == 0);
+ /* Both size flags cannot be switched on. */
+ SLJIT_ASSERT((flags & (EX86_BYTE_ARG | EX86_HALF_ARG)) != (EX86_BYTE_ARG | EX86_HALF_ARG));
+ /* SSE2 and immediate is not possible. */
+ SLJIT_ASSERT(!(a & SLJIT_IMM) || !(flags & EX86_SSE2));
+ SLJIT_ASSERT((flags & (EX86_PREF_F2 | EX86_PREF_F3)) != (EX86_PREF_F2 | EX86_PREF_F3)
+ && (flags & (EX86_PREF_F2 | EX86_PREF_66)) != (EX86_PREF_F2 | EX86_PREF_66)
+ && (flags & (EX86_PREF_F3 | EX86_PREF_66)) != (EX86_PREF_F3 | EX86_PREF_66));
+
+ size &= 0xf;
+ inst_size = size;
+
+ if (flags & (EX86_PREF_F2 | EX86_PREF_F3))
+ inst_size++;
+ if (flags & EX86_PREF_66)
+ inst_size++;
+
+ /* Calculate size of b. */
+ inst_size += 1; /* mod r/m byte. */
+ if (b & SLJIT_MEM) {
+ if (!(b & REG_MASK))
+ inst_size += sizeof(sljit_sw);
+ else {
+ if (immb != 0 && !(b & OFFS_REG_MASK)) {
+ /* Immediate operand. */
+ if (immb <= 127 && immb >= -128)
+ inst_size += sizeof(sljit_s8);
+ else
+ inst_size += sizeof(sljit_sw);
+ }
+ else if (reg_map[b & REG_MASK] == 5) {
+ /* Swap registers if possible. */
+ if ((b & OFFS_REG_MASK) && (immb & 0x3) == 0 && reg_map[OFFS_REG(b)] != 5)
+ b = SLJIT_MEM | OFFS_REG(b) | TO_OFFS_REG(b & REG_MASK);
+ else
+ inst_size += sizeof(sljit_s8);
+ }
+
+ if (reg_map[b & REG_MASK] == 4 && !(b & OFFS_REG_MASK))
+ b |= TO_OFFS_REG(SLJIT_SP);
+
+ if (b & OFFS_REG_MASK)
+ inst_size += 1; /* SIB byte. */
+ }
+ }
+
+ /* Calculate size of a. */
+ if (a & SLJIT_IMM) {
+ if (flags & EX86_BIN_INS) {
+ if (imma <= 127 && imma >= -128) {
+ inst_size += 1;
+ flags |= EX86_BYTE_ARG;
+ } else
+ inst_size += 4;
+ }
+ else if (flags & EX86_SHIFT_INS) {
+ SLJIT_ASSERT(imma <= 0x1f);
+ if (imma != 1) {
+ inst_size++;
+ flags |= EX86_BYTE_ARG;
+ }
+ } else if (flags & EX86_BYTE_ARG)
+ inst_size++;
+ else if (flags & EX86_HALF_ARG)
+ inst_size += sizeof(short);
+ else
+ inst_size += sizeof(sljit_sw);
+ }
+ else
+ SLJIT_ASSERT(!(flags & EX86_SHIFT_INS) || a == SLJIT_PREF_SHIFT_REG);
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + inst_size);
+ PTR_FAIL_IF(!inst);
+
+ /* Encoding the byte. */
+ INC_SIZE(inst_size);
+ if (flags & EX86_PREF_F2)
+ *inst++ = 0xf2;
+ if (flags & EX86_PREF_F3)
+ *inst++ = 0xf3;
+ if (flags & EX86_PREF_66)
+ *inst++ = 0x66;
+
+ buf_ptr = inst + size;
+
+ /* Encode mod/rm byte. */
+ if (!(flags & EX86_SHIFT_INS)) {
+ if ((flags & EX86_BIN_INS) && (a & SLJIT_IMM))
+ *inst = (flags & EX86_BYTE_ARG) ? GROUP_BINARY_83 : GROUP_BINARY_81;
+
+ if (a & SLJIT_IMM)
+ *buf_ptr = 0;
+ else if (!(flags & EX86_SSE2_OP1))
+ *buf_ptr = U8(reg_map[a] << 3);
+ else
+ *buf_ptr = U8(a << 3);
+ }
+ else {
+ if (a & SLJIT_IMM) {
+ if (imma == 1)
+ *inst = GROUP_SHIFT_1;
+ else
+ *inst = GROUP_SHIFT_N;
+ } else
+ *inst = GROUP_SHIFT_CL;
+ *buf_ptr = 0;
+ }
+
+ if (!(b & SLJIT_MEM)) {
+ *buf_ptr = U8(*buf_ptr | MOD_REG | (!(flags & EX86_SSE2_OP2) ? reg_map[b] : b));
+ buf_ptr++;
+ } else if (b & REG_MASK) {
+ reg_map_b = reg_map[b & REG_MASK];
+
+ if (!(b & OFFS_REG_MASK) || (b & OFFS_REG_MASK) == TO_OFFS_REG(SLJIT_SP)) {
+ if (immb != 0 || reg_map_b == 5) {
+ if (immb <= 127 && immb >= -128)
+ *buf_ptr |= 0x40;
+ else
+ *buf_ptr |= 0x80;
+ }
+
+ if (!(b & OFFS_REG_MASK))
+ *buf_ptr++ |= reg_map_b;
+ else {
+ *buf_ptr++ |= 0x04;
+ *buf_ptr++ = U8(reg_map_b | (reg_map[OFFS_REG(b)] << 3));
+ }
+
+ if (immb != 0 || reg_map_b == 5) {
+ if (immb <= 127 && immb >= -128)
+ *buf_ptr++ = U8(immb); /* 8 bit displacement. */
+ else {
+ sljit_unaligned_store_sw(buf_ptr, immb); /* 32 bit displacement. */
+ buf_ptr += sizeof(sljit_sw);
+ }
+ }
+ }
+ else {
+ if (reg_map_b == 5)
+ *buf_ptr |= 0x40;
+
+ *buf_ptr++ |= 0x04;
+ *buf_ptr++ = U8(reg_map_b | (reg_map[OFFS_REG(b)] << 3) | (immb << 6));
+
+ if (reg_map_b == 5)
+ *buf_ptr++ = 0;
+ }
+ }
+ else {
+ *buf_ptr++ |= 0x05;
+ sljit_unaligned_store_sw(buf_ptr, immb); /* 32 bit displacement. */
+ buf_ptr += sizeof(sljit_sw);
+ }
+
+ if (a & SLJIT_IMM) {
+ if (flags & EX86_BYTE_ARG)
+ *buf_ptr = U8(imma);
+ else if (flags & EX86_HALF_ARG)
+ sljit_unaligned_store_s16(buf_ptr, (sljit_s16)imma);
+ else if (!(flags & EX86_SHIFT_INS))
+ sljit_unaligned_store_sw(buf_ptr, imma);
+ }
+
+ return !(flags & EX86_SHIFT_INS) ? inst : (inst + 1);
+}
+
+/* --------------------------------------------------------------------- */
+/* Enter / return */
+/* --------------------------------------------------------------------- */
+
+static sljit_u8* generate_far_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr, sljit_sw executable_offset)
+{
+ sljit_uw type = jump->flags >> TYPE_SHIFT;
+
+ if (type == SLJIT_JUMP) {
+ *code_ptr++ = JMP_i32;
+ jump->addr++;
+ }
+ else if (type >= SLJIT_FAST_CALL) {
+ *code_ptr++ = CALL_i32;
+ jump->addr++;
+ }
+ else {
+ *code_ptr++ = GROUP_0F;
+ *code_ptr++ = get_jump_code(type);
+ jump->addr += 2;
+ }
+
+ if (jump->flags & JUMP_LABEL)
+ jump->flags |= PATCH_MW;
+ else
+ sljit_unaligned_store_sw(code_ptr, (sljit_sw)(jump->u.target - (jump->addr + 4) - (sljit_uw)executable_offset));
+ code_ptr += 4;
+
+ return code_ptr;
+}
+
+#define ENTER_TMP_TO_R4 0x00001
+#define ENTER_TMP_TO_S 0x00002
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 word_arg_count, saved_arg_count, float_arg_count;
+ sljit_s32 size, args_size, types, status;
+ sljit_s32 kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+ sljit_u8 *inst;
+#ifdef _WIN32
+ sljit_s32 r2_offset = -1;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ /* Emit ENDBR32 at function entry if needed. */
+ FAIL_IF(emit_endbranch(compiler));
+
+ SLJIT_COMPILE_ASSERT(SLJIT_FR0 == 1, float_register_index_start);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ word_arg_count = 0;
+ status = 0;
+
+ if (options & SLJIT_ENTER_REG_ARG) {
+ args_size = 3 * SSIZE_OF(sw);
+
+ while (arg_types) {
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) {
+ word_arg_count++;
+ if (word_arg_count >= 4)
+ status |= ENTER_TMP_TO_R4;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ compiler->args_size = 0;
+ } else {
+ types = arg_types;
+ saved_arg_count = 0;
+ float_arg_count = 0;
+ args_size = SSIZE_OF(sw);
+ while (types) {
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ float_arg_count++;
+ FAIL_IF(emit_sse2_load(compiler, 0, float_arg_count, SLJIT_MEM1(SLJIT_SP), args_size));
+ args_size += SSIZE_OF(f64);
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ float_arg_count++;
+ FAIL_IF(emit_sse2_load(compiler, 1, float_arg_count, SLJIT_MEM1(SLJIT_SP), args_size));
+ args_size += SSIZE_OF(f32);
+ break;
+ default:
+ word_arg_count++;
+
+ if (!(types & SLJIT_ARG_TYPE_SCRATCH_REG))
+ saved_arg_count++;
+
+ if (word_arg_count == 4) {
+ if (types & SLJIT_ARG_TYPE_SCRATCH_REG) {
+ status |= ENTER_TMP_TO_R4;
+ arg_types &= ~(SLJIT_ARG_FULL_MASK << 3 * SLJIT_ARG_SHIFT);
+ } else if (saved_arg_count == 4) {
+ status |= ENTER_TMP_TO_S;
+ arg_types &= ~(SLJIT_ARG_FULL_MASK << 3 * SLJIT_ARG_SHIFT);
+ }
+ }
+
+ args_size += SSIZE_OF(sw);
+ break;
+ }
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ args_size -= SSIZE_OF(sw);
+ compiler->args_size = args_size;
+ }
+
+ size = (scratches > 9 ? (scratches - 9) : 0) + (saveds <= 3 ? saveds : 3) - kept_saveds_count;
+ if (!(options & SLJIT_ENTER_REG_ARG))
+ size++;
+
+ if (size != 0) {
+ inst = (sljit_u8*)ensure_buf(compiler, (sljit_uw)(size + 1));
+ FAIL_IF(!inst);
+
+ INC_SIZE((sljit_uw)size);
+
+ if (!(options & SLJIT_ENTER_REG_ARG))
+ PUSH_REG(reg_map[TMP_REG1]);
+
+ if ((saveds > 2 && kept_saveds_count <= 2) || scratches > 9)
+ PUSH_REG(reg_map[SLJIT_S2]);
+ if ((saveds > 1 && kept_saveds_count <= 1) || scratches > 10)
+ PUSH_REG(reg_map[SLJIT_S1]);
+ if ((saveds > 0 && kept_saveds_count == 0) || scratches > 11)
+ PUSH_REG(reg_map[SLJIT_S0]);
+
+ size *= SSIZE_OF(sw);
+ }
+
+ if (status & (ENTER_TMP_TO_R4 | ENTER_TMP_TO_S))
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), args_size + size);
+
+ size += SSIZE_OF(sw);
+
+ local_size = ((SLJIT_LOCALS_OFFSET_BASE + local_size + size + 0xf) & ~0xf) - size;
+ compiler->local_size = local_size;
+
+ word_arg_count = 0;
+ saved_arg_count = 0;
+ args_size = size;
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ args_size += SSIZE_OF(f64);
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ args_size += SSIZE_OF(f32);
+ break;
+ default:
+ word_arg_count++;
+ SLJIT_ASSERT(word_arg_count <= 3 || (word_arg_count == 4 && !(status & (ENTER_TMP_TO_R4 | ENTER_TMP_TO_S))));
+
+ if (arg_types & SLJIT_ARG_TYPE_SCRATCH_REG) {
+#ifdef _WIN32
+ if (word_arg_count == 3 && local_size > 4 * 4096)
+ r2_offset = local_size + args_size;
+ else
+#endif
+ EMIT_MOV(compiler, word_arg_count, 0, SLJIT_MEM1(SLJIT_SP), args_size);
+
+ } else {
+ EMIT_MOV(compiler, SLJIT_S0 - saved_arg_count, 0, SLJIT_MEM1(SLJIT_SP), args_size);
+ saved_arg_count++;
+ }
+
+ args_size += SSIZE_OF(sw);
+ break;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ SLJIT_ASSERT(SLJIT_LOCALS_OFFSET > 0);
+
+#ifdef _WIN32
+ SLJIT_ASSERT(r2_offset == -1 || local_size > 4 * 4096);
+
+ if (local_size > 4096) {
+ if (local_size <= 4 * 4096) {
+ BINARY_IMM32(OR, 0, SLJIT_MEM1(SLJIT_SP), -4096);
+
+ if (local_size > 2 * 4096)
+ BINARY_IMM32(OR, 0, SLJIT_MEM1(SLJIT_SP), -4096 * 2);
+ if (local_size > 3 * 4096)
+ BINARY_IMM32(OR, 0, SLJIT_MEM1(SLJIT_SP), -4096 * 3);
+ }
+ else {
+ if (options & SLJIT_ENTER_REG_ARG) {
+ SLJIT_ASSERT(r2_offset == -1);
+
+ inst = (sljit_u8*)ensure_buf(compiler, (sljit_uw)(1 + 1));
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ PUSH_REG(reg_map[SLJIT_R2]);
+
+ local_size -= SSIZE_OF(sw);
+ r2_offset = local_size;
+ }
+
+ EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_IMM, local_size >> 12);
+
+ BINARY_IMM32(OR, 0, SLJIT_MEM1(SLJIT_SP), -4096);
+ BINARY_IMM32(SUB, 4096, SLJIT_SP, 0);
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2);
+ FAIL_IF(!inst);
+
+ INC_SIZE(2);
+ inst[0] = LOOP_i8;
+ inst[1] = (sljit_u8)-16;
+ local_size &= 0xfff;
+ }
+ }
+
+ if (local_size > 0) {
+ BINARY_IMM32(OR, 0, SLJIT_MEM1(SLJIT_SP), -local_size);
+ BINARY_IMM32(SUB, local_size, SLJIT_SP, 0);
+ }
+
+ if (r2_offset != -1)
+ EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), r2_offset);
+
+#else /* !_WIN32 */
+
+ SLJIT_ASSERT(local_size > 0);
+
+ BINARY_IMM32(SUB, local_size, SLJIT_SP, 0);
+
+#endif /* _WIN32 */
+
+ size = SLJIT_LOCALS_OFFSET_BASE - SSIZE_OF(sw);
+ kept_saveds_count = SLJIT_R3 - kept_saveds_count;
+
+ while (saved_arg_count > 3) {
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), size, kept_saveds_count, 0);
+ kept_saveds_count++;
+ size -= SSIZE_OF(sw);
+ saved_arg_count--;
+ }
+
+ if (status & (ENTER_TMP_TO_R4 | ENTER_TMP_TO_S)) {
+ if (status & ENTER_TMP_TO_R4)
+ size = 2 * SSIZE_OF(sw);
+
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), size, TMP_REG1, 0);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 args_size;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ args_size = 0;
+
+ if (!(options & SLJIT_ENTER_REG_ARG)) {
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ args_size += SSIZE_OF(f64);
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ args_size += SSIZE_OF(f32);
+ break;
+ default:
+ args_size += SSIZE_OF(sw);
+ break;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+ }
+
+ compiler->args_size = args_size;
+
+ /* [esp+0] for saving temporaries and for function calls. */
+
+ saveds = (1 + (scratches > 9 ? (scratches - 9) : 0) + (saveds <= 3 ? saveds : 3) - SLJIT_KEPT_SAVEDS_COUNT(options)) * SSIZE_OF(sw);
+
+ /* Saving ebp. */
+ if (!(options & SLJIT_ENTER_REG_ARG))
+ saveds += SSIZE_OF(sw);
+
+ compiler->local_size = ((SLJIT_LOCALS_OFFSET_BASE + local_size + saveds + 0xf) & ~0xf) - saveds;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 is_return_to)
+{
+ sljit_s32 kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(compiler->options);
+ sljit_s32 local_size, saveds;
+ sljit_uw size;
+ sljit_u8 *inst;
+
+ size = (sljit_uw)((compiler->scratches > 9 ? (compiler->scratches - 9) : 0) +
+ (compiler->saveds <= 3 ? compiler->saveds : 3) - kept_saveds_count);
+
+ local_size = compiler->local_size;
+
+ if (!(compiler->options & SLJIT_ENTER_REG_ARG))
+ size++;
+ else if (is_return_to && size == 0) {
+ local_size += SSIZE_OF(sw);
+ is_return_to = 0;
+ }
+
+ if (local_size > 0)
+ BINARY_IMM32(ADD, local_size, SLJIT_SP, 0);
+
+ if (size == 0)
+ return SLJIT_SUCCESS;
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+
+ INC_SIZE(size);
+
+ saveds = compiler->saveds;
+
+ if ((saveds > 0 && kept_saveds_count == 0) || compiler->scratches > 11)
+ POP_REG(reg_map[SLJIT_S0]);
+ if ((saveds > 1 && kept_saveds_count <= 1) || compiler->scratches > 10)
+ POP_REG(reg_map[SLJIT_S1]);
+ if ((saveds > 2 && kept_saveds_count <= 2) || compiler->scratches > 9)
+ POP_REG(reg_map[SLJIT_S2]);
+
+ if (!(compiler->options & SLJIT_ENTER_REG_ARG))
+ POP_REG(reg_map[TMP_REG1]);
+
+ if (is_return_to)
+ BINARY_IMM32(ADD, sizeof(sljit_sw), SLJIT_SP, 0);
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ sljit_u8 *inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ SLJIT_ASSERT(compiler->args_size >= 0);
+ SLJIT_ASSERT(compiler->local_size > 0);
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ RET();
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 src_r;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ if ((src & SLJIT_MEM) || (src > SLJIT_R2 && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options)))) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+
+ src_r = (compiler->options & SLJIT_ENTER_REG_ARG) ? TMP_REG1 : SLJIT_R1;
+
+ EMIT_MOV(compiler, src_r, 0, src, srcw);
+ src = src_r;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 1));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Call / return instructions */
+/* --------------------------------------------------------------------- */
+
+static sljit_s32 call_get_stack_size(sljit_s32 arg_types, sljit_s32 *word_arg_count_ptr)
+{
+ sljit_sw stack_size = 0;
+ sljit_s32 word_arg_count = 0;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ stack_size += SSIZE_OF(f64);
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ stack_size += SSIZE_OF(f32);
+ break;
+ default:
+ word_arg_count++;
+ stack_size += SSIZE_OF(sw);
+ break;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ if (word_arg_count_ptr)
+ *word_arg_count_ptr = word_arg_count;
+
+ if (stack_size <= 4 * SSIZE_OF(sw))
+ return 0;
+
+ return ((stack_size - (4 * SSIZE_OF(sw)) + 0xf) & ~0xf);
+}
+
+static sljit_s32 call_with_args(struct sljit_compiler *compiler,
+ sljit_s32 arg_types, sljit_sw stack_size, sljit_s32 word_arg_count, sljit_s32 keep_tmp1)
+{
+ sljit_s32 float_arg_count = 0, arg4_reg = 0, arg_offset;
+ sljit_u8 *inst;
+
+ if (word_arg_count >= 4) {
+ arg4_reg = SLJIT_R0;
+
+ if (!keep_tmp1) {
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), 2 * SSIZE_OF(sw));
+ arg4_reg = TMP_REG1;
+ }
+ }
+
+ if (stack_size > 0)
+ BINARY_IMM32(SUB, stack_size, SLJIT_SP, 0);
+
+ arg_offset = 0;
+ word_arg_count = 0;
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ float_arg_count++;
+ FAIL_IF(emit_sse2_store(compiler, 0, SLJIT_MEM1(SLJIT_SP), arg_offset, float_arg_count));
+ arg_offset += SSIZE_OF(f64);
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ float_arg_count++;
+ FAIL_IF(emit_sse2_store(compiler, 1, SLJIT_MEM1(SLJIT_SP), arg_offset, float_arg_count));
+ arg_offset += SSIZE_OF(f32);
+ break;
+ default:
+ word_arg_count++;
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), arg_offset, (word_arg_count >= 4) ? arg4_reg : word_arg_count, 0);
+
+ if (word_arg_count == 1 && arg4_reg == SLJIT_R0)
+ EMIT_MOV(compiler, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_SP), 2 * SSIZE_OF(sw) + stack_size);
+
+ arg_offset += SSIZE_OF(sw);
+ break;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 post_call_with_args(struct sljit_compiler *compiler,
+ sljit_s32 arg_types, sljit_s32 stack_size)
+{
+ sljit_u8 *inst;
+ sljit_s32 single;
+
+ if (stack_size > 0)
+ BINARY_IMM32(ADD, stack_size, SLJIT_SP, 0);
+
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64)
+ return SLJIT_SUCCESS;
+
+ single = ((arg_types & SLJIT_ARG_MASK) == SLJIT_ARG_TYPE_F32);
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 3);
+ FAIL_IF(!inst);
+ INC_SIZE(3);
+ inst[0] = single ? FSTPS : FSTPD;
+ inst[1] = (0x03 << 3) | 0x04;
+ inst[2] = (0x04 << 3) | reg_map[SLJIT_SP];
+
+ return emit_sse2_load(compiler, single, SLJIT_FR0, SLJIT_MEM1(SLJIT_SP), 0);
+}
+
+static sljit_s32 tail_call_with_args(struct sljit_compiler *compiler,
+ sljit_s32 *extra_space, sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_sw args_size, saved_regs_size;
+ sljit_sw types, word_arg_count, float_arg_count;
+ sljit_sw stack_size, prev_stack_size, min_size, offset;
+ sljit_sw word_arg4_offset;
+ sljit_u8 r2_offset = 0;
+ sljit_s32 kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(compiler->options);
+ sljit_u8* inst;
+
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+
+ saved_regs_size = (1 + (compiler->scratches > 9 ? (compiler->scratches - 9) : 0)
+ + (compiler->saveds <= 3 ? compiler->saveds : 3) - kept_saveds_count) * SSIZE_OF(sw);
+
+ word_arg_count = 0;
+ float_arg_count = 0;
+ arg_types >>= SLJIT_ARG_SHIFT;
+ types = 0;
+ args_size = 0;
+
+ while (arg_types != 0) {
+ types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK);
+
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ args_size += SSIZE_OF(f64);
+ float_arg_count++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ args_size += SSIZE_OF(f32);
+ float_arg_count++;
+ break;
+ default:
+ word_arg_count++;
+ args_size += SSIZE_OF(sw);
+ break;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ if (args_size <= compiler->args_size) {
+ *extra_space = 0;
+ stack_size = args_size + SSIZE_OF(sw) + saved_regs_size;
+
+ offset = stack_size + compiler->local_size;
+
+ if (!(src & SLJIT_IMM) && src != SLJIT_R0) {
+ if (word_arg_count >= 1) {
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), 0, SLJIT_R0, 0);
+ r2_offset = sizeof(sljit_sw);
+ }
+ EMIT_MOV(compiler, SLJIT_R0, 0, src, srcw);
+ }
+
+ while (types != 0) {
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(emit_sse2_store(compiler, 0, SLJIT_MEM1(SLJIT_SP), offset, float_arg_count));
+ float_arg_count--;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ offset -= SSIZE_OF(f32);
+ FAIL_IF(emit_sse2_store(compiler, 0, SLJIT_MEM1(SLJIT_SP), offset, float_arg_count));
+ float_arg_count--;
+ break;
+ default:
+ switch (word_arg_count) {
+ case 1:
+ offset -= SSIZE_OF(sw);
+ if (r2_offset != 0) {
+ EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), 0);
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R2, 0);
+ } else
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R0, 0);
+ break;
+ case 2:
+ offset -= SSIZE_OF(sw);
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R1, 0);
+ break;
+ case 3:
+ offset -= SSIZE_OF(sw);
+ break;
+ case 4:
+ offset -= SSIZE_OF(sw);
+ EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), 2 * SSIZE_OF(sw));
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R2, 0);
+ break;
+ }
+ word_arg_count--;
+ break;
+ }
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return emit_stack_frame_release(compiler, 0);
+ }
+
+ stack_size = args_size + SSIZE_OF(sw);
+
+ if (word_arg_count >= 1 && !(src & SLJIT_IMM) && src != SLJIT_R0) {
+ r2_offset = SSIZE_OF(sw);
+ stack_size += SSIZE_OF(sw);
+ }
+
+ if (word_arg_count >= 3)
+ stack_size += SSIZE_OF(sw);
+
+ prev_stack_size = SSIZE_OF(sw) + saved_regs_size;
+ min_size = prev_stack_size + compiler->local_size;
+
+ word_arg4_offset = 2 * SSIZE_OF(sw);
+
+ if (stack_size > min_size) {
+ BINARY_IMM32(SUB, stack_size - min_size, SLJIT_SP, 0);
+ if (src == SLJIT_MEM1(SLJIT_SP))
+ srcw += stack_size - min_size;
+ word_arg4_offset += stack_size - min_size;
+ }
+ else
+ stack_size = min_size;
+
+ if (word_arg_count >= 3) {
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), r2_offset, SLJIT_R2, 0);
+
+ if (word_arg_count >= 4)
+ EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), word_arg4_offset);
+ }
+
+ if (!(src & SLJIT_IMM) && src != SLJIT_R0) {
+ if (word_arg_count >= 1) {
+ SLJIT_ASSERT(r2_offset == sizeof(sljit_sw));
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), 0, SLJIT_R0, 0);
+ }
+ EMIT_MOV(compiler, SLJIT_R0, 0, src, srcw);
+ }
+
+ /* Restore saved registers. */
+ offset = stack_size - 2 * SSIZE_OF(sw);
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), offset);
+
+ if (compiler->saveds > 2 || compiler->scratches > 9) {
+ offset -= SSIZE_OF(sw);
+ EMIT_MOV(compiler, SLJIT_S2, 0, SLJIT_MEM1(SLJIT_SP), offset);
+ }
+ if ((compiler->saveds > 1 && kept_saveds_count <= 1) || compiler->scratches > 10) {
+ offset -= SSIZE_OF(sw);
+ EMIT_MOV(compiler, SLJIT_S1, 0, SLJIT_MEM1(SLJIT_SP), offset);
+ }
+ if ((compiler->saveds > 0 && kept_saveds_count == 0) || compiler->scratches > 11) {
+ offset -= SSIZE_OF(sw);
+ EMIT_MOV(compiler, SLJIT_S0, 0, SLJIT_MEM1(SLJIT_SP), offset);
+ }
+
+ /* Copy fourth argument and return address. */
+ offset = stack_size - SSIZE_OF(sw);
+ *extra_space = args_size;
+
+ if (word_arg_count >= 4) {
+ offset -= SSIZE_OF(sw);
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R2, 0);
+ }
+
+ while (types != 0) {
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(emit_sse2_store(compiler, 0, SLJIT_MEM1(SLJIT_SP), offset, float_arg_count));
+ float_arg_count--;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ offset -= SSIZE_OF(f32);
+ FAIL_IF(emit_sse2_store(compiler, 0, SLJIT_MEM1(SLJIT_SP), offset, float_arg_count));
+ float_arg_count--;
+ break;
+ default:
+ switch (word_arg_count) {
+ case 1:
+ offset -= SSIZE_OF(sw);
+ if (r2_offset != 0) {
+ EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), 0);
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R2, 0);
+ } else
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R0, 0);
+ break;
+ case 2:
+ offset -= SSIZE_OF(sw);
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R1, 0);
+ break;
+ case 3:
+ offset -= SSIZE_OF(sw);
+ EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), r2_offset);
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R2, 0);
+ break;
+ }
+ word_arg_count--;
+ break;
+ }
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ SLJIT_ASSERT(offset >= 0);
+
+ if (offset == 0)
+ return SLJIT_SUCCESS;
+
+ BINARY_IMM32(ADD, offset, SLJIT_SP, 0);
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_tail_call_end(struct sljit_compiler *compiler, sljit_s32 extra_space)
+{
+ /* Called when stack consumption cannot be reduced to 0. */
+ sljit_u8 *inst;
+
+ BINARY_IMM32(ADD, extra_space, SLJIT_SP, 0);
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ RET();
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 tail_call_reg_arg_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types)
+{
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 kept_saveds_count, offset;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64)
+ word_arg_count++;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ if (word_arg_count < 4)
+ return SLJIT_SUCCESS;
+
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), 2 * SSIZE_OF(sw));
+
+ kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(compiler->options);
+ offset = compiler->local_size + 3 * SSIZE_OF(sw);
+
+ if ((compiler->saveds > 0 && kept_saveds_count == 0) || compiler->scratches > 11)
+ offset += SSIZE_OF(sw);
+ if ((compiler->saveds > 1 && kept_saveds_count <= 1) || compiler->scratches > 10)
+ offset += SSIZE_OF(sw);
+ if ((compiler->saveds > 2 && kept_saveds_count <= 2) || compiler->scratches > 9)
+ offset += SSIZE_OF(sw);
+
+ return emit_mov(compiler, SLJIT_MEM1(SLJIT_SP), offset, TMP_REG1, 0);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ struct sljit_jump *jump;
+ sljit_sw stack_size = 0;
+ sljit_s32 word_arg_count;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ if (type & SLJIT_CALL_RETURN) {
+ if ((type & 0xff) == SLJIT_CALL_REG_ARG) {
+ PTR_FAIL_IF(tail_call_reg_arg_with_args(compiler, arg_types));
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, 0));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP));
+ }
+
+ stack_size = type;
+ PTR_FAIL_IF(tail_call_with_args(compiler, &stack_size, arg_types, SLJIT_IMM, 0));
+
+ SLJIT_SKIP_CHECKS(compiler);
+
+ if (stack_size == 0)
+ return sljit_emit_jump(compiler, SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP));
+
+ jump = sljit_emit_jump(compiler, type);
+ PTR_FAIL_IF(jump == NULL);
+
+ PTR_FAIL_IF(emit_tail_call_end(compiler, stack_size));
+ return jump;
+ }
+
+ if ((type & 0xff) == SLJIT_CALL_REG_ARG) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+ }
+
+ stack_size = call_get_stack_size(arg_types, &word_arg_count);
+ PTR_FAIL_IF(call_with_args(compiler, arg_types, stack_size, word_arg_count, 0));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ jump = sljit_emit_jump(compiler, type);
+ PTR_FAIL_IF(jump == NULL);
+
+ PTR_FAIL_IF(post_call_with_args(compiler, arg_types, stack_size));
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_sw stack_size = 0;
+ sljit_s32 word_arg_count;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ if (type & SLJIT_CALL_RETURN) {
+ if ((type & 0xff) == SLJIT_CALL_REG_ARG) {
+ FAIL_IF(tail_call_reg_arg_with_args(compiler, arg_types));
+
+ if ((src & SLJIT_MEM) || (src > SLJIT_R2 && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options)))) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+
+ EMIT_MOV(compiler, TMP_REG1, 0, src, srcw);
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+ }
+
+ stack_size = type;
+ FAIL_IF(tail_call_with_args(compiler, &stack_size, arg_types, src, srcw));
+
+ if (!(src & SLJIT_IMM)) {
+ src = SLJIT_R0;
+ srcw = 0;
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+
+ if (stack_size == 0)
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+
+ FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw));
+ return emit_tail_call_end(compiler, stack_size);
+ }
+
+ if ((type & 0xff) == SLJIT_CALL_REG_ARG) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+ }
+
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+
+ if (src & SLJIT_MEM) {
+ EMIT_MOV(compiler, TMP_REG1, 0, src, srcw);
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ stack_size = call_get_stack_size(arg_types, &word_arg_count);
+ FAIL_IF(call_with_args(compiler, arg_types, stack_size, word_arg_count, src == TMP_REG1));
+
+ if (stack_size > 0 && src == SLJIT_MEM1(SLJIT_SP))
+ srcw += stack_size;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw));
+
+ return post_call_with_args(compiler, arg_types, stack_size);
+}
+
+static SLJIT_INLINE sljit_s32 emit_fmov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+
+ if (compiler->options & SLJIT_ENTER_REG_ARG) {
+ if (src == SLJIT_FR0)
+ return SLJIT_SUCCESS;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_fop1(compiler, op, SLJIT_RETURN_FREG, 0, src, srcw);
+ }
+
+ if (FAST_IS_REG(src)) {
+ FAIL_IF(emit_sse2_store(compiler, op & SLJIT_32, SLJIT_MEM1(SLJIT_SP), 0, src));
+
+ src = SLJIT_MEM1(SLJIT_SP);
+ srcw = 0;
+ } else {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ }
+
+ inst = emit_x86_instruction(compiler, 1 | EX86_SSE2_OP1, 0, 0, src, srcw);
+ *inst = (op & SLJIT_32) ? FLDS : FLDL;
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ sljit_u8 *inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ CHECK_EXTRA_REGS(dst, dstw, (void)0);
+
+ if (FAST_IS_REG(dst)) {
+ /* Unused dest is possible here. */
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+
+ INC_SIZE(1);
+ POP_REG(reg_map[dst]);
+ return SLJIT_SUCCESS;
+ }
+
+ /* Memory. */
+ inst = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst++ = POP_rm;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_fast_return(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8 *inst;
+
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+
+ if (FAST_IS_REG(src)) {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + 1);
+ FAIL_IF(!inst);
+
+ INC_SIZE(1 + 1);
+ PUSH_REG(reg_map[src]);
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, 0, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_FF;
+ *inst |= PUSH_rm;
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ }
+
+ RET();
+ return SLJIT_SUCCESS;
+}
+
+/* --------------------------------------------------------------------- */
+/* Other operations */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_u8* inst;
+ sljit_s32 i, next, reg_idx, offset;
+ sljit_u8 regs[2];
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (!(reg & REG_PAIR_MASK))
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+
+ ADJUST_LOCAL_OFFSET(mem, memw);
+
+ regs[0] = U8(REG_PAIR_FIRST(reg));
+ regs[1] = U8(REG_PAIR_SECOND(reg));
+
+ next = SSIZE_OF(sw);
+
+ if (!(type & SLJIT_MEM_STORE) && (regs[0] == (mem & REG_MASK) || regs[0] == OFFS_REG(mem))) {
+ if (regs[1] == (mem & REG_MASK) || regs[1] == OFFS_REG(mem)) {
+ /* None of them are virtual register so TMP_REG1 will not be used. */
+ EMIT_MOV(compiler, TMP_REG1, 0, OFFS_REG(mem), 0);
+
+ if (regs[1] == OFFS_REG(mem))
+ next = -SSIZE_OF(sw);
+
+ mem = (mem & ~OFFS_REG_MASK) | TO_OFFS_REG(TMP_REG1);
+ } else {
+ next = -SSIZE_OF(sw);
+
+ if (!(mem & OFFS_REG_MASK))
+ memw += SSIZE_OF(sw);
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ reg_idx = next > 0 ? i : (i ^ 0x1);
+ reg = regs[reg_idx];
+
+ offset = -1;
+
+ if (reg >= SLJIT_R3 && reg <= SLJIT_S3) {
+ offset = (2 * SSIZE_OF(sw)) + ((reg) - SLJIT_R3) * SSIZE_OF(sw);
+ reg = TMP_REG1;
+
+ if (type & SLJIT_MEM_STORE)
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), offset);
+ }
+
+ if ((mem & OFFS_REG_MASK) && (reg_idx == 1)) {
+ inst = (sljit_u8*)ensure_buf(compiler, (sljit_uw)(1 + 4));
+ FAIL_IF(!inst);
+
+ INC_SIZE(4);
+
+ inst[0] = (type & SLJIT_MEM_STORE) ? MOV_rm_r : MOV_r_rm;
+ inst[1] = 0x44 | U8(reg_map[reg] << 3);
+ inst[2] = U8(memw << 6) | U8(reg_map[OFFS_REG(mem)] << 3) | reg_map[mem & REG_MASK];
+ inst[3] = sizeof(sljit_sw);
+ } else if (type & SLJIT_MEM_STORE) {
+ EMIT_MOV(compiler, mem, memw, reg, 0);
+ } else {
+ EMIT_MOV(compiler, reg, 0, mem, memw);
+ }
+
+ if (!(mem & OFFS_REG_MASK))
+ memw += next;
+
+ if (!(type & SLJIT_MEM_STORE) && offset != -1)
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, TMP_REG1, 0);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 skip_frames_before_return(struct sljit_compiler *compiler)
+{
+ sljit_sw size;
+
+ /* Don't adjust shadow stack if it isn't enabled. */
+ if (!cpu_has_shadow_stack())
+ return SLJIT_SUCCESS;
+
+ SLJIT_ASSERT(compiler->args_size >= 0);
+ SLJIT_ASSERT(compiler->local_size > 0);
+
+ size = compiler->local_size;
+ size += (1 + (compiler->scratches > 9 ? (compiler->scratches - 9) : 0)
+ + (compiler->saveds <= 3 ? compiler->saveds : 3)) * SSIZE_OF(sw);
+
+ return adjust_shadow_stack(compiler, SLJIT_MEM1(SLJIT_SP), size);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeX86_64.c b/contrib/libs/pcre2/src/sljit/sljitNativeX86_64.c
new file mode 100644
index 00000000000..4e938ffcf31
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeX86_64.c
@@ -0,0 +1,1092 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* x86 64-bit arch dependent functions. */
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+static sljit_s32 emit_load_imm64(struct sljit_compiler *compiler, sljit_s32 reg, sljit_sw imm)
+{
+ sljit_u8 *inst;
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2 + sizeof(sljit_sw));
+ FAIL_IF(!inst);
+ INC_SIZE(2 + sizeof(sljit_sw));
+ *inst++ = REX_W | ((reg_map[reg] <= 7) ? 0 : REX_B);
+ *inst++ = U8(MOV_r_i32 | (reg_map[reg] & 0x7));
+ sljit_unaligned_store_sw(inst, imm);
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_do_imm32(struct sljit_compiler *compiler, sljit_u8 rex, sljit_u8 opcode, sljit_sw imm)
+{
+ sljit_u8 *inst;
+ sljit_uw length = (rex ? 2 : 1) + sizeof(sljit_s32);
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + length);
+ FAIL_IF(!inst);
+ INC_SIZE(length);
+ if (rex)
+ *inst++ = rex;
+ *inst++ = opcode;
+ sljit_unaligned_store_s32(inst, (sljit_s32)imm);
+ return SLJIT_SUCCESS;
+}
+
+static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_uw size,
+ /* The register or immediate operand. */
+ sljit_s32 a, sljit_sw imma,
+ /* The general operand (not immediate). */
+ sljit_s32 b, sljit_sw immb)
+{
+ sljit_u8 *inst;
+ sljit_u8 *buf_ptr;
+ sljit_u8 rex = 0;
+ sljit_u8 reg_lmap_b;
+ sljit_uw flags = size;
+ sljit_uw inst_size;
+
+ /* The immediate operand must be 32 bit. */
+ SLJIT_ASSERT(!(a & SLJIT_IMM) || compiler->mode32 || IS_HALFWORD(imma));
+ /* Both cannot be switched on. */
+ SLJIT_ASSERT((flags & (EX86_BIN_INS | EX86_SHIFT_INS)) != (EX86_BIN_INS | EX86_SHIFT_INS));
+ /* Size flags not allowed for typed instructions. */
+ SLJIT_ASSERT(!(flags & (EX86_BIN_INS | EX86_SHIFT_INS)) || (flags & (EX86_BYTE_ARG | EX86_HALF_ARG)) == 0);
+ /* Both size flags cannot be switched on. */
+ SLJIT_ASSERT((flags & (EX86_BYTE_ARG | EX86_HALF_ARG)) != (EX86_BYTE_ARG | EX86_HALF_ARG));
+ /* SSE2 and immediate is not possible. */
+ SLJIT_ASSERT(!(a & SLJIT_IMM) || !(flags & EX86_SSE2));
+ SLJIT_ASSERT((flags & (EX86_PREF_F2 | EX86_PREF_F3)) != (EX86_PREF_F2 | EX86_PREF_F3)
+ && (flags & (EX86_PREF_F2 | EX86_PREF_66)) != (EX86_PREF_F2 | EX86_PREF_66)
+ && (flags & (EX86_PREF_F3 | EX86_PREF_66)) != (EX86_PREF_F3 | EX86_PREF_66));
+
+ size &= 0xf;
+ inst_size = size;
+
+ if (!compiler->mode32 && !(flags & EX86_NO_REXW))
+ rex |= REX_W;
+ else if (flags & EX86_REX)
+ rex |= REX;
+
+ if (flags & (EX86_PREF_F2 | EX86_PREF_F3))
+ inst_size++;
+ if (flags & EX86_PREF_66)
+ inst_size++;
+
+ /* Calculate size of b. */
+ inst_size += 1; /* mod r/m byte. */
+ if (b & SLJIT_MEM) {
+ if (!(b & OFFS_REG_MASK) && NOT_HALFWORD(immb)) {
+ PTR_FAIL_IF(emit_load_imm64(compiler, TMP_REG2, immb));
+ immb = 0;
+ if (b & REG_MASK)
+ b |= TO_OFFS_REG(TMP_REG2);
+ else
+ b |= TMP_REG2;
+ }
+
+ if (!(b & REG_MASK))
+ inst_size += 1 + sizeof(sljit_s32); /* SIB byte required to avoid RIP based addressing. */
+ else {
+ if (immb != 0 && !(b & OFFS_REG_MASK)) {
+ /* Immediate operand. */
+ if (immb <= 127 && immb >= -128)
+ inst_size += sizeof(sljit_s8);
+ else
+ inst_size += sizeof(sljit_s32);
+ }
+ else if (reg_lmap[b & REG_MASK] == 5) {
+ /* Swap registers if possible. */
+ if ((b & OFFS_REG_MASK) && (immb & 0x3) == 0 && reg_lmap[OFFS_REG(b)] != 5)
+ b = SLJIT_MEM | OFFS_REG(b) | TO_OFFS_REG(b & REG_MASK);
+ else
+ inst_size += sizeof(sljit_s8);
+ }
+
+ if (reg_map[b & REG_MASK] >= 8)
+ rex |= REX_B;
+
+ if (reg_lmap[b & REG_MASK] == 4 && !(b & OFFS_REG_MASK))
+ b |= TO_OFFS_REG(SLJIT_SP);
+
+ if (b & OFFS_REG_MASK) {
+ inst_size += 1; /* SIB byte. */
+ if (reg_map[OFFS_REG(b)] >= 8)
+ rex |= REX_X;
+ }
+ }
+ }
+ else if (!(flags & EX86_SSE2_OP2)) {
+ if (reg_map[b] >= 8)
+ rex |= REX_B;
+ }
+ else if (freg_map[b] >= 8)
+ rex |= REX_B;
+
+ if (a & SLJIT_IMM) {
+ if (flags & EX86_BIN_INS) {
+ if (imma <= 127 && imma >= -128) {
+ inst_size += 1;
+ flags |= EX86_BYTE_ARG;
+ } else
+ inst_size += 4;
+ }
+ else if (flags & EX86_SHIFT_INS) {
+ SLJIT_ASSERT(imma <= (compiler->mode32 ? 0x1f : 0x3f));
+ if (imma != 1) {
+ inst_size++;
+ flags |= EX86_BYTE_ARG;
+ }
+ } else if (flags & EX86_BYTE_ARG)
+ inst_size++;
+ else if (flags & EX86_HALF_ARG)
+ inst_size += sizeof(short);
+ else
+ inst_size += sizeof(sljit_s32);
+ }
+ else {
+ SLJIT_ASSERT(!(flags & EX86_SHIFT_INS) || a == SLJIT_PREF_SHIFT_REG);
+ /* reg_map[SLJIT_PREF_SHIFT_REG] is less than 8. */
+ if (!(flags & EX86_SSE2_OP1)) {
+ if (reg_map[a] >= 8)
+ rex |= REX_R;
+ }
+ else if (freg_map[a] >= 8)
+ rex |= REX_R;
+ }
+
+ if (rex)
+ inst_size++;
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + inst_size);
+ PTR_FAIL_IF(!inst);
+
+ /* Encoding the byte. */
+ INC_SIZE(inst_size);
+ if (flags & EX86_PREF_F2)
+ *inst++ = 0xf2;
+ if (flags & EX86_PREF_F3)
+ *inst++ = 0xf3;
+ if (flags & EX86_PREF_66)
+ *inst++ = 0x66;
+ if (rex)
+ *inst++ = rex;
+ buf_ptr = inst + size;
+
+ /* Encode mod/rm byte. */
+ if (!(flags & EX86_SHIFT_INS)) {
+ if ((flags & EX86_BIN_INS) && (a & SLJIT_IMM))
+ *inst = (flags & EX86_BYTE_ARG) ? GROUP_BINARY_83 : GROUP_BINARY_81;
+
+ if (a & SLJIT_IMM)
+ *buf_ptr = 0;
+ else if (!(flags & EX86_SSE2_OP1))
+ *buf_ptr = U8(reg_lmap[a] << 3);
+ else
+ *buf_ptr = U8(freg_lmap[a] << 3);
+ }
+ else {
+ if (a & SLJIT_IMM) {
+ if (imma == 1)
+ *inst = GROUP_SHIFT_1;
+ else
+ *inst = GROUP_SHIFT_N;
+ } else
+ *inst = GROUP_SHIFT_CL;
+ *buf_ptr = 0;
+ }
+
+ if (!(b & SLJIT_MEM)) {
+ *buf_ptr = U8(*buf_ptr | MOD_REG | (!(flags & EX86_SSE2_OP2) ? reg_lmap[b] : freg_lmap[b]));
+ buf_ptr++;
+ } else if (b & REG_MASK) {
+ reg_lmap_b = reg_lmap[b & REG_MASK];
+
+ if (!(b & OFFS_REG_MASK) || (b & OFFS_REG_MASK) == TO_OFFS_REG(SLJIT_SP)) {
+ if (immb != 0 || reg_lmap_b == 5) {
+ if (immb <= 127 && immb >= -128)
+ *buf_ptr |= 0x40;
+ else
+ *buf_ptr |= 0x80;
+ }
+
+ if (!(b & OFFS_REG_MASK))
+ *buf_ptr++ |= reg_lmap_b;
+ else {
+ *buf_ptr++ |= 0x04;
+ *buf_ptr++ = U8(reg_lmap_b | (reg_lmap[OFFS_REG(b)] << 3));
+ }
+
+ if (immb != 0 || reg_lmap_b == 5) {
+ if (immb <= 127 && immb >= -128)
+ *buf_ptr++ = U8(immb); /* 8 bit displacement. */
+ else {
+ sljit_unaligned_store_s32(buf_ptr, (sljit_s32)immb); /* 32 bit displacement. */
+ buf_ptr += sizeof(sljit_s32);
+ }
+ }
+ }
+ else {
+ if (reg_lmap_b == 5)
+ *buf_ptr |= 0x40;
+
+ *buf_ptr++ |= 0x04;
+ *buf_ptr++ = U8(reg_lmap_b | (reg_lmap[OFFS_REG(b)] << 3) | (immb << 6));
+
+ if (reg_lmap_b == 5)
+ *buf_ptr++ = 0;
+ }
+ }
+ else {
+ *buf_ptr++ |= 0x04;
+ *buf_ptr++ = 0x25;
+ sljit_unaligned_store_s32(buf_ptr, (sljit_s32)immb); /* 32 bit displacement. */
+ buf_ptr += sizeof(sljit_s32);
+ }
+
+ if (a & SLJIT_IMM) {
+ if (flags & EX86_BYTE_ARG)
+ *buf_ptr = U8(imma);
+ else if (flags & EX86_HALF_ARG)
+ sljit_unaligned_store_s16(buf_ptr, (sljit_s16)imma);
+ else if (!(flags & EX86_SHIFT_INS))
+ sljit_unaligned_store_s32(buf_ptr, (sljit_s32)imma);
+ }
+
+ return !(flags & EX86_SHIFT_INS) ? inst : (inst + 1);
+}
+
+/* --------------------------------------------------------------------- */
+/* Enter / return */
+/* --------------------------------------------------------------------- */
+
+static sljit_u8* generate_far_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr)
+{
+ sljit_uw type = jump->flags >> TYPE_SHIFT;
+
+ int short_addr = !(jump->flags & SLJIT_REWRITABLE_JUMP) && !(jump->flags & JUMP_LABEL) && (jump->u.target <= 0xffffffff);
+
+ /* The relative jump below specialized for this case. */
+ SLJIT_ASSERT(reg_map[TMP_REG2] >= 8);
+
+ if (type < SLJIT_JUMP) {
+ /* Invert type. */
+ *code_ptr++ = U8(get_jump_code(type ^ 0x1) - 0x10);
+ *code_ptr++ = short_addr ? (6 + 3) : (10 + 3);
+ }
+
+ *code_ptr++ = short_addr ? REX_B : (REX_W | REX_B);
+ *code_ptr++ = MOV_r_i32 | reg_lmap[TMP_REG2];
+ jump->addr = (sljit_uw)code_ptr;
+
+ if (jump->flags & JUMP_LABEL)
+ jump->flags |= PATCH_MD;
+ else if (short_addr)
+ sljit_unaligned_store_s32(code_ptr, (sljit_s32)jump->u.target);
+ else
+ sljit_unaligned_store_sw(code_ptr, (sljit_sw)jump->u.target);
+
+ code_ptr += short_addr ? sizeof(sljit_s32) : sizeof(sljit_sw);
+
+ *code_ptr++ = REX_B;
+ *code_ptr++ = GROUP_FF;
+ *code_ptr++ = U8(MOD_REG | (type >= SLJIT_FAST_CALL ? CALL_rm : JMP_rm) | reg_lmap[TMP_REG2]);
+
+ return code_ptr;
+}
+
+static sljit_u8* generate_put_label_code(struct sljit_put_label *put_label, sljit_u8 *code_ptr, sljit_uw max_label)
+{
+ if (max_label > HALFWORD_MAX) {
+ put_label->addr -= put_label->flags;
+ put_label->flags = PATCH_MD;
+ return code_ptr;
+ }
+
+ if (put_label->flags == 0) {
+ /* Destination is register. */
+ code_ptr = (sljit_u8*)put_label->addr - 2 - sizeof(sljit_uw);
+
+ SLJIT_ASSERT((code_ptr[0] & 0xf8) == REX_W);
+ SLJIT_ASSERT((code_ptr[1] & 0xf8) == MOV_r_i32);
+
+ if ((code_ptr[0] & 0x07) != 0) {
+ code_ptr[0] = U8(code_ptr[0] & ~0x08);
+ code_ptr += 2 + sizeof(sljit_s32);
+ }
+ else {
+ code_ptr[0] = code_ptr[1];
+ code_ptr += 1 + sizeof(sljit_s32);
+ }
+
+ put_label->addr = (sljit_uw)code_ptr;
+ return code_ptr;
+ }
+
+ code_ptr -= put_label->flags + (2 + sizeof(sljit_uw));
+ SLJIT_MEMMOVE(code_ptr, code_ptr + (2 + sizeof(sljit_uw)), put_label->flags);
+
+ SLJIT_ASSERT((code_ptr[0] & 0xf8) == REX_W);
+
+ if ((code_ptr[1] & 0xf8) == MOV_r_i32) {
+ code_ptr += 2 + sizeof(sljit_uw);
+ SLJIT_ASSERT((code_ptr[0] & 0xf8) == REX_W);
+ }
+
+ SLJIT_ASSERT(code_ptr[1] == MOV_rm_r);
+
+ code_ptr[0] = U8(code_ptr[0] & ~0x4);
+ code_ptr[1] = MOV_rm_i32;
+ code_ptr[2] = U8(code_ptr[2] & ~(0x7 << 3));
+
+ code_ptr = (sljit_u8*)(put_label->addr - (2 + sizeof(sljit_uw)) + sizeof(sljit_s32));
+ put_label->addr = (sljit_uw)code_ptr;
+ put_label->flags = 0;
+ return code_ptr;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_uw size;
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+ sljit_s32 saved_regs_size, tmp, i;
+#ifdef _WIN64
+ sljit_s32 saved_float_regs_size;
+ sljit_s32 saved_float_regs_offset = 0;
+ sljit_s32 float_arg_count = 0;
+#endif /* _WIN64 */
+ sljit_u8 *inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ if (options & SLJIT_ENTER_REG_ARG)
+ arg_types = 0;
+
+ /* Emit ENDBR64 at function entry if needed. */
+ FAIL_IF(emit_endbranch(compiler));
+
+ compiler->mode32 = 0;
+
+ /* Including the return address saved by the call instruction. */
+ saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 1);
+
+ tmp = SLJIT_S0 - saveds;
+ for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) {
+ size = reg_map[i] >= 8 ? 2 : 1;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+ INC_SIZE(size);
+ if (reg_map[i] >= 8)
+ *inst++ = REX_B;
+ PUSH_REG(reg_lmap[i]);
+ }
+
+ for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ size = reg_map[i] >= 8 ? 2 : 1;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+ INC_SIZE(size);
+ if (reg_map[i] >= 8)
+ *inst++ = REX_B;
+ PUSH_REG(reg_lmap[i]);
+ }
+
+#ifdef _WIN64
+ local_size += SLJIT_LOCALS_OFFSET;
+ saved_float_regs_size = GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, 16);
+
+ if (saved_float_regs_size > 0) {
+ saved_float_regs_offset = ((local_size + 0xf) & ~0xf);
+ local_size = saved_float_regs_offset + saved_float_regs_size;
+ }
+#else /* !_WIN64 */
+ SLJIT_ASSERT(SLJIT_LOCALS_OFFSET == 0);
+#endif /* _WIN64 */
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types > 0) {
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) {
+ tmp = 0;
+#ifndef _WIN64
+ switch (word_arg_count) {
+ case 0:
+ tmp = SLJIT_R2;
+ break;
+ case 1:
+ tmp = SLJIT_R1;
+ break;
+ case 2:
+ tmp = TMP_REG1;
+ break;
+ default:
+ tmp = SLJIT_R3;
+ break;
+ }
+#else /* !_WIN64 */
+ switch (word_arg_count + float_arg_count) {
+ case 0:
+ tmp = SLJIT_R3;
+ break;
+ case 1:
+ tmp = SLJIT_R1;
+ break;
+ case 2:
+ tmp = SLJIT_R2;
+ break;
+ default:
+ tmp = TMP_REG1;
+ break;
+ }
+#endif /* _WIN64 */
+ if (arg_types & SLJIT_ARG_TYPE_SCRATCH_REG) {
+ if (tmp != SLJIT_R0 + word_arg_count)
+ EMIT_MOV(compiler, SLJIT_R0 + word_arg_count, 0, tmp, 0);
+ } else {
+ EMIT_MOV(compiler, SLJIT_S0 - saved_arg_count, 0, tmp, 0);
+ saved_arg_count++;
+ }
+ word_arg_count++;
+ } else {
+#ifdef _WIN64
+ SLJIT_COMPILE_ASSERT(SLJIT_FR0 == 1, float_register_index_start);
+ float_arg_count++;
+ if (float_arg_count != float_arg_count + word_arg_count)
+ FAIL_IF(emit_sse2_load(compiler, (arg_types & SLJIT_ARG_MASK) == SLJIT_ARG_TYPE_F32,
+ float_arg_count, float_arg_count + word_arg_count, 0));
+#endif /* _WIN64 */
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ local_size = ((local_size + saved_regs_size + 0xf) & ~0xf) - saved_regs_size;
+ compiler->local_size = local_size;
+
+#ifdef _WIN64
+ if (local_size > 0) {
+ if (local_size <= 4 * 4096) {
+ if (local_size > 4096)
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), -4096);
+ if (local_size > 2 * 4096)
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), -4096 * 2);
+ if (local_size > 3 * 4096)
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), -4096 * 3);
+ }
+ else {
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, local_size >> 12);
+
+ EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_MEM1(SLJIT_SP), -4096);
+ BINARY_IMM32(SUB, 4096, SLJIT_SP, 0);
+ BINARY_IMM32(SUB, 1, TMP_REG1, 0);
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2);
+ FAIL_IF(!inst);
+
+ INC_SIZE(2);
+ inst[0] = JNE_i8;
+ inst[1] = (sljit_u8)-21;
+ local_size &= 0xfff;
+ }
+
+ if (local_size > 0)
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), -local_size);
+ }
+#endif /* _WIN64 */
+
+ if (local_size > 0)
+ BINARY_IMM32(SUB, local_size, SLJIT_SP, 0);
+
+#ifdef _WIN64
+ if (saved_float_regs_size > 0) {
+ compiler->mode32 = 1;
+
+ tmp = SLJIT_FS0 - fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ inst = emit_x86_instruction(compiler, 2 | EX86_SSE2, i, 0, SLJIT_MEM1(SLJIT_SP), saved_float_regs_offset);
+ *inst++ = GROUP_0F;
+ *inst = MOVAPS_xm_x;
+ saved_float_regs_offset += 16;
+ }
+
+ for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ inst = emit_x86_instruction(compiler, 2 | EX86_SSE2, i, 0, SLJIT_MEM1(SLJIT_SP), saved_float_regs_offset);
+ *inst++ = GROUP_0F;
+ *inst = MOVAPS_xm_x;
+ saved_float_regs_offset += 16;
+ }
+ }
+#endif /* _WIN64 */
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 saved_regs_size;
+#ifdef _WIN64
+ sljit_s32 saved_float_regs_size;
+#endif /* _WIN64 */
+
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+#ifdef _WIN64
+ local_size += SLJIT_LOCALS_OFFSET;
+ saved_float_regs_size = GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, 16);
+
+ if (saved_float_regs_size > 0)
+ local_size = ((local_size + 0xf) & ~0xf) + saved_float_regs_size;
+#else /* !_WIN64 */
+ SLJIT_ASSERT(SLJIT_LOCALS_OFFSET == 0);
+#endif /* _WIN64 */
+
+ /* Including the return address saved by the call instruction. */
+ saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 1);
+ compiler->local_size = ((local_size + saved_regs_size + 0xf) & ~0xf) - saved_regs_size;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 is_return_to)
+{
+ sljit_uw size;
+ sljit_s32 local_size, i, tmp;
+ sljit_u8 *inst;
+#ifdef _WIN64
+ sljit_s32 saved_float_regs_offset;
+ sljit_s32 fscratches = compiler->fscratches;
+ sljit_s32 fsaveds = compiler->fsaveds;
+#endif /* _WIN64 */
+
+#ifdef _WIN64
+ saved_float_regs_offset = GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, 16);
+
+ if (saved_float_regs_offset > 0) {
+ compiler->mode32 = 1;
+ saved_float_regs_offset = (compiler->local_size - saved_float_regs_offset) & ~0xf;
+
+ tmp = SLJIT_FS0 - fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ inst = emit_x86_instruction(compiler, 2 | EX86_SSE2, i, 0, SLJIT_MEM1(SLJIT_SP), saved_float_regs_offset);
+ *inst++ = GROUP_0F;
+ *inst = MOVAPS_x_xm;
+ saved_float_regs_offset += 16;
+ }
+
+ for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ inst = emit_x86_instruction(compiler, 2 | EX86_SSE2, i, 0, SLJIT_MEM1(SLJIT_SP), saved_float_regs_offset);
+ *inst++ = GROUP_0F;
+ *inst = MOVAPS_x_xm;
+ saved_float_regs_offset += 16;
+ }
+
+ compiler->mode32 = 0;
+ }
+#endif /* _WIN64 */
+
+ local_size = compiler->local_size;
+
+ if (is_return_to && compiler->scratches < SLJIT_FIRST_SAVED_REG && (compiler->saveds == SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ local_size += SSIZE_OF(sw);
+ is_return_to = 0;
+ }
+
+ if (local_size > 0)
+ BINARY_IMM32(ADD, local_size, SLJIT_SP, 0);
+
+ tmp = compiler->scratches;
+ for (i = SLJIT_FIRST_SAVED_REG; i <= tmp; i++) {
+ size = reg_map[i] >= 8 ? 2 : 1;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+ INC_SIZE(size);
+ if (reg_map[i] >= 8)
+ *inst++ = REX_B;
+ POP_REG(reg_lmap[i]);
+ }
+
+ tmp = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options);
+ for (i = SLJIT_S0 + 1 - compiler->saveds; i <= tmp; i++) {
+ size = reg_map[i] >= 8 ? 2 : 1;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+ INC_SIZE(size);
+ if (reg_map[i] >= 8)
+ *inst++ = REX_B;
+ POP_REG(reg_lmap[i]);
+ }
+
+ if (is_return_to)
+ BINARY_IMM32(ADD, sizeof(sljit_sw), SLJIT_SP, 0);
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ sljit_u8 *inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ compiler->mode32 = 0;
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ RET();
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ compiler->mode32 = 0;
+
+ if ((src & SLJIT_MEM) || (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options)))) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ EMIT_MOV(compiler, TMP_REG2, 0, src, srcw);
+ src = TMP_REG2;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 1));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Call / return instructions */
+/* --------------------------------------------------------------------- */
+
+#ifndef _WIN64
+
+static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src_ptr)
+{
+ sljit_s32 src = src_ptr ? (*src_ptr) : 0;
+ sljit_s32 word_arg_count = 0;
+
+ SLJIT_ASSERT(reg_map[SLJIT_R1] == 6 && reg_map[SLJIT_R3] == 1 && reg_map[TMP_REG1] == 2);
+ SLJIT_ASSERT(!(src & SLJIT_MEM));
+
+ /* Remove return value. */
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64)
+ word_arg_count++;
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ if (word_arg_count == 0)
+ return SLJIT_SUCCESS;
+
+ if (word_arg_count >= 3) {
+ if (src == SLJIT_R2)
+ *src_ptr = TMP_REG1;
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_R2, 0);
+ }
+
+ return emit_mov(compiler, SLJIT_R2, 0, SLJIT_R0, 0);
+}
+
+#else
+
+static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src_ptr)
+{
+ sljit_s32 src = src_ptr ? (*src_ptr) : 0;
+ sljit_s32 arg_count = 0;
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 float_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_s32 data_trandfer = 0;
+ static sljit_u8 word_arg_regs[5] = { 0, SLJIT_R3, SLJIT_R1, SLJIT_R2, TMP_REG1 };
+
+ SLJIT_ASSERT(reg_map[SLJIT_R3] == 1 && reg_map[SLJIT_R1] == 2 && reg_map[SLJIT_R2] == 8 && reg_map[TMP_REG1] == 9);
+ SLJIT_ASSERT(!(src & SLJIT_MEM));
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK);
+
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ case SLJIT_ARG_TYPE_F32:
+ arg_count++;
+ float_arg_count++;
+
+ if (arg_count != float_arg_count)
+ data_trandfer = 1;
+ break;
+ default:
+ arg_count++;
+ word_arg_count++;
+
+ if (arg_count != word_arg_count || arg_count != word_arg_regs[arg_count]) {
+ data_trandfer = 1;
+
+ if (src == word_arg_regs[arg_count]) {
+ EMIT_MOV(compiler, TMP_REG2, 0, src, 0);
+ *src_ptr = TMP_REG2;
+ }
+ }
+ break;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ if (!data_trandfer)
+ return SLJIT_SUCCESS;
+
+ while (types) {
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (arg_count != float_arg_count)
+ FAIL_IF(emit_sse2_load(compiler, 0, arg_count, float_arg_count, 0));
+ arg_count--;
+ float_arg_count--;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (arg_count != float_arg_count)
+ FAIL_IF(emit_sse2_load(compiler, 1, arg_count, float_arg_count, 0));
+ arg_count--;
+ float_arg_count--;
+ break;
+ default:
+ if (arg_count != word_arg_count || arg_count != word_arg_regs[arg_count])
+ EMIT_MOV(compiler, word_arg_regs[arg_count], 0, word_arg_count, 0);
+ arg_count--;
+ word_arg_count--;
+ break;
+ }
+
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ compiler->mode32 = 0;
+
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ PTR_FAIL_IF(call_with_args(compiler, arg_types, NULL));
+
+ if (type & SLJIT_CALL_RETURN) {
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, 0));
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ compiler->mode32 = 0;
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ EMIT_MOV(compiler, TMP_REG2, 0, src, srcw);
+ src = TMP_REG2;
+ }
+
+ if (type & SLJIT_CALL_RETURN) {
+ if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ EMIT_MOV(compiler, TMP_REG2, 0, src, srcw);
+ src = TMP_REG2;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+ }
+
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ FAIL_IF(call_with_args(compiler, arg_types, &src));
+
+ if (type & SLJIT_CALL_RETURN)
+ type = SLJIT_JUMP;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ sljit_u8 *inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ if (FAST_IS_REG(dst)) {
+ if (reg_map[dst] < 8) {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ POP_REG(reg_lmap[dst]);
+ return SLJIT_SUCCESS;
+ }
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2);
+ FAIL_IF(!inst);
+ INC_SIZE(2);
+ *inst++ = REX_B;
+ POP_REG(reg_lmap[dst]);
+ return SLJIT_SUCCESS;
+ }
+
+ /* REX_W is not necessary (src is not immediate). */
+ compiler->mode32 = 1;
+ inst = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst++ = POP_rm;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_fast_return(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8 *inst;
+
+ if (FAST_IS_REG(src)) {
+ if (reg_map[src] < 8) {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + 1);
+ FAIL_IF(!inst);
+
+ INC_SIZE(1 + 1);
+ PUSH_REG(reg_lmap[src]);
+ }
+ else {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2 + 1);
+ FAIL_IF(!inst);
+
+ INC_SIZE(2 + 1);
+ *inst++ = REX_B;
+ PUSH_REG(reg_lmap[src]);
+ }
+ }
+ else {
+ /* REX_W is not necessary (src is not immediate). */
+ compiler->mode32 = 1;
+ inst = emit_x86_instruction(compiler, 1, 0, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_FF;
+ *inst |= PUSH_rm;
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ }
+
+ RET();
+ return SLJIT_SUCCESS;
+}
+
+/* --------------------------------------------------------------------- */
+/* Other operations */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_u8* inst;
+ sljit_s32 i, next, reg_idx;
+ sljit_u8 regs[2];
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (!(reg & REG_PAIR_MASK))
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+
+ ADJUST_LOCAL_OFFSET(mem, memw);
+
+ compiler->mode32 = 0;
+
+ if ((mem & REG_MASK) == 0) {
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, memw);
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ memw = 0;
+ } else if (!(mem & OFFS_REG_MASK) && ((memw < HALFWORD_MIN) || (memw > HALFWORD_MAX - SSIZE_OF(sw)))) {
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, memw);
+
+ mem = SLJIT_MEM2(mem & REG_MASK, TMP_REG1);
+ memw = 0;
+ }
+
+ regs[0] = U8(REG_PAIR_FIRST(reg));
+ regs[1] = U8(REG_PAIR_SECOND(reg));
+
+ next = SSIZE_OF(sw);
+
+ if (!(type & SLJIT_MEM_STORE) && (regs[0] == (mem & REG_MASK) || regs[0] == OFFS_REG(mem))) {
+ if (regs[1] == (mem & REG_MASK) || regs[1] == OFFS_REG(mem)) {
+ /* Base and offset cannot be TMP_REG1. */
+ EMIT_MOV(compiler, TMP_REG1, 0, OFFS_REG(mem), 0);
+
+ if (regs[1] == OFFS_REG(mem))
+ next = -SSIZE_OF(sw);
+
+ mem = (mem & ~OFFS_REG_MASK) | TO_OFFS_REG(TMP_REG1);
+ } else {
+ next = -SSIZE_OF(sw);
+
+ if (!(mem & OFFS_REG_MASK))
+ memw += SSIZE_OF(sw);
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ reg_idx = next > 0 ? i : (i ^ 0x1);
+ reg = regs[reg_idx];
+
+ if ((mem & OFFS_REG_MASK) && (reg_idx == 1)) {
+ inst = (sljit_u8*)ensure_buf(compiler, (sljit_uw)(1 + 5));
+ FAIL_IF(!inst);
+
+ INC_SIZE(5);
+
+ inst[0] = U8(REX_W | ((reg_map[reg] >= 8) ? REX_R : 0) | ((reg_map[mem & REG_MASK] >= 8) ? REX_B : 0) | ((reg_map[OFFS_REG(mem)] >= 8) ? REX_X : 0));
+ inst[1] = (type & SLJIT_MEM_STORE) ? MOV_rm_r : MOV_r_rm;
+ inst[2] = 0x44 | U8(reg_lmap[reg] << 3);
+ inst[3] = U8(memw << 6) | U8(reg_lmap[OFFS_REG(mem)] << 3) | reg_lmap[mem & REG_MASK];
+ inst[4] = sizeof(sljit_sw);
+ } else if (type & SLJIT_MEM_STORE) {
+ EMIT_MOV(compiler, mem, memw, reg, 0);
+ } else {
+ EMIT_MOV(compiler, reg, 0, mem, memw);
+ }
+
+ if (!(mem & OFFS_REG_MASK))
+ memw += next;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_mov_int(struct sljit_compiler *compiler, sljit_s32 sign,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+ sljit_s32 dst_r;
+
+ compiler->mode32 = 0;
+
+ if (src & SLJIT_IMM) {
+ if (FAST_IS_REG(dst)) {
+ if (sign || ((sljit_uw)srcw <= 0x7fffffff)) {
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, (sljit_sw)(sljit_s32)srcw, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_i32;
+ return SLJIT_SUCCESS;
+ }
+ return emit_load_imm64(compiler, dst, srcw);
+ }
+ compiler->mode32 = 1;
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, (sljit_sw)(sljit_s32)srcw, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_i32;
+ compiler->mode32 = 0;
+ return SLJIT_SUCCESS;
+ }
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if ((dst & SLJIT_MEM) && FAST_IS_REG(src))
+ dst_r = src;
+ else {
+ if (sign) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = MOVSXD_r_rm;
+ } else {
+ compiler->mode32 = 1;
+ FAIL_IF(emit_mov(compiler, dst_r, 0, src, srcw));
+ compiler->mode32 = 0;
+ }
+ }
+
+ if (dst & SLJIT_MEM) {
+ compiler->mode32 = 1;
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_r;
+ compiler->mode32 = 0;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 skip_frames_before_return(struct sljit_compiler *compiler)
+{
+ sljit_s32 tmp, size;
+
+ /* Don't adjust shadow stack if it isn't enabled. */
+ if (!cpu_has_shadow_stack())
+ return SLJIT_SUCCESS;
+
+ size = compiler->local_size;
+ tmp = compiler->scratches;
+ if (tmp >= SLJIT_FIRST_SAVED_REG)
+ size += (tmp - SLJIT_FIRST_SAVED_REG + 1) * SSIZE_OF(sw);
+ tmp = compiler->saveds < SLJIT_NUMBER_OF_SAVED_REGISTERS ? (SLJIT_S0 + 1 - compiler->saveds) : SLJIT_FIRST_SAVED_REG;
+ if (SLJIT_S0 >= tmp)
+ size += (SLJIT_S0 - tmp + 1) * SSIZE_OF(sw);
+
+ return adjust_shadow_stack(compiler, SLJIT_MEM1(SLJIT_SP), size);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeX86_common.c b/contrib/libs/pcre2/src/sljit/sljitNativeX86_common.c
new file mode 100644
index 00000000000..651942be80a
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeX86_common.c
@@ -0,0 +1,3422 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+ return "x86" SLJIT_CPUINFO;
+}
+
+/*
+ 32b register indexes:
+ 0 - EAX
+ 1 - ECX
+ 2 - EDX
+ 3 - EBX
+ 4 - ESP
+ 5 - EBP
+ 6 - ESI
+ 7 - EDI
+*/
+
+/*
+ 64b register indexes:
+ 0 - RAX
+ 1 - RCX
+ 2 - RDX
+ 3 - RBX
+ 4 - RSP
+ 5 - RBP
+ 6 - RSI
+ 7 - RDI
+ 8 - R8 - From now on REX prefix is required
+ 9 - R9
+ 10 - R10
+ 11 - R11
+ 12 - R12
+ 13 - R13
+ 14 - R14
+ 15 - R15
+*/
+
+#define TMP_FREG (0)
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+
+/* Last register + 1. */
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 3] = {
+ 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 7, 6, 3, 4, 5
+};
+
+#define CHECK_EXTRA_REGS(p, w, do) \
+ if (p >= SLJIT_R3 && p <= SLJIT_S3) { \
+ w = (2 * SSIZE_OF(sw)) + ((p) - SLJIT_R3) * SSIZE_OF(sw); \
+ p = SLJIT_MEM1(SLJIT_SP); \
+ do; \
+ }
+
+#else /* SLJIT_CONFIG_X86_32 */
+
+/* Last register + 1. */
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
+
+/* Note: r12 & 0x7 == 0b100, which decoded as SIB byte present
+ Note: avoid to use r12 and r13 for memory addessing
+ therefore r12 is better to be a higher saved register. */
+#ifndef _WIN64
+/* Args: rdi(=7), rsi(=6), rdx(=2), rcx(=1), r8, r9. Scratches: rax(=0), r10, r11 */
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 4] = {
+ 0, 0, 6, 7, 1, 8, 11, 10, 12, 5, 13, 14, 15, 3, 4, 2, 9
+};
+/* low-map. reg_map & 0x7. */
+static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 4] = {
+ 0, 0, 6, 7, 1, 0, 3, 2, 4, 5, 5, 6, 7, 3, 4, 2, 1
+};
+#else
+/* Args: rcx(=1), rdx(=2), r8, r9. Scratches: rax(=0), r10, r11 */
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 4] = {
+ 0, 0, 2, 8, 1, 11, 12, 5, 13, 14, 15, 7, 6, 3, 4, 9, 10
+};
+/* low-map. reg_map & 0x7. */
+static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 4] = {
+ 0, 0, 2, 0, 1, 3, 4, 5, 5, 6, 7, 7, 6, 3, 4, 1, 2
+};
+#endif
+
+/* Args: xmm0-xmm3 */
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = {
+ 4, 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+};
+/* low-map. freg_map & 0x7. */
+static const sljit_u8 freg_lmap[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = {
+ 4, 0, 1, 2, 3, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7
+};
+
+#define REX_W 0x48
+#define REX_R 0x44
+#define REX_X 0x42
+#define REX_B 0x41
+#define REX 0x40
+
+#ifndef _WIN64
+#define HALFWORD_MAX 0x7fffffffl
+#define HALFWORD_MIN -0x80000000l
+#else
+#define HALFWORD_MAX 0x7fffffffll
+#define HALFWORD_MIN -0x80000000ll
+#endif
+
+#define IS_HALFWORD(x) ((x) <= HALFWORD_MAX && (x) >= HALFWORD_MIN)
+#define NOT_HALFWORD(x) ((x) > HALFWORD_MAX || (x) < HALFWORD_MIN)
+
+#define CHECK_EXTRA_REGS(p, w, do)
+
+#endif /* SLJIT_CONFIG_X86_32 */
+
+#define U8(v) ((sljit_u8)(v))
+
+
+/* Size flags for emit_x86_instruction: */
+#define EX86_BIN_INS 0x0010
+#define EX86_SHIFT_INS 0x0020
+#define EX86_REX 0x0040
+#define EX86_NO_REXW 0x0080
+#define EX86_BYTE_ARG 0x0100
+#define EX86_HALF_ARG 0x0200
+#define EX86_PREF_66 0x0400
+#define EX86_PREF_F2 0x0800
+#define EX86_PREF_F3 0x1000
+#define EX86_SSE2_OP1 0x2000
+#define EX86_SSE2_OP2 0x4000
+#define EX86_SSE2 (EX86_SSE2_OP1 | EX86_SSE2_OP2)
+
+/* --------------------------------------------------------------------- */
+/* Instrucion forms */
+/* --------------------------------------------------------------------- */
+
+#define ADD (/* BINARY */ 0 << 3)
+#define ADD_EAX_i32 0x05
+#define ADD_r_rm 0x03
+#define ADD_rm_r 0x01
+#define ADDSD_x_xm 0x58
+#define ADC (/* BINARY */ 2 << 3)
+#define ADC_EAX_i32 0x15
+#define ADC_r_rm 0x13
+#define ADC_rm_r 0x11
+#define AND (/* BINARY */ 4 << 3)
+#define AND_EAX_i32 0x25
+#define AND_r_rm 0x23
+#define AND_rm_r 0x21
+#define ANDPD_x_xm 0x54
+#define BSR_r_rm (/* GROUP_0F */ 0xbd)
+#define BSF_r_rm (/* GROUP_0F */ 0xbc)
+#define CALL_i32 0xe8
+#define CALL_rm (/* GROUP_FF */ 2 << 3)
+#define CDQ 0x99
+#define CMOVE_r_rm (/* GROUP_0F */ 0x44)
+#define CMP (/* BINARY */ 7 << 3)
+#define CMP_EAX_i32 0x3d
+#define CMP_r_rm 0x3b
+#define CMP_rm_r 0x39
+#define CVTPD2PS_x_xm 0x5a
+#define CVTSI2SD_x_rm 0x2a
+#define CVTTSD2SI_r_xm 0x2c
+#define DIV (/* GROUP_F7 */ 6 << 3)
+#define DIVSD_x_xm 0x5e
+#define FLDS 0xd9
+#define FLDL 0xdd
+#define FSTPS 0xd9
+#define FSTPD 0xdd
+#define INT3 0xcc
+#define IDIV (/* GROUP_F7 */ 7 << 3)
+#define IMUL (/* GROUP_F7 */ 5 << 3)
+#define IMUL_r_rm (/* GROUP_0F */ 0xaf)
+#define IMUL_r_rm_i8 0x6b
+#define IMUL_r_rm_i32 0x69
+#define JE_i8 0x74
+#define JNE_i8 0x75
+#define JMP_i8 0xeb
+#define JMP_i32 0xe9
+#define JMP_rm (/* GROUP_FF */ 4 << 3)
+#define LEA_r_m 0x8d
+#define LOOP_i8 0xe2
+#define LZCNT_r_rm (/* GROUP_F3 */ /* GROUP_0F */ 0xbd)
+#define MOV_r_rm 0x8b
+#define MOV_r_i32 0xb8
+#define MOV_rm_r 0x89
+#define MOV_rm_i32 0xc7
+#define MOV_rm8_i8 0xc6
+#define MOV_rm8_r8 0x88
+#define MOVAPS_x_xm 0x28
+#define MOVAPS_xm_x 0x29
+#define MOVSD_x_xm 0x10
+#define MOVSD_xm_x 0x11
+#define MOVSXD_r_rm 0x63
+#define MOVSX_r_rm8 (/* GROUP_0F */ 0xbe)
+#define MOVSX_r_rm16 (/* GROUP_0F */ 0xbf)
+#define MOVZX_r_rm8 (/* GROUP_0F */ 0xb6)
+#define MOVZX_r_rm16 (/* GROUP_0F */ 0xb7)
+#define MUL (/* GROUP_F7 */ 4 << 3)
+#define MULSD_x_xm 0x59
+#define NEG_rm (/* GROUP_F7 */ 3 << 3)
+#define NOP 0x90
+#define NOT_rm (/* GROUP_F7 */ 2 << 3)
+#define OR (/* BINARY */ 1 << 3)
+#define OR_r_rm 0x0b
+#define OR_EAX_i32 0x0d
+#define OR_rm_r 0x09
+#define OR_rm8_r8 0x08
+#define POP_r 0x58
+#define POP_rm 0x8f
+#define POPF 0x9d
+#define PREFETCH 0x18
+#define PUSH_i32 0x68
+#define PUSH_r 0x50
+#define PUSH_rm (/* GROUP_FF */ 6 << 3)
+#define PUSHF 0x9c
+#define ROL (/* SHIFT */ 0 << 3)
+#define ROR (/* SHIFT */ 1 << 3)
+#define RET_near 0xc3
+#define RET_i16 0xc2
+#define SBB (/* BINARY */ 3 << 3)
+#define SBB_EAX_i32 0x1d
+#define SBB_r_rm 0x1b
+#define SBB_rm_r 0x19
+#define SAR (/* SHIFT */ 7 << 3)
+#define SHL (/* SHIFT */ 4 << 3)
+#define SHLD (/* GROUP_0F */ 0xa5)
+#define SHRD (/* GROUP_0F */ 0xad)
+#define SHR (/* SHIFT */ 5 << 3)
+#define SUB (/* BINARY */ 5 << 3)
+#define SUB_EAX_i32 0x2d
+#define SUB_r_rm 0x2b
+#define SUB_rm_r 0x29
+#define SUBSD_x_xm 0x5c
+#define TEST_EAX_i32 0xa9
+#define TEST_rm_r 0x85
+#define TZCNT_r_rm (/* GROUP_F3 */ /* GROUP_0F */ 0xbc)
+#define UCOMISD_x_xm 0x2e
+#define UNPCKLPD_x_xm 0x14
+#define XCHG_EAX_r 0x90
+#define XCHG_r_rm 0x87
+#define XOR (/* BINARY */ 6 << 3)
+#define XOR_EAX_i32 0x35
+#define XOR_r_rm 0x33
+#define XOR_rm_r 0x31
+#define XORPD_x_xm 0x57
+
+#define GROUP_0F 0x0f
+#define GROUP_F3 0xf3
+#define GROUP_F7 0xf7
+#define GROUP_FF 0xff
+#define GROUP_BINARY_81 0x81
+#define GROUP_BINARY_83 0x83
+#define GROUP_SHIFT_1 0xd1
+#define GROUP_SHIFT_N 0xc1
+#define GROUP_SHIFT_CL 0xd3
+
+#define MOD_REG 0xc0
+#define MOD_DISP8 0x40
+
+#define INC_SIZE(s) (*inst++ = U8(s), compiler->size += (s))
+
+#define PUSH_REG(r) (*inst++ = U8(PUSH_r + (r)))
+#define POP_REG(r) (*inst++ = U8(POP_r + (r)))
+#define RET() (*inst++ = RET_near)
+#define RET_I16(n) (*inst++ = RET_i16, *inst++ = U8(n), *inst++ = 0)
+
+/* Multithreading does not affect these static variables, since they store
+ built-in CPU features. Therefore they can be overwritten by different threads
+ if they detect the CPU features in the same time. */
+#define CPU_FEATURE_DETECTED 0x001
+#if (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2)
+#define CPU_FEATURE_SSE2 0x002
+#endif
+#define CPU_FEATURE_LZCNT 0x004
+#define CPU_FEATURE_TZCNT 0x008
+#define CPU_FEATURE_CMOV 0x010
+
+static sljit_u32 cpu_feature_list = 0;
+
+#ifdef _WIN32_WCE
+#include <cmnintrin.h>
+#elif defined(_MSC_VER) && _MSC_VER >= 1400
+#include <intrin.h>
+#endif
+
+/******************************************************/
+/* Unaligned-store functions */
+/******************************************************/
+
+static SLJIT_INLINE void sljit_unaligned_store_s16(void *addr, sljit_s16 value)
+{
+ SLJIT_MEMCPY(addr, &value, sizeof(value));
+}
+
+static SLJIT_INLINE void sljit_unaligned_store_s32(void *addr, sljit_s32 value)
+{
+ SLJIT_MEMCPY(addr, &value, sizeof(value));
+}
+
+static SLJIT_INLINE void sljit_unaligned_store_sw(void *addr, sljit_sw value)
+{
+ SLJIT_MEMCPY(addr, &value, sizeof(value));
+}
+
+/******************************************************/
+/* Utility functions */
+/******************************************************/
+
+static void get_cpu_features(void)
+{
+ sljit_u32 feature_list = CPU_FEATURE_DETECTED;
+ sljit_u32 value;
+
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+
+ int CPUInfo[4];
+
+ __cpuid(CPUInfo, 0);
+ if (CPUInfo[0] >= 7) {
+ __cpuidex(CPUInfo, 7, 0);
+ if (CPUInfo[1] & 0x8)
+ feature_list |= CPU_FEATURE_TZCNT;
+ }
+
+ __cpuid(CPUInfo, (int)0x80000001);
+ if (CPUInfo[2] & 0x20)
+ feature_list |= CPU_FEATURE_LZCNT;
+
+ __cpuid(CPUInfo, 1);
+ value = (sljit_u32)CPUInfo[3];
+
+#elif defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_C)
+
+ /* AT&T syntax. */
+ __asm__ (
+ "movl $0x0, %%eax\n"
+ "lzcnt %%eax, %%eax\n"
+ "setnz %%al\n"
+ "movl %%eax, %0\n"
+ : "=g" (value)
+ :
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ : "eax"
+#else
+ : "rax"
+#endif
+ );
+
+ if (value & 0x1)
+ feature_list |= CPU_FEATURE_LZCNT;
+
+ __asm__ (
+ "movl $0x0, %%eax\n"
+ "tzcnt %%eax, %%eax\n"
+ "setnz %%al\n"
+ "movl %%eax, %0\n"
+ : "=g" (value)
+ :
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ : "eax"
+#else
+ : "rax"
+#endif
+ );
+
+ if (value & 0x1)
+ feature_list |= CPU_FEATURE_TZCNT;
+
+ __asm__ (
+ "movl $0x1, %%eax\n"
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ /* On x86-32, there is no red zone, so this
+ should work (no need for a local variable). */
+ "push %%ebx\n"
+#endif
+ "cpuid\n"
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ "pop %%ebx\n"
+#endif
+ "movl %%edx, %0\n"
+ : "=g" (value)
+ :
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ : "%eax", "%ecx", "%edx"
+#else
+ : "%rax", "%rbx", "%rcx", "%rdx"
+#endif
+ );
+
+#else /* _MSC_VER && _MSC_VER >= 1400 */
+
+ /* Intel syntax. */
+ __asm {
+ mov eax, 0
+ lzcnt eax, eax
+ setnz al
+ mov value, eax
+ }
+
+ if (value & 0x1)
+ feature_list |= CPU_FEATURE_LZCNT;
+
+ __asm {
+ mov eax, 0
+ tzcnt eax, eax
+ setnz al
+ mov value, eax
+ }
+
+ if (value & 0x1)
+ feature_list |= CPU_FEATURE_TZCNT;
+
+ __asm {
+ mov eax, 1
+ cpuid
+ mov value, edx
+ }
+
+#endif /* _MSC_VER && _MSC_VER >= 1400 */
+
+#if (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2)
+ if (value & 0x4000000)
+ feature_list |= CPU_FEATURE_SSE2;
+#endif
+ if (value & 0x8000)
+ feature_list |= CPU_FEATURE_CMOV;
+
+ cpu_feature_list = feature_list;
+}
+
+static sljit_u8 get_jump_code(sljit_uw type)
+{
+ switch (type) {
+ case SLJIT_EQUAL:
+ case SLJIT_F_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL:
+ case SLJIT_ORDERED_EQUAL: /* Not supported. */
+ return 0x84 /* je */;
+
+ case SLJIT_NOT_EQUAL:
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL: /* Not supported. */
+ return 0x85 /* jne */;
+
+ case SLJIT_LESS:
+ case SLJIT_CARRY:
+ case SLJIT_F_LESS:
+ case SLJIT_UNORDERED_OR_LESS:
+ case SLJIT_UNORDERED_OR_GREATER:
+ return 0x82 /* jc */;
+
+ case SLJIT_GREATER_EQUAL:
+ case SLJIT_NOT_CARRY:
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ return 0x83 /* jae */;
+
+ case SLJIT_GREATER:
+ case SLJIT_F_GREATER:
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_ORDERED_GREATER:
+ return 0x87 /* jnbe */;
+
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ return 0x86 /* jbe */;
+
+ case SLJIT_SIG_LESS:
+ return 0x8c /* jl */;
+
+ case SLJIT_SIG_GREATER_EQUAL:
+ return 0x8d /* jnl */;
+
+ case SLJIT_SIG_GREATER:
+ return 0x8f /* jnle */;
+
+ case SLJIT_SIG_LESS_EQUAL:
+ return 0x8e /* jle */;
+
+ case SLJIT_OVERFLOW:
+ return 0x80 /* jo */;
+
+ case SLJIT_NOT_OVERFLOW:
+ return 0x81 /* jno */;
+
+ case SLJIT_UNORDERED:
+ return 0x8a /* jp */;
+
+ case SLJIT_ORDERED:
+ return 0x8b /* jpo */;
+ }
+ return 0;
+}
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+static sljit_u8* generate_far_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr, sljit_sw executable_offset);
+#else
+static sljit_u8* generate_far_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr);
+static sljit_u8* generate_put_label_code(struct sljit_put_label *put_label, sljit_u8 *code_ptr, sljit_uw max_label);
+#endif
+
+static sljit_u8* generate_near_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr, sljit_u8 *code, sljit_sw executable_offset)
+{
+ sljit_uw type = jump->flags >> TYPE_SHIFT;
+ sljit_s32 short_jump;
+ sljit_uw label_addr;
+
+ if (jump->flags & JUMP_LABEL)
+ label_addr = (sljit_uw)(code + jump->u.label->size);
+ else
+ label_addr = jump->u.target - (sljit_uw)executable_offset;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if ((sljit_sw)(label_addr - (jump->addr + 1)) > HALFWORD_MAX || (sljit_sw)(label_addr - (jump->addr + 1)) < HALFWORD_MIN)
+ return generate_far_jump_code(jump, code_ptr);
+#endif
+
+ short_jump = (sljit_sw)(label_addr - (jump->addr + 2)) >= -128 && (sljit_sw)(label_addr - (jump->addr + 2)) <= 127;
+
+ if (type == SLJIT_JUMP) {
+ if (short_jump)
+ *code_ptr++ = JMP_i8;
+ else
+ *code_ptr++ = JMP_i32;
+ jump->addr++;
+ }
+ else if (type >= SLJIT_FAST_CALL) {
+ short_jump = 0;
+ *code_ptr++ = CALL_i32;
+ jump->addr++;
+ }
+ else if (short_jump) {
+ *code_ptr++ = U8(get_jump_code(type) - 0x10);
+ jump->addr++;
+ }
+ else {
+ *code_ptr++ = GROUP_0F;
+ *code_ptr++ = get_jump_code(type);
+ jump->addr += 2;
+ }
+
+ if (short_jump) {
+ jump->flags |= PATCH_MB;
+ code_ptr += sizeof(sljit_s8);
+ } else {
+ jump->flags |= PATCH_MW;
+ code_ptr += sizeof(sljit_s32);
+ }
+
+ return code_ptr;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf;
+ sljit_u8 *code;
+ sljit_u8 *code_ptr;
+ sljit_u8 *buf_ptr;
+ sljit_u8 *buf_end;
+ sljit_u8 len;
+ sljit_sw executable_offset;
+ sljit_uw jump_addr;
+
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ struct sljit_const *const_;
+ struct sljit_put_label *put_label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_generate_code(compiler));
+ reverse_buf(compiler);
+
+ /* Second code generation pass. */
+ code = (sljit_u8*)SLJIT_MALLOC_EXEC(compiler->size, compiler->exec_allocator_data);
+ PTR_FAIL_WITH_EXEC_IF(code);
+ buf = compiler->buf;
+
+ code_ptr = code;
+ label = compiler->labels;
+ jump = compiler->jumps;
+ const_ = compiler->consts;
+ put_label = compiler->put_labels;
+ executable_offset = SLJIT_EXEC_OFFSET(code);
+
+ do {
+ buf_ptr = buf->memory;
+ buf_end = buf_ptr + buf->used_size;
+ do {
+ len = *buf_ptr++;
+ if (len > 0) {
+ /* The code is already generated. */
+ SLJIT_MEMCPY(code_ptr, buf_ptr, len);
+ code_ptr += len;
+ buf_ptr += len;
+ }
+ else {
+ switch (*buf_ptr) {
+ case 0:
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ break;
+ case 1:
+ jump->addr = (sljit_uw)code_ptr;
+ if (!(jump->flags & SLJIT_REWRITABLE_JUMP))
+ code_ptr = generate_near_jump_code(jump, code_ptr, code, executable_offset);
+ else {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ code_ptr = generate_far_jump_code(jump, code_ptr, executable_offset);
+#else
+ code_ptr = generate_far_jump_code(jump, code_ptr);
+#endif
+ }
+ jump = jump->next;
+ break;
+ case 2:
+ const_->addr = ((sljit_uw)code_ptr) - sizeof(sljit_sw);
+ const_ = const_->next;
+ break;
+ default:
+ SLJIT_ASSERT(*buf_ptr == 3);
+ SLJIT_ASSERT(put_label->label);
+ put_label->addr = (sljit_uw)code_ptr;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ code_ptr = generate_put_label_code(put_label, code_ptr, (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code, executable_offset) + put_label->label->size);
+#endif
+ put_label = put_label->next;
+ break;
+ }
+ buf_ptr++;
+ }
+ } while (buf_ptr < buf_end);
+ SLJIT_ASSERT(buf_ptr == buf_end);
+ buf = buf->next;
+ } while (buf);
+
+ SLJIT_ASSERT(!label);
+ SLJIT_ASSERT(!jump);
+ SLJIT_ASSERT(!const_);
+ SLJIT_ASSERT(!put_label);
+ SLJIT_ASSERT(code_ptr <= code + compiler->size);
+
+ jump = compiler->jumps;
+ while (jump) {
+ if (jump->flags & (PATCH_MB | PATCH_MW)) {
+ if (jump->flags & JUMP_LABEL)
+ jump_addr = jump->u.label->addr;
+ else
+ jump_addr = jump->u.target;
+
+ jump_addr -= jump->addr + (sljit_uw)executable_offset;
+
+ if (jump->flags & PATCH_MB) {
+ jump_addr -= sizeof(sljit_s8);
+ SLJIT_ASSERT((sljit_sw)jump_addr >= -128 && (sljit_sw)jump_addr <= 127);
+ *(sljit_u8*)jump->addr = U8(jump_addr);
+ } else {
+ jump_addr -= sizeof(sljit_s32);
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ sljit_unaligned_store_sw((void*)jump->addr, (sljit_sw)jump_addr);
+#else
+ SLJIT_ASSERT((sljit_sw)jump_addr >= HALFWORD_MIN && (sljit_sw)jump_addr <= HALFWORD_MAX);
+ sljit_unaligned_store_s32((void*)jump->addr, (sljit_s32)jump_addr);
+#endif
+ }
+ }
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ else if (jump->flags & PATCH_MD) {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ sljit_unaligned_store_sw((void*)jump->addr, (sljit_sw)jump->u.label->addr);
+ }
+#endif
+
+ jump = jump->next;
+ }
+
+ put_label = compiler->put_labels;
+ while (put_label) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ sljit_unaligned_store_sw((void*)(put_label->addr - sizeof(sljit_sw)), (sljit_sw)put_label->label->addr);
+#else
+ if (put_label->flags & PATCH_MD) {
+ SLJIT_ASSERT(put_label->label->addr > HALFWORD_MAX);
+ sljit_unaligned_store_sw((void*)(put_label->addr - sizeof(sljit_sw)), (sljit_sw)put_label->label->addr);
+ }
+ else {
+ SLJIT_ASSERT(put_label->label->addr <= HALFWORD_MAX);
+ sljit_unaligned_store_s32((void*)(put_label->addr - sizeof(sljit_s32)), (sljit_s32)put_label->label->addr);
+ }
+#endif
+
+ put_label = put_label->next;
+ }
+
+ compiler->error = SLJIT_ERR_COMPILED;
+ compiler->executable_offset = executable_offset;
+ compiler->executable_size = (sljit_uw)(code_ptr - code);
+
+ code = (sljit_u8*)SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS(code, (sljit_u8*)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset), 1);
+ return (void*)code;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#elif (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2)
+ if (cpu_feature_list == 0)
+ get_cpu_features();
+ return (cpu_feature_list & CPU_FEATURE_SSE2) != 0;
+#else /* SLJIT_DETECT_SSE2 */
+ return 1;
+#endif /* SLJIT_DETECT_SSE2 */
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ case SLJIT_HAS_VIRTUAL_REGISTERS:
+ return 1;
+#endif /* SLJIT_CONFIG_X86_32 */
+
+ case SLJIT_HAS_CLZ:
+ if (cpu_feature_list == 0)
+ get_cpu_features();
+
+ return (cpu_feature_list & CPU_FEATURE_LZCNT) ? 1 : 2;
+
+ case SLJIT_HAS_CTZ:
+ if (cpu_feature_list == 0)
+ get_cpu_features();
+
+ return (cpu_feature_list & CPU_FEATURE_TZCNT) ? 1 : 2;
+
+ case SLJIT_HAS_CMOV:
+ if (cpu_feature_list == 0)
+ get_cpu_features();
+ return (cpu_feature_list & CPU_FEATURE_CMOV) != 0;
+
+ case SLJIT_HAS_ROT:
+ case SLJIT_HAS_PREFETCH:
+ return 1;
+
+ case SLJIT_HAS_SSE2:
+#if (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2)
+ if (cpu_feature_list == 0)
+ get_cpu_features();
+ return (cpu_feature_list & CPU_FEATURE_SSE2) != 0;
+#else /* !SLJIT_DETECT_SSE2 */
+ return 1;
+#endif /* SLJIT_DETECT_SSE2 */
+
+ default:
+ return 0;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type)
+{
+ if (type < SLJIT_UNORDERED || type > SLJIT_ORDERED_LESS_EQUAL)
+ return 0;
+
+ switch (type) {
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ return 0;
+ }
+
+ return 1;
+}
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+#define BINARY_OPCODE(opcode) (((opcode ## _EAX_i32) << 24) | ((opcode ## _r_rm) << 16) | ((opcode ## _rm_r) << 8) | (opcode))
+
+#define BINARY_IMM32(op_imm, immw, arg, argw) \
+ do { \
+ inst = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, immw, arg, argw); \
+ FAIL_IF(!inst); \
+ *(inst + 1) |= (op_imm); \
+ } while (0)
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+
+#define BINARY_IMM(op_imm, op_mr, immw, arg, argw) \
+ do { \
+ if (IS_HALFWORD(immw) || compiler->mode32) { \
+ BINARY_IMM32(op_imm, immw, arg, argw); \
+ } \
+ else { \
+ FAIL_IF(emit_load_imm64(compiler, (arg == TMP_REG1) ? TMP_REG2 : TMP_REG1, immw)); \
+ inst = emit_x86_instruction(compiler, 1, (arg == TMP_REG1) ? TMP_REG2 : TMP_REG1, 0, arg, argw); \
+ FAIL_IF(!inst); \
+ *inst = (op_mr); \
+ } \
+ } while (0)
+
+#define BINARY_EAX_IMM(op_eax_imm, immw) \
+ FAIL_IF(emit_do_imm32(compiler, (!compiler->mode32) ? REX_W : 0, (op_eax_imm), immw))
+
+#else /* !SLJIT_CONFIG_X86_64 */
+
+#define BINARY_IMM(op_imm, op_mr, immw, arg, argw) \
+ BINARY_IMM32(op_imm, immw, arg, argw)
+
+#define BINARY_EAX_IMM(op_eax_imm, immw) \
+ FAIL_IF(emit_do_imm(compiler, (op_eax_imm), immw))
+
+#endif /* SLJIT_CONFIG_X86_64 */
+
+static sljit_s32 emit_mov(struct sljit_compiler *compiler,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw);
+
+#define EMIT_MOV(compiler, dst, dstw, src, srcw) \
+ FAIL_IF(emit_mov(compiler, dst, dstw, src, srcw));
+
+static SLJIT_INLINE sljit_s32 emit_sse2_store(struct sljit_compiler *compiler,
+ sljit_s32 single, sljit_s32 dst, sljit_sw dstw, sljit_s32 src);
+
+static SLJIT_INLINE sljit_s32 emit_sse2_load(struct sljit_compiler *compiler,
+ sljit_s32 single, sljit_s32 dst, sljit_s32 src, sljit_sw srcw);
+
+static sljit_s32 emit_cmp_binary(struct sljit_compiler *compiler,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w);
+
+static SLJIT_INLINE sljit_s32 emit_endbranch(struct sljit_compiler *compiler)
+{
+#if (defined SLJIT_CONFIG_X86_CET && SLJIT_CONFIG_X86_CET)
+ /* Emit endbr32/endbr64 when CET is enabled. */
+ sljit_u8 *inst;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 4);
+ FAIL_IF(!inst);
+ INC_SIZE(4);
+ *inst++ = 0xf3;
+ *inst++ = 0x0f;
+ *inst++ = 0x1e;
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ *inst = 0xfb;
+#else
+ *inst = 0xfa;
+#endif
+#else /* !SLJIT_CONFIG_X86_CET */
+ SLJIT_UNUSED_ARG(compiler);
+#endif /* SLJIT_CONFIG_X86_CET */
+ return SLJIT_SUCCESS;
+}
+
+#if (defined SLJIT_CONFIG_X86_CET && SLJIT_CONFIG_X86_CET) && defined (__SHSTK__)
+
+static SLJIT_INLINE sljit_s32 emit_rdssp(struct sljit_compiler *compiler, sljit_s32 reg)
+{
+ sljit_u8 *inst;
+ sljit_s32 size;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ size = 5;
+#else
+ size = 4;
+#endif
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+ INC_SIZE(size);
+ *inst++ = 0xf3;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ *inst++ = REX_W | (reg_map[reg] <= 7 ? 0 : REX_B);
+#endif
+ *inst++ = 0x0f;
+ *inst++ = 0x1e;
+ *inst = (0x3 << 6) | (0x1 << 3) | (reg_map[reg] & 0x7);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_incssp(struct sljit_compiler *compiler, sljit_s32 reg)
+{
+ sljit_u8 *inst;
+ sljit_s32 size;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ size = 5;
+#else
+ size = 4;
+#endif
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+ INC_SIZE(size);
+ *inst++ = 0xf3;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ *inst++ = REX_W | (reg_map[reg] <= 7 ? 0 : REX_B);
+#endif
+ *inst++ = 0x0f;
+ *inst++ = 0xae;
+ *inst = (0x3 << 6) | (0x5 << 3) | (reg_map[reg] & 0x7);
+ return SLJIT_SUCCESS;
+}
+
+#endif /* SLJIT_CONFIG_X86_CET && __SHSTK__ */
+
+static SLJIT_INLINE sljit_s32 cpu_has_shadow_stack(void)
+{
+#if (defined SLJIT_CONFIG_X86_CET && SLJIT_CONFIG_X86_CET) && defined (__SHSTK__)
+ return _get_ssp() != 0;
+#else /* !SLJIT_CONFIG_X86_CET || !__SHSTK__ */
+ return 0;
+#endif /* SLJIT_CONFIG_X86_CET && __SHSTK__ */
+}
+
+static SLJIT_INLINE sljit_s32 adjust_shadow_stack(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_CONFIG_X86_CET && SLJIT_CONFIG_X86_CET) && defined (__SHSTK__)
+ sljit_u8 *inst, *jz_after_cmp_inst;
+ sljit_uw size_jz_after_cmp_inst;
+
+ sljit_uw size_before_rdssp_inst = compiler->size;
+
+ /* Generate "RDSSP TMP_REG1". */
+ FAIL_IF(emit_rdssp(compiler, TMP_REG1));
+
+ /* Load return address on shadow stack into TMP_REG1. */
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ SLJIT_ASSERT(reg_map[TMP_REG1] == 5);
+
+ /* Hand code unsupported "mov 0x0(%ebp),%ebp". */
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 3);
+ FAIL_IF(!inst);
+ INC_SIZE(3);
+ *inst++ = 0x8b;
+ *inst++ = 0x6d;
+ *inst = 0;
+#else /* !SLJIT_CONFIG_X86_32 */
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(TMP_REG1), 0);
+#endif /* SLJIT_CONFIG_X86_32 */
+
+ /* Compare return address against TMP_REG1. */
+ FAIL_IF(emit_cmp_binary (compiler, TMP_REG1, 0, src, srcw));
+
+ /* Generate JZ to skip shadow stack ajdustment when shadow
+ stack matches normal stack. */
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2);
+ FAIL_IF(!inst);
+ INC_SIZE(2);
+ *inst++ = get_jump_code(SLJIT_EQUAL) - 0x10;
+ size_jz_after_cmp_inst = compiler->size;
+ jz_after_cmp_inst = inst;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ /* REX_W is not necessary. */
+ compiler->mode32 = 1;
+#endif
+ /* Load 1 into TMP_REG1. */
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, 1);
+
+ /* Generate "INCSSP TMP_REG1". */
+ FAIL_IF(emit_incssp(compiler, TMP_REG1));
+
+ /* Jump back to "RDSSP TMP_REG1" to check shadow stack again. */
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2);
+ FAIL_IF(!inst);
+ INC_SIZE(2);
+ *inst++ = JMP_i8;
+ *inst = size_before_rdssp_inst - compiler->size;
+
+ *jz_after_cmp_inst = compiler->size - size_jz_after_cmp_inst;
+#else /* !SLJIT_CONFIG_X86_CET || !__SHSTK__ */
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+#endif /* SLJIT_CONFIG_X86_CET && __SHSTK__ */
+ return SLJIT_SUCCESS;
+}
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+#include "sljitNativeX86_32.c"
+#else
+#include "sljitNativeX86_64.c"
+#endif
+
+static sljit_s32 emit_mov(struct sljit_compiler *compiler,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+
+ if (FAST_IS_REG(src)) {
+ inst = emit_x86_instruction(compiler, 1, src, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_r;
+ return SLJIT_SUCCESS;
+ }
+ if (src & SLJIT_IMM) {
+ if (FAST_IS_REG(dst)) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ return emit_do_imm(compiler, MOV_r_i32 | reg_map[dst], srcw);
+#else
+ if (!compiler->mode32) {
+ if (NOT_HALFWORD(srcw))
+ return emit_load_imm64(compiler, dst, srcw);
+ }
+ else
+ return emit_do_imm32(compiler, (reg_map[dst] >= 8) ? REX_B : 0, U8(MOV_r_i32 | reg_lmap[dst]), srcw);
+#endif
+ }
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (!compiler->mode32 && NOT_HALFWORD(srcw)) {
+ /* Immediate to memory move. Only SLJIT_MOV operation copies
+ an immediate directly into memory so TMP_REG1 can be used. */
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG1, srcw));
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_r;
+ return SLJIT_SUCCESS;
+ }
+#endif
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, srcw, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_i32;
+ return SLJIT_SUCCESS;
+ }
+ if (FAST_IS_REG(dst)) {
+ inst = emit_x86_instruction(compiler, 1, dst, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst = MOV_r_rm;
+ return SLJIT_SUCCESS;
+ }
+
+ /* Memory to memory move. Only SLJIT_MOV operation copies
+ data from memory to memory so TMP_REG1 can be used. */
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst = MOV_r_rm;
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_r;
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+ sljit_u8 *inst;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ sljit_uw size;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op0(compiler, op));
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_BREAKPOINT:
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ *inst = INT3;
+ break;
+ case SLJIT_NOP:
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ *inst = NOP;
+ break;
+ case SLJIT_LMUL_UW:
+ case SLJIT_LMUL_SW:
+ case SLJIT_DIVMOD_UW:
+ case SLJIT_DIVMOD_SW:
+ case SLJIT_DIV_UW:
+ case SLJIT_DIV_SW:
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+#ifdef _WIN64
+ SLJIT_ASSERT(
+ reg_map[SLJIT_R0] == 0
+ && reg_map[SLJIT_R1] == 2
+ && reg_map[TMP_REG1] > 7);
+#else
+ SLJIT_ASSERT(
+ reg_map[SLJIT_R0] == 0
+ && reg_map[SLJIT_R1] < 7
+ && reg_map[TMP_REG1] == 2);
+#endif
+ compiler->mode32 = op & SLJIT_32;
+#endif
+ SLJIT_COMPILE_ASSERT((SLJIT_DIVMOD_UW & 0x2) == 0 && SLJIT_DIV_UW - 0x2 == SLJIT_DIVMOD_UW, bad_div_opcode_assignments);
+
+ op = GET_OPCODE(op);
+ if ((op | 0x2) == SLJIT_DIV_UW) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || defined(_WIN64)
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_R1, 0);
+ inst = emit_x86_instruction(compiler, 1, SLJIT_R1, 0, SLJIT_R1, 0);
+#else
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, TMP_REG1, 0);
+#endif
+ FAIL_IF(!inst);
+ *inst = XOR_r_rm;
+ }
+
+ if ((op | 0x2) == SLJIT_DIV_SW) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || defined(_WIN64)
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_R1, 0);
+#endif
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ *inst = CDQ;
+#else
+ if (compiler->mode32) {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ *inst = CDQ;
+ } else {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2);
+ FAIL_IF(!inst);
+ INC_SIZE(2);
+ *inst++ = REX_W;
+ *inst = CDQ;
+ }
+#endif
+ }
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2);
+ FAIL_IF(!inst);
+ INC_SIZE(2);
+ *inst++ = GROUP_F7;
+ *inst = MOD_REG | ((op >= SLJIT_DIVMOD_UW) ? reg_map[TMP_REG1] : reg_map[SLJIT_R1]);
+#else
+#ifdef _WIN64
+ size = (!compiler->mode32 || op >= SLJIT_DIVMOD_UW) ? 3 : 2;
+#else
+ size = (!compiler->mode32) ? 3 : 2;
+#endif
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+ INC_SIZE(size);
+#ifdef _WIN64
+ if (!compiler->mode32)
+ *inst++ = REX_W | ((op >= SLJIT_DIVMOD_UW) ? REX_B : 0);
+ else if (op >= SLJIT_DIVMOD_UW)
+ *inst++ = REX_B;
+ *inst++ = GROUP_F7;
+ *inst = MOD_REG | ((op >= SLJIT_DIVMOD_UW) ? reg_lmap[TMP_REG1] : reg_lmap[SLJIT_R1]);
+#else
+ if (!compiler->mode32)
+ *inst++ = REX_W;
+ *inst++ = GROUP_F7;
+ *inst = MOD_REG | reg_map[SLJIT_R1];
+#endif
+#endif
+ switch (op) {
+ case SLJIT_LMUL_UW:
+ *inst |= MUL;
+ break;
+ case SLJIT_LMUL_SW:
+ *inst |= IMUL;
+ break;
+ case SLJIT_DIVMOD_UW:
+ case SLJIT_DIV_UW:
+ *inst |= DIV;
+ break;
+ case SLJIT_DIVMOD_SW:
+ case SLJIT_DIV_SW:
+ *inst |= IDIV;
+ break;
+ }
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) && !defined(_WIN64)
+ if (op <= SLJIT_DIVMOD_SW)
+ EMIT_MOV(compiler, SLJIT_R1, 0, TMP_REG1, 0);
+#else
+ if (op >= SLJIT_DIV_UW)
+ EMIT_MOV(compiler, SLJIT_R1, 0, TMP_REG1, 0);
+#endif
+ break;
+ case SLJIT_ENDBR:
+ return emit_endbranch(compiler);
+ case SLJIT_SKIP_FRAMES_BEFORE_RETURN:
+ return skip_frames_before_return(compiler);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#define ENCODE_PREFIX(prefix) \
+ do { \
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1); \
+ FAIL_IF(!inst); \
+ INC_SIZE(1); \
+ *inst = U8(prefix); \
+ } while (0)
+
+static sljit_s32 emit_mov_byte(struct sljit_compiler *compiler, sljit_s32 sign,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+ sljit_s32 dst_r;
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ sljit_s32 work_r;
+#endif
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+#endif
+
+ if (src & SLJIT_IMM) {
+ if (FAST_IS_REG(dst)) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ return emit_do_imm(compiler, MOV_r_i32 | reg_map[dst], srcw);
+#else
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, srcw, dst, 0);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_i32;
+ return SLJIT_SUCCESS;
+#endif
+ }
+ inst = emit_x86_instruction(compiler, 1 | EX86_BYTE_ARG | EX86_NO_REXW, SLJIT_IMM, srcw, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm8_i8;
+ return SLJIT_SUCCESS;
+ }
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if ((dst & SLJIT_MEM) && FAST_IS_REG(src)) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ if (reg_map[src] >= 4) {
+ SLJIT_ASSERT(dst_r == TMP_REG1);
+ EMIT_MOV(compiler, TMP_REG1, 0, src, 0);
+ } else
+ dst_r = src;
+#else
+ dst_r = src;
+#endif
+ }
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ else if (FAST_IS_REG(src) && reg_map[src] >= 4) {
+ /* src, dst are registers. */
+ SLJIT_ASSERT(FAST_IS_REG(dst));
+ if (reg_map[dst] < 4) {
+ if (dst != src)
+ EMIT_MOV(compiler, dst, 0, src, 0);
+ inst = emit_x86_instruction(compiler, 2, dst, 0, dst, 0);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = sign ? MOVSX_r_rm8 : MOVZX_r_rm8;
+ }
+ else {
+ if (dst != src)
+ EMIT_MOV(compiler, dst, 0, src, 0);
+ if (sign) {
+ /* shl reg, 24 */
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_IMM, 24, dst, 0);
+ FAIL_IF(!inst);
+ *inst |= SHL;
+ /* sar reg, 24 */
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_IMM, 24, dst, 0);
+ FAIL_IF(!inst);
+ *inst |= SAR;
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, 0xff, dst, 0);
+ FAIL_IF(!inst);
+ *(inst + 1) |= AND;
+ }
+ }
+ return SLJIT_SUCCESS;
+ }
+#endif
+ else {
+ /* src can be memory addr or reg_map[src] < 4 on x86_32 architectures. */
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = sign ? MOVSX_r_rm8 : MOVZX_r_rm8;
+ }
+
+ if (dst & SLJIT_MEM) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ if (dst_r == TMP_REG1) {
+ /* Find a non-used register, whose reg_map[src] < 4. */
+ if ((dst & REG_MASK) == SLJIT_R0) {
+ if ((dst & OFFS_REG_MASK) == TO_OFFS_REG(SLJIT_R1))
+ work_r = SLJIT_R2;
+ else
+ work_r = SLJIT_R1;
+ }
+ else {
+ if ((dst & OFFS_REG_MASK) != TO_OFFS_REG(SLJIT_R0))
+ work_r = SLJIT_R0;
+ else if ((dst & REG_MASK) == SLJIT_R1)
+ work_r = SLJIT_R2;
+ else
+ work_r = SLJIT_R1;
+ }
+
+ if (work_r == SLJIT_R0) {
+ ENCODE_PREFIX(XCHG_EAX_r | reg_map[TMP_REG1]);
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, work_r, 0, dst_r, 0);
+ FAIL_IF(!inst);
+ *inst = XCHG_r_rm;
+ }
+
+ inst = emit_x86_instruction(compiler, 1, work_r, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm8_r8;
+
+ if (work_r == SLJIT_R0) {
+ ENCODE_PREFIX(XCHG_EAX_r | reg_map[TMP_REG1]);
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, work_r, 0, dst_r, 0);
+ FAIL_IF(!inst);
+ *inst = XCHG_r_rm;
+ }
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm8_r8;
+ }
+#else
+ inst = emit_x86_instruction(compiler, 1 | EX86_REX | EX86_NO_REXW, dst_r, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm8_r8;
+#endif
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_prefetch(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 1;
+#endif
+
+ inst = emit_x86_instruction(compiler, 2, 0, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst++ = PREFETCH;
+
+ if (op == SLJIT_PREFETCH_L1)
+ *inst |= (1 << 3);
+ else if (op == SLJIT_PREFETCH_L2)
+ *inst |= (2 << 3);
+ else if (op == SLJIT_PREFETCH_L3)
+ *inst |= (3 << 3);
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_mov_half(struct sljit_compiler *compiler, sljit_s32 sign,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+ sljit_s32 dst_r;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+#endif
+
+ if (src & SLJIT_IMM) {
+ if (FAST_IS_REG(dst)) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ return emit_do_imm(compiler, MOV_r_i32 | reg_map[dst], srcw);
+#else
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, srcw, dst, 0);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_i32;
+ return SLJIT_SUCCESS;
+#endif
+ }
+ inst = emit_x86_instruction(compiler, 1 | EX86_HALF_ARG | EX86_NO_REXW | EX86_PREF_66, SLJIT_IMM, srcw, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_i32;
+ return SLJIT_SUCCESS;
+ }
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if ((dst & SLJIT_MEM) && FAST_IS_REG(src))
+ dst_r = src;
+ else {
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = sign ? MOVSX_r_rm16 : MOVZX_r_rm16;
+ }
+
+ if (dst & SLJIT_MEM) {
+ inst = emit_x86_instruction(compiler, 1 | EX86_NO_REXW | EX86_PREF_66, dst_r, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_r;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_unary(struct sljit_compiler *compiler, sljit_u8 opcode,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+
+ if (dst == src && dstw == srcw) {
+ /* Same input and output */
+ inst = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_F7;
+ *inst |= opcode;
+ return SLJIT_SUCCESS;
+ }
+
+ if (FAST_IS_REG(dst)) {
+ EMIT_MOV(compiler, dst, 0, src, srcw);
+ inst = emit_x86_instruction(compiler, 1, 0, 0, dst, 0);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_F7;
+ *inst |= opcode;
+ return SLJIT_SUCCESS;
+ }
+
+ EMIT_MOV(compiler, TMP_REG1, 0, src, srcw);
+ inst = emit_x86_instruction(compiler, 1, 0, 0, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_F7;
+ *inst |= opcode;
+ EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_not_with_flags(struct sljit_compiler *compiler,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+
+ if (FAST_IS_REG(dst)) {
+ EMIT_MOV(compiler, dst, 0, src, srcw);
+ inst = emit_x86_instruction(compiler, 1, 0, 0, dst, 0);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_F7;
+ *inst |= NOT_rm;
+ inst = emit_x86_instruction(compiler, 1, dst, 0, dst, 0);
+ FAIL_IF(!inst);
+ *inst = OR_r_rm;
+ return SLJIT_SUCCESS;
+ }
+
+ EMIT_MOV(compiler, TMP_REG1, 0, src, srcw);
+ inst = emit_x86_instruction(compiler, 1, 0, 0, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_F7;
+ *inst |= NOT_rm;
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst = OR_r_rm;
+ EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
+ return SLJIT_SUCCESS;
+}
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+static const sljit_sw emit_clz_arg = 32 + 31;
+static const sljit_sw emit_ctz_arg = 32;
+#endif
+
+static sljit_s32 emit_clz_ctz(struct sljit_compiler *compiler, sljit_s32 is_clz,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+ sljit_s32 dst_r;
+ sljit_sw max;
+
+ if (cpu_feature_list == 0)
+ get_cpu_features();
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if (is_clz ? (cpu_feature_list & CPU_FEATURE_LZCNT) : (cpu_feature_list & CPU_FEATURE_TZCNT)) {
+ /* Group prefix added separately. */
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ *inst++ = GROUP_F3;
+
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = is_clz ? LZCNT_r_rm : TZCNT_r_rm;
+
+ if (dst & SLJIT_MEM)
+ EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
+ return SLJIT_SUCCESS;
+ }
+
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = is_clz ? BSR_r_rm : BSF_r_rm;
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ max = is_clz ? (32 + 31) : 32;
+
+ if (cpu_feature_list & CPU_FEATURE_CMOV) {
+ if (dst_r != TMP_REG1) {
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, max);
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG1, 0);
+ }
+ else
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, SLJIT_MEM0(), is_clz ? (sljit_sw)&emit_clz_arg : (sljit_sw)&emit_ctz_arg);
+
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = CMOVE_r_rm;
+ }
+ else
+ FAIL_IF(sljit_emit_cmov_generic(compiler, SLJIT_EQUAL, dst_r, SLJIT_IMM, max));
+
+ if (is_clz) {
+ inst = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, 31, dst_r, 0);
+ FAIL_IF(!inst);
+ *(inst + 1) |= XOR;
+ }
+#else
+ if (is_clz)
+ max = compiler->mode32 ? (32 + 31) : (64 + 63);
+ else
+ max = compiler->mode32 ? 32 : 64;
+
+ if (cpu_feature_list & CPU_FEATURE_CMOV) {
+ EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_IMM, max);
+
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG2, 0);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = CMOVE_r_rm;
+ }
+ else
+ FAIL_IF(sljit_emit_cmov_generic(compiler, SLJIT_EQUAL, dst_r, SLJIT_IMM, max));
+
+ if (is_clz) {
+ inst = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, max >> 1, dst_r, 0);
+ FAIL_IF(!inst);
+ *(inst + 1) |= XOR;
+ }
+#endif
+
+ if (dst & SLJIT_MEM)
+ EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 op_flags = GET_ALL_FLAGS(op);
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ sljit_s32 dst_is_ereg = 0;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ CHECK_EXTRA_REGS(dst, dstw, dst_is_ereg = 1);
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = op_flags & SLJIT_32;
+#endif
+
+ op = GET_OPCODE(op);
+
+ if (op >= SLJIT_MOV && op <= SLJIT_MOV_P) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+#endif
+
+ if (FAST_IS_REG(src) && src == dst) {
+ if (!TYPE_CAST_NEEDED(op))
+ return SLJIT_SUCCESS;
+ }
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (op_flags & SLJIT_32) {
+ if (src & SLJIT_MEM) {
+ if (op == SLJIT_MOV_S32)
+ op = SLJIT_MOV_U32;
+ }
+ else if (src & SLJIT_IMM) {
+ if (op == SLJIT_MOV_U32)
+ op = SLJIT_MOV_S32;
+ }
+ }
+#endif
+
+ if (src & SLJIT_IMM) {
+ switch (op) {
+ case SLJIT_MOV_U8:
+ srcw = (sljit_u8)srcw;
+ break;
+ case SLJIT_MOV_S8:
+ srcw = (sljit_s8)srcw;
+ break;
+ case SLJIT_MOV_U16:
+ srcw = (sljit_u16)srcw;
+ break;
+ case SLJIT_MOV_S16:
+ srcw = (sljit_s16)srcw;
+ break;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ case SLJIT_MOV_U32:
+ srcw = (sljit_u32)srcw;
+ break;
+ case SLJIT_MOV_S32:
+ srcw = (sljit_s32)srcw;
+ break;
+#endif
+ }
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ if (SLJIT_UNLIKELY(dst_is_ereg))
+ return emit_mov(compiler, dst, dstw, src, srcw);
+#endif
+ }
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ if (SLJIT_UNLIKELY(dst_is_ereg) && (!(op == SLJIT_MOV || op == SLJIT_MOV_U32 || op == SLJIT_MOV_S32 || op == SLJIT_MOV_P) || (src & SLJIT_MEM))) {
+ SLJIT_ASSERT(dst == SLJIT_MEM1(SLJIT_SP));
+ dst = TMP_REG1;
+ }
+#endif
+
+ switch (op) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+#endif
+ EMIT_MOV(compiler, dst, dstw, src, srcw);
+ break;
+ case SLJIT_MOV_U8:
+ FAIL_IF(emit_mov_byte(compiler, 0, dst, dstw, src, srcw));
+ break;
+ case SLJIT_MOV_S8:
+ FAIL_IF(emit_mov_byte(compiler, 1, dst, dstw, src, srcw));
+ break;
+ case SLJIT_MOV_U16:
+ FAIL_IF(emit_mov_half(compiler, 0, dst, dstw, src, srcw));
+ break;
+ case SLJIT_MOV_S16:
+ FAIL_IF(emit_mov_half(compiler, 1, dst, dstw, src, srcw));
+ break;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ case SLJIT_MOV_U32:
+ FAIL_IF(emit_mov_int(compiler, 0, dst, dstw, src, srcw));
+ break;
+ case SLJIT_MOV_S32:
+ FAIL_IF(emit_mov_int(compiler, 1, dst, dstw, src, srcw));
+ break;
+ case SLJIT_MOV32:
+ compiler->mode32 = 1;
+ EMIT_MOV(compiler, dst, dstw, src, srcw);
+ compiler->mode32 = 0;
+ break;
+#endif
+ }
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ if (SLJIT_UNLIKELY(dst_is_ereg) && dst == TMP_REG1)
+ return emit_mov(compiler, SLJIT_MEM1(SLJIT_SP), dstw, TMP_REG1, 0);
+#endif
+ return SLJIT_SUCCESS;
+ }
+
+ switch (op) {
+ case SLJIT_NOT:
+ if (SLJIT_UNLIKELY(op_flags & SLJIT_SET_Z))
+ return emit_not_with_flags(compiler, dst, dstw, src, srcw);
+ return emit_unary(compiler, NOT_rm, dst, dstw, src, srcw);
+
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ return emit_clz_ctz(compiler, (op == SLJIT_CLZ), dst, dstw, src, srcw);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_cum_binary(struct sljit_compiler *compiler,
+ sljit_u32 op_types,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_u8* inst;
+ sljit_u8 op_eax_imm = U8(op_types >> 24);
+ sljit_u8 op_rm = U8((op_types >> 16) & 0xff);
+ sljit_u8 op_mr = U8((op_types >> 8) & 0xff);
+ sljit_u8 op_imm = U8(op_types & 0xff);
+
+ if (dst == src1 && dstw == src1w) {
+ if (src2 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if ((dst == SLJIT_R0) && (src2w > 127 || src2w < -128) && (compiler->mode32 || IS_HALFWORD(src2w))) {
+#else
+ if ((dst == SLJIT_R0) && (src2w > 127 || src2w < -128)) {
+#endif
+ BINARY_EAX_IMM(op_eax_imm, src2w);
+ }
+ else {
+ BINARY_IMM(op_imm, op_mr, src2w, dst, dstw);
+ }
+ }
+ else if (FAST_IS_REG(dst)) {
+ inst = emit_x86_instruction(compiler, 1, dst, dstw, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = op_rm;
+ }
+ else if (FAST_IS_REG(src2)) {
+ /* Special exception for sljit_emit_op_flags. */
+ inst = emit_x86_instruction(compiler, 1, src2, src2w, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = op_mr;
+ }
+ else {
+ EMIT_MOV(compiler, TMP_REG1, 0, src2, src2w);
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = op_mr;
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ /* Only for cumulative operations. */
+ if (dst == src2 && dstw == src2w) {
+ if (src1 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if ((dst == SLJIT_R0) && (src1w > 127 || src1w < -128) && (compiler->mode32 || IS_HALFWORD(src1w))) {
+#else
+ if ((dst == SLJIT_R0) && (src1w > 127 || src1w < -128)) {
+#endif
+ BINARY_EAX_IMM(op_eax_imm, src1w);
+ }
+ else {
+ BINARY_IMM(op_imm, op_mr, src1w, dst, dstw);
+ }
+ }
+ else if (FAST_IS_REG(dst)) {
+ inst = emit_x86_instruction(compiler, 1, dst, dstw, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = op_rm;
+ }
+ else if (FAST_IS_REG(src1)) {
+ inst = emit_x86_instruction(compiler, 1, src1, src1w, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = op_mr;
+ }
+ else {
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = op_mr;
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ /* General version. */
+ if (FAST_IS_REG(dst)) {
+ EMIT_MOV(compiler, dst, 0, src1, src1w);
+ if (src2 & SLJIT_IMM) {
+ BINARY_IMM(op_imm, op_mr, src2w, dst, 0);
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, dst, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = op_rm;
+ }
+ }
+ else {
+ /* This version requires less memory writing. */
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ if (src2 & SLJIT_IMM) {
+ BINARY_IMM(op_imm, op_mr, src2w, TMP_REG1, 0);
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = op_rm;
+ }
+ EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_non_cum_binary(struct sljit_compiler *compiler,
+ sljit_u32 op_types,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_u8* inst;
+ sljit_u8 op_eax_imm = U8(op_types >> 24);
+ sljit_u8 op_rm = U8((op_types >> 16) & 0xff);
+ sljit_u8 op_mr = U8((op_types >> 8) & 0xff);
+ sljit_u8 op_imm = U8(op_types & 0xff);
+
+ if (dst == src1 && dstw == src1w) {
+ if (src2 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if ((dst == SLJIT_R0) && (src2w > 127 || src2w < -128) && (compiler->mode32 || IS_HALFWORD(src2w))) {
+#else
+ if ((dst == SLJIT_R0) && (src2w > 127 || src2w < -128)) {
+#endif
+ BINARY_EAX_IMM(op_eax_imm, src2w);
+ }
+ else {
+ BINARY_IMM(op_imm, op_mr, src2w, dst, dstw);
+ }
+ }
+ else if (FAST_IS_REG(dst)) {
+ inst = emit_x86_instruction(compiler, 1, dst, dstw, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = op_rm;
+ }
+ else if (FAST_IS_REG(src2)) {
+ inst = emit_x86_instruction(compiler, 1, src2, src2w, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = op_mr;
+ }
+ else {
+ EMIT_MOV(compiler, TMP_REG1, 0, src2, src2w);
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = op_mr;
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ /* General version. */
+ if (FAST_IS_REG(dst) && dst != src2) {
+ EMIT_MOV(compiler, dst, 0, src1, src1w);
+ if (src2 & SLJIT_IMM) {
+ BINARY_IMM(op_imm, op_mr, src2w, dst, 0);
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, dst, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = op_rm;
+ }
+ }
+ else {
+ /* This version requires less memory writing. */
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ if (src2 & SLJIT_IMM) {
+ BINARY_IMM(op_imm, op_mr, src2w, TMP_REG1, 0);
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = op_rm;
+ }
+ EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_mul(struct sljit_compiler *compiler,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_u8* inst;
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ /* Register destination. */
+ if (dst_r == src1 && !(src2 & SLJIT_IMM)) {
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = IMUL_r_rm;
+ }
+ else if (dst_r == src2 && !(src1 & SLJIT_IMM)) {
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, src1, src1w);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = IMUL_r_rm;
+ }
+ else if (src1 & SLJIT_IMM) {
+ if (src2 & SLJIT_IMM) {
+ EMIT_MOV(compiler, dst_r, 0, SLJIT_IMM, src2w);
+ src2 = dst_r;
+ src2w = 0;
+ }
+
+ if (src1w <= 127 && src1w >= -128) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = IMUL_r_rm_i8;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ *inst = U8(src1w);
+ }
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ else {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = IMUL_r_rm_i32;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 4);
+ FAIL_IF(!inst);
+ INC_SIZE(4);
+ sljit_unaligned_store_sw(inst, src1w);
+ }
+#else
+ else if (IS_HALFWORD(src1w)) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = IMUL_r_rm_i32;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 4);
+ FAIL_IF(!inst);
+ INC_SIZE(4);
+ sljit_unaligned_store_s32(inst, (sljit_s32)src1w);
+ }
+ else {
+ if (dst_r != src2)
+ EMIT_MOV(compiler, dst_r, 0, src2, src2w);
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG2, src1w));
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG2, 0);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = IMUL_r_rm;
+ }
+#endif
+ }
+ else if (src2 & SLJIT_IMM) {
+ /* Note: src1 is NOT immediate. */
+
+ if (src2w <= 127 && src2w >= -128) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = IMUL_r_rm_i8;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ *inst = U8(src2w);
+ }
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ else {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = IMUL_r_rm_i32;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 4);
+ FAIL_IF(!inst);
+ INC_SIZE(4);
+ sljit_unaligned_store_sw(inst, src2w);
+ }
+#else
+ else if (IS_HALFWORD(src2w)) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = IMUL_r_rm_i32;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 4);
+ FAIL_IF(!inst);
+ INC_SIZE(4);
+ sljit_unaligned_store_s32(inst, (sljit_s32)src2w);
+ }
+ else {
+ if (dst_r != src1)
+ EMIT_MOV(compiler, dst_r, 0, src1, src1w);
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG2, src2w));
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG2, 0);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = IMUL_r_rm;
+ }
+#endif
+ }
+ else {
+ /* Neither argument is immediate. */
+ if (ADDRESSING_DEPENDS_ON(src2, dst_r))
+ dst_r = TMP_REG1;
+ EMIT_MOV(compiler, dst_r, 0, src1, src1w);
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = IMUL_r_rm;
+ }
+
+ if (dst & SLJIT_MEM)
+ EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_lea_binary(struct sljit_compiler *compiler,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_u8* inst;
+ sljit_s32 dst_r, done = 0;
+
+ /* These cases better be left to handled by normal way. */
+ if (dst == src1 && dstw == src1w)
+ return SLJIT_ERR_UNSUPPORTED;
+ if (dst == src2 && dstw == src2w)
+ return SLJIT_ERR_UNSUPPORTED;
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if (FAST_IS_REG(src1)) {
+ if (FAST_IS_REG(src2)) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, SLJIT_MEM2(src1, src2), 0);
+ FAIL_IF(!inst);
+ *inst = LEA_r_m;
+ done = 1;
+ }
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if ((src2 & SLJIT_IMM) && (compiler->mode32 || IS_HALFWORD(src2w))) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, SLJIT_MEM1(src1), (sljit_s32)src2w);
+#else
+ if (src2 & SLJIT_IMM) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, SLJIT_MEM1(src1), src2w);
+#endif
+ FAIL_IF(!inst);
+ *inst = LEA_r_m;
+ done = 1;
+ }
+ }
+ else if (FAST_IS_REG(src2)) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if ((src1 & SLJIT_IMM) && (compiler->mode32 || IS_HALFWORD(src1w))) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, SLJIT_MEM1(src2), (sljit_s32)src1w);
+#else
+ if (src1 & SLJIT_IMM) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, SLJIT_MEM1(src2), src1w);
+#endif
+ FAIL_IF(!inst);
+ *inst = LEA_r_m;
+ done = 1;
+ }
+ }
+
+ if (done) {
+ if (dst_r == TMP_REG1)
+ return emit_mov(compiler, dst, dstw, TMP_REG1, 0);
+ return SLJIT_SUCCESS;
+ }
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+static sljit_s32 emit_cmp_binary(struct sljit_compiler *compiler,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_u8* inst;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (src1 == SLJIT_R0 && (src2 & SLJIT_IMM) && (src2w > 127 || src2w < -128) && (compiler->mode32 || IS_HALFWORD(src2w))) {
+#else
+ if (src1 == SLJIT_R0 && (src2 & SLJIT_IMM) && (src2w > 127 || src2w < -128)) {
+#endif
+ BINARY_EAX_IMM(CMP_EAX_i32, src2w);
+ return SLJIT_SUCCESS;
+ }
+
+ if (FAST_IS_REG(src1)) {
+ if (src2 & SLJIT_IMM) {
+ BINARY_IMM(CMP, CMP_rm_r, src2w, src1, 0);
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, src1, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = CMP_r_rm;
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ if (FAST_IS_REG(src2) && !(src1 & SLJIT_IMM)) {
+ inst = emit_x86_instruction(compiler, 1, src2, 0, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = CMP_rm_r;
+ return SLJIT_SUCCESS;
+ }
+
+ if (src2 & SLJIT_IMM) {
+ if (src1 & SLJIT_IMM) {
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ src1 = TMP_REG1;
+ src1w = 0;
+ }
+ BINARY_IMM(CMP, CMP_rm_r, src2w, src1, src1w);
+ }
+ else {
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = CMP_r_rm;
+ }
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_test_binary(struct sljit_compiler *compiler,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_u8* inst;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (src1 == SLJIT_R0 && (src2 & SLJIT_IMM) && (src2w > 127 || src2w < -128) && (compiler->mode32 || IS_HALFWORD(src2w))) {
+#else
+ if (src1 == SLJIT_R0 && (src2 & SLJIT_IMM) && (src2w > 127 || src2w < -128)) {
+#endif
+ BINARY_EAX_IMM(TEST_EAX_i32, src2w);
+ return SLJIT_SUCCESS;
+ }
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (src2 == SLJIT_R0 && (src1 & SLJIT_IMM) && (src1w > 127 || src1w < -128) && (compiler->mode32 || IS_HALFWORD(src1w))) {
+#else
+ if (src2 == SLJIT_R0 && (src1 & SLJIT_IMM) && (src1w > 127 || src1w < -128)) {
+#endif
+ BINARY_EAX_IMM(TEST_EAX_i32, src1w);
+ return SLJIT_SUCCESS;
+ }
+
+ if (!(src1 & SLJIT_IMM)) {
+ if (src2 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (IS_HALFWORD(src2w) || compiler->mode32) {
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, src2w, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = GROUP_F7;
+ }
+ else {
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG1, src2w));
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = TEST_rm_r;
+ }
+#else
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, src2w, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = GROUP_F7;
+#endif
+ return SLJIT_SUCCESS;
+ }
+ else if (FAST_IS_REG(src1)) {
+ inst = emit_x86_instruction(compiler, 1, src1, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = TEST_rm_r;
+ return SLJIT_SUCCESS;
+ }
+ }
+
+ if (!(src2 & SLJIT_IMM)) {
+ if (src1 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (IS_HALFWORD(src1w) || compiler->mode32) {
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, src1w, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = GROUP_F7;
+ }
+ else {
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG1, src1w));
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = TEST_rm_r;
+ }
+#else
+ inst = emit_x86_instruction(compiler, 1, src1, src1w, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = GROUP_F7;
+#endif
+ return SLJIT_SUCCESS;
+ }
+ else if (FAST_IS_REG(src2)) {
+ inst = emit_x86_instruction(compiler, 1, src2, 0, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = TEST_rm_r;
+ return SLJIT_SUCCESS;
+ }
+ }
+
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ if (src2 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (IS_HALFWORD(src2w) || compiler->mode32) {
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, src2w, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst = GROUP_F7;
+ }
+ else {
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG2, src2w));
+ inst = emit_x86_instruction(compiler, 1, TMP_REG2, 0, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst = TEST_rm_r;
+ }
+#else
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, src2w, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst = GROUP_F7;
+#endif
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = TEST_rm_r;
+ }
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_shift(struct sljit_compiler *compiler,
+ sljit_u8 mode,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ sljit_s32 mode32;
+#endif
+ sljit_u8* inst;
+
+ if ((src2 & SLJIT_IMM) || (src2 == SLJIT_PREF_SHIFT_REG)) {
+ if (dst == src1 && dstw == src1w) {
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, src2, src2w, dst, dstw);
+ FAIL_IF(!inst);
+ *inst |= mode;
+ return SLJIT_SUCCESS;
+ }
+ if (dst == SLJIT_PREF_SHIFT_REG && src2 == SLJIT_PREF_SHIFT_REG) {
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst |= mode;
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
+ return SLJIT_SUCCESS;
+ }
+ if (FAST_IS_REG(dst)) {
+ EMIT_MOV(compiler, dst, 0, src1, src1w);
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, src2, src2w, dst, 0);
+ FAIL_IF(!inst);
+ *inst |= mode;
+ return SLJIT_SUCCESS;
+ }
+
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, src2, src2w, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst |= mode;
+ EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
+ return SLJIT_SUCCESS;
+ }
+
+ if (dst == SLJIT_PREF_SHIFT_REG) {
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w);
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst |= mode;
+ return emit_mov(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
+ }
+
+ if (FAST_IS_REG(dst) && dst != src2 && dst != TMP_REG1 && !ADDRESSING_DEPENDS_ON(src2, dst)) {
+ if (src1 != dst)
+ EMIT_MOV(compiler, dst, 0, src1, src1w);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ mode32 = compiler->mode32;
+ compiler->mode32 = 0;
+#endif
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_PREF_SHIFT_REG, 0);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = mode32;
+#endif
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w);
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, dst, 0);
+ FAIL_IF(!inst);
+ *inst |= mode;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+#endif
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = mode32;
+#endif
+ return SLJIT_SUCCESS;
+ }
+
+ /* This case is complex since ecx itself may be used for
+ addressing, and this case must be supported as well. */
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), 0, SLJIT_PREF_SHIFT_REG, 0);
+#else /* !SLJIT_CONFIG_X86_32 */
+ mode32 = compiler->mode32;
+ compiler->mode32 = 0;
+ EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_PREF_SHIFT_REG, 0);
+ compiler->mode32 = mode32;
+#endif /* SLJIT_CONFIG_X86_32 */
+
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w);
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst |= mode;
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, SLJIT_MEM1(SLJIT_SP), 0);
+#else
+ compiler->mode32 = 0;
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG2, 0);
+ compiler->mode32 = mode32;
+#endif /* SLJIT_CONFIG_X86_32 */
+
+ if (dst != TMP_REG1)
+ return emit_mov(compiler, dst, dstw, TMP_REG1, 0);
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_shift_with_flags(struct sljit_compiler *compiler,
+ sljit_u8 mode, sljit_s32 set_flags,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ /* The CPU does not set flags if the shift count is 0. */
+ if (src2 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ src2w &= compiler->mode32 ? 0x1f : 0x3f;
+#else /* !SLJIT_CONFIG_X86_64 */
+ src2w &= 0x1f;
+#endif /* SLJIT_CONFIG_X86_64 */
+ if (src2w != 0)
+ return emit_shift(compiler, mode, dst, dstw, src1, src1w, src2, src2w);
+
+ if (!set_flags)
+ return emit_mov(compiler, dst, dstw, src1, src1w);
+ /* OR dst, src, 0 */
+ return emit_cum_binary(compiler, BINARY_OPCODE(OR),
+ dst, dstw, src1, src1w, SLJIT_IMM, 0);
+ }
+
+ if (!set_flags)
+ return emit_shift(compiler, mode, dst, dstw, src1, src1w, src2, src2w);
+
+ if (!FAST_IS_REG(dst))
+ FAIL_IF(emit_cmp_binary(compiler, src1, src1w, SLJIT_IMM, 0));
+
+ FAIL_IF(emit_shift(compiler, mode, dst, dstw, src1, src1w, src2, src2w));
+
+ if (FAST_IS_REG(dst))
+ return emit_cmp_binary(compiler, dst, dstw, SLJIT_IMM, 0);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ CHECK_EXTRA_REGS(dst, dstw, (void)0);
+ CHECK_EXTRA_REGS(src1, src1w, (void)0);
+ CHECK_EXTRA_REGS(src2, src2w, (void)0);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = op & SLJIT_32;
+#endif
+
+ SLJIT_ASSERT(dst != TMP_REG1 || HAS_FLAGS(op));
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD:
+ if (!HAS_FLAGS(op)) {
+ if (emit_lea_binary(compiler, dst, dstw, src1, src1w, src2, src2w) != SLJIT_ERR_UNSUPPORTED)
+ return compiler->error;
+ }
+ return emit_cum_binary(compiler, BINARY_OPCODE(ADD),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_ADDC:
+ return emit_cum_binary(compiler, BINARY_OPCODE(ADC),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_SUB:
+ if (src1 == SLJIT_IMM && src1w == 0)
+ return emit_unary(compiler, NEG_rm, dst, dstw, src2, src2w);
+
+ if (!HAS_FLAGS(op)) {
+ if ((src2 & SLJIT_IMM) && emit_lea_binary(compiler, dst, dstw, src1, src1w, SLJIT_IMM, -src2w) != SLJIT_ERR_UNSUPPORTED)
+ return compiler->error;
+ if (FAST_IS_REG(dst) && src2 == dst) {
+ FAIL_IF(emit_non_cum_binary(compiler, BINARY_OPCODE(SUB), dst, 0, dst, 0, src1, src1w));
+ return emit_unary(compiler, NEG_rm, dst, 0, dst, 0);
+ }
+ }
+
+ return emit_non_cum_binary(compiler, BINARY_OPCODE(SUB),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_SUBC:
+ return emit_non_cum_binary(compiler, BINARY_OPCODE(SBB),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_MUL:
+ return emit_mul(compiler, dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_AND:
+ return emit_cum_binary(compiler, BINARY_OPCODE(AND),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_OR:
+ return emit_cum_binary(compiler, BINARY_OPCODE(OR),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_XOR:
+ return emit_cum_binary(compiler, BINARY_OPCODE(XOR),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ return emit_shift_with_flags(compiler, SHL, HAS_FLAGS(op),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ return emit_shift_with_flags(compiler, SHR, HAS_FLAGS(op),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ return emit_shift_with_flags(compiler, SAR, HAS_FLAGS(op),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_ROTL:
+ return emit_shift_with_flags(compiler, ROL, 0,
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_ROTR:
+ return emit_shift_with_flags(compiler, ROR, 0,
+ dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 opcode = GET_OPCODE(op);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w));
+
+ if (opcode != SLJIT_SUB && opcode != SLJIT_AND) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, TMP_REG1, 0, src1, src1w, src2, src2w);
+ }
+
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ CHECK_EXTRA_REGS(src1, src1w, (void)0);
+ CHECK_EXTRA_REGS(src2, src2w, (void)0);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = op & SLJIT_32;
+#endif
+
+ if (opcode == SLJIT_SUB) {
+ return emit_cmp_binary(compiler, src1, src1w, src2, src2w);
+ }
+ return emit_test_binary(compiler, src1, src1w, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 restore_ecx = 0;
+ sljit_s32 is_rotate, is_left;
+ sljit_u8* inst;
+ sljit_sw dstw = 0;
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ sljit_s32 tmp2 = SLJIT_MEM1(SLJIT_SP);
+#else /* !SLJIT_CONFIG_X86_32 */
+ sljit_s32 tmp2 = TMP_REG2;
+#endif /* SLJIT_CONFIG_X86_32 */
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ CHECK_EXTRA_REGS(src1, src1w, (void)0);
+ CHECK_EXTRA_REGS(src2, src2w, (void)0);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = op & SLJIT_32;
+#endif
+
+ if (src2 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ src2w &= 0x1f;
+#else /* !SLJIT_CONFIG_X86_32 */
+ src2w &= (op & SLJIT_32) ? 0x1f : 0x3f;
+#endif /* SLJIT_CONFIG_X86_32 */
+
+ if (src2w == 0)
+ return SLJIT_SUCCESS;
+ }
+
+ is_left = (GET_OPCODE(op) == SLJIT_SHL || GET_OPCODE(op) == SLJIT_MSHL);
+
+ is_rotate = (src_dst == src1);
+ CHECK_EXTRA_REGS(src_dst, dstw, (void)0);
+
+ if (is_rotate)
+ return emit_shift(compiler, is_left ? ROL : ROR, src_dst, dstw, src1, src1w, src2, src2w);
+
+ if ((src2 & SLJIT_IMM) || src2 == SLJIT_PREF_SHIFT_REG) {
+ if (!FAST_IS_REG(src1)) {
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ src1 = TMP_REG1;
+ }
+ } else if (FAST_IS_REG(src1)) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+#endif
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_PREF_SHIFT_REG, 0);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = op & SLJIT_32;
+#endif
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w);
+
+ if (src1 == SLJIT_PREF_SHIFT_REG)
+ src1 = TMP_REG1;
+
+ if (src_dst == SLJIT_PREF_SHIFT_REG)
+ src_dst = TMP_REG1;
+
+ restore_ecx = 1;
+ } else {
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+#endif
+ EMIT_MOV(compiler, tmp2, 0, SLJIT_PREF_SHIFT_REG, 0);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = op & SLJIT_32;
+#endif
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w);
+
+ src1 = TMP_REG1;
+
+ if (src_dst == SLJIT_PREF_SHIFT_REG) {
+ src_dst = tmp2;
+ SLJIT_ASSERT(dstw == 0);
+ }
+
+ restore_ecx = 2;
+ }
+
+ inst = emit_x86_instruction(compiler, 2, src1, 0, src_dst, dstw);
+ FAIL_IF(!inst);
+ inst[0] = GROUP_0F;
+
+ if (src2 & SLJIT_IMM) {
+ inst[1] = U8((is_left ? SHLD : SHRD) - 1);
+
+ /* Immedate argument is added separately. */
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ *inst = U8(src2w);
+ } else
+ inst[1] = U8(is_left ? SHLD : SHRD);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+#endif
+
+ if (restore_ecx == 1)
+ return emit_mov(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
+ if (restore_ecx == 2)
+ return emit_mov(compiler, SLJIT_PREF_SHIFT_REG, 0, tmp2, 0);
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_src(compiler, op, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+
+ switch (op) {
+ case SLJIT_FAST_RETURN:
+ return emit_fast_return(compiler, src, srcw);
+ case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN:
+ /* Don't adjust shadow stack if it isn't enabled. */
+ if (!cpu_has_shadow_stack ())
+ return SLJIT_SUCCESS;
+ return adjust_shadow_stack(compiler, src, srcw);
+ case SLJIT_PREFETCH_L1:
+ case SLJIT_PREFETCH_L2:
+ case SLJIT_PREFETCH_L3:
+ case SLJIT_PREFETCH_ONCE:
+ return emit_prefetch(compiler, op, src, srcw);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_register_index(reg));
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ if (reg >= SLJIT_R3 && reg <= SLJIT_R8)
+ return -1;
+#endif
+ return reg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ return reg;
+#else
+ return freg_map[reg];
+#endif
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ sljit_u8 *inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_custom(compiler, instruction, size));
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+ INC_SIZE(size);
+ SLJIT_MEMCPY(inst, instruction, size);
+ return SLJIT_SUCCESS;
+}
+
+/* --------------------------------------------------------------------- */
+/* Floating point operators */
+/* --------------------------------------------------------------------- */
+
+/* Alignment(3) + 4 * 16 bytes. */
+static sljit_u32 sse2_data[3 + (4 * 4)];
+static sljit_u32 *sse2_buffer;
+
+static void init_compiler(void)
+{
+ /* Align to 16 bytes. */
+ sse2_buffer = (sljit_u32*)(((sljit_uw)sse2_data + 15) & ~(sljit_uw)0xf);
+
+ /* Single precision constants (each constant is 16 byte long). */
+ sse2_buffer[0] = 0x80000000;
+ sse2_buffer[4] = 0x7fffffff;
+ /* Double precision constants (each constant is 16 byte long). */
+ sse2_buffer[8] = 0;
+ sse2_buffer[9] = 0x80000000;
+ sse2_buffer[12] = 0xffffffff;
+ sse2_buffer[13] = 0x7fffffff;
+}
+
+static sljit_s32 emit_sse2(struct sljit_compiler *compiler, sljit_u8 opcode,
+ sljit_s32 single, sljit_s32 xmm1, sljit_s32 xmm2, sljit_sw xmm2w)
+{
+ sljit_u8 *inst;
+
+ inst = emit_x86_instruction(compiler, 2 | (single ? EX86_PREF_F3 : EX86_PREF_F2) | EX86_SSE2, xmm1, 0, xmm2, xmm2w);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = opcode;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_sse2_logic(struct sljit_compiler *compiler, sljit_u8 opcode,
+ sljit_s32 pref66, sljit_s32 xmm1, sljit_s32 xmm2, sljit_sw xmm2w)
+{
+ sljit_u8 *inst;
+
+ inst = emit_x86_instruction(compiler, 2 | (pref66 ? EX86_PREF_66 : 0) | EX86_SSE2, xmm1, 0, xmm2, xmm2w);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = opcode;
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_sse2_load(struct sljit_compiler *compiler,
+ sljit_s32 single, sljit_s32 dst, sljit_s32 src, sljit_sw srcw)
+{
+ return emit_sse2(compiler, MOVSD_x_xm, single, dst, src, srcw);
+}
+
+static SLJIT_INLINE sljit_s32 emit_sse2_store(struct sljit_compiler *compiler,
+ sljit_s32 single, sljit_s32 dst, sljit_sw dstw, sljit_s32 src)
+{
+ return emit_sse2(compiler, MOVSD_xm_x, single, src, dst, dstw);
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+ sljit_u8 *inst;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (GET_OPCODE(op) == SLJIT_CONV_SW_FROM_F64)
+ compiler->mode32 = 0;
+#endif
+
+ inst = emit_x86_instruction(compiler, 2 | ((op & SLJIT_32) ? EX86_PREF_F3 : EX86_PREF_F2) | EX86_SSE2_OP2, dst_r, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = CVTTSD2SI_r_xm;
+
+ if (dst & SLJIT_MEM)
+ return emit_mov(compiler, dst, dstw, TMP_REG1, 0);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG;
+ sljit_u8 *inst;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_SW)
+ compiler->mode32 = 0;
+#endif
+
+ if (src & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32)
+ srcw = (sljit_s32)srcw;
+#endif
+ EMIT_MOV(compiler, TMP_REG1, 0, src, srcw);
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ inst = emit_x86_instruction(compiler, 2 | ((op & SLJIT_32) ? EX86_PREF_F3 : EX86_PREF_F2) | EX86_SSE2_OP1, dst_r, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = CVTSI2SD_x_rm;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 1;
+#endif
+ if (dst_r == TMP_FREG)
+ return emit_sse2_store(compiler, op & SLJIT_32, dst, dstw, TMP_FREG);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ switch (GET_FLAG_TYPE(op)) {
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ if (!FAST_IS_REG(src2)) {
+ FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, TMP_FREG, src2, src2w));
+ src2 = TMP_FREG;
+ }
+
+ return emit_sse2_logic(compiler, UCOMISD_x_xm, !(op & SLJIT_32), src2, src1, src1w);
+ }
+
+ if (!FAST_IS_REG(src1)) {
+ FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, TMP_FREG, src1, src1w));
+ src1 = TMP_FREG;
+ }
+
+ return emit_sse2_logic(compiler, UCOMISD_x_xm, !(op & SLJIT_32), src1, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 1;
+#endif
+
+ CHECK_ERROR();
+ SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
+
+ if (GET_OPCODE(op) == SLJIT_MOV_F64) {
+ if (FAST_IS_REG(dst))
+ return emit_sse2_load(compiler, op & SLJIT_32, dst, src, srcw);
+ if (FAST_IS_REG(src))
+ return emit_sse2_store(compiler, op & SLJIT_32, dst, dstw, src);
+ FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, TMP_FREG, src, srcw));
+ return emit_sse2_store(compiler, op & SLJIT_32, dst, dstw, TMP_FREG);
+ }
+
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32) {
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG;
+ if (FAST_IS_REG(src)) {
+ /* We overwrite the high bits of source. From SLJIT point of view,
+ this is not an issue.
+ Note: In SSE3, we could also use MOVDDUP and MOVSLDUP. */
+ FAIL_IF(emit_sse2_logic(compiler, UNPCKLPD_x_xm, op & SLJIT_32, src, src, 0));
+ }
+ else {
+ FAIL_IF(emit_sse2_load(compiler, !(op & SLJIT_32), TMP_FREG, src, srcw));
+ src = TMP_FREG;
+ }
+
+ FAIL_IF(emit_sse2_logic(compiler, CVTPD2PS_x_xm, op & SLJIT_32, dst_r, src, 0));
+ if (dst_r == TMP_FREG)
+ return emit_sse2_store(compiler, op & SLJIT_32, dst, dstw, TMP_FREG);
+ return SLJIT_SUCCESS;
+ }
+
+ if (FAST_IS_REG(dst)) {
+ dst_r = dst;
+ if (dst != src)
+ FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, dst_r, src, srcw));
+ }
+ else {
+ dst_r = TMP_FREG;
+ FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, dst_r, src, srcw));
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_NEG_F64:
+ FAIL_IF(emit_sse2_logic(compiler, XORPD_x_xm, 1, dst_r, SLJIT_MEM0(), (sljit_sw)(op & SLJIT_32 ? sse2_buffer : sse2_buffer + 8)));
+ break;
+
+ case SLJIT_ABS_F64:
+ FAIL_IF(emit_sse2_logic(compiler, ANDPD_x_xm, 1, dst_r, SLJIT_MEM0(), (sljit_sw)(op & SLJIT_32 ? sse2_buffer + 4 : sse2_buffer + 12)));
+ break;
+ }
+
+ if (dst_r == TMP_FREG)
+ return emit_sse2_store(compiler, op & SLJIT_32, dst, dstw, TMP_FREG);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 1;
+#endif
+
+ if (FAST_IS_REG(dst)) {
+ dst_r = dst;
+ if (dst == src1)
+ ; /* Do nothing here. */
+ else if (dst == src2 && (op == SLJIT_ADD_F64 || op == SLJIT_MUL_F64)) {
+ /* Swap arguments. */
+ src2 = src1;
+ src2w = src1w;
+ }
+ else if (dst != src2)
+ FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, dst_r, src1, src1w));
+ else {
+ dst_r = TMP_FREG;
+ FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, TMP_FREG, src1, src1w));
+ }
+ }
+ else {
+ dst_r = TMP_FREG;
+ FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, TMP_FREG, src1, src1w));
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD_F64:
+ FAIL_IF(emit_sse2(compiler, ADDSD_x_xm, op & SLJIT_32, dst_r, src2, src2w));
+ break;
+
+ case SLJIT_SUB_F64:
+ FAIL_IF(emit_sse2(compiler, SUBSD_x_xm, op & SLJIT_32, dst_r, src2, src2w));
+ break;
+
+ case SLJIT_MUL_F64:
+ FAIL_IF(emit_sse2(compiler, MULSD_x_xm, op & SLJIT_32, dst_r, src2, src2w));
+ break;
+
+ case SLJIT_DIV_F64:
+ FAIL_IF(emit_sse2(compiler, DIVSD_x_xm, op & SLJIT_32, dst_r, src2, src2w));
+ break;
+ }
+
+ if (dst_r == TMP_FREG)
+ return emit_sse2_store(compiler, op & SLJIT_32, dst, dstw, TMP_FREG);
+ return SLJIT_SUCCESS;
+}
+
+/* --------------------------------------------------------------------- */
+/* Conditional instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ sljit_u8 *inst;
+ struct sljit_label *label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_label(compiler));
+
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ return compiler->last_label;
+
+ label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
+ PTR_FAIL_IF(!label);
+ set_label(label, compiler);
+
+ inst = (sljit_u8*)ensure_buf(compiler, 2);
+ PTR_FAIL_IF(!inst);
+
+ *inst++ = 0;
+ *inst++ = 0;
+
+ return label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ sljit_u8 *inst;
+ struct sljit_jump *jump;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_jump(compiler, type));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF_NULL(jump);
+ set_jump(jump, compiler, (sljit_u32)((type & SLJIT_REWRITABLE_JUMP) | ((type & 0xff) << TYPE_SHIFT)));
+ type &= 0xff;
+
+ /* Worst case size. */
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ compiler->size += (type >= SLJIT_JUMP) ? 5 : 6;
+#else
+ compiler->size += (type >= SLJIT_JUMP) ? (10 + 3) : (2 + 10 + 3);
+#endif
+
+ inst = (sljit_u8*)ensure_buf(compiler, 2);
+ PTR_FAIL_IF_NULL(inst);
+
+ *inst++ = 0;
+ *inst++ = 1;
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8 *inst;
+ struct sljit_jump *jump;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+
+ if (src == SLJIT_IMM) {
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ FAIL_IF_NULL(jump);
+ set_jump(jump, compiler, (sljit_u32)(JUMP_ADDR | (type << TYPE_SHIFT)));
+ jump->u.target = (sljit_uw)srcw;
+
+ /* Worst case size. */
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ compiler->size += 5;
+#else
+ compiler->size += 10 + 3;
+#endif
+
+ inst = (sljit_u8*)ensure_buf(compiler, 2);
+ FAIL_IF_NULL(inst);
+
+ *inst++ = 0;
+ *inst++ = 1;
+ }
+ else {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ /* REX_W is not necessary (src is not immediate). */
+ compiler->mode32 = 1;
+#endif
+ inst = emit_x86_instruction(compiler, 1, 0, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_FF;
+ *inst = U8(*inst | ((type >= SLJIT_FAST_CALL) ? CALL_rm : JMP_rm));
+ }
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ sljit_u8 *inst;
+ sljit_u8 cond_set = 0;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ sljit_s32 reg;
+#endif
+ /* ADJUST_LOCAL_OFFSET and CHECK_EXTRA_REGS might overwrite these values. */
+ sljit_s32 dst_save = dst;
+ sljit_sw dstw_save = dstw;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
+
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ CHECK_EXTRA_REGS(dst, dstw, (void)0);
+
+ /* setcc = jcc + 0x10. */
+ cond_set = U8(get_jump_code((sljit_uw)type) + 0x10);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (GET_OPCODE(op) == SLJIT_OR && !GET_ALL_FLAGS(op) && FAST_IS_REG(dst)) {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 4 + 3);
+ FAIL_IF(!inst);
+ INC_SIZE(4 + 3);
+ /* Set low register to conditional flag. */
+ *inst++ = (reg_map[TMP_REG1] <= 7) ? REX : REX_B;
+ *inst++ = GROUP_0F;
+ *inst++ = cond_set;
+ *inst++ = MOD_REG | reg_lmap[TMP_REG1];
+ *inst++ = U8(REX | (reg_map[TMP_REG1] <= 7 ? 0 : REX_R) | (reg_map[dst] <= 7 ? 0 : REX_B));
+ *inst++ = OR_rm8_r8;
+ *inst++ = U8(MOD_REG | (reg_lmap[TMP_REG1] << 3) | reg_lmap[dst]);
+ return SLJIT_SUCCESS;
+ }
+
+ reg = (GET_OPCODE(op) < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG1;
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 4 + 4);
+ FAIL_IF(!inst);
+ INC_SIZE(4 + 4);
+ /* Set low register to conditional flag. */
+ *inst++ = (reg_map[reg] <= 7) ? REX : REX_B;
+ *inst++ = GROUP_0F;
+ *inst++ = cond_set;
+ *inst++ = MOD_REG | reg_lmap[reg];
+ *inst++ = REX_W | (reg_map[reg] <= 7 ? 0 : (REX_B | REX_R));
+ /* The movzx instruction does not affect flags. */
+ *inst++ = GROUP_0F;
+ *inst++ = MOVZX_r_rm8;
+ *inst = U8(MOD_REG | (reg_lmap[reg] << 3) | reg_lmap[reg]);
+
+ if (reg != TMP_REG1)
+ return SLJIT_SUCCESS;
+
+ if (GET_OPCODE(op) < SLJIT_ADD) {
+ compiler->mode32 = GET_OPCODE(op) != SLJIT_MOV;
+ return emit_mov(compiler, dst, dstw, TMP_REG1, 0);
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, dst_save, dstw_save, dst_save, dstw_save, TMP_REG1, 0);
+
+#else
+ /* The SLJIT_CONFIG_X86_32 code path starts here. */
+ if (GET_OPCODE(op) < SLJIT_ADD && FAST_IS_REG(dst)) {
+ if (reg_map[dst] <= 4) {
+ /* Low byte is accessible. */
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 3 + 3);
+ FAIL_IF(!inst);
+ INC_SIZE(3 + 3);
+ /* Set low byte to conditional flag. */
+ *inst++ = GROUP_0F;
+ *inst++ = cond_set;
+ *inst++ = U8(MOD_REG | reg_map[dst]);
+
+ *inst++ = GROUP_0F;
+ *inst++ = MOVZX_r_rm8;
+ *inst = U8(MOD_REG | (reg_map[dst] << 3) | reg_map[dst]);
+ return SLJIT_SUCCESS;
+ }
+
+ /* Low byte is not accessible. */
+ if (cpu_feature_list == 0)
+ get_cpu_features();
+
+ if (cpu_feature_list & CPU_FEATURE_CMOV) {
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, 1);
+ /* a xor reg, reg operation would overwrite the flags. */
+ EMIT_MOV(compiler, dst, 0, SLJIT_IMM, 0);
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 3);
+ FAIL_IF(!inst);
+ INC_SIZE(3);
+
+ *inst++ = GROUP_0F;
+ /* cmovcc = setcc - 0x50. */
+ *inst++ = U8(cond_set - 0x50);
+ *inst++ = U8(MOD_REG | (reg_map[dst] << 3) | reg_map[TMP_REG1]);
+ return SLJIT_SUCCESS;
+ }
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + 3 + 3 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1 + 3 + 3 + 1);
+ *inst++ = U8(XCHG_EAX_r | reg_map[TMP_REG1]);
+ /* Set al to conditional flag. */
+ *inst++ = GROUP_0F;
+ *inst++ = cond_set;
+ *inst++ = MOD_REG | 0 /* eax */;
+
+ *inst++ = GROUP_0F;
+ *inst++ = MOVZX_r_rm8;
+ *inst++ = U8(MOD_REG | (reg_map[dst] << 3) | 0 /* eax */);
+ *inst++ = U8(XCHG_EAX_r | reg_map[TMP_REG1]);
+ return SLJIT_SUCCESS;
+ }
+
+ if (GET_OPCODE(op) == SLJIT_OR && !GET_ALL_FLAGS(op) && FAST_IS_REG(dst) && reg_map[dst] <= 4) {
+ SLJIT_ASSERT(reg_map[SLJIT_R0] == 0);
+
+ if (dst != SLJIT_R0) {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + 3 + 2 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1 + 3 + 2 + 1);
+ /* Set low register to conditional flag. */
+ *inst++ = U8(XCHG_EAX_r | reg_map[TMP_REG1]);
+ *inst++ = GROUP_0F;
+ *inst++ = cond_set;
+ *inst++ = MOD_REG | 0 /* eax */;
+ *inst++ = OR_rm8_r8;
+ *inst++ = MOD_REG | (0 /* eax */ << 3) | reg_map[dst];
+ *inst++ = U8(XCHG_EAX_r | reg_map[TMP_REG1]);
+ }
+ else {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2 + 3 + 2 + 2);
+ FAIL_IF(!inst);
+ INC_SIZE(2 + 3 + 2 + 2);
+ /* Set low register to conditional flag. */
+ *inst++ = XCHG_r_rm;
+ *inst++ = U8(MOD_REG | (1 /* ecx */ << 3) | reg_map[TMP_REG1]);
+ *inst++ = GROUP_0F;
+ *inst++ = cond_set;
+ *inst++ = MOD_REG | 1 /* ecx */;
+ *inst++ = OR_rm8_r8;
+ *inst++ = MOD_REG | (1 /* ecx */ << 3) | 0 /* eax */;
+ *inst++ = XCHG_r_rm;
+ *inst++ = U8(MOD_REG | (1 /* ecx */ << 3) | reg_map[TMP_REG1]);
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ /* Set TMP_REG1 to the bit. */
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + 3 + 3 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1 + 3 + 3 + 1);
+ *inst++ = U8(XCHG_EAX_r | reg_map[TMP_REG1]);
+ /* Set al to conditional flag. */
+ *inst++ = GROUP_0F;
+ *inst++ = cond_set;
+ *inst++ = MOD_REG | 0 /* eax */;
+
+ *inst++ = GROUP_0F;
+ *inst++ = MOVZX_r_rm8;
+ *inst++ = MOD_REG | (0 << 3) /* eax */ | 0 /* eax */;
+
+ *inst++ = U8(XCHG_EAX_r | reg_map[TMP_REG1]);
+
+ if (GET_OPCODE(op) < SLJIT_ADD)
+ return emit_mov(compiler, dst, dstw, TMP_REG1, 0);
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, dst_save, dstw_save, dst_save, dstw_save, TMP_REG1, 0);
+#endif /* SLJIT_CONFIG_X86_64 */
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ type &= ~SLJIT_32;
+
+ if (!sljit_has_cpu_feature(SLJIT_HAS_CMOV) || (dst_reg >= SLJIT_R3 && dst_reg <= SLJIT_S3))
+ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);
+#else
+ if (!sljit_has_cpu_feature(SLJIT_HAS_CMOV))
+ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);
+#endif
+
+ /* ADJUST_LOCAL_OFFSET is not needed. */
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = type & SLJIT_32;
+ type &= ~SLJIT_32;
+#endif
+
+ if (SLJIT_UNLIKELY(src & SLJIT_IMM)) {
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, srcw);
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ inst = emit_x86_instruction(compiler, 2, dst_reg, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = U8(get_jump_code((sljit_uw)type) - 0x40);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_get_local_base(compiler, dst, dstw, offset));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ CHECK_EXTRA_REGS(dst, dstw, (void)0);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+#endif
+
+ ADJUST_LOCAL_OFFSET(SLJIT_MEM1(SLJIT_SP), offset);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (NOT_HALFWORD(offset)) {
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG1, offset));
+#if (defined SLJIT_DEBUG && SLJIT_DEBUG)
+ SLJIT_ASSERT(emit_lea_binary(compiler, dst, dstw, SLJIT_SP, 0, TMP_REG1, 0) != SLJIT_ERR_UNSUPPORTED);
+ return compiler->error;
+#else
+ return emit_lea_binary(compiler, dst, dstw, SLJIT_SP, 0, TMP_REG1, 0);
+#endif
+ }
+#endif
+
+ if (offset != 0)
+ return emit_lea_binary(compiler, dst, dstw, SLJIT_SP, 0, SLJIT_IMM, offset);
+ return emit_mov(compiler, dst, dstw, SLJIT_SP, 0);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ sljit_u8 *inst;
+ struct sljit_const *const_;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ sljit_s32 reg;
+#endif
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ CHECK_EXTRA_REGS(dst, dstw, (void)0);
+
+ const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const));
+ PTR_FAIL_IF(!const_);
+ set_const(const_, compiler);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+ reg = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if (emit_load_imm64(compiler, reg, init_value))
+ return NULL;
+#else
+ if (emit_mov(compiler, dst, dstw, SLJIT_IMM, init_value))
+ return NULL;
+#endif
+
+ inst = (sljit_u8*)ensure_buf(compiler, 2);
+ PTR_FAIL_IF(!inst);
+
+ *inst++ = 0;
+ *inst++ = 2;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (dst & SLJIT_MEM)
+ if (emit_mov(compiler, dst, dstw, TMP_REG1, 0))
+ return NULL;
+#endif
+
+ return const_;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ struct sljit_put_label *put_label;
+ sljit_u8 *inst;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ sljit_s32 reg;
+ sljit_uw start_size;
+#endif
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ CHECK_EXTRA_REGS(dst, dstw, (void)0);
+
+ put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label));
+ PTR_FAIL_IF(!put_label);
+ set_put_label(put_label, compiler, 0);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+ reg = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if (emit_load_imm64(compiler, reg, 0))
+ return NULL;
+#else
+ if (emit_mov(compiler, dst, dstw, SLJIT_IMM, 0))
+ return NULL;
+#endif
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (dst & SLJIT_MEM) {
+ start_size = compiler->size;
+ if (emit_mov(compiler, dst, dstw, TMP_REG1, 0))
+ return NULL;
+ put_label->flags = compiler->size - start_size;
+ }
+#endif
+
+ inst = (sljit_u8*)ensure_buf(compiler, 2);
+ PTR_FAIL_IF(!inst);
+
+ *inst++ = 0;
+ *inst++ = 3;
+
+ return put_label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS((void*)addr, (void*)(addr + sizeof(sljit_uw)), 0);
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ sljit_unaligned_store_sw((void*)addr, (sljit_sw)(new_target - (addr + 4) - (sljit_uw)executable_offset));
+#else
+ sljit_unaligned_store_sw((void*)addr, (sljit_sw)new_target);
+#endif
+ SLJIT_UPDATE_WX_FLAGS((void*)addr, (void*)(addr + sizeof(sljit_uw)), 1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS((void*)addr, (void*)(addr + sizeof(sljit_sw)), 0);
+ sljit_unaligned_store_sw((void*)addr, new_constant);
+ SLJIT_UPDATE_WX_FLAGS((void*)addr, (void*)(addr + sizeof(sljit_sw)), 1);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitProtExecAllocator.c b/contrib/libs/pcre2/src/sljit/sljitProtExecAllocator.c
new file mode 100644
index 00000000000..915411fbed6
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitProtExecAllocator.c
@@ -0,0 +1,474 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ This file contains a simple executable memory allocator
+
+ It is assumed, that executable code blocks are usually medium (or sometimes
+ large) memory blocks, and the allocator is not too frequently called (less
+ optimized than other allocators). Thus, using it as a generic allocator is
+ not suggested.
+
+ How does it work:
+ Memory is allocated in continuous memory areas called chunks by alloc_chunk()
+ Chunk format:
+ [ block ][ block ] ... [ block ][ block terminator ]
+
+ All blocks and the block terminator is started with block_header. The block
+ header contains the size of the previous and the next block. These sizes
+ can also contain special values.
+ Block size:
+ 0 - The block is a free_block, with a different size member.
+ 1 - The block is a block terminator.
+ n - The block is used at the moment, and the value contains its size.
+ Previous block size:
+ 0 - This is the first block of the memory chunk.
+ n - The size of the previous block.
+
+ Using these size values we can go forward or backward on the block chain.
+ The unused blocks are stored in a chain list pointed by free_blocks. This
+ list is useful if we need to find a suitable memory area when the allocator
+ is called.
+
+ When a block is freed, the new free block is connected to its adjacent free
+ blocks if possible.
+
+ [ free block ][ used block ][ free block ]
+ and "used block" is freed, the three blocks are connected together:
+ [ one big free block ]
+*/
+
+/* --------------------------------------------------------------------- */
+/* System (OS) functions */
+/* --------------------------------------------------------------------- */
+
+/* 64 KByte. */
+#define CHUNK_SIZE (sljit_uw)0x10000
+
+struct chunk_header {
+ void *executable;
+};
+
+/*
+ alloc_chunk / free_chunk :
+ * allocate executable system memory chunks
+ * the size is always divisible by CHUNK_SIZE
+ SLJIT_ALLOCATOR_LOCK / SLJIT_ALLOCATOR_UNLOCK :
+ * provided as part of sljitUtils
+ * only the allocator requires this lock, sljit is fully thread safe
+ as it only uses local variables
+*/
+
+#ifndef __NetBSD__
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef O_NOATIME
+#define O_NOATIME 0
+#endif
+
+/* this is a linux extension available since kernel 3.11 */
+#ifndef O_TMPFILE
+#define O_TMPFILE 020200000
+#endif
+
+#ifndef _GNU_SOURCE
+char *secure_getenv(const char *name);
+int mkostemp(char *template, int flags);
+#endif
+
+static SLJIT_INLINE int create_tempfile(void)
+{
+ int fd;
+ char tmp_name[256];
+ size_t tmp_name_len = 0;
+ char *dir;
+ struct stat st;
+#if defined(SLJIT_SINGLE_THREADED) && SLJIT_SINGLE_THREADED
+ mode_t mode;
+#endif
+
+#ifdef HAVE_MEMFD_CREATE
+ /* this is a GNU extension, make sure to use -D_GNU_SOURCE */
+ fd = memfd_create("sljit", MFD_CLOEXEC);
+ if (fd != -1) {
+ fchmod(fd, 0);
+ return fd;
+ }
+#endif
+
+ dir = secure_getenv("TMPDIR");
+
+ if (dir) {
+ tmp_name_len = strlen(dir);
+ if (tmp_name_len > 0 && tmp_name_len < sizeof(tmp_name)) {
+ if ((stat(dir, &st) == 0) && S_ISDIR(st.st_mode))
+ strcpy(tmp_name, dir);
+ }
+ }
+
+#ifdef P_tmpdir
+ if (!tmp_name_len) {
+ tmp_name_len = strlen(P_tmpdir);
+ if (tmp_name_len > 0 && tmp_name_len < sizeof(tmp_name))
+ strcpy(tmp_name, P_tmpdir);
+ }
+#endif
+ if (!tmp_name_len) {
+ strcpy(tmp_name, "/tmp");
+ tmp_name_len = 4;
+ }
+
+ SLJIT_ASSERT(tmp_name_len > 0 && tmp_name_len < sizeof(tmp_name));
+
+ if (tmp_name[tmp_name_len - 1] == '/')
+ tmp_name[--tmp_name_len] = '\0';
+
+#ifdef __linux__
+ /*
+ * the previous trimming might had left an empty string if TMPDIR="/"
+ * so work around the problem below
+ */
+ fd = open(tmp_name_len ? tmp_name : "/",
+ O_TMPFILE | O_EXCL | O_RDWR | O_NOATIME | O_CLOEXEC, 0);
+ if (fd != -1)
+ return fd;
+#endif
+
+ if (tmp_name_len + 7 >= sizeof(tmp_name))
+ return -1;
+
+ strcpy(tmp_name + tmp_name_len, "/XXXXXX");
+#if defined(SLJIT_SINGLE_THREADED) && SLJIT_SINGLE_THREADED
+ mode = umask(0777);
+#endif
+ fd = mkostemp(tmp_name, O_CLOEXEC | O_NOATIME);
+#if defined(SLJIT_SINGLE_THREADED) && SLJIT_SINGLE_THREADED
+ umask(mode);
+#else
+ fchmod(fd, 0);
+#endif
+
+ if (fd == -1)
+ return -1;
+
+ if (unlink(tmp_name)) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static SLJIT_INLINE struct chunk_header* alloc_chunk(sljit_uw size)
+{
+ struct chunk_header *retval;
+ int fd;
+
+ fd = create_tempfile();
+ if (fd == -1)
+ return NULL;
+
+ if (ftruncate(fd, (off_t)size)) {
+ close(fd);
+ return NULL;
+ }
+
+ retval = (struct chunk_header *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+ if (retval == MAP_FAILED) {
+ close(fd);
+ return NULL;
+ }
+
+ retval->executable = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0);
+
+ if (retval->executable == MAP_FAILED) {
+ munmap((void *)retval, size);
+ close(fd);
+ return NULL;
+ }
+
+ close(fd);
+ return retval;
+}
+#else
+/*
+ * MAP_REMAPDUP is a NetBSD extension available sinde 8.0, make sure to
+ * adjust your feature macros (ex: -D_NETBSD_SOURCE) as needed
+ */
+static SLJIT_INLINE struct chunk_header* alloc_chunk(sljit_uw size)
+{
+ struct chunk_header *retval;
+
+ retval = (struct chunk_header *)mmap(NULL, size,
+ PROT_READ | PROT_WRITE | PROT_MPROTECT(PROT_EXEC),
+ MAP_ANON | MAP_SHARED, -1, 0);
+
+ if (retval == MAP_FAILED)
+ return NULL;
+
+ retval->executable = mremap(retval, size, NULL, size, MAP_REMAPDUP);
+ if (retval->executable == MAP_FAILED) {
+ munmap((void *)retval, size);
+ return NULL;
+ }
+
+ if (mprotect(retval->executable, size, PROT_READ | PROT_EXEC) == -1) {
+ munmap(retval->executable, size);
+ munmap((void *)retval, size);
+ return NULL;
+ }
+
+ return retval;
+}
+#endif /* NetBSD */
+
+static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
+{
+ struct chunk_header *header = ((struct chunk_header *)chunk) - 1;
+
+ munmap(header->executable, size);
+ munmap((void *)header, size);
+}
+
+/* --------------------------------------------------------------------- */
+/* Common functions */
+/* --------------------------------------------------------------------- */
+
+#define CHUNK_MASK (~(CHUNK_SIZE - 1))
+
+struct block_header {
+ sljit_uw size;
+ sljit_uw prev_size;
+ sljit_sw executable_offset;
+};
+
+struct free_block {
+ struct block_header header;
+ struct free_block *next;
+ struct free_block *prev;
+ sljit_uw size;
+};
+
+#define AS_BLOCK_HEADER(base, offset) \
+ ((struct block_header*)(((sljit_u8*)base) + offset))
+#define AS_FREE_BLOCK(base, offset) \
+ ((struct free_block*)(((sljit_u8*)base) + offset))
+#define MEM_START(base) ((void*)((base) + 1))
+#define ALIGN_SIZE(size) (((size) + sizeof(struct block_header) + 7u) & ~(sljit_uw)7)
+
+static struct free_block* free_blocks;
+static sljit_uw allocated_size;
+static sljit_uw total_size;
+
+static SLJIT_INLINE void sljit_insert_free_block(struct free_block *free_block, sljit_uw size)
+{
+ free_block->header.size = 0;
+ free_block->size = size;
+
+ free_block->next = free_blocks;
+ free_block->prev = NULL;
+ if (free_blocks)
+ free_blocks->prev = free_block;
+ free_blocks = free_block;
+}
+
+static SLJIT_INLINE void sljit_remove_free_block(struct free_block *free_block)
+{
+ if (free_block->next)
+ free_block->next->prev = free_block->prev;
+
+ if (free_block->prev)
+ free_block->prev->next = free_block->next;
+ else {
+ SLJIT_ASSERT(free_blocks == free_block);
+ free_blocks = free_block->next;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
+{
+ struct chunk_header *chunk_header;
+ struct block_header *header;
+ struct block_header *next_header;
+ struct free_block *free_block;
+ sljit_uw chunk_size;
+ sljit_sw executable_offset;
+
+ SLJIT_ALLOCATOR_LOCK();
+ if (size < (64 - sizeof(struct block_header)))
+ size = (64 - sizeof(struct block_header));
+ size = ALIGN_SIZE(size);
+
+ free_block = free_blocks;
+ while (free_block) {
+ if (free_block->size >= size) {
+ chunk_size = free_block->size;
+ if (chunk_size > size + 64) {
+ /* We just cut a block from the end of the free block. */
+ chunk_size -= size;
+ free_block->size = chunk_size;
+ header = AS_BLOCK_HEADER(free_block, chunk_size);
+ header->prev_size = chunk_size;
+ header->executable_offset = free_block->header.executable_offset;
+ AS_BLOCK_HEADER(header, size)->prev_size = size;
+ }
+ else {
+ sljit_remove_free_block(free_block);
+ header = (struct block_header*)free_block;
+ size = chunk_size;
+ }
+ allocated_size += size;
+ header->size = size;
+ SLJIT_ALLOCATOR_UNLOCK();
+ return MEM_START(header);
+ }
+ free_block = free_block->next;
+ }
+
+ chunk_size = sizeof(struct chunk_header) + sizeof(struct block_header);
+ chunk_size = (chunk_size + size + CHUNK_SIZE - 1) & CHUNK_MASK;
+
+ chunk_header = alloc_chunk(chunk_size);
+ if (!chunk_header) {
+ SLJIT_ALLOCATOR_UNLOCK();
+ return NULL;
+ }
+
+ executable_offset = (sljit_sw)((sljit_u8*)chunk_header->executable - (sljit_u8*)chunk_header);
+
+ chunk_size -= sizeof(struct chunk_header) + sizeof(struct block_header);
+ total_size += chunk_size;
+
+ header = (struct block_header *)(chunk_header + 1);
+
+ header->prev_size = 0;
+ header->executable_offset = executable_offset;
+ if (chunk_size > size + 64) {
+ /* Cut the allocated space into a free and a used block. */
+ allocated_size += size;
+ header->size = size;
+ chunk_size -= size;
+
+ free_block = AS_FREE_BLOCK(header, size);
+ free_block->header.prev_size = size;
+ free_block->header.executable_offset = executable_offset;
+ sljit_insert_free_block(free_block, chunk_size);
+ next_header = AS_BLOCK_HEADER(free_block, chunk_size);
+ }
+ else {
+ /* All space belongs to this allocation. */
+ allocated_size += chunk_size;
+ header->size = chunk_size;
+ next_header = AS_BLOCK_HEADER(header, chunk_size);
+ }
+ next_header->size = 1;
+ next_header->prev_size = chunk_size;
+ next_header->executable_offset = executable_offset;
+ SLJIT_ALLOCATOR_UNLOCK();
+ return MEM_START(header);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
+{
+ struct block_header *header;
+ struct free_block* free_block;
+
+ SLJIT_ALLOCATOR_LOCK();
+ header = AS_BLOCK_HEADER(ptr, -(sljit_sw)sizeof(struct block_header));
+ header = AS_BLOCK_HEADER(header, -header->executable_offset);
+ allocated_size -= header->size;
+
+ /* Connecting free blocks together if possible. */
+
+ /* If header->prev_size == 0, free_block will equal to header.
+ In this case, free_block->header.size will be > 0. */
+ free_block = AS_FREE_BLOCK(header, -(sljit_sw)header->prev_size);
+ if (SLJIT_UNLIKELY(!free_block->header.size)) {
+ free_block->size += header->size;
+ header = AS_BLOCK_HEADER(free_block, free_block->size);
+ header->prev_size = free_block->size;
+ }
+ else {
+ free_block = (struct free_block*)header;
+ sljit_insert_free_block(free_block, header->size);
+ }
+
+ header = AS_BLOCK_HEADER(free_block, free_block->size);
+ if (SLJIT_UNLIKELY(!header->size)) {
+ free_block->size += ((struct free_block*)header)->size;
+ sljit_remove_free_block((struct free_block*)header);
+ header = AS_BLOCK_HEADER(free_block, free_block->size);
+ header->prev_size = free_block->size;
+ }
+
+ /* The whole chunk is free. */
+ if (SLJIT_UNLIKELY(!free_block->header.prev_size && header->size == 1)) {
+ /* If this block is freed, we still have (allocated_size / 2) free space. */
+ if (total_size - free_block->size > (allocated_size * 3 / 2)) {
+ total_size -= free_block->size;
+ sljit_remove_free_block(free_block);
+ free_chunk(free_block, free_block->size +
+ sizeof(struct chunk_header) +
+ sizeof(struct block_header));
+ }
+ }
+
+ SLJIT_ALLOCATOR_UNLOCK();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
+{
+ struct free_block* free_block;
+ struct free_block* next_free_block;
+
+ SLJIT_ALLOCATOR_LOCK();
+
+ free_block = free_blocks;
+ while (free_block) {
+ next_free_block = free_block->next;
+ if (!free_block->header.prev_size &&
+ AS_BLOCK_HEADER(free_block, free_block->size)->size == 1) {
+ total_size -= free_block->size;
+ sljit_remove_free_block(free_block);
+ free_chunk(free_block, free_block->size +
+ sizeof(struct chunk_header) +
+ sizeof(struct block_header));
+ }
+ free_block = next_free_block;
+ }
+
+ SLJIT_ASSERT((total_size && free_blocks) || (!total_size && !free_blocks));
+ SLJIT_ALLOCATOR_UNLOCK();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr)
+{
+ return ((struct block_header *)(ptr))[-1].executable_offset;
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitUtils.c b/contrib/libs/pcre2/src/sljit/sljitUtils.c
new file mode 100644
index 00000000000..967593b1578
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitUtils.c
@@ -0,0 +1,344 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* ------------------------------------------------------------------------ */
+/* Locks */
+/* ------------------------------------------------------------------------ */
+
+/* Executable Allocator */
+
+#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR) \
+ && !(defined SLJIT_WX_EXECUTABLE_ALLOCATOR && SLJIT_WX_EXECUTABLE_ALLOCATOR)
+#if (defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED)
+#define SLJIT_ALLOCATOR_LOCK()
+#define SLJIT_ALLOCATOR_UNLOCK()
+#elif !(defined _WIN32)
+#include <pthread.h>
+
+static pthread_mutex_t allocator_lock = PTHREAD_MUTEX_INITIALIZER;
+
+#define SLJIT_ALLOCATOR_LOCK() pthread_mutex_lock(&allocator_lock)
+#define SLJIT_ALLOCATOR_UNLOCK() pthread_mutex_unlock(&allocator_lock)
+#else /* windows */
+static HANDLE allocator_lock;
+
+static SLJIT_INLINE void allocator_grab_lock(void)
+{
+ HANDLE lock;
+ if (SLJIT_UNLIKELY(!InterlockedCompareExchangePointer(&allocator_lock, NULL, NULL))) {
+ lock = CreateMutex(NULL, FALSE, NULL);
+ if (InterlockedCompareExchangePointer(&allocator_lock, lock, NULL))
+ CloseHandle(lock);
+ }
+ WaitForSingleObject(allocator_lock, INFINITE);
+}
+
+#define SLJIT_ALLOCATOR_LOCK() allocator_grab_lock()
+#define SLJIT_ALLOCATOR_UNLOCK() ReleaseMutex(allocator_lock)
+#endif /* thread implementation */
+#endif /* SLJIT_EXECUTABLE_ALLOCATOR && !SLJIT_WX_EXECUTABLE_ALLOCATOR */
+
+/* ------------------------------------------------------------------------ */
+/* Stack */
+/* ------------------------------------------------------------------------ */
+
+#if ((defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK) \
+ && !(defined SLJIT_UTIL_SIMPLE_STACK_ALLOCATION && SLJIT_UTIL_SIMPLE_STACK_ALLOCATION)) \
+ || ((defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR) \
+ && !((defined SLJIT_PROT_EXECUTABLE_ALLOCATOR && SLJIT_PROT_EXECUTABLE_ALLOCATOR) \
+ || (defined SLJIT_WX_EXECUTABLE_ALLOCATOR && SLJIT_WX_EXECUTABLE_ALLOCATOR)))
+
+#ifndef _WIN32
+/* Provides mmap function. */
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#ifndef MAP_ANON
+#ifdef MAP_ANONYMOUS
+#define MAP_ANON MAP_ANONYMOUS
+#endif /* MAP_ANONYMOUS */
+#endif /* !MAP_ANON */
+
+#ifndef MAP_ANON
+
+#include <fcntl.h>
+
+#ifdef O_CLOEXEC
+#define SLJIT_CLOEXEC O_CLOEXEC
+#else /* !O_CLOEXEC */
+#define SLJIT_CLOEXEC 0
+#endif /* O_CLOEXEC */
+
+/* Some old systems do not have MAP_ANON. */
+static int dev_zero = -1;
+
+#if (defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED)
+
+static SLJIT_INLINE int open_dev_zero(void)
+{
+ dev_zero = open("/dev/zero", O_RDWR | SLJIT_CLOEXEC);
+
+ return dev_zero < 0;
+}
+
+#else /* !SLJIT_SINGLE_THREADED */
+
+#include <pthread.h>
+
+static pthread_mutex_t dev_zero_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static SLJIT_INLINE int open_dev_zero(void)
+{
+ pthread_mutex_lock(&dev_zero_mutex);
+ if (SLJIT_UNLIKELY(dev_zero < 0))
+ dev_zero = open("/dev/zero", O_RDWR | SLJIT_CLOEXEC);
+
+ pthread_mutex_unlock(&dev_zero_mutex);
+ return dev_zero < 0;
+}
+
+#endif /* SLJIT_SINGLE_THREADED */
+#undef SLJIT_CLOEXEC
+#endif /* !MAP_ANON */
+#endif /* !_WIN32 */
+#endif /* open_dev_zero */
+
+#if (defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK) \
+ || (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
+
+#ifdef _WIN32
+
+static SLJIT_INLINE sljit_uw get_page_alignment(void) {
+ SYSTEM_INFO si;
+ static sljit_uw sljit_page_align = 0;
+ if (!sljit_page_align) {
+ GetSystemInfo(&si);
+ sljit_page_align = (sljit_uw)si.dwPageSize - 1;
+ }
+ return sljit_page_align;
+}
+
+#else
+
+#include <unistd.h>
+
+static SLJIT_INLINE sljit_uw get_page_alignment(void) {
+ static sljit_uw sljit_page_align = 0;
+
+ sljit_sw align;
+
+ if (!sljit_page_align) {
+#ifdef _SC_PAGESIZE
+ align = sysconf(_SC_PAGESIZE);
+#else
+ align = getpagesize();
+#endif
+ /* Should never happen. */
+ if (align < 0)
+ align = 4096;
+ sljit_page_align = (sljit_uw)align - 1;
+ }
+ return sljit_page_align;
+}
+
+#endif /* _WIN32 */
+
+#endif /* get_page_alignment() */
+
+#if (defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK)
+
+#if (defined SLJIT_UTIL_SIMPLE_STACK_ALLOCATION && SLJIT_UTIL_SIMPLE_STACK_ALLOCATION)
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_FUNC sljit_allocate_stack(sljit_uw start_size, sljit_uw max_size, void *allocator_data)
+{
+ struct sljit_stack *stack;
+ void *ptr;
+
+ SLJIT_UNUSED_ARG(allocator_data);
+
+ if (start_size > max_size || start_size < 1)
+ return NULL;
+
+ stack = (struct sljit_stack*)SLJIT_MALLOC(sizeof(struct sljit_stack), allocator_data);
+ if (stack == NULL)
+ return NULL;
+
+ ptr = SLJIT_MALLOC(max_size, allocator_data);
+ if (ptr == NULL) {
+ SLJIT_FREE(stack, allocator_data);
+ return NULL;
+ }
+
+ stack->min_start = (sljit_u8 *)ptr;
+ stack->end = stack->min_start + max_size;
+ stack->start = stack->end - start_size;
+ stack->top = stack->end;
+ return stack;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_free_stack(struct sljit_stack *stack, void *allocator_data)
+{
+ SLJIT_UNUSED_ARG(allocator_data);
+ SLJIT_FREE((void*)stack->min_start, allocator_data);
+ SLJIT_FREE(stack, allocator_data);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_u8 *SLJIT_FUNC sljit_stack_resize(struct sljit_stack *stack, sljit_u8 *new_start)
+{
+ if ((new_start < stack->min_start) || (new_start >= stack->end))
+ return NULL;
+ stack->start = new_start;
+ return new_start;
+}
+
+#else /* !SLJIT_UTIL_SIMPLE_STACK_ALLOCATION */
+
+#ifdef _WIN32
+
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_free_stack(struct sljit_stack *stack, void *allocator_data)
+{
+ SLJIT_UNUSED_ARG(allocator_data);
+ VirtualFree((void*)stack->min_start, 0, MEM_RELEASE);
+ SLJIT_FREE(stack, allocator_data);
+}
+
+#else /* !_WIN32 */
+
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_free_stack(struct sljit_stack *stack, void *allocator_data)
+{
+ SLJIT_UNUSED_ARG(allocator_data);
+ munmap((void*)stack->min_start, (size_t)(stack->end - stack->min_start));
+ SLJIT_FREE(stack, allocator_data);
+}
+
+#endif /* _WIN32 */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_FUNC sljit_allocate_stack(sljit_uw start_size, sljit_uw max_size, void *allocator_data)
+{
+ struct sljit_stack *stack;
+ void *ptr;
+ sljit_uw page_align;
+
+ SLJIT_UNUSED_ARG(allocator_data);
+
+ if (start_size > max_size || start_size < 1)
+ return NULL;
+
+ stack = (struct sljit_stack*)SLJIT_MALLOC(sizeof(struct sljit_stack), allocator_data);
+ if (stack == NULL)
+ return NULL;
+
+ /* Align max_size. */
+ page_align = get_page_alignment();
+ max_size = (max_size + page_align) & ~page_align;
+
+#ifdef _WIN32
+ ptr = VirtualAlloc(NULL, max_size, MEM_RESERVE, PAGE_READWRITE);
+ if (!ptr) {
+ SLJIT_FREE(stack, allocator_data);
+ return NULL;
+ }
+
+ stack->min_start = (sljit_u8 *)ptr;
+ stack->end = stack->min_start + max_size;
+ stack->start = stack->end;
+
+ if (sljit_stack_resize(stack, stack->end - start_size) == NULL) {
+ sljit_free_stack(stack, allocator_data);
+ return NULL;
+ }
+#else /* !_WIN32 */
+#ifdef MAP_ANON
+ ptr = mmap(NULL, max_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+#else /* !MAP_ANON */
+ if (SLJIT_UNLIKELY((dev_zero < 0) && open_dev_zero())) {
+ SLJIT_FREE(stack, allocator_data);
+ return NULL;
+ }
+ ptr = mmap(NULL, max_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, dev_zero, 0);
+#endif /* MAP_ANON */
+ if (ptr == MAP_FAILED) {
+ SLJIT_FREE(stack, allocator_data);
+ return NULL;
+ }
+ stack->min_start = (sljit_u8 *)ptr;
+ stack->end = stack->min_start + max_size;
+ stack->start = stack->end - start_size;
+#endif /* _WIN32 */
+
+ stack->top = stack->end;
+ return stack;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_u8 *SLJIT_FUNC sljit_stack_resize(struct sljit_stack *stack, sljit_u8 *new_start)
+{
+#if defined _WIN32 || defined(POSIX_MADV_DONTNEED)
+ sljit_uw aligned_old_start;
+ sljit_uw aligned_new_start;
+ sljit_uw page_align;
+#endif
+
+ if ((new_start < stack->min_start) || (new_start >= stack->end))
+ return NULL;
+
+#ifdef _WIN32
+ page_align = get_page_alignment();
+
+ aligned_new_start = (sljit_uw)new_start & ~page_align;
+ aligned_old_start = ((sljit_uw)stack->start) & ~page_align;
+ if (aligned_new_start != aligned_old_start) {
+ if (aligned_new_start < aligned_old_start) {
+ if (!VirtualAlloc((void*)aligned_new_start, aligned_old_start - aligned_new_start, MEM_COMMIT, PAGE_READWRITE))
+ return NULL;
+ }
+ else {
+ if (!VirtualFree((void*)aligned_old_start, aligned_new_start - aligned_old_start, MEM_DECOMMIT))
+ return NULL;
+ }
+ }
+#elif defined(POSIX_MADV_DONTNEED)
+ if (stack->start < new_start) {
+ page_align = get_page_alignment();
+
+ aligned_new_start = (sljit_uw)new_start & ~page_align;
+ aligned_old_start = ((sljit_uw)stack->start) & ~page_align;
+
+ if (aligned_new_start > aligned_old_start) {
+ posix_madvise((void*)aligned_old_start, aligned_new_start - aligned_old_start, POSIX_MADV_DONTNEED);
+#ifdef MADV_FREE
+ madvise((void*)aligned_old_start, aligned_new_start - aligned_old_start, MADV_FREE);
+#endif /* MADV_FREE */
+ }
+ }
+#endif /* _WIN32 */
+
+ stack->start = new_start;
+ return new_start;
+}
+
+#endif /* SLJIT_UTIL_SIMPLE_STACK_ALLOCATION */
+
+#endif /* SLJIT_UTIL_STACK */
diff --git a/contrib/libs/pcre2/src/sljit/sljitWXExecAllocator.c b/contrib/libs/pcre2/src/sljit/sljitWXExecAllocator.c
new file mode 100644
index 00000000000..68938131559
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitWXExecAllocator.c
@@ -0,0 +1,204 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ This file contains a simple W^X executable memory allocator for POSIX
+ like systems and Windows
+
+ In *NIX, MAP_ANON is required (that is considered a feature) so make
+ sure to set the right availability macros for your system or the code
+ will fail to build.
+
+ If your system doesn't support mapping of anonymous pages (ex: IRIX) it
+ is also likely that it doesn't need this allocator and should be using
+ the standard one instead.
+
+ It allocates a separate map for each code block and may waste a lot of
+ memory, because whatever was requested, will be rounded up to the page
+ size (minimum 4KB, but could be even bigger).
+
+ It changes the page permissions (RW <-> RX) as needed and therefore, if you
+ will be updating the code after it has been generated, need to make sure to
+ block any concurrent execution, or could result in a SIGBUS, that could
+ even manifest itself at a different address than the one that was being
+ modified.
+
+ Only use if you are unable to use the regular allocator because of security
+ restrictions and adding exceptions to your application or the system are
+ not possible.
+*/
+
+#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec) \
+ sljit_update_wx_flags((from), (to), (enable_exec))
+
+#ifndef _WIN32
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#ifdef __NetBSD__
+#define SLJIT_PROT_WX PROT_MPROTECT(PROT_EXEC)
+#define check_se_protected(ptr, size) (0)
+#else /* POSIX */
+#if !(defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED)
+#include <pthread.h>
+#define SLJIT_SE_LOCK() pthread_mutex_lock(&se_lock)
+#define SLJIT_SE_UNLOCK() pthread_mutex_unlock(&se_lock)
+#endif /* !SLJIT_SINGLE_THREADED */
+
+#define check_se_protected(ptr, size) generic_se_protected(ptr, size)
+
+static SLJIT_INLINE int generic_se_protected(void *ptr, sljit_uw size)
+{
+ if (SLJIT_LIKELY(!mprotect(ptr, size, PROT_EXEC)))
+ return mprotect(ptr, size, PROT_READ | PROT_WRITE);
+
+ return -1;
+}
+#endif /* NetBSD */
+
+#ifndef SLJIT_SE_LOCK
+#define SLJIT_SE_LOCK()
+#endif
+#ifndef SLJIT_SE_UNLOCK
+#define SLJIT_SE_UNLOCK()
+#endif
+#ifndef SLJIT_PROT_WX
+#define SLJIT_PROT_WX 0
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
+{
+#if !(defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED) \
+ && !defined(__NetBSD__)
+ static pthread_mutex_t se_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+ static int se_protected = !SLJIT_PROT_WX;
+ int prot = PROT_READ | PROT_WRITE | SLJIT_PROT_WX;
+ sljit_uw* ptr;
+
+ if (SLJIT_UNLIKELY(se_protected < 0))
+ return NULL;
+
+#ifdef PROT_MAX
+ prot |= PROT_MAX(PROT_READ | PROT_WRITE | PROT_EXEC);
+#endif
+
+ size += sizeof(sljit_uw);
+ ptr = (sljit_uw*)mmap(NULL, size, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
+
+ if (ptr == MAP_FAILED)
+ return NULL;
+
+ if (SLJIT_UNLIKELY(se_protected > 0)) {
+ SLJIT_SE_LOCK();
+ se_protected = check_se_protected(ptr, size);
+ SLJIT_SE_UNLOCK();
+ if (SLJIT_UNLIKELY(se_protected < 0)) {
+ munmap((void *)ptr, size);
+ return NULL;
+ }
+ }
+
+ *ptr++ = size;
+ return ptr;
+}
+
+#undef SLJIT_PROT_WX
+#undef SLJIT_SE_UNLOCK
+#undef SLJIT_SE_LOCK
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
+{
+ sljit_uw *start_ptr = ((sljit_uw*)ptr) - 1;
+ munmap((void*)start_ptr, *start_ptr);
+}
+
+static void sljit_update_wx_flags(void *from, void *to, sljit_s32 enable_exec)
+{
+ sljit_uw page_mask = (sljit_uw)get_page_alignment();
+ sljit_uw start = (sljit_uw)from;
+ sljit_uw end = (sljit_uw)to;
+ int prot = PROT_READ | (enable_exec ? PROT_EXEC : PROT_WRITE);
+
+ SLJIT_ASSERT(start < end);
+
+ start &= ~page_mask;
+ end = (end + page_mask) & ~page_mask;
+
+ mprotect((void*)start, end - start, prot);
+}
+
+#else /* windows */
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
+{
+ sljit_uw *ptr;
+
+ size += sizeof(sljit_uw);
+ ptr = (sljit_uw*)VirtualAlloc(NULL, size,
+ MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+
+ if (!ptr)
+ return NULL;
+
+ *ptr++ = size;
+
+ return ptr;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
+{
+ sljit_uw start = (sljit_uw)ptr - sizeof(sljit_uw);
+#if defined(SLJIT_DEBUG) && SLJIT_DEBUG
+ sljit_uw page_mask = (sljit_uw)get_page_alignment();
+
+ SLJIT_ASSERT(!(start & page_mask));
+#endif
+ VirtualFree((void*)start, 0, MEM_RELEASE);
+}
+
+static void sljit_update_wx_flags(void *from, void *to, sljit_s32 enable_exec)
+{
+ DWORD oldprot;
+ sljit_uw page_mask = (sljit_uw)get_page_alignment();
+ sljit_uw start = (sljit_uw)from;
+ sljit_uw end = (sljit_uw)to;
+ DWORD prot = enable_exec ? PAGE_EXECUTE : PAGE_READWRITE;
+
+ SLJIT_ASSERT(start < end);
+
+ start &= ~page_mask;
+ end = (end + page_mask) & ~page_mask;
+
+ VirtualProtect((void*)start, end - start, prot, &oldprot);
+}
+
+#endif /* !windows */
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
+{
+ /* This allocator does not keep unused memory for future allocations. */
+}
diff --git a/contrib/libs/pcre2/ya.make b/contrib/libs/pcre2/ya.make
new file mode 100644
index 00000000000..27d3962e4bd
--- /dev/null
+++ b/contrib/libs/pcre2/ya.make
@@ -0,0 +1,70 @@
+# Generated by devtools/yamaker from nixpkgs 22.05.
+
+LIBRARY()
+
+VERSION(10.42)
+
+ORIGINAL_SOURCE(https://github.com/PCRE2Project/pcre2/archive/pcre2-10.42.tar.gz)
+
+LICENSE(
+ BSD-2-Clause AND
+ BSD-3-Clause AND
+ FSFAP AND
+ GPL-1.0-or-later AND
+ LicenseRef-scancode-pcre AND
+ LicenseRef-scancode-unknown-license-reference AND
+ Public-Domain
+)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+ADDINCL(
+ contrib/libs/pcre2
+ contrib/libs/pcre2/src
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DHAVE_CONFIG_H
+ -DPCRE2_CODE_UNIT_WIDTH=8
+)
+
+SRCS(
+ src/pcre2_auto_possess.c
+ src/pcre2_chartables.c
+ src/pcre2_compile.c
+ src/pcre2_config.c
+ src/pcre2_context.c
+ src/pcre2_convert.c
+ src/pcre2_dfa_match.c
+ src/pcre2_error.c
+ src/pcre2_extuni.c
+ src/pcre2_find_bracket.c
+ src/pcre2_jit_compile.c
+ src/pcre2_maketables.c
+ src/pcre2_match.c
+ src/pcre2_match_data.c
+ src/pcre2_newline.c
+ src/pcre2_ord2utf.c
+ src/pcre2_pattern_info.c
+ src/pcre2_script_run.c
+ src/pcre2_serialize.c
+ src/pcre2_string_utils.c
+ src/pcre2_study.c
+ src/pcre2_substitute.c
+ src/pcre2_substring.c
+ src/pcre2_tables.c
+ src/pcre2_ucd.c
+ src/pcre2_valid_utf.c
+ src/pcre2_xclass.c
+)
+
+END()
+
+RECURSE(
+ pcre2-16
+ pcre2-32
+)